>From 88ca304ef749e66961e11ee1e258ea1e001e471f Mon Sep 17 00:00:00 2001
From: Christian Koenig <deathsimple@vodafone.de>
Date: Sat, 10 Apr 2010 03:13:16 +0200
Subject: [PATCH 2/2] drm/radeon/kms: HDMI irq support
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

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>
---
 drivers/gpu/drm/radeon/r600.c       |    6 ++++
 drivers/gpu/drm/radeon/r600_audio.c |   20 +++++++++----
 drivers/gpu/drm/radeon/r600_hdmi.c  |   56 ++++++++++++++++++++++++----------
 drivers/gpu/drm/radeon/radeon.h     |    2 +
 4 files changed, 61 insertions(+), 23 deletions(-)

diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c
index 8f3454e..a275728 100644
--- a/drivers/gpu/drm/radeon/r600.c
+++ b/drivers/gpu/drm/radeon/r600.c
@@ -2713,6 +2713,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
@@ -2852,6 +2854,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 */
diff --git a/drivers/gpu/drm/radeon/r600_audio.c b/drivers/gpu/drm/radeon/r600_audio.c
index 03bf8bc..487b56b 100644
--- a/drivers/gpu/drm/radeon/r600_audio.c
+++ b/drivers/gpu/drm/radeon/r600_audio.c
@@ -104,6 +104,15 @@ uint8_t r600_audio_category_code(struct radeon_device *rdev)
 }
 
 /*
+ * 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
  */
 static void r600_audio_update_hdmi(unsigned long param)
@@ -136,16 +145,15 @@ 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))
+		int irq_pending = r600_hdmi_irq_pending(encoder);
+		if (irq_pending || radeon_encoder->audio_polling_active) {
+			still_going |= radeon_encoder->audio_polling_active;
+			if (irq_pending || 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);
 }
 
 /*
diff --git a/drivers/gpu/drm/radeon/r600_hdmi.c b/drivers/gpu/drm/radeon/r600_hdmi.c
index 1960ac3..8ef377a 100644
--- a/drivers/gpu/drm/radeon/r600_hdmi.c
+++ b/drivers/gpu/drm/radeon/r600_hdmi.c
@@ -278,6 +278,21 @@ int r600_hdmi_buffer_status_changed(struct drm_encoder *encoder)
 }
 
 /*
+ * is there still an irq pending?
+ */
+int r600_hdmi_irq_pending(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);
+
+	if (!radeon_encoder->hdmi_offset)
+		return 0;
+
+	return (RREG32(radeon_encoder->hdmi_offset + R600_HDMI_STATUS) & 0x20000000) != 0;
+}
+
+/*
  * write the audio workaround status to the hardware
  */
 void r600_hdmi_audio_workaround(struct drm_encoder *encoder)
@@ -290,17 +305,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);
 	}
 }
 
@@ -342,9 +355,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);
 }
 
 /*
@@ -367,6 +377,9 @@ void r600_hdmi_update_audio_settings(struct drm_encoder *encoder)
 	if (!offset)
 		return;
 
+	/* first reset any pending irq */
+	WREG32_P(offset + R600_HDMI_CNTL, 0x20000000, ~0x20000000);
+
 	DRM_DEBUG("%s with %d channels, %d Hz sampling rate, %d bits per sample,\n",
 		 r600_hdmi_is_audio_buffer_filled(encoder) ? "playing" : "stopped",
 		channels, rate, bps);
@@ -413,9 +426,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)
@@ -513,7 +523,15 @@ void r600_hdmi_enable(struct drm_encoder *encoder)
 		}
 	}
 
-	r600_audio_enable_polling(encoder);
+	if (rdev->ih.enabled) {
+		/* if irq is available use it */
+		WREG32_P(radeon_encoder->hdmi_offset + R600_HDMI_CNTL, 0x10000000, ~0x10000000);
+		r600_audio_disable_polling(encoder);
+	} else {
+		/* if not fallback to polling */
+		WREG32_P(radeon_encoder->hdmi_offset + R600_HDMI_CNTL, 0x0, ~0x10000000);
+		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,11 +551,15 @@ void r600_hdmi_disable(struct drm_encoder *encoder)
 		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);
 
+	/* disable irq */
+	WREG32_P(radeon_encoder->hdmi_offset + R600_HDMI_CNTL, 0x0, ~0x10000000);
+
+	/* 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)) {
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h
index 5baf7fd..fca8e9e 100644
--- a/drivers/gpu/drm/radeon/radeon.h
+++ b/drivers/gpu/drm/radeon/radeon.h
@@ -1292,6 +1292,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);
@@ -1300,6 +1301,7 @@ extern void r600_hdmi_enable(struct drm_encoder *encoder);
 extern void r600_hdmi_disable(struct drm_encoder *encoder);
 extern void r600_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *mode);
 extern int r600_hdmi_buffer_status_changed(struct drm_encoder *encoder);
+extern int r600_hdmi_irq_pending(struct drm_encoder *encoder);
 extern void r600_hdmi_update_audio_settings(struct drm_encoder *encoder);
 
 /* evergreen */
-- 
1.7.0

