Skip to content

Commit

Permalink
drm/radeon/kms: HDMI irq support
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Christian Koenig authored and Dave Airlie committed Apr 23, 2010
1 parent 58bd086 commit f259493
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 56 deletions.
33 changes: 33 additions & 0 deletions drivers/gpu/drm/radeon/r600.c
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -2879,6 +2908,10 @@ int r600_irq_process(struct radeon_device *rdev)
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 */
Expand Down
21 changes: 13 additions & 8 deletions drivers/gpu/drm/radeon/r600_audio.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down Expand Up @@ -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);
}

/*
Expand Down
54 changes: 33 additions & 21 deletions drivers/gpu/drm/radeon/r600_hdmi.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
WREG32_P(offset+R600_HDMI_CNTL, 0x00000001, ~0x00001001);
if (!radeon_encoder->hdmi_audio_workaround ||
r600_hdmi_is_audio_buffer_filled(encoder)) {

} else if (radeon_encoder->hdmi_audio_workaround) {
/* enable audio workaround and start delivering of audio frames */
WREG32_P(offset+R600_HDMI_CNTL, 0x00001001, ~0x00001001);
/* disable audio workaround */
WREG32_P(offset+R600_HDMI_CNTL, 0x00000001, ~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);
}
}

Expand Down Expand Up @@ -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);
}

/*
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand Down
57 changes: 30 additions & 27 deletions drivers/gpu/drm/radeon/r600_reg.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions drivers/gpu/drm/radeon/radeon.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit f259493

Please sign in to comment.