Commit f2594933 authored by Christian Koenig's avatar Christian Koenig Committed by Dave Airlie

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: default avatarChristian König <deathsimple@vodafone.de>
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
parent 58bd0863
...@@ -2527,6 +2527,7 @@ int r600_irq_set(struct radeon_device *rdev) ...@@ -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 cp_int_cntl = CNTX_BUSY_INT_ENABLE | CNTX_EMPTY_INT_ENABLE;
u32 mode_int = 0; u32 mode_int = 0;
u32 hpd1, hpd2, hpd3, hpd4 = 0, hpd5 = 0, hpd6 = 0; u32 hpd1, hpd2, hpd3, hpd4 = 0, hpd5 = 0, hpd6 = 0;
u32 hdmi1, hdmi2;
if (!rdev->irq.installed) { if (!rdev->irq.installed) {
WARN(1, "Can't enable IRQ/MSI because no handler is installed.\n"); 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) ...@@ -2540,7 +2541,9 @@ int r600_irq_set(struct radeon_device *rdev)
return 0; return 0;
} }
hdmi1 = RREG32(R600_HDMI_BLOCK1 + R600_HDMI_CNTL) & ~R600_HDMI_INT_EN;
if (ASIC_IS_DCE3(rdev)) { 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; hpd1 = RREG32(DC_HPD1_INT_CONTROL) & ~DC_HPDx_INT_EN;
hpd2 = RREG32(DC_HPD2_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; hpd3 = RREG32(DC_HPD3_INT_CONTROL) & ~DC_HPDx_INT_EN;
...@@ -2550,6 +2553,7 @@ int r600_irq_set(struct radeon_device *rdev) ...@@ -2550,6 +2553,7 @@ int r600_irq_set(struct radeon_device *rdev)
hpd6 = RREG32(DC_HPD6_INT_CONTROL) & ~DC_HPDx_INT_EN; hpd6 = RREG32(DC_HPD6_INT_CONTROL) & ~DC_HPDx_INT_EN;
} }
} else { } 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; hpd1 = RREG32(DC_HOT_PLUG_DETECT1_INT_CONTROL) & ~DC_HPDx_INT_EN;
hpd2 = RREG32(DC_HOT_PLUG_DETECT2_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; hpd3 = RREG32(DC_HOT_PLUG_DETECT3_INT_CONTROL) & ~DC_HPDx_INT_EN;
...@@ -2591,10 +2595,20 @@ int r600_irq_set(struct radeon_device *rdev) ...@@ -2591,10 +2595,20 @@ int r600_irq_set(struct radeon_device *rdev)
DRM_DEBUG("r600_irq_set: hpd 6\n"); DRM_DEBUG("r600_irq_set: hpd 6\n");
hpd6 |= DC_HPDx_INT_EN; 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(CP_INT_CNTL, cp_int_cntl);
WREG32(DxMODE_INT_MASK, mode_int); WREG32(DxMODE_INT_MASK, mode_int);
WREG32(R600_HDMI_BLOCK1 + R600_HDMI_CNTL, hdmi1);
if (ASIC_IS_DCE3(rdev)) { if (ASIC_IS_DCE3(rdev)) {
WREG32(R600_HDMI_BLOCK3 + R600_HDMI_CNTL, hdmi2);
WREG32(DC_HPD1_INT_CONTROL, hpd1); WREG32(DC_HPD1_INT_CONTROL, hpd1);
WREG32(DC_HPD2_INT_CONTROL, hpd2); WREG32(DC_HPD2_INT_CONTROL, hpd2);
WREG32(DC_HPD3_INT_CONTROL, hpd3); WREG32(DC_HPD3_INT_CONTROL, hpd3);
...@@ -2604,6 +2618,7 @@ int r600_irq_set(struct radeon_device *rdev) ...@@ -2604,6 +2618,7 @@ int r600_irq_set(struct radeon_device *rdev)
WREG32(DC_HPD6_INT_CONTROL, hpd6); WREG32(DC_HPD6_INT_CONTROL, hpd6);
} }
} else { } else {
WREG32(R600_HDMI_BLOCK2 + R600_HDMI_CNTL, hdmi2);
WREG32(DC_HOT_PLUG_DETECT1_INT_CONTROL, hpd1); WREG32(DC_HOT_PLUG_DETECT1_INT_CONTROL, hpd1);
WREG32(DC_HOT_PLUG_DETECT2_INT_CONTROL, hpd2); WREG32(DC_HOT_PLUG_DETECT2_INT_CONTROL, hpd2);
WREG32(DC_HOT_PLUG_DETECT3_INT_CONTROL, hpd3); WREG32(DC_HOT_PLUG_DETECT3_INT_CONTROL, hpd3);
...@@ -2687,6 +2702,18 @@ static inline void r600_irq_ack(struct radeon_device *rdev, ...@@ -2687,6 +2702,18 @@ static inline void r600_irq_ack(struct radeon_device *rdev,
WREG32(DC_HPD6_INT_CONTROL, tmp); 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) void r600_irq_disable(struct radeon_device *rdev)
...@@ -2740,6 +2767,8 @@ static inline u32 r600_get_ih_wptr(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 1 FP Hot plug detection B
* 19 2 DAC A auto-detection * 19 2 DAC A auto-detection
* 19 3 DAC B auto-detection * 19 3 DAC B auto-detection
* 21 4 HDMI block A
* 21 5 HDMI block B
* 176 - CP_INT RB * 176 - CP_INT RB
* 177 - CP_INT IB1 * 177 - CP_INT IB1
* 178 - CP_INT IB2 * 178 - CP_INT IB2
...@@ -2879,6 +2908,10 @@ int r600_irq_process(struct radeon_device *rdev) ...@@ -2879,6 +2908,10 @@ int r600_irq_process(struct radeon_device *rdev)
break; break;
} }
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 176: /* CP_INT in ring buffer */
case 177: /* CP_INT in IB1 */ case 177: /* CP_INT in IB1 */
case 178: /* CP_INT in IB2 */ case 178: /* CP_INT in IB2 */
......
...@@ -103,6 +103,15 @@ uint8_t r600_audio_category_code(struct radeon_device *rdev) ...@@ -103,6 +103,15 @@ uint8_t r600_audio_category_code(struct radeon_device *rdev)
return (RREG32(R600_AUDIO_STATUS_BITS) >> 8) & 0xff; 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 * update all hdmi interfaces with current audio parameters
*/ */
...@@ -136,16 +145,12 @@ static void r600_audio_update_hdmi(unsigned long param) ...@@ -136,16 +145,12 @@ static void r600_audio_update_hdmi(unsigned long param)
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
if (radeon_encoder->audio_polling_active) { still_going |= radeon_encoder->audio_polling_active;
still_going = 1;
if (changes || r600_hdmi_buffer_status_changed(encoder)) if (changes || r600_hdmi_buffer_status_changed(encoder))
r600_hdmi_update_audio_settings(encoder); r600_hdmi_update_audio_settings(encoder);
} }
}
if(still_going) if(still_going) r600_audio_schedule_polling(rdev);
mod_timer(&rdev->audio_timer,
jiffies + msecs_to_jiffies(AUDIO_TIMER_INTERVALL));
} }
/* /*
......
...@@ -290,17 +290,15 @@ void r600_hdmi_audio_workaround(struct drm_encoder *encoder) ...@@ -290,17 +290,15 @@ void r600_hdmi_audio_workaround(struct drm_encoder *encoder)
if (!offset) if (!offset)
return; return;
if (r600_hdmi_is_audio_buffer_filled(encoder)) { if (!radeon_encoder->hdmi_audio_workaround ||
/* disable audio workaround and start delivering of audio frames */ r600_hdmi_is_audio_buffer_filled(encoder)) {
WREG32_P(offset+R600_HDMI_CNTL, 0x00000001, ~0x00001001);
} else if (radeon_encoder->hdmi_audio_workaround) { /* disable audio workaround */
/* enable audio workaround and start delivering of audio frames */ WREG32_P(offset+R600_HDMI_CNTL, 0x00000001, ~0x00001001);
WREG32_P(offset+R600_HDMI_CNTL, 0x00001001, ~0x00001001);
} else { } else {
/* disable audio workaround and stop delivering of audio frames */ /* enable audio workaround */
WREG32_P(offset+R600_HDMI_CNTL, 0x00000000, ~0x00001001); 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 ...@@ -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 ? */ /* audio packets per line, does anyone know how to calc this ? */
WREG32_P(offset+R600_HDMI_CNTL, 0x00040000, ~0x001F0000); 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) ...@@ -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_audioinfoframe(encoder, channels-1, 0, 0, 0, 0, 0, 0, 0);
r600_hdmi_audio_workaround(encoder); 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) static int r600_hdmi_find_free_block(struct drm_device *dev)
...@@ -487,6 +479,7 @@ void r600_hdmi_enable(struct drm_encoder *encoder) ...@@ -487,6 +479,7 @@ void r600_hdmi_enable(struct drm_encoder *encoder)
struct drm_device *dev = encoder->dev; struct drm_device *dev = encoder->dev;
struct radeon_device *rdev = dev->dev_private; struct radeon_device *rdev = dev->dev_private;
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
uint32_t offset;
if (ASIC_IS_DCE4(rdev)) if (ASIC_IS_DCE4(rdev))
return; return;
...@@ -500,10 +493,10 @@ void r600_hdmi_enable(struct drm_encoder *encoder) ...@@ -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)) { if (ASIC_IS_DCE32(rdev) && !ASIC_IS_DCE4(rdev)) {
WREG32_P(radeon_encoder->hdmi_config_offset + 0x4, 0x1, ~0x1); WREG32_P(radeon_encoder->hdmi_config_offset + 0x4, 0x1, ~0x1);
} else if (rdev->family >= CHIP_R600 && !ASIC_IS_DCE3(rdev)) { } else if (rdev->family >= CHIP_R600 && !ASIC_IS_DCE3(rdev)) {
int offset = radeon_encoder->hdmi_offset;
switch (radeon_encoder->encoder_id) { switch (radeon_encoder->encoder_id) {
case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1:
WREG32_P(AVIVO_TMDSA_CNTL, 0x4, ~0x4); WREG32_P(AVIVO_TMDSA_CNTL, 0x4, ~0x4);
...@@ -519,7 +512,20 @@ void r600_hdmi_enable(struct drm_encoder *encoder) ...@@ -519,7 +512,20 @@ void r600_hdmi_enable(struct drm_encoder *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); r600_audio_enable_polling(encoder);
}
DRM_DEBUG("Enabling HDMI interface @ 0x%04X for encoder 0x%x\n", DRM_DEBUG("Enabling HDMI interface @ 0x%04X for encoder 0x%x\n",
radeon_encoder->hdmi_offset, radeon_encoder->encoder_id); radeon_encoder->hdmi_offset, radeon_encoder->encoder_id);
...@@ -533,24 +539,30 @@ void r600_hdmi_disable(struct drm_encoder *encoder) ...@@ -533,24 +539,30 @@ void r600_hdmi_disable(struct drm_encoder *encoder)
struct drm_device *dev = encoder->dev; struct drm_device *dev = encoder->dev;
struct radeon_device *rdev = dev->dev_private; struct radeon_device *rdev = dev->dev_private;
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
uint8_t offset;
if (ASIC_IS_DCE4(rdev)) if (ASIC_IS_DCE4(rdev))
return; return;
if (!radeon_encoder->hdmi_offset) { offset = radeon_encoder->hdmi_offset;
if (!offset) {
dev_err(rdev->dev, "Disabling not enabled HDMI\n"); dev_err(rdev->dev, "Disabling not enabled HDMI\n");
return; return;
} }
r600_audio_disable_polling(encoder);
DRM_DEBUG("Disabling HDMI interface @ 0x%04X for encoder 0x%x\n", 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)) { if (ASIC_IS_DCE32(rdev) && !ASIC_IS_DCE4(rdev)) {
WREG32_P(radeon_encoder->hdmi_config_offset + 0x4, 0, ~0x1); WREG32_P(radeon_encoder->hdmi_config_offset + 0x4, 0, ~0x1);
} else if (rdev->family >= CHIP_R600 && !ASIC_IS_DCE3(rdev)) { } else if (rdev->family >= CHIP_R600 && !ASIC_IS_DCE3(rdev)) {
int offset = radeon_encoder->hdmi_offset;
switch (radeon_encoder->encoder_id) { switch (radeon_encoder->encoder_id) {
case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1:
WREG32_P(AVIVO_TMDSA_CNTL, 0, ~0x4); WREG32_P(AVIVO_TMDSA_CNTL, 0, ~0x4);
......
...@@ -159,7 +159,10 @@ ...@@ -159,7 +159,10 @@
/* HDMI registers */ /* HDMI registers */
#define R600_HDMI_ENABLE 0x00 #define R600_HDMI_ENABLE 0x00
#define R600_HDMI_STATUS 0x04 #define R600_HDMI_STATUS 0x04
# define R600_HDMI_INT_PENDING (1 << 29)
#define R600_HDMI_CNTL 0x08 #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_UNKNOWN_0 0x0C
#define R600_HDMI_AUDIOCNTL 0x10 #define R600_HDMI_AUDIOCNTL 0x10
#define R600_HDMI_VIDEOCNTL 0x14 #define R600_HDMI_VIDEOCNTL 0x14
......
...@@ -376,6 +376,8 @@ struct radeon_irq { ...@@ -376,6 +376,8 @@ struct radeon_irq {
wait_queue_head_t vblank_queue; wait_queue_head_t vblank_queue;
/* FIXME: use defines for max hpd/dacs */ /* FIXME: use defines for max hpd/dacs */
bool hpd[6]; bool hpd[6];
/* FIXME: use defines for max HDMI blocks */
bool hdmi[2];
spinlock_t sw_lock; spinlock_t sw_lock;
int sw_refcount; int sw_refcount;
}; };
...@@ -1332,6 +1334,7 @@ extern int r600_audio_bits_per_sample(struct radeon_device *rdev); ...@@ -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 int r600_audio_rate(struct radeon_device *rdev);
extern uint8_t r600_audio_status_bits(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 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_enable_polling(struct drm_encoder *encoder);
extern void r600_audio_disable_polling(struct drm_encoder *encoder); extern void r600_audio_disable_polling(struct drm_encoder *encoder);
extern void r600_audio_fini(struct radeon_device *rdev); extern void r600_audio_fini(struct radeon_device *rdev);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment