drm/radeon/kms: HDMI irq support
authorChristian Koenig <deathsimple@vodafone.de>
Sat, 10 Apr 2010 01:13:16 +0000 (03:13 +0200)
committerDave Airlie <airlied@redhat.com>
Fri, 23 Apr 2010 04:12:17 +0000 (14:12 +1000)
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>
drivers/gpu/drm/radeon/r600.c
drivers/gpu/drm/radeon/r600_audio.c
drivers/gpu/drm/radeon/r600_hdmi.c
drivers/gpu/drm/radeon/r600_reg.h
drivers/gpu/drm/radeon/radeon.h

index c325cb1..2ec423c 100644 (file)
@@ -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 */
index ed8d398..2b26553 100644 (file)
@@ -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);
 }
 
 /*
index d014472..40b1aca 100644 (file)
@@ -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);
        }
 }
 
@@ -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);
index 7b1d223..d84612a 100644 (file)
 #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
index 42217af..ab29d97 100644 (file)
@@ -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);