forked from Minki/linux
drm/radeon/kms: HDMI irq support
Implements irq support for HDMI audio output. Now the polling timer is only enabled if irq support isn't available. Signed-off-by: Christian König <deathsimple@vodafone.de> Signed-off-by: Dave Airlie <airlied@redhat.com>
This commit is contained in:
parent
58bd086313
commit
f2594933df
@ -2527,6 +2527,7 @@ int r600_irq_set(struct radeon_device *rdev)
|
||||
u32 cp_int_cntl = CNTX_BUSY_INT_ENABLE | CNTX_EMPTY_INT_ENABLE;
|
||||
u32 mode_int = 0;
|
||||
u32 hpd1, hpd2, hpd3, hpd4 = 0, hpd5 = 0, hpd6 = 0;
|
||||
u32 hdmi1, hdmi2;
|
||||
|
||||
if (!rdev->irq.installed) {
|
||||
WARN(1, "Can't enable IRQ/MSI because no handler is installed.\n");
|
||||
@ -2540,7 +2541,9 @@ int r600_irq_set(struct radeon_device *rdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
hdmi1 = RREG32(R600_HDMI_BLOCK1 + R600_HDMI_CNTL) & ~R600_HDMI_INT_EN;
|
||||
if (ASIC_IS_DCE3(rdev)) {
|
||||
hdmi2 = RREG32(R600_HDMI_BLOCK3 + R600_HDMI_CNTL) & ~R600_HDMI_INT_EN;
|
||||
hpd1 = RREG32(DC_HPD1_INT_CONTROL) & ~DC_HPDx_INT_EN;
|
||||
hpd2 = RREG32(DC_HPD2_INT_CONTROL) & ~DC_HPDx_INT_EN;
|
||||
hpd3 = RREG32(DC_HPD3_INT_CONTROL) & ~DC_HPDx_INT_EN;
|
||||
@ -2550,6 +2553,7 @@ int r600_irq_set(struct radeon_device *rdev)
|
||||
hpd6 = RREG32(DC_HPD6_INT_CONTROL) & ~DC_HPDx_INT_EN;
|
||||
}
|
||||
} else {
|
||||
hdmi2 = RREG32(R600_HDMI_BLOCK2 + R600_HDMI_CNTL) & ~R600_HDMI_INT_EN;
|
||||
hpd1 = RREG32(DC_HOT_PLUG_DETECT1_INT_CONTROL) & ~DC_HPDx_INT_EN;
|
||||
hpd2 = RREG32(DC_HOT_PLUG_DETECT2_INT_CONTROL) & ~DC_HPDx_INT_EN;
|
||||
hpd3 = RREG32(DC_HOT_PLUG_DETECT3_INT_CONTROL) & ~DC_HPDx_INT_EN;
|
||||
@ -2591,10 +2595,20 @@ int r600_irq_set(struct radeon_device *rdev)
|
||||
DRM_DEBUG("r600_irq_set: hpd 6\n");
|
||||
hpd6 |= DC_HPDx_INT_EN;
|
||||
}
|
||||
if (rdev->irq.hdmi[0]) {
|
||||
DRM_DEBUG("r600_irq_set: hdmi 1\n");
|
||||
hdmi1 |= R600_HDMI_INT_EN;
|
||||
}
|
||||
if (rdev->irq.hdmi[1]) {
|
||||
DRM_DEBUG("r600_irq_set: hdmi 2\n");
|
||||
hdmi2 |= R600_HDMI_INT_EN;
|
||||
}
|
||||
|
||||
WREG32(CP_INT_CNTL, cp_int_cntl);
|
||||
WREG32(DxMODE_INT_MASK, mode_int);
|
||||
WREG32(R600_HDMI_BLOCK1 + R600_HDMI_CNTL, hdmi1);
|
||||
if (ASIC_IS_DCE3(rdev)) {
|
||||
WREG32(R600_HDMI_BLOCK3 + R600_HDMI_CNTL, hdmi2);
|
||||
WREG32(DC_HPD1_INT_CONTROL, hpd1);
|
||||
WREG32(DC_HPD2_INT_CONTROL, hpd2);
|
||||
WREG32(DC_HPD3_INT_CONTROL, hpd3);
|
||||
@ -2604,6 +2618,7 @@ int r600_irq_set(struct radeon_device *rdev)
|
||||
WREG32(DC_HPD6_INT_CONTROL, hpd6);
|
||||
}
|
||||
} else {
|
||||
WREG32(R600_HDMI_BLOCK2 + R600_HDMI_CNTL, hdmi2);
|
||||
WREG32(DC_HOT_PLUG_DETECT1_INT_CONTROL, hpd1);
|
||||
WREG32(DC_HOT_PLUG_DETECT2_INT_CONTROL, hpd2);
|
||||
WREG32(DC_HOT_PLUG_DETECT3_INT_CONTROL, hpd3);
|
||||
@ -2687,6 +2702,18 @@ static inline void r600_irq_ack(struct radeon_device *rdev,
|
||||
WREG32(DC_HPD6_INT_CONTROL, tmp);
|
||||
}
|
||||
}
|
||||
if (RREG32(R600_HDMI_BLOCK1 + R600_HDMI_STATUS) & R600_HDMI_INT_PENDING) {
|
||||
WREG32_P(R600_HDMI_BLOCK1 + R600_HDMI_CNTL, R600_HDMI_INT_ACK, ~R600_HDMI_INT_ACK);
|
||||
}
|
||||
if (ASIC_IS_DCE3(rdev)) {
|
||||
if (RREG32(R600_HDMI_BLOCK3 + R600_HDMI_STATUS) & R600_HDMI_INT_PENDING) {
|
||||
WREG32_P(R600_HDMI_BLOCK3 + R600_HDMI_CNTL, R600_HDMI_INT_ACK, ~R600_HDMI_INT_ACK);
|
||||
}
|
||||
} else {
|
||||
if (RREG32(R600_HDMI_BLOCK2 + R600_HDMI_STATUS) & R600_HDMI_INT_PENDING) {
|
||||
WREG32_P(R600_HDMI_BLOCK2 + R600_HDMI_CNTL, R600_HDMI_INT_ACK, ~R600_HDMI_INT_ACK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void r600_irq_disable(struct radeon_device *rdev)
|
||||
@ -2740,6 +2767,8 @@ static inline u32 r600_get_ih_wptr(struct radeon_device *rdev)
|
||||
* 19 1 FP Hot plug detection B
|
||||
* 19 2 DAC A auto-detection
|
||||
* 19 3 DAC B auto-detection
|
||||
* 21 4 HDMI block A
|
||||
* 21 5 HDMI block B
|
||||
* 176 - CP_INT RB
|
||||
* 177 - CP_INT IB1
|
||||
* 178 - CP_INT IB2
|
||||
@ -2879,6 +2908,10 @@ restart_ih:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 21: /* HDMI */
|
||||
DRM_DEBUG("IH: HDMI: 0x%x\n", src_data);
|
||||
r600_audio_schedule_polling(rdev);
|
||||
break;
|
||||
case 176: /* CP_INT in ring buffer */
|
||||
case 177: /* CP_INT in IB1 */
|
||||
case 178: /* CP_INT in IB2 */
|
||||
|
@ -103,6 +103,15 @@ uint8_t r600_audio_category_code(struct radeon_device *rdev)
|
||||
return (RREG32(R600_AUDIO_STATUS_BITS) >> 8) & 0xff;
|
||||
}
|
||||
|
||||
/*
|
||||
* schedule next audio update event
|
||||
*/
|
||||
void r600_audio_schedule_polling(struct radeon_device *rdev)
|
||||
{
|
||||
mod_timer(&rdev->audio_timer,
|
||||
jiffies + msecs_to_jiffies(AUDIO_TIMER_INTERVALL));
|
||||
}
|
||||
|
||||
/*
|
||||
* update all hdmi interfaces with current audio parameters
|
||||
*/
|
||||
@ -136,16 +145,12 @@ static void r600_audio_update_hdmi(unsigned long param)
|
||||
|
||||
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
|
||||
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
|
||||
if (radeon_encoder->audio_polling_active) {
|
||||
still_going = 1;
|
||||
if (changes || r600_hdmi_buffer_status_changed(encoder))
|
||||
r600_hdmi_update_audio_settings(encoder);
|
||||
}
|
||||
still_going |= radeon_encoder->audio_polling_active;
|
||||
if (changes || r600_hdmi_buffer_status_changed(encoder))
|
||||
r600_hdmi_update_audio_settings(encoder);
|
||||
}
|
||||
|
||||
if(still_going)
|
||||
mod_timer(&rdev->audio_timer,
|
||||
jiffies + msecs_to_jiffies(AUDIO_TIMER_INTERVALL));
|
||||
if(still_going) r600_audio_schedule_polling(rdev);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -290,17 +290,15 @@ void r600_hdmi_audio_workaround(struct drm_encoder *encoder)
|
||||
if (!offset)
|
||||
return;
|
||||
|
||||
if (r600_hdmi_is_audio_buffer_filled(encoder)) {
|
||||
/* disable audio workaround and start delivering of audio frames */
|
||||
if (!radeon_encoder->hdmi_audio_workaround ||
|
||||
r600_hdmi_is_audio_buffer_filled(encoder)) {
|
||||
|
||||
/* disable audio workaround */
|
||||
WREG32_P(offset+R600_HDMI_CNTL, 0x00000001, ~0x00001001);
|
||||
|
||||
} else if (radeon_encoder->hdmi_audio_workaround) {
|
||||
/* enable audio workaround and start delivering of audio frames */
|
||||
WREG32_P(offset+R600_HDMI_CNTL, 0x00001001, ~0x00001001);
|
||||
|
||||
} else {
|
||||
/* disable audio workaround and stop delivering of audio frames */
|
||||
WREG32_P(offset+R600_HDMI_CNTL, 0x00000000, ~0x00001001);
|
||||
/* enable audio workaround */
|
||||
WREG32_P(offset+R600_HDMI_CNTL, 0x00001001, ~0x00001001);
|
||||
}
|
||||
}
|
||||
|
||||
@ -345,9 +343,6 @@ void r600_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *mod
|
||||
|
||||
/* audio packets per line, does anyone know how to calc this ? */
|
||||
WREG32_P(offset+R600_HDMI_CNTL, 0x00040000, ~0x001F0000);
|
||||
|
||||
/* update? reset? don't realy know */
|
||||
WREG32_P(offset+R600_HDMI_CNTL, 0x14000000, ~0x14000000);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -416,9 +411,6 @@ void r600_hdmi_update_audio_settings(struct drm_encoder *encoder)
|
||||
r600_hdmi_audioinfoframe(encoder, channels-1, 0, 0, 0, 0, 0, 0, 0);
|
||||
|
||||
r600_hdmi_audio_workaround(encoder);
|
||||
|
||||
/* update? reset? don't realy know */
|
||||
WREG32_P(offset+R600_HDMI_CNTL, 0x04000000, ~0x04000000);
|
||||
}
|
||||
|
||||
static int r600_hdmi_find_free_block(struct drm_device *dev)
|
||||
@ -487,6 +479,7 @@ void r600_hdmi_enable(struct drm_encoder *encoder)
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct radeon_device *rdev = dev->dev_private;
|
||||
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
|
||||
uint32_t offset;
|
||||
|
||||
if (ASIC_IS_DCE4(rdev))
|
||||
return;
|
||||
@ -500,10 +493,10 @@ void r600_hdmi_enable(struct drm_encoder *encoder)
|
||||
}
|
||||
}
|
||||
|
||||
offset = radeon_encoder->hdmi_offset;
|
||||
if (ASIC_IS_DCE32(rdev) && !ASIC_IS_DCE4(rdev)) {
|
||||
WREG32_P(radeon_encoder->hdmi_config_offset + 0x4, 0x1, ~0x1);
|
||||
} else if (rdev->family >= CHIP_R600 && !ASIC_IS_DCE3(rdev)) {
|
||||
int offset = radeon_encoder->hdmi_offset;
|
||||
switch (radeon_encoder->encoder_id) {
|
||||
case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1:
|
||||
WREG32_P(AVIVO_TMDSA_CNTL, 0x4, ~0x4);
|
||||
@ -519,7 +512,20 @@ void r600_hdmi_enable(struct drm_encoder *encoder)
|
||||
}
|
||||
}
|
||||
|
||||
r600_audio_enable_polling(encoder);
|
||||
if (rdev->irq.installed
|
||||
&& rdev->family != CHIP_RS600
|
||||
&& rdev->family != CHIP_RS690
|
||||
&& rdev->family != CHIP_RS740) {
|
||||
|
||||
/* if irq is available use it */
|
||||
rdev->irq.hdmi[offset == R600_HDMI_BLOCK1 ? 0 : 1] = true;
|
||||
radeon_irq_set(rdev);
|
||||
|
||||
r600_audio_disable_polling(encoder);
|
||||
} else {
|
||||
/* if not fallback to polling */
|
||||
r600_audio_enable_polling(encoder);
|
||||
}
|
||||
|
||||
DRM_DEBUG("Enabling HDMI interface @ 0x%04X for encoder 0x%x\n",
|
||||
radeon_encoder->hdmi_offset, radeon_encoder->encoder_id);
|
||||
@ -533,24 +539,30 @@ void r600_hdmi_disable(struct drm_encoder *encoder)
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct radeon_device *rdev = dev->dev_private;
|
||||
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
|
||||
uint8_t offset;
|
||||
|
||||
if (ASIC_IS_DCE4(rdev))
|
||||
return;
|
||||
|
||||
if (!radeon_encoder->hdmi_offset) {
|
||||
offset = radeon_encoder->hdmi_offset;
|
||||
if (!offset) {
|
||||
dev_err(rdev->dev, "Disabling not enabled HDMI\n");
|
||||
return;
|
||||
}
|
||||
|
||||
r600_audio_disable_polling(encoder);
|
||||
|
||||
DRM_DEBUG("Disabling HDMI interface @ 0x%04X for encoder 0x%x\n",
|
||||
radeon_encoder->hdmi_offset, radeon_encoder->encoder_id);
|
||||
offset, radeon_encoder->encoder_id);
|
||||
|
||||
/* disable irq */
|
||||
rdev->irq.hdmi[offset == R600_HDMI_BLOCK1 ? 0 : 1] = false;
|
||||
radeon_irq_set(rdev);
|
||||
|
||||
/* disable polling */
|
||||
r600_audio_disable_polling(encoder);
|
||||
|
||||
if (ASIC_IS_DCE32(rdev) && !ASIC_IS_DCE4(rdev)) {
|
||||
WREG32_P(radeon_encoder->hdmi_config_offset + 0x4, 0, ~0x1);
|
||||
} else if (rdev->family >= CHIP_R600 && !ASIC_IS_DCE3(rdev)) {
|
||||
int offset = radeon_encoder->hdmi_offset;
|
||||
switch (radeon_encoder->encoder_id) {
|
||||
case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1:
|
||||
WREG32_P(AVIVO_TMDSA_CNTL, 0, ~0x4);
|
||||
|
@ -157,33 +157,36 @@
|
||||
#define R600_HDMI_BLOCK3 0x7800
|
||||
|
||||
/* HDMI registers */
|
||||
#define R600_HDMI_ENABLE 0x00
|
||||
#define R600_HDMI_STATUS 0x04
|
||||
#define R600_HDMI_CNTL 0x08
|
||||
#define R600_HDMI_UNKNOWN_0 0x0C
|
||||
#define R600_HDMI_AUDIOCNTL 0x10
|
||||
#define R600_HDMI_VIDEOCNTL 0x14
|
||||
#define R600_HDMI_VERSION 0x18
|
||||
#define R600_HDMI_UNKNOWN_1 0x28
|
||||
#define R600_HDMI_VIDEOINFOFRAME_0 0x54
|
||||
#define R600_HDMI_VIDEOINFOFRAME_1 0x58
|
||||
#define R600_HDMI_VIDEOINFOFRAME_2 0x5c
|
||||
#define R600_HDMI_VIDEOINFOFRAME_3 0x60
|
||||
#define R600_HDMI_32kHz_CTS 0xac
|
||||
#define R600_HDMI_32kHz_N 0xb0
|
||||
#define R600_HDMI_44_1kHz_CTS 0xb4
|
||||
#define R600_HDMI_44_1kHz_N 0xb8
|
||||
#define R600_HDMI_48kHz_CTS 0xbc
|
||||
#define R600_HDMI_48kHz_N 0xc0
|
||||
#define R600_HDMI_AUDIOINFOFRAME_0 0xcc
|
||||
#define R600_HDMI_AUDIOINFOFRAME_1 0xd0
|
||||
#define R600_HDMI_IEC60958_1 0xd4
|
||||
#define R600_HDMI_IEC60958_2 0xd8
|
||||
#define R600_HDMI_UNKNOWN_2 0xdc
|
||||
#define R600_HDMI_AUDIO_DEBUG_0 0xe0
|
||||
#define R600_HDMI_AUDIO_DEBUG_1 0xe4
|
||||
#define R600_HDMI_AUDIO_DEBUG_2 0xe8
|
||||
#define R600_HDMI_AUDIO_DEBUG_3 0xec
|
||||
#define R600_HDMI_ENABLE 0x00
|
||||
#define R600_HDMI_STATUS 0x04
|
||||
# define R600_HDMI_INT_PENDING (1 << 29)
|
||||
#define R600_HDMI_CNTL 0x08
|
||||
# define R600_HDMI_INT_EN (1 << 28)
|
||||
# define R600_HDMI_INT_ACK (1 << 29)
|
||||
#define R600_HDMI_UNKNOWN_0 0x0C
|
||||
#define R600_HDMI_AUDIOCNTL 0x10
|
||||
#define R600_HDMI_VIDEOCNTL 0x14
|
||||
#define R600_HDMI_VERSION 0x18
|
||||
#define R600_HDMI_UNKNOWN_1 0x28
|
||||
#define R600_HDMI_VIDEOINFOFRAME_0 0x54
|
||||
#define R600_HDMI_VIDEOINFOFRAME_1 0x58
|
||||
#define R600_HDMI_VIDEOINFOFRAME_2 0x5c
|
||||
#define R600_HDMI_VIDEOINFOFRAME_3 0x60
|
||||
#define R600_HDMI_32kHz_CTS 0xac
|
||||
#define R600_HDMI_32kHz_N 0xb0
|
||||
#define R600_HDMI_44_1kHz_CTS 0xb4
|
||||
#define R600_HDMI_44_1kHz_N 0xb8
|
||||
#define R600_HDMI_48kHz_CTS 0xbc
|
||||
#define R600_HDMI_48kHz_N 0xc0
|
||||
#define R600_HDMI_AUDIOINFOFRAME_0 0xcc
|
||||
#define R600_HDMI_AUDIOINFOFRAME_1 0xd0
|
||||
#define R600_HDMI_IEC60958_1 0xd4
|
||||
#define R600_HDMI_IEC60958_2 0xd8
|
||||
#define R600_HDMI_UNKNOWN_2 0xdc
|
||||
#define R600_HDMI_AUDIO_DEBUG_0 0xe0
|
||||
#define R600_HDMI_AUDIO_DEBUG_1 0xe4
|
||||
#define R600_HDMI_AUDIO_DEBUG_2 0xe8
|
||||
#define R600_HDMI_AUDIO_DEBUG_3 0xec
|
||||
|
||||
/* HDMI additional config base register addresses */
|
||||
#define R600_HDMI_CONFIG1 0x7600
|
||||
|
@ -376,6 +376,8 @@ struct radeon_irq {
|
||||
wait_queue_head_t vblank_queue;
|
||||
/* FIXME: use defines for max hpd/dacs */
|
||||
bool hpd[6];
|
||||
/* FIXME: use defines for max HDMI blocks */
|
||||
bool hdmi[2];
|
||||
spinlock_t sw_lock;
|
||||
int sw_refcount;
|
||||
};
|
||||
@ -1332,6 +1334,7 @@ extern int r600_audio_bits_per_sample(struct radeon_device *rdev);
|
||||
extern int r600_audio_rate(struct radeon_device *rdev);
|
||||
extern uint8_t r600_audio_status_bits(struct radeon_device *rdev);
|
||||
extern uint8_t r600_audio_category_code(struct radeon_device *rdev);
|
||||
extern void r600_audio_schedule_polling(struct radeon_device *rdev);
|
||||
extern void r600_audio_enable_polling(struct drm_encoder *encoder);
|
||||
extern void r600_audio_disable_polling(struct drm_encoder *encoder);
|
||||
extern void r600_audio_fini(struct radeon_device *rdev);
|
||||
|
Loading…
Reference in New Issue
Block a user