All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/12] dw-hdmi development
@ 2015-08-08 16:02 ` Russell King - ARM Linux
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-08-08 16:02 UTC (permalink / raw)
  To: linux-rockchip, alsa-devel, dri-devel, linux-kernel, linux-arm-kernel
  Cc: Andy Yan, David Airlie, Fabio Estevam, Jon Nettleton,
	Philipp Zabel, Sascha Hauer, Yakir Yang

This sub-series is a mixture of development:

* Removing the incorrect pixel repetition configuration code
* Preventing pixel-doubled modes from being used
* Adding interlaced video support
* Implementing the sink_is_hdmi/sink_has_audio flags I suggested a few
  months ago
* Only enabling audio support if the sink indicates it has audio
* Avoiding double-enabling the HDMI interface
* Fixing the mis-leading name of "dw_hdmi_phy_enable_power"
* Adding connector mode forcing (important if your monitor bounces
  RXSENSE and HPD signals while in low-power mode.)
* Improving the HDMI enable/disabling on sink status

For review (and testing if people feel like it).  Acks/tested-bys etc
welcome.  It applies on top of my drm-dwhdmi-devel branch, which is
waiting for David Airlie to pull (see pull request on dri-devel, 15th
July.)

 drivers/gpu/drm/bridge/dw_hdmi.c | 275 ++++++++++++++++++++++++++++++---------
 drivers/gpu/ipu-v3/ipu-dc.c      |  18 ++-
 drivers/gpu/ipu-v3/ipu-di.c      | 129 +++++++++---------
 3 files changed, 291 insertions(+), 131 deletions(-)

-- 
FTTC broadband for 0.8mile line: currently at 10.5Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 00/12] dw-hdmi development
@ 2015-08-08 16:02 ` Russell King - ARM Linux
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-08-08 16:02 UTC (permalink / raw)
  To: linux-rockchip, alsa-devel, dri-devel, linux-kernel, linux-arm-kernel
  Cc: Fabio Estevam, Yakir Yang, Andy Yan

This sub-series is a mixture of development:

* Removing the incorrect pixel repetition configuration code
* Preventing pixel-doubled modes from being used
* Adding interlaced video support
* Implementing the sink_is_hdmi/sink_has_audio flags I suggested a few
  months ago
* Only enabling audio support if the sink indicates it has audio
* Avoiding double-enabling the HDMI interface
* Fixing the mis-leading name of "dw_hdmi_phy_enable_power"
* Adding connector mode forcing (important if your monitor bounces
  RXSENSE and HPD signals while in low-power mode.)
* Improving the HDMI enable/disabling on sink status

For review (and testing if people feel like it).  Acks/tested-bys etc
welcome.  It applies on top of my drm-dwhdmi-devel branch, which is
waiting for David Airlie to pull (see pull request on dri-devel, 15th
July.)

 drivers/gpu/drm/bridge/dw_hdmi.c | 275 ++++++++++++++++++++++++++++++---------
 drivers/gpu/ipu-v3/ipu-dc.c      |  18 ++-
 drivers/gpu/ipu-v3/ipu-di.c      | 129 +++++++++---------
 3 files changed, 291 insertions(+), 131 deletions(-)

-- 
FTTC broadband for 0.8mile line: currently at 10.5Mbps down 400kbps up
according to speedtest.net.
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 00/12] dw-hdmi development
@ 2015-08-08 16:02 ` Russell King - ARM Linux
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-08-08 16:02 UTC (permalink / raw)
  To: linux-arm-kernel

This sub-series is a mixture of development:

* Removing the incorrect pixel repetition configuration code
* Preventing pixel-doubled modes from being used
* Adding interlaced video support
* Implementing the sink_is_hdmi/sink_has_audio flags I suggested a few
  months ago
* Only enabling audio support if the sink indicates it has audio
* Avoiding double-enabling the HDMI interface
* Fixing the mis-leading name of "dw_hdmi_phy_enable_power"
* Adding connector mode forcing (important if your monitor bounces
  RXSENSE and HPD signals while in low-power mode.)
* Improving the HDMI enable/disabling on sink status

For review (and testing if people feel like it).  Acks/tested-bys etc
welcome.  It applies on top of my drm-dwhdmi-devel branch, which is
waiting for David Airlie to pull (see pull request on dri-devel, 15th
July.)

 drivers/gpu/drm/bridge/dw_hdmi.c | 275 ++++++++++++++++++++++++++++++---------
 drivers/gpu/ipu-v3/ipu-dc.c      |  18 ++-
 drivers/gpu/ipu-v3/ipu-di.c      | 129 +++++++++---------
 3 files changed, 291 insertions(+), 131 deletions(-)

-- 
FTTC broadband for 0.8mile line: currently at 10.5Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 01/12] drm: bridge/dw_hdmi: remove pixel repetition setting for all VICs
  2015-08-08 16:02 ` Russell King - ARM Linux
@ 2015-08-08 16:03   ` Russell King
  -1 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:03 UTC (permalink / raw)
  To: linux-rockchip, dri-devel, linux-kernel, linux-arm-kernel
  Cc: Philipp Zabel, Andy Yan, Yakir Yang, Fabio Estevam, Sascha Hauer,
	Jon Nettleton, David Airlie

dw_hdmi sets a pixel repetition factor of 1 for VICs 10-15, 25-30 and
35-38.  However, DRM uses their native resolutions in its timing
information.  For example, VIC 14 can be 1440x480 with no repetition,
or 720x480 with one pixel repetition.  As DRM uses 1440 pixels per line
for this video mode, we need no pixel repetition.

In any case, pixel repetition appears broken in dw_hdmi.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/dw_hdmi.c | 13 +------------
 1 file changed, 1 insertion(+), 12 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index f070ee07b8c9..1c0ee3476138 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -1236,18 +1236,7 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
 	else
 		hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_709;
 
-	if ((hdmi->vic == 10) || (hdmi->vic == 11) ||
-	    (hdmi->vic == 12) || (hdmi->vic == 13) ||
-	    (hdmi->vic == 14) || (hdmi->vic == 15) ||
-	    (hdmi->vic == 25) || (hdmi->vic == 26) ||
-	    (hdmi->vic == 27) || (hdmi->vic == 28) ||
-	    (hdmi->vic == 29) || (hdmi->vic == 30) ||
-	    (hdmi->vic == 35) || (hdmi->vic == 36) ||
-	    (hdmi->vic == 37) || (hdmi->vic == 38))
-		hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 1;
-	else
-		hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 0;
-
+	hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 0;
 	hdmi->hdmi_data.video_mode.mpixelrepetitioninput = 0;
 
 	/* TODO: Get input format from IPU (via FB driver interface) */
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 01/12] drm: bridge/dw_hdmi: remove pixel repetition setting for all VICs
@ 2015-08-08 16:03   ` Russell King
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:03 UTC (permalink / raw)
  To: linux-arm-kernel

dw_hdmi sets a pixel repetition factor of 1 for VICs 10-15, 25-30 and
35-38.  However, DRM uses their native resolutions in its timing
information.  For example, VIC 14 can be 1440x480 with no repetition,
or 720x480 with one pixel repetition.  As DRM uses 1440 pixels per line
for this video mode, we need no pixel repetition.

In any case, pixel repetition appears broken in dw_hdmi.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/dw_hdmi.c | 13 +------------
 1 file changed, 1 insertion(+), 12 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index f070ee07b8c9..1c0ee3476138 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -1236,18 +1236,7 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
 	else
 		hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_709;
 
-	if ((hdmi->vic == 10) || (hdmi->vic == 11) ||
-	    (hdmi->vic == 12) || (hdmi->vic == 13) ||
-	    (hdmi->vic == 14) || (hdmi->vic == 15) ||
-	    (hdmi->vic == 25) || (hdmi->vic == 26) ||
-	    (hdmi->vic == 27) || (hdmi->vic == 28) ||
-	    (hdmi->vic == 29) || (hdmi->vic == 30) ||
-	    (hdmi->vic == 35) || (hdmi->vic == 36) ||
-	    (hdmi->vic == 37) || (hdmi->vic == 38))
-		hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 1;
-	else
-		hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 0;
-
+	hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 0;
 	hdmi->hdmi_data.video_mode.mpixelrepetitioninput = 0;
 
 	/* TODO: Get input format from IPU (via FB driver interface) */
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 02/12] drm: bridge/dw_hdmi: don't support any pixel doubled modes
  2015-08-08 16:02 ` Russell King - ARM Linux
@ 2015-08-08 16:03   ` Russell King
  -1 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:03 UTC (permalink / raw)
  To: linux-rockchip, dri-devel, linux-kernel, linux-arm-kernel
  Cc: Philipp Zabel, Andy Yan, Yakir Yang, Fabio Estevam, Sascha Hauer,
	Jon Nettleton, David Airlie

As mentioned in the previous commit, the dw-hdmi driver does not support
pixel doubled modes at present; it does not configure the PLL correctly
for these modes.  Therefore, filter out the double-clocked modes as we
presently are unable to support them.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/dw_hdmi.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index 1c0ee3476138..8edf4c31f55c 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -1448,6 +1448,10 @@ dw_hdmi_connector_mode_valid(struct drm_connector *connector,
 					   struct dw_hdmi, connector);
 	enum drm_mode_status mode_status = MODE_OK;
 
+	/* We don't support double-clocked modes */
+	if (mode->flags & DRM_MODE_FLAG_DBLCLK)
+		return MODE_BAD;
+
 	if (hdmi->plat_data->mode_valid)
 		mode_status = hdmi->plat_data->mode_valid(connector, mode);
 
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 02/12] drm: bridge/dw_hdmi: don't support any pixel doubled modes
@ 2015-08-08 16:03   ` Russell King
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:03 UTC (permalink / raw)
  To: linux-arm-kernel

As mentioned in the previous commit, the dw-hdmi driver does not support
pixel doubled modes at present; it does not configure the PLL correctly
for these modes.  Therefore, filter out the double-clocked modes as we
presently are unable to support them.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/dw_hdmi.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index 1c0ee3476138..8edf4c31f55c 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -1448,6 +1448,10 @@ dw_hdmi_connector_mode_valid(struct drm_connector *connector,
 					   struct dw_hdmi, connector);
 	enum drm_mode_status mode_status = MODE_OK;
 
+	/* We don't support double-clocked modes */
+	if (mode->flags & DRM_MODE_FLAG_DBLCLK)
+		return MODE_BAD;
+
 	if (hdmi->plat_data->mode_valid)
 		mode_status = hdmi->plat_data->mode_valid(connector, mode);
 
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 03/12] gpu: imx: simplify sync polarity setting
  2015-08-08 16:02 ` Russell King - ARM Linux
@ 2015-08-08 16:03   ` Russell King
  -1 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:03 UTC (permalink / raw)
  To: linux-rockchip, dri-devel, linux-kernel, linux-arm-kernel
  Cc: Philipp Zabel, Andy Yan, Yakir Yang, Fabio Estevam, Sascha Hauer,
	Jon Nettleton

Use a function to convert the sync pin to a bit mask for the DI_GENERAL
register, and move this out of the interlace/non-interlace path to the
common path.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/ipu-v3/ipu-di.c | 50 +++++++++++++++++++++++++--------------------
 1 file changed, 28 insertions(+), 22 deletions(-)

diff --git a/drivers/gpu/ipu-v3/ipu-di.c b/drivers/gpu/ipu-v3/ipu-di.c
index 2970c6bb668c..a96991c5c15f 100644
--- a/drivers/gpu/ipu-v3/ipu-di.c
+++ b/drivers/gpu/ipu-v3/ipu-di.c
@@ -543,6 +543,29 @@ int ipu_di_adjust_videomode(struct ipu_di *di, struct videomode *mode)
 }
 EXPORT_SYMBOL_GPL(ipu_di_adjust_videomode);
 
+static u32 ipu_di_gen_polarity(int pin)
+{
+	switch (pin) {
+	case 1:
+		return DI_GEN_POLARITY_1;
+	case 2:
+		return DI_GEN_POLARITY_2;
+	case 3:
+		return DI_GEN_POLARITY_3;
+	case 4:
+		return DI_GEN_POLARITY_4;
+	case 5:
+		return DI_GEN_POLARITY_5;
+	case 6:
+		return DI_GEN_POLARITY_6;
+	case 7:
+		return DI_GEN_POLARITY_7;
+	case 8:
+		return DI_GEN_POLARITY_8;
+	}
+	return 0;
+}
+
 int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig)
 {
 	u32 reg;
@@ -586,11 +609,6 @@ int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig)
 		di_gen |= DI_GEN_POLARITY_8;
 
 		vsync_cnt = 7;
-
-		if (sig->mode.flags & DISPLAY_FLAGS_HSYNC_HIGH)
-			di_gen |= DI_GEN_POLARITY_3;
-		if (sig->mode.flags & DISPLAY_FLAGS_VSYNC_HIGH)
-			di_gen |= DI_GEN_POLARITY_2;
 	} else {
 		ipu_di_sync_config_noninterlaced(di, sig, div);
 
@@ -602,25 +620,13 @@ int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig)
 			 */
 			if (!(sig->hsync_pin == 2 && sig->vsync_pin == 3))
 				vsync_cnt = 6;
-
-		if (sig->mode.flags & DISPLAY_FLAGS_HSYNC_HIGH) {
-			if (sig->hsync_pin == 2)
-				di_gen |= DI_GEN_POLARITY_2;
-			else if (sig->hsync_pin == 4)
-				di_gen |= DI_GEN_POLARITY_4;
-			else if (sig->hsync_pin == 7)
-				di_gen |= DI_GEN_POLARITY_7;
-		}
-		if (sig->mode.flags & DISPLAY_FLAGS_VSYNC_HIGH) {
-			if (sig->vsync_pin == 3)
-				di_gen |= DI_GEN_POLARITY_3;
-			else if (sig->vsync_pin == 6)
-				di_gen |= DI_GEN_POLARITY_6;
-			else if (sig->vsync_pin == 8)
-				di_gen |= DI_GEN_POLARITY_8;
-		}
 	}
 
+	if (sig->mode.flags & DISPLAY_FLAGS_HSYNC_HIGH)
+		di_gen |= ipu_di_gen_polarity(sig->hsync_pin);
+	if (sig->mode.flags & DISPLAY_FLAGS_VSYNC_HIGH)
+		di_gen |= ipu_di_gen_polarity(sig->vsync_pin);
+
 	if (sig->clk_pol)
 		di_gen |= DI_GEN_POLARITY_DISP_CLK;
 
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 03/12] gpu: imx: simplify sync polarity setting
@ 2015-08-08 16:03   ` Russell King
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:03 UTC (permalink / raw)
  To: linux-arm-kernel

Use a function to convert the sync pin to a bit mask for the DI_GENERAL
register, and move this out of the interlace/non-interlace path to the
common path.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/ipu-v3/ipu-di.c | 50 +++++++++++++++++++++++++--------------------
 1 file changed, 28 insertions(+), 22 deletions(-)

diff --git a/drivers/gpu/ipu-v3/ipu-di.c b/drivers/gpu/ipu-v3/ipu-di.c
index 2970c6bb668c..a96991c5c15f 100644
--- a/drivers/gpu/ipu-v3/ipu-di.c
+++ b/drivers/gpu/ipu-v3/ipu-di.c
@@ -543,6 +543,29 @@ int ipu_di_adjust_videomode(struct ipu_di *di, struct videomode *mode)
 }
 EXPORT_SYMBOL_GPL(ipu_di_adjust_videomode);
 
+static u32 ipu_di_gen_polarity(int pin)
+{
+	switch (pin) {
+	case 1:
+		return DI_GEN_POLARITY_1;
+	case 2:
+		return DI_GEN_POLARITY_2;
+	case 3:
+		return DI_GEN_POLARITY_3;
+	case 4:
+		return DI_GEN_POLARITY_4;
+	case 5:
+		return DI_GEN_POLARITY_5;
+	case 6:
+		return DI_GEN_POLARITY_6;
+	case 7:
+		return DI_GEN_POLARITY_7;
+	case 8:
+		return DI_GEN_POLARITY_8;
+	}
+	return 0;
+}
+
 int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig)
 {
 	u32 reg;
@@ -586,11 +609,6 @@ int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig)
 		di_gen |= DI_GEN_POLARITY_8;
 
 		vsync_cnt = 7;
-
-		if (sig->mode.flags & DISPLAY_FLAGS_HSYNC_HIGH)
-			di_gen |= DI_GEN_POLARITY_3;
-		if (sig->mode.flags & DISPLAY_FLAGS_VSYNC_HIGH)
-			di_gen |= DI_GEN_POLARITY_2;
 	} else {
 		ipu_di_sync_config_noninterlaced(di, sig, div);
 
@@ -602,25 +620,13 @@ int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig)
 			 */
 			if (!(sig->hsync_pin == 2 && sig->vsync_pin == 3))
 				vsync_cnt = 6;
-
-		if (sig->mode.flags & DISPLAY_FLAGS_HSYNC_HIGH) {
-			if (sig->hsync_pin == 2)
-				di_gen |= DI_GEN_POLARITY_2;
-			else if (sig->hsync_pin == 4)
-				di_gen |= DI_GEN_POLARITY_4;
-			else if (sig->hsync_pin == 7)
-				di_gen |= DI_GEN_POLARITY_7;
-		}
-		if (sig->mode.flags & DISPLAY_FLAGS_VSYNC_HIGH) {
-			if (sig->vsync_pin == 3)
-				di_gen |= DI_GEN_POLARITY_3;
-			else if (sig->vsync_pin == 6)
-				di_gen |= DI_GEN_POLARITY_6;
-			else if (sig->vsync_pin == 8)
-				di_gen |= DI_GEN_POLARITY_8;
-		}
 	}
 
+	if (sig->mode.flags & DISPLAY_FLAGS_HSYNC_HIGH)
+		di_gen |= ipu_di_gen_polarity(sig->hsync_pin);
+	if (sig->mode.flags & DISPLAY_FLAGS_VSYNC_HIGH)
+		di_gen |= ipu_di_gen_polarity(sig->vsync_pin);
+
 	if (sig->clk_pol)
 		di_gen |= DI_GEN_POLARITY_DISP_CLK;
 
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 04/12] gpu: imx: fix support for interlaced modes
  2015-08-08 16:02 ` Russell King - ARM Linux
@ 2015-08-08 16:03   ` Russell King
  -1 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:03 UTC (permalink / raw)
  To: linux-rockchip, dri-devel, linux-kernel, linux-arm-kernel
  Cc: Philipp Zabel, Andy Yan, Yakir Yang, Fabio Estevam, Sascha Hauer,
	Jon Nettleton

The support for interlaced video modes seems to be broken; we don't use
anything other than the vtotal/htotal from the timing information to
define the various sync counters.

Freescale patches for interlaced video support contain an alternative
sync counter setup, which we include here.  This setup produces the
hsync and vsync via the normal counter 2 and 3, but moves the display
enable signal from counter 5 to counter 6.  Therefore, we need to
change the display controller setup as well.

The corresponding Freescale patches for this change are:
  iMX6-HDMI-support-interlaced-display-mode.patch
  IPU-fine-tuning-the-interlace-display-timing-for-CEA.patch

This produces a working interlace format output from the IPU.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/ipu-v3/ipu-dc.c | 18 ++++++++---
 drivers/gpu/ipu-v3/ipu-di.c | 79 +++++++++++++++++++++------------------------
 2 files changed, 51 insertions(+), 46 deletions(-)

diff --git a/drivers/gpu/ipu-v3/ipu-dc.c b/drivers/gpu/ipu-v3/ipu-dc.c
index 9ef2e1f54ca4..aa560855c1dc 100644
--- a/drivers/gpu/ipu-v3/ipu-dc.c
+++ b/drivers/gpu/ipu-v3/ipu-dc.c
@@ -183,12 +183,22 @@ int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced,
 	}
 
 	if (interlaced) {
-		dc_link_event(dc, DC_EVT_NL, 0, 3);
-		dc_link_event(dc, DC_EVT_EOL, 0, 2);
-		dc_link_event(dc, DC_EVT_NEW_DATA, 0, 1);
+		int word, addr;
+
+		if (dc->di) {
+			addr = 1;
+			word = 1;
+		} else {
+			addr = 0;
+			word = 0;
+		}
+
+		dc_link_event(dc, DC_EVT_NL, addr, 3);
+		dc_link_event(dc, DC_EVT_EOL, addr, 2);
+		dc_link_event(dc, DC_EVT_NEW_DATA, addr, 1);
 
 		/* Init template microcode */
-		dc_write_tmpl(dc, 0, WROD(0), 0, map, SYNC_WAVE, 0, 8, 1);
+		dc_write_tmpl(dc, word, WROD(0), 0, map, SYNC_WAVE, 0, 6, 1);
 	} else {
 		if (dc->di) {
 			dc_link_event(dc, DC_EVT_NL, 2, 3);
diff --git a/drivers/gpu/ipu-v3/ipu-di.c b/drivers/gpu/ipu-v3/ipu-di.c
index a96991c5c15f..359268e3a166 100644
--- a/drivers/gpu/ipu-v3/ipu-di.c
+++ b/drivers/gpu/ipu-v3/ipu-di.c
@@ -71,6 +71,10 @@ enum di_sync_wave {
 	DI_SYNC_HSYNC = 3,
 	DI_SYNC_VSYNC = 4,
 	DI_SYNC_DE = 6,
+
+	DI_SYNC_CNT1 = 2,	/* counter >= 2 only */
+	DI_SYNC_CNT4 = 5,	/* counter >= 5 only */
+	DI_SYNC_CNT5 = 6,	/* counter >= 6 only */
 };
 
 #define SYNC_WAVE 0
@@ -211,66 +215,59 @@ static void ipu_di_sync_config_interlaced(struct ipu_di *di,
 		sig->mode.hback_porch + sig->mode.hfront_porch;
 	u32 v_total = sig->mode.vactive + sig->mode.vsync_len +
 		sig->mode.vback_porch + sig->mode.vfront_porch;
-	u32 reg;
 	struct di_sync_config cfg[] = {
 		{
-			.run_count = h_total / 2 - 1,
-			.run_src = DI_SYNC_CLK,
+			/* 1: internal VSYNC for each frame */
+			.run_count = v_total * 2 - 1,
+			.run_src = 3,			/* == counter 7 */
 		}, {
-			.run_count = h_total - 11,
+			/* PIN2: HSYNC waveform */
+			.run_count = h_total - 1,
 			.run_src = DI_SYNC_CLK,
-			.cnt_down = 4,
+			.cnt_polarity_gen_en = 1,
+			.cnt_polarity_trigger_src = DI_SYNC_CLK,
+			.cnt_down = sig->mode.hsync_len * 2,
 		}, {
-			.run_count = v_total * 2 - 1,
-			.run_src = DI_SYNC_INT_HSYNC,
-			.offset_count = 1,
-			.offset_src = DI_SYNC_INT_HSYNC,
-			.cnt_down = 4,
+			/* PIN3: VSYNC waveform */
+			.run_count = v_total - 1,
+			.run_src = 4,			/* == counter 7 */
+			.cnt_polarity_gen_en = 1,
+			.cnt_polarity_trigger_src = 4,	/* == counter 7 */
+			.cnt_down = sig->mode.vsync_len * 2,
+			.cnt_clr_src = DI_SYNC_CNT1,
 		}, {
-			.run_count = v_total / 2 - 1,
+			/* 4: Field */
+			.run_count = v_total / 2,
 			.run_src = DI_SYNC_HSYNC,
-			.offset_count = sig->mode.vback_porch,
-			.offset_src = DI_SYNC_HSYNC,
+			.offset_count = h_total / 2,
+			.offset_src = DI_SYNC_CLK,
 			.repeat_count = 2,
-			.cnt_clr_src = DI_SYNC_VSYNC,
+			.cnt_clr_src = DI_SYNC_CNT1,
 		}, {
+			/* 5: Active lines */
 			.run_src = DI_SYNC_HSYNC,
-			.repeat_count = sig->mode.vactive / 2,
-			.cnt_clr_src = 4,
-		}, {
-			.run_count = v_total - 1,
-			.run_src = DI_SYNC_HSYNC,
-		}, {
-			.run_count = v_total / 2 - 1,
-			.run_src = DI_SYNC_HSYNC,
-			.offset_count = 9,
+			.offset_count = (sig->mode.vsync_len +
+					 sig->mode.vback_porch) / 2,
 			.offset_src = DI_SYNC_HSYNC,
-			.repeat_count = 2,
-			.cnt_clr_src = DI_SYNC_VSYNC,
+			.repeat_count = sig->mode.vactive / 2,
+			.cnt_clr_src = DI_SYNC_CNT4,
 		}, {
+			/* 6: Active pixel, referenced by DC */
 			.run_src = DI_SYNC_CLK,
-			.offset_count = sig->mode.hback_porch,
+			.offset_count = sig->mode.hsync_len +
+					sig->mode.hback_porch,
 			.offset_src = DI_SYNC_CLK,
 			.repeat_count = sig->mode.hactive,
-			.cnt_clr_src = 5,
+			.cnt_clr_src = DI_SYNC_CNT5,
 		}, {
-			.run_count = v_total - 1,
-			.run_src = DI_SYNC_INT_HSYNC,
-			.offset_count = v_total / 2,
-			.offset_src = DI_SYNC_INT_HSYNC,
-			.cnt_clr_src = DI_SYNC_HSYNC,
-			.cnt_down = 4,
+			/* 7: Half line HSYNC */
+			.run_count = h_total / 2 - 1,
+			.run_src = DI_SYNC_CLK,
 		}
 	};
 
 	ipu_di_sync_config(di, cfg, 0, ARRAY_SIZE(cfg));
 
-	/* set gentime select and tag sel */
-	reg = ipu_di_read(di, DI_SW_GEN1(9));
-	reg &= 0x1FFFFFFF;
-	reg |= (3 - 1) << 29 | 0x00008000;
-	ipu_di_write(di, reg, DI_SW_GEN1(9));
-
 	ipu_di_write(di, v_total / 2 - 1, DI_SCR_CONF);
 }
 
@@ -605,10 +602,8 @@ int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig)
 
 		/* set y_sel = 1 */
 		di_gen |= 0x10000000;
-		di_gen |= DI_GEN_POLARITY_5;
-		di_gen |= DI_GEN_POLARITY_8;
 
-		vsync_cnt = 7;
+		vsync_cnt = 3;
 	} else {
 		ipu_di_sync_config_noninterlaced(di, sig, div);
 
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 04/12] gpu: imx: fix support for interlaced modes
@ 2015-08-08 16:03   ` Russell King
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:03 UTC (permalink / raw)
  To: linux-arm-kernel

The support for interlaced video modes seems to be broken; we don't use
anything other than the vtotal/htotal from the timing information to
define the various sync counters.

Freescale patches for interlaced video support contain an alternative
sync counter setup, which we include here.  This setup produces the
hsync and vsync via the normal counter 2 and 3, but moves the display
enable signal from counter 5 to counter 6.  Therefore, we need to
change the display controller setup as well.

The corresponding Freescale patches for this change are:
  iMX6-HDMI-support-interlaced-display-mode.patch
  IPU-fine-tuning-the-interlace-display-timing-for-CEA.patch

This produces a working interlace format output from the IPU.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/ipu-v3/ipu-dc.c | 18 ++++++++---
 drivers/gpu/ipu-v3/ipu-di.c | 79 +++++++++++++++++++++------------------------
 2 files changed, 51 insertions(+), 46 deletions(-)

diff --git a/drivers/gpu/ipu-v3/ipu-dc.c b/drivers/gpu/ipu-v3/ipu-dc.c
index 9ef2e1f54ca4..aa560855c1dc 100644
--- a/drivers/gpu/ipu-v3/ipu-dc.c
+++ b/drivers/gpu/ipu-v3/ipu-dc.c
@@ -183,12 +183,22 @@ int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced,
 	}
 
 	if (interlaced) {
-		dc_link_event(dc, DC_EVT_NL, 0, 3);
-		dc_link_event(dc, DC_EVT_EOL, 0, 2);
-		dc_link_event(dc, DC_EVT_NEW_DATA, 0, 1);
+		int word, addr;
+
+		if (dc->di) {
+			addr = 1;
+			word = 1;
+		} else {
+			addr = 0;
+			word = 0;
+		}
+
+		dc_link_event(dc, DC_EVT_NL, addr, 3);
+		dc_link_event(dc, DC_EVT_EOL, addr, 2);
+		dc_link_event(dc, DC_EVT_NEW_DATA, addr, 1);
 
 		/* Init template microcode */
-		dc_write_tmpl(dc, 0, WROD(0), 0, map, SYNC_WAVE, 0, 8, 1);
+		dc_write_tmpl(dc, word, WROD(0), 0, map, SYNC_WAVE, 0, 6, 1);
 	} else {
 		if (dc->di) {
 			dc_link_event(dc, DC_EVT_NL, 2, 3);
diff --git a/drivers/gpu/ipu-v3/ipu-di.c b/drivers/gpu/ipu-v3/ipu-di.c
index a96991c5c15f..359268e3a166 100644
--- a/drivers/gpu/ipu-v3/ipu-di.c
+++ b/drivers/gpu/ipu-v3/ipu-di.c
@@ -71,6 +71,10 @@ enum di_sync_wave {
 	DI_SYNC_HSYNC = 3,
 	DI_SYNC_VSYNC = 4,
 	DI_SYNC_DE = 6,
+
+	DI_SYNC_CNT1 = 2,	/* counter >= 2 only */
+	DI_SYNC_CNT4 = 5,	/* counter >= 5 only */
+	DI_SYNC_CNT5 = 6,	/* counter >= 6 only */
 };
 
 #define SYNC_WAVE 0
@@ -211,66 +215,59 @@ static void ipu_di_sync_config_interlaced(struct ipu_di *di,
 		sig->mode.hback_porch + sig->mode.hfront_porch;
 	u32 v_total = sig->mode.vactive + sig->mode.vsync_len +
 		sig->mode.vback_porch + sig->mode.vfront_porch;
-	u32 reg;
 	struct di_sync_config cfg[] = {
 		{
-			.run_count = h_total / 2 - 1,
-			.run_src = DI_SYNC_CLK,
+			/* 1: internal VSYNC for each frame */
+			.run_count = v_total * 2 - 1,
+			.run_src = 3,			/* == counter 7 */
 		}, {
-			.run_count = h_total - 11,
+			/* PIN2: HSYNC waveform */
+			.run_count = h_total - 1,
 			.run_src = DI_SYNC_CLK,
-			.cnt_down = 4,
+			.cnt_polarity_gen_en = 1,
+			.cnt_polarity_trigger_src = DI_SYNC_CLK,
+			.cnt_down = sig->mode.hsync_len * 2,
 		}, {
-			.run_count = v_total * 2 - 1,
-			.run_src = DI_SYNC_INT_HSYNC,
-			.offset_count = 1,
-			.offset_src = DI_SYNC_INT_HSYNC,
-			.cnt_down = 4,
+			/* PIN3: VSYNC waveform */
+			.run_count = v_total - 1,
+			.run_src = 4,			/* == counter 7 */
+			.cnt_polarity_gen_en = 1,
+			.cnt_polarity_trigger_src = 4,	/* == counter 7 */
+			.cnt_down = sig->mode.vsync_len * 2,
+			.cnt_clr_src = DI_SYNC_CNT1,
 		}, {
-			.run_count = v_total / 2 - 1,
+			/* 4: Field */
+			.run_count = v_total / 2,
 			.run_src = DI_SYNC_HSYNC,
-			.offset_count = sig->mode.vback_porch,
-			.offset_src = DI_SYNC_HSYNC,
+			.offset_count = h_total / 2,
+			.offset_src = DI_SYNC_CLK,
 			.repeat_count = 2,
-			.cnt_clr_src = DI_SYNC_VSYNC,
+			.cnt_clr_src = DI_SYNC_CNT1,
 		}, {
+			/* 5: Active lines */
 			.run_src = DI_SYNC_HSYNC,
-			.repeat_count = sig->mode.vactive / 2,
-			.cnt_clr_src = 4,
-		}, {
-			.run_count = v_total - 1,
-			.run_src = DI_SYNC_HSYNC,
-		}, {
-			.run_count = v_total / 2 - 1,
-			.run_src = DI_SYNC_HSYNC,
-			.offset_count = 9,
+			.offset_count = (sig->mode.vsync_len +
+					 sig->mode.vback_porch) / 2,
 			.offset_src = DI_SYNC_HSYNC,
-			.repeat_count = 2,
-			.cnt_clr_src = DI_SYNC_VSYNC,
+			.repeat_count = sig->mode.vactive / 2,
+			.cnt_clr_src = DI_SYNC_CNT4,
 		}, {
+			/* 6: Active pixel, referenced by DC */
 			.run_src = DI_SYNC_CLK,
-			.offset_count = sig->mode.hback_porch,
+			.offset_count = sig->mode.hsync_len +
+					sig->mode.hback_porch,
 			.offset_src = DI_SYNC_CLK,
 			.repeat_count = sig->mode.hactive,
-			.cnt_clr_src = 5,
+			.cnt_clr_src = DI_SYNC_CNT5,
 		}, {
-			.run_count = v_total - 1,
-			.run_src = DI_SYNC_INT_HSYNC,
-			.offset_count = v_total / 2,
-			.offset_src = DI_SYNC_INT_HSYNC,
-			.cnt_clr_src = DI_SYNC_HSYNC,
-			.cnt_down = 4,
+			/* 7: Half line HSYNC */
+			.run_count = h_total / 2 - 1,
+			.run_src = DI_SYNC_CLK,
 		}
 	};
 
 	ipu_di_sync_config(di, cfg, 0, ARRAY_SIZE(cfg));
 
-	/* set gentime select and tag sel */
-	reg = ipu_di_read(di, DI_SW_GEN1(9));
-	reg &= 0x1FFFFFFF;
-	reg |= (3 - 1) << 29 | 0x00008000;
-	ipu_di_write(di, reg, DI_SW_GEN1(9));
-
 	ipu_di_write(di, v_total / 2 - 1, DI_SCR_CONF);
 }
 
@@ -605,10 +602,8 @@ int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig)
 
 		/* set y_sel = 1 */
 		di_gen |= 0x10000000;
-		di_gen |= DI_GEN_POLARITY_5;
-		di_gen |= DI_GEN_POLARITY_8;
 
-		vsync_cnt = 7;
+		vsync_cnt = 3;
 	} else {
 		ipu_di_sync_config_noninterlaced(di, sig, div);
 
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 05/12] drm: bridge/dw_hdmi: add support for interlaced video modes
  2015-08-08 16:02 ` Russell King - ARM Linux
  (?)
@ 2015-08-08 16:03   ` Russell King
  -1 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:03 UTC (permalink / raw)
  To: linux-rockchip, dri-devel, linux-kernel, linux-arm-kernel
  Cc: Philipp Zabel, Andy Yan, Yakir Yang, Fabio Estevam, Sascha Hauer,
	Jon Nettleton, David Airlie

Add support for interlaced video modes to the dw_hdmi bridge.  This
mainly involves halving the vertical parameters to be programmed into
the bridge registers, and setting the interlace_allowed connector flag.

This brings working 1080i support.  However, 480i and 576i fail to
work due to the lack of proper pixel repetition support, which is not
trivial to add due to the tabular PLL parameterisation.  Hence, we
filter out these modes in our mode_valid() method.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/dw_hdmi.c | 26 +++++++++++++++++++++-----
 1 file changed, 21 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index 8edf4c31f55c..2e211b8331ed 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -1060,6 +1060,7 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
 	u8 inv_val;
 	struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode;
 	int hblank, vblank, h_de_hs, v_de_vs, hsync_len, vsync_len;
+	unsigned int vdisplay;
 
 	vmode->mpixelclock = mode->clock * 1000;
 
@@ -1099,13 +1100,29 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
 
 	hdmi_writeb(hdmi, inv_val, HDMI_FC_INVIDCONF);
 
+	vdisplay = mode->vdisplay;
+	vblank = mode->vtotal - mode->vdisplay;
+	v_de_vs = mode->vsync_start - mode->vdisplay;
+	vsync_len = mode->vsync_end - mode->vsync_start;
+
+	/*
+	 * When we're setting an interlaced mode, we need
+	 * to adjust the vertical timing to suit.
+	 */
+	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
+		vdisplay /= 2;
+		vblank /= 2;
+		v_de_vs /= 2;
+		vsync_len /= 2;
+	}
+
 	/* Set up horizontal active pixel width */
 	hdmi_writeb(hdmi, mode->hdisplay >> 8, HDMI_FC_INHACTV1);
 	hdmi_writeb(hdmi, mode->hdisplay, HDMI_FC_INHACTV0);
 
 	/* Set up vertical active lines */
-	hdmi_writeb(hdmi, mode->vdisplay >> 8, HDMI_FC_INVACTV1);
-	hdmi_writeb(hdmi, mode->vdisplay, HDMI_FC_INVACTV0);
+	hdmi_writeb(hdmi, vdisplay >> 8, HDMI_FC_INVACTV1);
+	hdmi_writeb(hdmi, vdisplay, HDMI_FC_INVACTV0);
 
 	/* Set up horizontal blanking pixel region width */
 	hblank = mode->htotal - mode->hdisplay;
@@ -1113,7 +1130,6 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
 	hdmi_writeb(hdmi, hblank, HDMI_FC_INHBLANK0);
 
 	/* Set up vertical blanking pixel region width */
-	vblank = mode->vtotal - mode->vdisplay;
 	hdmi_writeb(hdmi, vblank, HDMI_FC_INVBLANK);
 
 	/* Set up HSYNC active edge delay width (in pixel clks) */
@@ -1122,7 +1138,6 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
 	hdmi_writeb(hdmi, h_de_hs, HDMI_FC_HSYNCINDELAY0);
 
 	/* Set up VSYNC active edge delay (in lines) */
-	v_de_vs = mode->vsync_start - mode->vdisplay;
 	hdmi_writeb(hdmi, v_de_vs, HDMI_FC_VSYNCINDELAY);
 
 	/* Set up HSYNC active pulse width (in pixel clks) */
@@ -1131,7 +1146,6 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
 	hdmi_writeb(hdmi, hsync_len, HDMI_FC_HSYNCINWIDTH0);
 
 	/* Set up VSYNC active edge delay (in lines) */
-	vsync_len = mode->vsync_end - mode->vsync_start;
 	hdmi_writeb(hdmi, vsync_len, HDMI_FC_VSYNCINWIDTH);
 }
 
@@ -1593,6 +1607,8 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 	if (!hdmi)
 		return -ENOMEM;
 
+	hdmi->connector.interlace_allowed = 1;
+
 	hdmi->plat_data = plat_data;
 	hdmi->dev = dev;
 	hdmi->dev_type = plat_data->dev_type;
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 05/12] drm: bridge/dw_hdmi: add support for interlaced video modes
@ 2015-08-08 16:03   ` Russell King
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:03 UTC (permalink / raw)
  To: linux-rockchip, dri-devel, linux-kernel, linux-arm-kernel
  Cc: Fabio Estevam, Yakir Yang, Andy Yan

Add support for interlaced video modes to the dw_hdmi bridge.  This
mainly involves halving the vertical parameters to be programmed into
the bridge registers, and setting the interlace_allowed connector flag.

This brings working 1080i support.  However, 480i and 576i fail to
work due to the lack of proper pixel repetition support, which is not
trivial to add due to the tabular PLL parameterisation.  Hence, we
filter out these modes in our mode_valid() method.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/dw_hdmi.c | 26 +++++++++++++++++++++-----
 1 file changed, 21 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index 8edf4c31f55c..2e211b8331ed 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -1060,6 +1060,7 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
 	u8 inv_val;
 	struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode;
 	int hblank, vblank, h_de_hs, v_de_vs, hsync_len, vsync_len;
+	unsigned int vdisplay;
 
 	vmode->mpixelclock = mode->clock * 1000;
 
@@ -1099,13 +1100,29 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
 
 	hdmi_writeb(hdmi, inv_val, HDMI_FC_INVIDCONF);
 
+	vdisplay = mode->vdisplay;
+	vblank = mode->vtotal - mode->vdisplay;
+	v_de_vs = mode->vsync_start - mode->vdisplay;
+	vsync_len = mode->vsync_end - mode->vsync_start;
+
+	/*
+	 * When we're setting an interlaced mode, we need
+	 * to adjust the vertical timing to suit.
+	 */
+	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
+		vdisplay /= 2;
+		vblank /= 2;
+		v_de_vs /= 2;
+		vsync_len /= 2;
+	}
+
 	/* Set up horizontal active pixel width */
 	hdmi_writeb(hdmi, mode->hdisplay >> 8, HDMI_FC_INHACTV1);
 	hdmi_writeb(hdmi, mode->hdisplay, HDMI_FC_INHACTV0);
 
 	/* Set up vertical active lines */
-	hdmi_writeb(hdmi, mode->vdisplay >> 8, HDMI_FC_INVACTV1);
-	hdmi_writeb(hdmi, mode->vdisplay, HDMI_FC_INVACTV0);
+	hdmi_writeb(hdmi, vdisplay >> 8, HDMI_FC_INVACTV1);
+	hdmi_writeb(hdmi, vdisplay, HDMI_FC_INVACTV0);
 
 	/* Set up horizontal blanking pixel region width */
 	hblank = mode->htotal - mode->hdisplay;
@@ -1113,7 +1130,6 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
 	hdmi_writeb(hdmi, hblank, HDMI_FC_INHBLANK0);
 
 	/* Set up vertical blanking pixel region width */
-	vblank = mode->vtotal - mode->vdisplay;
 	hdmi_writeb(hdmi, vblank, HDMI_FC_INVBLANK);
 
 	/* Set up HSYNC active edge delay width (in pixel clks) */
@@ -1122,7 +1138,6 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
 	hdmi_writeb(hdmi, h_de_hs, HDMI_FC_HSYNCINDELAY0);
 
 	/* Set up VSYNC active edge delay (in lines) */
-	v_de_vs = mode->vsync_start - mode->vdisplay;
 	hdmi_writeb(hdmi, v_de_vs, HDMI_FC_VSYNCINDELAY);
 
 	/* Set up HSYNC active pulse width (in pixel clks) */
@@ -1131,7 +1146,6 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
 	hdmi_writeb(hdmi, hsync_len, HDMI_FC_HSYNCINWIDTH0);
 
 	/* Set up VSYNC active edge delay (in lines) */
-	vsync_len = mode->vsync_end - mode->vsync_start;
 	hdmi_writeb(hdmi, vsync_len, HDMI_FC_VSYNCINWIDTH);
 }
 
@@ -1593,6 +1607,8 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 	if (!hdmi)
 		return -ENOMEM;
 
+	hdmi->connector.interlace_allowed = 1;
+
 	hdmi->plat_data = plat_data;
 	hdmi->dev = dev;
 	hdmi->dev_type = plat_data->dev_type;
-- 
2.1.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 05/12] drm: bridge/dw_hdmi: add support for interlaced video modes
@ 2015-08-08 16:03   ` Russell King
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:03 UTC (permalink / raw)
  To: linux-arm-kernel

Add support for interlaced video modes to the dw_hdmi bridge.  This
mainly involves halving the vertical parameters to be programmed into
the bridge registers, and setting the interlace_allowed connector flag.

This brings working 1080i support.  However, 480i and 576i fail to
work due to the lack of proper pixel repetition support, which is not
trivial to add due to the tabular PLL parameterisation.  Hence, we
filter out these modes in our mode_valid() method.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/dw_hdmi.c | 26 +++++++++++++++++++++-----
 1 file changed, 21 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index 8edf4c31f55c..2e211b8331ed 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -1060,6 +1060,7 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
 	u8 inv_val;
 	struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode;
 	int hblank, vblank, h_de_hs, v_de_vs, hsync_len, vsync_len;
+	unsigned int vdisplay;
 
 	vmode->mpixelclock = mode->clock * 1000;
 
@@ -1099,13 +1100,29 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
 
 	hdmi_writeb(hdmi, inv_val, HDMI_FC_INVIDCONF);
 
+	vdisplay = mode->vdisplay;
+	vblank = mode->vtotal - mode->vdisplay;
+	v_de_vs = mode->vsync_start - mode->vdisplay;
+	vsync_len = mode->vsync_end - mode->vsync_start;
+
+	/*
+	 * When we're setting an interlaced mode, we need
+	 * to adjust the vertical timing to suit.
+	 */
+	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
+		vdisplay /= 2;
+		vblank /= 2;
+		v_de_vs /= 2;
+		vsync_len /= 2;
+	}
+
 	/* Set up horizontal active pixel width */
 	hdmi_writeb(hdmi, mode->hdisplay >> 8, HDMI_FC_INHACTV1);
 	hdmi_writeb(hdmi, mode->hdisplay, HDMI_FC_INHACTV0);
 
 	/* Set up vertical active lines */
-	hdmi_writeb(hdmi, mode->vdisplay >> 8, HDMI_FC_INVACTV1);
-	hdmi_writeb(hdmi, mode->vdisplay, HDMI_FC_INVACTV0);
+	hdmi_writeb(hdmi, vdisplay >> 8, HDMI_FC_INVACTV1);
+	hdmi_writeb(hdmi, vdisplay, HDMI_FC_INVACTV0);
 
 	/* Set up horizontal blanking pixel region width */
 	hblank = mode->htotal - mode->hdisplay;
@@ -1113,7 +1130,6 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
 	hdmi_writeb(hdmi, hblank, HDMI_FC_INHBLANK0);
 
 	/* Set up vertical blanking pixel region width */
-	vblank = mode->vtotal - mode->vdisplay;
 	hdmi_writeb(hdmi, vblank, HDMI_FC_INVBLANK);
 
 	/* Set up HSYNC active edge delay width (in pixel clks) */
@@ -1122,7 +1138,6 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
 	hdmi_writeb(hdmi, h_de_hs, HDMI_FC_HSYNCINDELAY0);
 
 	/* Set up VSYNC active edge delay (in lines) */
-	v_de_vs = mode->vsync_start - mode->vdisplay;
 	hdmi_writeb(hdmi, v_de_vs, HDMI_FC_VSYNCINDELAY);
 
 	/* Set up HSYNC active pulse width (in pixel clks) */
@@ -1131,7 +1146,6 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
 	hdmi_writeb(hdmi, hsync_len, HDMI_FC_HSYNCINWIDTH0);
 
 	/* Set up VSYNC active edge delay (in lines) */
-	vsync_len = mode->vsync_end - mode->vsync_start;
 	hdmi_writeb(hdmi, vsync_len, HDMI_FC_VSYNCINWIDTH);
 }
 
@@ -1593,6 +1607,8 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 	if (!hdmi)
 		return -ENOMEM;
 
+	hdmi->connector.interlace_allowed = 1;
+
 	hdmi->plat_data = plat_data;
 	hdmi->dev = dev;
 	hdmi->dev_type = plat_data->dev_type;
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 06/12] drm: bridge/dw_hdmi: clean up HDMI vs DVI mode handling
  2015-08-08 16:02 ` Russell King - ARM Linux
@ 2015-08-08 16:03   ` Russell King
  -1 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:03 UTC (permalink / raw)
  To: linux-rockchip, dri-devel, linux-kernel, linux-arm-kernel
  Cc: Philipp Zabel, Andy Yan, Yakir Yang, Fabio Estevam, Sascha Hauer,
	Jon Nettleton, David Airlie

The FSL kernel detects the HDMI vendor id, and uses this to set
hdmi->edid_cfg.hdmi_cap, which is then used to set mdvi appropriately,
rather than detecting whether we are outputting a CEA mode.  Update
the dw_hdmi code to use this logic, but lets eliminate the mdvi
variable, prefering the more verbose "hdmi->sink_is_hdmi" instead.

Use the generic drm_detect_hdmi_monitor() to detect a HDMI sink.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/dw_hdmi.c | 26 ++++++++++++--------------
 1 file changed, 12 insertions(+), 14 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index 2e211b8331ed..7f764716f3c4 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -82,7 +82,6 @@ static const u16 csc_coeff_rgb_in_eitu709[3][4] = {
 };
 
 struct hdmi_vmode {
-	bool mdvi;
 	bool mdataenablepolarity;
 
 	unsigned int mpixelclock;
@@ -123,6 +122,7 @@ struct dw_hdmi {
 
 	struct i2c_adapter *ddc;
 	void __iomem *regs;
+	bool sink_is_hdmi;
 
 	spinlock_t audio_lock;
 	struct mutex audio_mutex;
@@ -913,11 +913,10 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep,
 static int dw_hdmi_phy_init(struct dw_hdmi *hdmi)
 {
 	int i, ret;
-	bool cscon = false;
+	bool cscon;
 
 	/*check csc whether needed activated in HDMI mode */
-	cscon = (is_color_space_conversion(hdmi) &&
-			!hdmi->hdmi_data.video_mode.mdvi);
+	cscon = hdmi->sink_is_hdmi && is_color_space_conversion(hdmi);
 
 	/* HDMI Phy spec says to do the phy initialization sequence twice */
 	for (i = 0; i < 2; i++) {
@@ -1094,9 +1093,9 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
 		HDMI_FC_INVIDCONF_IN_I_P_INTERLACED :
 		HDMI_FC_INVIDCONF_IN_I_P_PROGRESSIVE;
 
-	inv_val |= (vmode->mdvi ?
-		HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE :
-		HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE);
+	inv_val |= hdmi->sink_is_hdmi ?
+		HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE :
+		HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE;
 
 	hdmi_writeb(hdmi, inv_val, HDMI_FC_INVIDCONF);
 
@@ -1236,10 +1235,8 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
 
 	if (!hdmi->vic) {
 		dev_dbg(hdmi->dev, "Non-CEA mode used in HDMI\n");
-		hdmi->hdmi_data.video_mode.mdvi = true;
 	} else {
 		dev_dbg(hdmi->dev, "CEA mode used vic=%d\n", hdmi->vic);
-		hdmi->hdmi_data.video_mode.mdvi = false;
 	}
 
 	if ((hdmi->vic == 6) || (hdmi->vic == 7) ||
@@ -1275,10 +1272,8 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
 	dw_hdmi_enable_video_path(hdmi);
 
 	/* not for DVI mode */
-	if (hdmi->hdmi_data.video_mode.mdvi) {
-		dev_dbg(hdmi->dev, "%s DVI mode\n", __func__);
-	} else {
-		dev_dbg(hdmi->dev, "%s CEA mode\n", __func__);
+	if (hdmi->sink_is_hdmi) {
+		dev_dbg(hdmi->dev, "%s HDMI mode\n", __func__);
 
 		/* HDMI Initialization Step E - Configure audio */
 		hdmi_clk_regenerator_update_pixel_clock(hdmi);
@@ -1286,6 +1281,8 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
 
 		/* HDMI Initialization Step F - Configure AVI InfoFrame */
 		hdmi_config_AVI(hdmi, mode);
+	} else {
+		dev_dbg(hdmi->dev, "%s DVI mode\n", __func__);
 	}
 
 	hdmi_video_packetize(hdmi);
@@ -1294,7 +1291,7 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
 	hdmi_tx_hdcp_config(hdmi);
 
 	dw_hdmi_clear_overflow(hdmi);
-	if (hdmi->cable_plugin && !hdmi->hdmi_data.video_mode.mdvi)
+	if (hdmi->cable_plugin && hdmi->sink_is_hdmi)
 		hdmi_enable_overflow_interrupts(hdmi);
 
 	return 0;
@@ -1444,6 +1441,7 @@ static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
 		dev_dbg(hdmi->dev, "got edid: width[%d] x height[%d]\n",
 			edid->width_cm, edid->height_cm);
 
+		hdmi->sink_is_hdmi = drm_detect_hdmi_monitor(edid);
 		drm_mode_connector_update_edid_property(connector, edid);
 		ret = drm_add_edid_modes(connector, edid);
 		kfree(edid);
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 06/12] drm: bridge/dw_hdmi: clean up HDMI vs DVI mode handling
@ 2015-08-08 16:03   ` Russell King
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:03 UTC (permalink / raw)
  To: linux-arm-kernel

The FSL kernel detects the HDMI vendor id, and uses this to set
hdmi->edid_cfg.hdmi_cap, which is then used to set mdvi appropriately,
rather than detecting whether we are outputting a CEA mode.  Update
the dw_hdmi code to use this logic, but lets eliminate the mdvi
variable, prefering the more verbose "hdmi->sink_is_hdmi" instead.

Use the generic drm_detect_hdmi_monitor() to detect a HDMI sink.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/dw_hdmi.c | 26 ++++++++++++--------------
 1 file changed, 12 insertions(+), 14 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index 2e211b8331ed..7f764716f3c4 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -82,7 +82,6 @@ static const u16 csc_coeff_rgb_in_eitu709[3][4] = {
 };
 
 struct hdmi_vmode {
-	bool mdvi;
 	bool mdataenablepolarity;
 
 	unsigned int mpixelclock;
@@ -123,6 +122,7 @@ struct dw_hdmi {
 
 	struct i2c_adapter *ddc;
 	void __iomem *regs;
+	bool sink_is_hdmi;
 
 	spinlock_t audio_lock;
 	struct mutex audio_mutex;
@@ -913,11 +913,10 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep,
 static int dw_hdmi_phy_init(struct dw_hdmi *hdmi)
 {
 	int i, ret;
-	bool cscon = false;
+	bool cscon;
 
 	/*check csc whether needed activated in HDMI mode */
-	cscon = (is_color_space_conversion(hdmi) &&
-			!hdmi->hdmi_data.video_mode.mdvi);
+	cscon = hdmi->sink_is_hdmi && is_color_space_conversion(hdmi);
 
 	/* HDMI Phy spec says to do the phy initialization sequence twice */
 	for (i = 0; i < 2; i++) {
@@ -1094,9 +1093,9 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
 		HDMI_FC_INVIDCONF_IN_I_P_INTERLACED :
 		HDMI_FC_INVIDCONF_IN_I_P_PROGRESSIVE;
 
-	inv_val |= (vmode->mdvi ?
-		HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE :
-		HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE);
+	inv_val |= hdmi->sink_is_hdmi ?
+		HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE :
+		HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE;
 
 	hdmi_writeb(hdmi, inv_val, HDMI_FC_INVIDCONF);
 
@@ -1236,10 +1235,8 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
 
 	if (!hdmi->vic) {
 		dev_dbg(hdmi->dev, "Non-CEA mode used in HDMI\n");
-		hdmi->hdmi_data.video_mode.mdvi = true;
 	} else {
 		dev_dbg(hdmi->dev, "CEA mode used vic=%d\n", hdmi->vic);
-		hdmi->hdmi_data.video_mode.mdvi = false;
 	}
 
 	if ((hdmi->vic == 6) || (hdmi->vic == 7) ||
@@ -1275,10 +1272,8 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
 	dw_hdmi_enable_video_path(hdmi);
 
 	/* not for DVI mode */
-	if (hdmi->hdmi_data.video_mode.mdvi) {
-		dev_dbg(hdmi->dev, "%s DVI mode\n", __func__);
-	} else {
-		dev_dbg(hdmi->dev, "%s CEA mode\n", __func__);
+	if (hdmi->sink_is_hdmi) {
+		dev_dbg(hdmi->dev, "%s HDMI mode\n", __func__);
 
 		/* HDMI Initialization Step E - Configure audio */
 		hdmi_clk_regenerator_update_pixel_clock(hdmi);
@@ -1286,6 +1281,8 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
 
 		/* HDMI Initialization Step F - Configure AVI InfoFrame */
 		hdmi_config_AVI(hdmi, mode);
+	} else {
+		dev_dbg(hdmi->dev, "%s DVI mode\n", __func__);
 	}
 
 	hdmi_video_packetize(hdmi);
@@ -1294,7 +1291,7 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
 	hdmi_tx_hdcp_config(hdmi);
 
 	dw_hdmi_clear_overflow(hdmi);
-	if (hdmi->cable_plugin && !hdmi->hdmi_data.video_mode.mdvi)
+	if (hdmi->cable_plugin && hdmi->sink_is_hdmi)
 		hdmi_enable_overflow_interrupts(hdmi);
 
 	return 0;
@@ -1444,6 +1441,7 @@ static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
 		dev_dbg(hdmi->dev, "got edid: width[%d] x height[%d]\n",
 			edid->width_cm, edid->height_cm);
 
+		hdmi->sink_is_hdmi = drm_detect_hdmi_monitor(edid);
 		drm_mode_connector_update_edid_property(connector, edid);
 		ret = drm_add_edid_modes(connector, edid);
 		kfree(edid);
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 07/12] drm: bridge/dw_hdmi: enable audio only if sink supports audio
  2015-08-08 16:02 ` Russell King - ARM Linux
@ 2015-08-08 16:03   ` Russell King
  -1 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:03 UTC (permalink / raw)
  To: linux-rockchip, dri-devel, linux-kernel, linux-arm-kernel
  Cc: Philipp Zabel, Andy Yan, Yakir Yang, Fabio Estevam, Sascha Hauer,
	Jon Nettleton, David Airlie

Only enable audio support if the sink supports audio in some form, as
defined via its EDID.  We discover this capability using the generic
drm_detect_monitor_audio() function.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/dw_hdmi.c | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index 7f764716f3c4..578d7362cd65 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -123,6 +123,7 @@ struct dw_hdmi {
 	struct i2c_adapter *ddc;
 	void __iomem *regs;
 	bool sink_is_hdmi;
+	bool sink_has_audio;
 
 	spinlock_t audio_lock;
 	struct mutex audio_mutex;
@@ -1271,13 +1272,17 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
 	/* HDMI Initialization Step B.3 */
 	dw_hdmi_enable_video_path(hdmi);
 
-	/* not for DVI mode */
-	if (hdmi->sink_is_hdmi) {
-		dev_dbg(hdmi->dev, "%s HDMI mode\n", __func__);
+	if (hdmi->sink_has_audio) {
+		dev_dbg(hdmi->dev, "sink has audio support\n");
 
 		/* HDMI Initialization Step E - Configure audio */
 		hdmi_clk_regenerator_update_pixel_clock(hdmi);
 		hdmi_enable_audio_clk(hdmi);
+	}
+
+	/* not for DVI mode */
+	if (hdmi->sink_is_hdmi) {
+		dev_dbg(hdmi->dev, "%s HDMI mode\n", __func__);
 
 		/* HDMI Initialization Step F - Configure AVI InfoFrame */
 		hdmi_config_AVI(hdmi, mode);
@@ -1442,6 +1447,7 @@ static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
 			edid->width_cm, edid->height_cm);
 
 		hdmi->sink_is_hdmi = drm_detect_hdmi_monitor(edid);
+		hdmi->sink_has_audio = drm_detect_monitor_audio(edid);
 		drm_mode_connector_update_edid_property(connector, edid);
 		ret = drm_add_edid_modes(connector, edid);
 		kfree(edid);
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 07/12] drm: bridge/dw_hdmi: enable audio only if sink supports audio
@ 2015-08-08 16:03   ` Russell King
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:03 UTC (permalink / raw)
  To: linux-arm-kernel

Only enable audio support if the sink supports audio in some form, as
defined via its EDID.  We discover this capability using the generic
drm_detect_monitor_audio() function.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/dw_hdmi.c | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index 7f764716f3c4..578d7362cd65 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -123,6 +123,7 @@ struct dw_hdmi {
 	struct i2c_adapter *ddc;
 	void __iomem *regs;
 	bool sink_is_hdmi;
+	bool sink_has_audio;
 
 	spinlock_t audio_lock;
 	struct mutex audio_mutex;
@@ -1271,13 +1272,17 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
 	/* HDMI Initialization Step B.3 */
 	dw_hdmi_enable_video_path(hdmi);
 
-	/* not for DVI mode */
-	if (hdmi->sink_is_hdmi) {
-		dev_dbg(hdmi->dev, "%s HDMI mode\n", __func__);
+	if (hdmi->sink_has_audio) {
+		dev_dbg(hdmi->dev, "sink has audio support\n");
 
 		/* HDMI Initialization Step E - Configure audio */
 		hdmi_clk_regenerator_update_pixel_clock(hdmi);
 		hdmi_enable_audio_clk(hdmi);
+	}
+
+	/* not for DVI mode */
+	if (hdmi->sink_is_hdmi) {
+		dev_dbg(hdmi->dev, "%s HDMI mode\n", __func__);
 
 		/* HDMI Initialization Step F - Configure AVI InfoFrame */
 		hdmi_config_AVI(hdmi, mode);
@@ -1442,6 +1447,7 @@ static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
 			edid->width_cm, edid->height_cm);
 
 		hdmi->sink_is_hdmi = drm_detect_hdmi_monitor(edid);
+		hdmi->sink_has_audio = drm_detect_monitor_audio(edid);
 		drm_mode_connector_update_edid_property(connector, edid);
 		ret = drm_add_edid_modes(connector, edid);
 		kfree(edid);
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 08/12] drm: bridge/dw_hdmi: avoid enabling interface in mode_set
  2015-08-08 16:02 ` Russell King - ARM Linux
@ 2015-08-08 16:04   ` Russell King
  -1 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:04 UTC (permalink / raw)
  To: linux-rockchip, dri-devel, linux-kernel, linux-arm-kernel
  Cc: Philipp Zabel, Andy Yan, Yakir Yang, Fabio Estevam, Sascha Hauer,
	Jon Nettleton, David Airlie

On a mode set, DRM makes the following sequence of calls:
* for_each_encoder
*   bridge	mode_fixup
*   encoder	mode_fixup
* crtc		mode_fixup
* for_each_encoder
*   bridge	disable
*   encoder	prepare
*   bridge	post_disable
* disable unused encoders
* crtc		prepare
* crtc		mode_set
* for_each_encoder
*   encoder	mode_set
*   bridge	mode_set
* crtc		commit
* for_each_encoder
*   bridge	pre_enable
*   encoder	commit
*   bridge	enable

dw_hdmi enables the HDMI output in both the bridge mode_set() and also
the bridge enable() step.  This is duplicated work - we can avoid the
setup in mode_set() and just do it in the enable() stage.  This
simplifies the code a little.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/dw_hdmi.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index 578d7362cd65..fbac8386552b 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -1389,8 +1389,6 @@ static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge,
 {
 	struct dw_hdmi *hdmi = bridge->driver_private;
 
-	dw_hdmi_setup(hdmi, mode);
-
 	/* Store the display mode for plugin/DKMS poweron events */
 	memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode));
 }
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 08/12] drm: bridge/dw_hdmi: avoid enabling interface in mode_set
@ 2015-08-08 16:04   ` Russell King
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:04 UTC (permalink / raw)
  To: linux-arm-kernel

On a mode set, DRM makes the following sequence of calls:
* for_each_encoder
*   bridge	mode_fixup
*   encoder	mode_fixup
* crtc		mode_fixup
* for_each_encoder
*   bridge	disable
*   encoder	prepare
*   bridge	post_disable
* disable unused encoders
* crtc		prepare
* crtc		mode_set
* for_each_encoder
*   encoder	mode_set
*   bridge	mode_set
* crtc		commit
* for_each_encoder
*   bridge	pre_enable
*   encoder	commit
*   bridge	enable

dw_hdmi enables the HDMI output in both the bridge mode_set() and also
the bridge enable() step.  This is duplicated work - we can avoid the
setup in mode_set() and just do it in the enable() stage.  This
simplifies the code a little.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/dw_hdmi.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index 578d7362cd65..fbac8386552b 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -1389,8 +1389,6 @@ static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge,
 {
 	struct dw_hdmi *hdmi = bridge->driver_private;
 
-	dw_hdmi_setup(hdmi, mode);
-
 	/* Store the display mode for plugin/DKMS poweron events */
 	memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode));
 }
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 09/12] drm: bridge/dw_hdmi: rename dw_hdmi_phy_enable_power()
  2015-08-08 16:02 ` Russell King - ARM Linux
  (?)
@ 2015-08-08 16:04   ` Russell King
  -1 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:04 UTC (permalink / raw)
  To: linux-rockchip, dri-devel, linux-kernel, linux-arm-kernel
  Cc: Philipp Zabel, Andy Yan, Yakir Yang, Fabio Estevam, Sascha Hauer,
	Jon Nettleton, David Airlie

dw_hdmi_phy_enable_power() is not about enabling and disabling power.
It is about allowing or preventing power-down mode being entered - the
register is documented as "Power-down enable (active low 0b)."

This can be seen as the bit has no effect when the HDMI phy is
operational on iMX6 hardware.

Rename the function to dw_hdmi_phy_enable_powerdown() to reflect the
documentation, make it take a bool for the 'enable' argument, and invert
the value to be written.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/dw_hdmi.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index fbac8386552b..7b8a4e942a71 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -737,9 +737,9 @@ static int hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data,
 	return 0;
 }
 
-static void dw_hdmi_phy_enable_power(struct dw_hdmi *hdmi, u8 enable)
+static void dw_hdmi_phy_enable_powerdown(struct dw_hdmi *hdmi, bool enable)
 {
-	hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
+	hdmi_mask_writeb(hdmi, !enable, HDMI_PHY_CONF0,
 			 HDMI_PHY_CONF0_PDZ_OFFSET,
 			 HDMI_PHY_CONF0_PDZ_MASK);
 }
@@ -879,7 +879,7 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep,
 	/* REMOVE CLK TERM */
 	hdmi_phy_i2c_write(hdmi, 0x8000, 0x05);  /* CKCALCTRL */
 
-	dw_hdmi_phy_enable_power(hdmi, 1);
+	dw_hdmi_phy_enable_powerdown(hdmi, false);
 
 	/* toggle TMDS enable */
 	dw_hdmi_phy_enable_tmds(hdmi, 0);
@@ -924,7 +924,7 @@ static int dw_hdmi_phy_init(struct dw_hdmi *hdmi)
 		dw_hdmi_phy_sel_data_en_pol(hdmi, 1);
 		dw_hdmi_phy_sel_interface_control(hdmi, 0);
 		dw_hdmi_phy_enable_tmds(hdmi, 0);
-		dw_hdmi_phy_enable_power(hdmi, 0);
+		dw_hdmi_phy_enable_powerdown(hdmi, true);
 
 		/* Enable CSC */
 		ret = hdmi_phy_configure(hdmi, 0, 8, cscon);
@@ -1155,7 +1155,7 @@ static void dw_hdmi_phy_disable(struct dw_hdmi *hdmi)
 		return;
 
 	dw_hdmi_phy_enable_tmds(hdmi, 0);
-	dw_hdmi_phy_enable_power(hdmi, 0);
+	dw_hdmi_phy_enable_powerdown(hdmi, true);
 
 	hdmi->phy_enabled = false;
 }
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 09/12] drm: bridge/dw_hdmi: rename dw_hdmi_phy_enable_power()
@ 2015-08-08 16:04   ` Russell King
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:04 UTC (permalink / raw)
  To: linux-rockchip, dri-devel, linux-kernel, linux-arm-kernel
  Cc: Fabio Estevam, Yakir Yang, Andy Yan

dw_hdmi_phy_enable_power() is not about enabling and disabling power.
It is about allowing or preventing power-down mode being entered - the
register is documented as "Power-down enable (active low 0b)."

This can be seen as the bit has no effect when the HDMI phy is
operational on iMX6 hardware.

Rename the function to dw_hdmi_phy_enable_powerdown() to reflect the
documentation, make it take a bool for the 'enable' argument, and invert
the value to be written.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/dw_hdmi.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index fbac8386552b..7b8a4e942a71 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -737,9 +737,9 @@ static int hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data,
 	return 0;
 }
 
-static void dw_hdmi_phy_enable_power(struct dw_hdmi *hdmi, u8 enable)
+static void dw_hdmi_phy_enable_powerdown(struct dw_hdmi *hdmi, bool enable)
 {
-	hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
+	hdmi_mask_writeb(hdmi, !enable, HDMI_PHY_CONF0,
 			 HDMI_PHY_CONF0_PDZ_OFFSET,
 			 HDMI_PHY_CONF0_PDZ_MASK);
 }
@@ -879,7 +879,7 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep,
 	/* REMOVE CLK TERM */
 	hdmi_phy_i2c_write(hdmi, 0x8000, 0x05);  /* CKCALCTRL */
 
-	dw_hdmi_phy_enable_power(hdmi, 1);
+	dw_hdmi_phy_enable_powerdown(hdmi, false);
 
 	/* toggle TMDS enable */
 	dw_hdmi_phy_enable_tmds(hdmi, 0);
@@ -924,7 +924,7 @@ static int dw_hdmi_phy_init(struct dw_hdmi *hdmi)
 		dw_hdmi_phy_sel_data_en_pol(hdmi, 1);
 		dw_hdmi_phy_sel_interface_control(hdmi, 0);
 		dw_hdmi_phy_enable_tmds(hdmi, 0);
-		dw_hdmi_phy_enable_power(hdmi, 0);
+		dw_hdmi_phy_enable_powerdown(hdmi, true);
 
 		/* Enable CSC */
 		ret = hdmi_phy_configure(hdmi, 0, 8, cscon);
@@ -1155,7 +1155,7 @@ static void dw_hdmi_phy_disable(struct dw_hdmi *hdmi)
 		return;
 
 	dw_hdmi_phy_enable_tmds(hdmi, 0);
-	dw_hdmi_phy_enable_power(hdmi, 0);
+	dw_hdmi_phy_enable_powerdown(hdmi, true);
 
 	hdmi->phy_enabled = false;
 }
-- 
2.1.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 09/12] drm: bridge/dw_hdmi: rename dw_hdmi_phy_enable_power()
@ 2015-08-08 16:04   ` Russell King
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:04 UTC (permalink / raw)
  To: linux-arm-kernel

dw_hdmi_phy_enable_power() is not about enabling and disabling power.
It is about allowing or preventing power-down mode being entered - the
register is documented as "Power-down enable (active low 0b)."

This can be seen as the bit has no effect when the HDMI phy is
operational on iMX6 hardware.

Rename the function to dw_hdmi_phy_enable_powerdown() to reflect the
documentation, make it take a bool for the 'enable' argument, and invert
the value to be written.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/dw_hdmi.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index fbac8386552b..7b8a4e942a71 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -737,9 +737,9 @@ static int hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data,
 	return 0;
 }
 
-static void dw_hdmi_phy_enable_power(struct dw_hdmi *hdmi, u8 enable)
+static void dw_hdmi_phy_enable_powerdown(struct dw_hdmi *hdmi, bool enable)
 {
-	hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
+	hdmi_mask_writeb(hdmi, !enable, HDMI_PHY_CONF0,
 			 HDMI_PHY_CONF0_PDZ_OFFSET,
 			 HDMI_PHY_CONF0_PDZ_MASK);
 }
@@ -879,7 +879,7 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep,
 	/* REMOVE CLK TERM */
 	hdmi_phy_i2c_write(hdmi, 0x8000, 0x05);  /* CKCALCTRL */
 
-	dw_hdmi_phy_enable_power(hdmi, 1);
+	dw_hdmi_phy_enable_powerdown(hdmi, false);
 
 	/* toggle TMDS enable */
 	dw_hdmi_phy_enable_tmds(hdmi, 0);
@@ -924,7 +924,7 @@ static int dw_hdmi_phy_init(struct dw_hdmi *hdmi)
 		dw_hdmi_phy_sel_data_en_pol(hdmi, 1);
 		dw_hdmi_phy_sel_interface_control(hdmi, 0);
 		dw_hdmi_phy_enable_tmds(hdmi, 0);
-		dw_hdmi_phy_enable_power(hdmi, 0);
+		dw_hdmi_phy_enable_powerdown(hdmi, true);
 
 		/* Enable CSC */
 		ret = hdmi_phy_configure(hdmi, 0, 8, cscon);
@@ -1155,7 +1155,7 @@ static void dw_hdmi_phy_disable(struct dw_hdmi *hdmi)
 		return;
 
 	dw_hdmi_phy_enable_tmds(hdmi, 0);
-	dw_hdmi_phy_enable_power(hdmi, 0);
+	dw_hdmi_phy_enable_powerdown(hdmi, true);
 
 	hdmi->phy_enabled = false;
 }
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 10/12] drm: bridge/dw_hdmi: fix phy enable/disable handling
@ 2015-08-08 16:04   ` Russell King
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:04 UTC (permalink / raw)
  To: linux-rockchip, dri-devel, linux-kernel, linux-arm-kernel
  Cc: Philipp Zabel, Andy Yan, Yakir Yang, Fabio Estevam, Sascha Hauer,
	Jon Nettleton, David Airlie

The dw_hdmi enable/disable handling is particularly weak in several
regards:
* The hotplug interrupt could call hdmi_poweron() or hdmi_poweroff()
  while DRM is setting a mode, which could race with a mode being set.
* Hotplug will always re-enable the phy whenever it detects an active
  hotplug signal, even if DRM has disabled the output.

Resolve all of these by introducing a mutex to prevent races, and a
state-tracking bool so we know whether DRM wishes the output to be
enabled.  We choose to use our own mutex rather than ->struct_mutex
so that we can still process interrupts in a timely fashion.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/dw_hdmi.c | 29 ++++++++++++++++++++++-------
 1 file changed, 22 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index 7b8a4e942a71..0ee188930d26 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -125,6 +125,9 @@ struct dw_hdmi {
 	bool sink_is_hdmi;
 	bool sink_has_audio;
 
+	struct mutex mutex;		/* for state below and previous_mode */
+	bool disabled;			/* DRM has disabled our bridge */
+
 	spinlock_t audio_lock;
 	struct mutex audio_mutex;
 	unsigned int sample_rate;
@@ -1389,8 +1392,12 @@ static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge,
 {
 	struct dw_hdmi *hdmi = bridge->driver_private;
 
+	mutex_lock(&hdmi->mutex);
+
 	/* Store the display mode for plugin/DKMS poweron events */
 	memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode));
+
+	mutex_unlock(&hdmi->mutex);
 }
 
 static bool dw_hdmi_bridge_mode_fixup(struct drm_bridge *bridge,
@@ -1404,14 +1411,20 @@ static void dw_hdmi_bridge_disable(struct drm_bridge *bridge)
 {
 	struct dw_hdmi *hdmi = bridge->driver_private;
 
+	mutex_lock(&hdmi->mutex);
+	hdmi->disabled = true;
 	dw_hdmi_poweroff(hdmi);
+	mutex_unlock(&hdmi->mutex);
 }
 
 static void dw_hdmi_bridge_enable(struct drm_bridge *bridge)
 {
 	struct dw_hdmi *hdmi = bridge->driver_private;
 
+	mutex_lock(&hdmi->mutex);
 	dw_hdmi_poweron(hdmi);
+	hdmi->disabled = false;
+	mutex_unlock(&hdmi->mutex);
 }
 
 static void dw_hdmi_bridge_nop(struct drm_bridge *bridge)
@@ -1534,20 +1547,20 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
 	phy_int_pol = hdmi_readb(hdmi, HDMI_PHY_POL0);
 
 	if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
+		hdmi_modb(hdmi, ~phy_int_pol, HDMI_PHY_HPD, HDMI_PHY_POL0);
+		mutex_lock(&hdmi->mutex);
 		if (phy_int_pol & HDMI_PHY_HPD) {
 			dev_dbg(hdmi->dev, "EVENT=plugin\n");
 
-			hdmi_modb(hdmi, 0, HDMI_PHY_HPD, HDMI_PHY_POL0);
-
-			dw_hdmi_poweron(hdmi);
+			if (!hdmi->disabled)
+				dw_hdmi_poweron(hdmi);
 		} else {
 			dev_dbg(hdmi->dev, "EVENT=plugout\n");
 
-			hdmi_modb(hdmi, HDMI_PHY_HPD, HDMI_PHY_HPD,
-				  HDMI_PHY_POL0);
-
-			dw_hdmi_poweroff(hdmi);
+			if (!hdmi->disabled)
+				dw_hdmi_poweroff(hdmi);
 		}
+		mutex_unlock(&hdmi->mutex);
 		drm_helper_hpd_irq_event(hdmi->bridge->dev);
 	}
 
@@ -1617,7 +1630,9 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 	hdmi->sample_rate = 48000;
 	hdmi->ratio = 100;
 	hdmi->encoder = encoder;
+	hdmi->disabled = true;
 
+	mutex_init(&hdmi->mutex);
 	mutex_init(&hdmi->audio_mutex);
 	spin_lock_init(&hdmi->audio_lock);
 
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 10/12] drm: bridge/dw_hdmi: fix phy enable/disable handling
@ 2015-08-08 16:04   ` Russell King
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:04 UTC (permalink / raw)
  To: linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: Fabio Estevam, David Airlie, Sascha Hauer, Philipp Zabel,
	Yakir Yang, Andy Yan, Jon Nettleton

The dw_hdmi enable/disable handling is particularly weak in several
regards:
* The hotplug interrupt could call hdmi_poweron() or hdmi_poweroff()
  while DRM is setting a mode, which could race with a mode being set.
* Hotplug will always re-enable the phy whenever it detects an active
  hotplug signal, even if DRM has disabled the output.

Resolve all of these by introducing a mutex to prevent races, and a
state-tracking bool so we know whether DRM wishes the output to be
enabled.  We choose to use our own mutex rather than ->struct_mutex
so that we can still process interrupts in a timely fashion.

Signed-off-by: Russell King <rmk+kernel-lFZ/pmaqli7XmaaqVzeoHQ@public.gmane.org>
---
 drivers/gpu/drm/bridge/dw_hdmi.c | 29 ++++++++++++++++++++++-------
 1 file changed, 22 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index 7b8a4e942a71..0ee188930d26 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -125,6 +125,9 @@ struct dw_hdmi {
 	bool sink_is_hdmi;
 	bool sink_has_audio;
 
+	struct mutex mutex;		/* for state below and previous_mode */
+	bool disabled;			/* DRM has disabled our bridge */
+
 	spinlock_t audio_lock;
 	struct mutex audio_mutex;
 	unsigned int sample_rate;
@@ -1389,8 +1392,12 @@ static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge,
 {
 	struct dw_hdmi *hdmi = bridge->driver_private;
 
+	mutex_lock(&hdmi->mutex);
+
 	/* Store the display mode for plugin/DKMS poweron events */
 	memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode));
+
+	mutex_unlock(&hdmi->mutex);
 }
 
 static bool dw_hdmi_bridge_mode_fixup(struct drm_bridge *bridge,
@@ -1404,14 +1411,20 @@ static void dw_hdmi_bridge_disable(struct drm_bridge *bridge)
 {
 	struct dw_hdmi *hdmi = bridge->driver_private;
 
+	mutex_lock(&hdmi->mutex);
+	hdmi->disabled = true;
 	dw_hdmi_poweroff(hdmi);
+	mutex_unlock(&hdmi->mutex);
 }
 
 static void dw_hdmi_bridge_enable(struct drm_bridge *bridge)
 {
 	struct dw_hdmi *hdmi = bridge->driver_private;
 
+	mutex_lock(&hdmi->mutex);
 	dw_hdmi_poweron(hdmi);
+	hdmi->disabled = false;
+	mutex_unlock(&hdmi->mutex);
 }
 
 static void dw_hdmi_bridge_nop(struct drm_bridge *bridge)
@@ -1534,20 +1547,20 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
 	phy_int_pol = hdmi_readb(hdmi, HDMI_PHY_POL0);
 
 	if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
+		hdmi_modb(hdmi, ~phy_int_pol, HDMI_PHY_HPD, HDMI_PHY_POL0);
+		mutex_lock(&hdmi->mutex);
 		if (phy_int_pol & HDMI_PHY_HPD) {
 			dev_dbg(hdmi->dev, "EVENT=plugin\n");
 
-			hdmi_modb(hdmi, 0, HDMI_PHY_HPD, HDMI_PHY_POL0);
-
-			dw_hdmi_poweron(hdmi);
+			if (!hdmi->disabled)
+				dw_hdmi_poweron(hdmi);
 		} else {
 			dev_dbg(hdmi->dev, "EVENT=plugout\n");
 
-			hdmi_modb(hdmi, HDMI_PHY_HPD, HDMI_PHY_HPD,
-				  HDMI_PHY_POL0);
-
-			dw_hdmi_poweroff(hdmi);
+			if (!hdmi->disabled)
+				dw_hdmi_poweroff(hdmi);
 		}
+		mutex_unlock(&hdmi->mutex);
 		drm_helper_hpd_irq_event(hdmi->bridge->dev);
 	}
 
@@ -1617,7 +1630,9 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 	hdmi->sample_rate = 48000;
 	hdmi->ratio = 100;
 	hdmi->encoder = encoder;
+	hdmi->disabled = true;
 
+	mutex_init(&hdmi->mutex);
 	mutex_init(&hdmi->audio_mutex);
 	spin_lock_init(&hdmi->audio_lock);
 
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 10/12] drm: bridge/dw_hdmi: fix phy enable/disable handling
@ 2015-08-08 16:04   ` Russell King
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:04 UTC (permalink / raw)
  To: linux-arm-kernel

The dw_hdmi enable/disable handling is particularly weak in several
regards:
* The hotplug interrupt could call hdmi_poweron() or hdmi_poweroff()
  while DRM is setting a mode, which could race with a mode being set.
* Hotplug will always re-enable the phy whenever it detects an active
  hotplug signal, even if DRM has disabled the output.

Resolve all of these by introducing a mutex to prevent races, and a
state-tracking bool so we know whether DRM wishes the output to be
enabled.  We choose to use our own mutex rather than ->struct_mutex
so that we can still process interrupts in a timely fashion.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/dw_hdmi.c | 29 ++++++++++++++++++++++-------
 1 file changed, 22 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index 7b8a4e942a71..0ee188930d26 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -125,6 +125,9 @@ struct dw_hdmi {
 	bool sink_is_hdmi;
 	bool sink_has_audio;
 
+	struct mutex mutex;		/* for state below and previous_mode */
+	bool disabled;			/* DRM has disabled our bridge */
+
 	spinlock_t audio_lock;
 	struct mutex audio_mutex;
 	unsigned int sample_rate;
@@ -1389,8 +1392,12 @@ static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge,
 {
 	struct dw_hdmi *hdmi = bridge->driver_private;
 
+	mutex_lock(&hdmi->mutex);
+
 	/* Store the display mode for plugin/DKMS poweron events */
 	memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode));
+
+	mutex_unlock(&hdmi->mutex);
 }
 
 static bool dw_hdmi_bridge_mode_fixup(struct drm_bridge *bridge,
@@ -1404,14 +1411,20 @@ static void dw_hdmi_bridge_disable(struct drm_bridge *bridge)
 {
 	struct dw_hdmi *hdmi = bridge->driver_private;
 
+	mutex_lock(&hdmi->mutex);
+	hdmi->disabled = true;
 	dw_hdmi_poweroff(hdmi);
+	mutex_unlock(&hdmi->mutex);
 }
 
 static void dw_hdmi_bridge_enable(struct drm_bridge *bridge)
 {
 	struct dw_hdmi *hdmi = bridge->driver_private;
 
+	mutex_lock(&hdmi->mutex);
 	dw_hdmi_poweron(hdmi);
+	hdmi->disabled = false;
+	mutex_unlock(&hdmi->mutex);
 }
 
 static void dw_hdmi_bridge_nop(struct drm_bridge *bridge)
@@ -1534,20 +1547,20 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
 	phy_int_pol = hdmi_readb(hdmi, HDMI_PHY_POL0);
 
 	if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
+		hdmi_modb(hdmi, ~phy_int_pol, HDMI_PHY_HPD, HDMI_PHY_POL0);
+		mutex_lock(&hdmi->mutex);
 		if (phy_int_pol & HDMI_PHY_HPD) {
 			dev_dbg(hdmi->dev, "EVENT=plugin\n");
 
-			hdmi_modb(hdmi, 0, HDMI_PHY_HPD, HDMI_PHY_POL0);
-
-			dw_hdmi_poweron(hdmi);
+			if (!hdmi->disabled)
+				dw_hdmi_poweron(hdmi);
 		} else {
 			dev_dbg(hdmi->dev, "EVENT=plugout\n");
 
-			hdmi_modb(hdmi, HDMI_PHY_HPD, HDMI_PHY_HPD,
-				  HDMI_PHY_POL0);
-
-			dw_hdmi_poweroff(hdmi);
+			if (!hdmi->disabled)
+				dw_hdmi_poweroff(hdmi);
 		}
+		mutex_unlock(&hdmi->mutex);
 		drm_helper_hpd_irq_event(hdmi->bridge->dev);
 	}
 
@@ -1617,7 +1630,9 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 	hdmi->sample_rate = 48000;
 	hdmi->ratio = 100;
 	hdmi->encoder = encoder;
+	hdmi->disabled = true;
 
+	mutex_init(&hdmi->mutex);
 	mutex_init(&hdmi->audio_mutex);
 	spin_lock_init(&hdmi->audio_lock);
 
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 11/12] drm: bridge/dw_hdmi: add connector mode forcing
  2015-08-08 16:02 ` Russell King - ARM Linux
  (?)
@ 2015-08-08 16:04   ` Russell King
  -1 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:04 UTC (permalink / raw)
  To: linux-rockchip, dri-devel, linux-kernel, linux-arm-kernel
  Cc: Philipp Zabel, Andy Yan, Yakir Yang, Fabio Estevam, Sascha Hauer,
	Jon Nettleton, David Airlie

When connected to HDMI sources, some DVI monitors de-assert their HPD
signal and TDMS loads for one seconds every four seconds when there is
no signal present on the connection.

Unfortunately, this behaviour is indistinguishable from a proper HDMI
setup with an AV receiver in the path to the display: the HDMI spec
requires us to detect HPD deassertions as short as 100ms, which indicate
that the EDID has changed.

Since it is possible to connect a DVI monitor to an AV receiver and then
to a HDMI source, merely working around this by detecting the lack of
HDMI vendor block in the EDID is insufficient - the AV receiver is at
liberty to modify the EDID as it sees fit, and it will place its own
parameters into the EDID including the HDMI vendor block.

DRM has support for forcing the state of a connector, which we should
implement to allow us to work around these broken DVI monitors - we can
tell DRM to force the connection state to indicate that there is always
a device connected to work around this problem.  Although this requires
manual configuration, it is better than nothing at all.

When a forced connection state has been set, there is no point handling
our RXSENSE interrupts, so disable them in this circumstance.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/dw_hdmi.c | 51 ++++++++++++++++++++++++++++++++++++----
 1 file changed, 47 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index 0ee188930d26..2d1c7e4ec086 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -126,7 +126,9 @@ struct dw_hdmi {
 	bool sink_has_audio;
 
 	struct mutex mutex;		/* for state below and previous_mode */
+	enum drm_connector_force force;	/* mutex-protected force state */
 	bool disabled;			/* DRM has disabled our bridge */
+	bool bridge_is_on;		/* indicates the bridge is on */
 
 	spinlock_t audio_lock;
 	struct mutex audio_mutex;
@@ -1378,12 +1380,36 @@ static void initialize_hdmi_ih_mutes(struct dw_hdmi *hdmi)
 
 static void dw_hdmi_poweron(struct dw_hdmi *hdmi)
 {
+	hdmi->bridge_is_on = true;
 	dw_hdmi_setup(hdmi, &hdmi->previous_mode);
 }
 
 static void dw_hdmi_poweroff(struct dw_hdmi *hdmi)
 {
 	dw_hdmi_phy_disable(hdmi);
+	hdmi->bridge_is_on = false;
+}
+
+static void dw_hdmi_update_power(struct dw_hdmi *hdmi)
+{
+	int force = hdmi->force;
+
+	if (hdmi->disabled) {
+		force = DRM_FORCE_OFF;
+	} else if (force == DRM_FORCE_UNSPECIFIED) {
+		if (hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD)
+			force = DRM_FORCE_ON;
+		else
+			force = DRM_FORCE_OFF;
+	}
+
+	if (force == DRM_FORCE_OFF) {
+		if (hdmi->bridge_is_on)
+			dw_hdmi_poweroff(hdmi);
+	} else {
+		if (!hdmi->bridge_is_on)
+			dw_hdmi_poweron(hdmi);
+	}
 }
 
 static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge,
@@ -1413,7 +1439,7 @@ static void dw_hdmi_bridge_disable(struct drm_bridge *bridge)
 
 	mutex_lock(&hdmi->mutex);
 	hdmi->disabled = true;
-	dw_hdmi_poweroff(hdmi);
+	dw_hdmi_update_power(hdmi);
 	mutex_unlock(&hdmi->mutex);
 }
 
@@ -1422,8 +1448,8 @@ static void dw_hdmi_bridge_enable(struct drm_bridge *bridge)
 	struct dw_hdmi *hdmi = bridge->driver_private;
 
 	mutex_lock(&hdmi->mutex);
-	dw_hdmi_poweron(hdmi);
 	hdmi->disabled = false;
+	dw_hdmi_update_power(hdmi);
 	mutex_unlock(&hdmi->mutex);
 }
 
@@ -1438,6 +1464,11 @@ dw_hdmi_connector_detect(struct drm_connector *connector, bool force)
 	struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
 					     connector);
 
+	mutex_lock(&hdmi->mutex);
+	hdmi->force = DRM_FORCE_UNSPECIFIED;
+	dw_hdmi_update_power(hdmi);
+	mutex_unlock(&hdmi->mutex);
+
 	return hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD ?
 		connector_status_connected : connector_status_disconnected;
 }
@@ -1502,11 +1533,23 @@ static void dw_hdmi_connector_destroy(struct drm_connector *connector)
 	drm_connector_cleanup(connector);
 }
 
+static void dw_hdmi_connector_force(struct drm_connector *connector)
+{
+	struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
+					     connector);
+
+	mutex_lock(&hdmi->mutex);
+	hdmi->force = connector->force;
+	dw_hdmi_update_power(hdmi);
+	mutex_unlock(&hdmi->mutex);
+}
+
 static struct drm_connector_funcs dw_hdmi_connector_funcs = {
 	.dpms = drm_helper_connector_dpms,
 	.fill_modes = drm_helper_probe_single_connector_modes,
 	.detect = dw_hdmi_connector_detect,
 	.destroy = dw_hdmi_connector_destroy,
+	.force = dw_hdmi_connector_force,
 };
 
 static struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs = {
@@ -1552,12 +1595,12 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
 		if (phy_int_pol & HDMI_PHY_HPD) {
 			dev_dbg(hdmi->dev, "EVENT=plugin\n");
 
-			if (!hdmi->disabled)
+			if (!hdmi->disabled && !hdmi->force)
 				dw_hdmi_poweron(hdmi);
 		} else {
 			dev_dbg(hdmi->dev, "EVENT=plugout\n");
 
-			if (!hdmi->disabled)
+			if (!hdmi->disabled && !hdmi->force)
 				dw_hdmi_poweroff(hdmi);
 		}
 		mutex_unlock(&hdmi->mutex);
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 11/12] drm: bridge/dw_hdmi: add connector mode forcing
@ 2015-08-08 16:04   ` Russell King
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:04 UTC (permalink / raw)
  To: linux-rockchip, dri-devel, linux-kernel, linux-arm-kernel
  Cc: Fabio Estevam, Yakir Yang, Andy Yan

When connected to HDMI sources, some DVI monitors de-assert their HPD
signal and TDMS loads for one seconds every four seconds when there is
no signal present on the connection.

Unfortunately, this behaviour is indistinguishable from a proper HDMI
setup with an AV receiver in the path to the display: the HDMI spec
requires us to detect HPD deassertions as short as 100ms, which indicate
that the EDID has changed.

Since it is possible to connect a DVI monitor to an AV receiver and then
to a HDMI source, merely working around this by detecting the lack of
HDMI vendor block in the EDID is insufficient - the AV receiver is at
liberty to modify the EDID as it sees fit, and it will place its own
parameters into the EDID including the HDMI vendor block.

DRM has support for forcing the state of a connector, which we should
implement to allow us to work around these broken DVI monitors - we can
tell DRM to force the connection state to indicate that there is always
a device connected to work around this problem.  Although this requires
manual configuration, it is better than nothing at all.

When a forced connection state has been set, there is no point handling
our RXSENSE interrupts, so disable them in this circumstance.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/dw_hdmi.c | 51 ++++++++++++++++++++++++++++++++++++----
 1 file changed, 47 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index 0ee188930d26..2d1c7e4ec086 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -126,7 +126,9 @@ struct dw_hdmi {
 	bool sink_has_audio;
 
 	struct mutex mutex;		/* for state below and previous_mode */
+	enum drm_connector_force force;	/* mutex-protected force state */
 	bool disabled;			/* DRM has disabled our bridge */
+	bool bridge_is_on;		/* indicates the bridge is on */
 
 	spinlock_t audio_lock;
 	struct mutex audio_mutex;
@@ -1378,12 +1380,36 @@ static void initialize_hdmi_ih_mutes(struct dw_hdmi *hdmi)
 
 static void dw_hdmi_poweron(struct dw_hdmi *hdmi)
 {
+	hdmi->bridge_is_on = true;
 	dw_hdmi_setup(hdmi, &hdmi->previous_mode);
 }
 
 static void dw_hdmi_poweroff(struct dw_hdmi *hdmi)
 {
 	dw_hdmi_phy_disable(hdmi);
+	hdmi->bridge_is_on = false;
+}
+
+static void dw_hdmi_update_power(struct dw_hdmi *hdmi)
+{
+	int force = hdmi->force;
+
+	if (hdmi->disabled) {
+		force = DRM_FORCE_OFF;
+	} else if (force == DRM_FORCE_UNSPECIFIED) {
+		if (hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD)
+			force = DRM_FORCE_ON;
+		else
+			force = DRM_FORCE_OFF;
+	}
+
+	if (force == DRM_FORCE_OFF) {
+		if (hdmi->bridge_is_on)
+			dw_hdmi_poweroff(hdmi);
+	} else {
+		if (!hdmi->bridge_is_on)
+			dw_hdmi_poweron(hdmi);
+	}
 }
 
 static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge,
@@ -1413,7 +1439,7 @@ static void dw_hdmi_bridge_disable(struct drm_bridge *bridge)
 
 	mutex_lock(&hdmi->mutex);
 	hdmi->disabled = true;
-	dw_hdmi_poweroff(hdmi);
+	dw_hdmi_update_power(hdmi);
 	mutex_unlock(&hdmi->mutex);
 }
 
@@ -1422,8 +1448,8 @@ static void dw_hdmi_bridge_enable(struct drm_bridge *bridge)
 	struct dw_hdmi *hdmi = bridge->driver_private;
 
 	mutex_lock(&hdmi->mutex);
-	dw_hdmi_poweron(hdmi);
 	hdmi->disabled = false;
+	dw_hdmi_update_power(hdmi);
 	mutex_unlock(&hdmi->mutex);
 }
 
@@ -1438,6 +1464,11 @@ dw_hdmi_connector_detect(struct drm_connector *connector, bool force)
 	struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
 					     connector);
 
+	mutex_lock(&hdmi->mutex);
+	hdmi->force = DRM_FORCE_UNSPECIFIED;
+	dw_hdmi_update_power(hdmi);
+	mutex_unlock(&hdmi->mutex);
+
 	return hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD ?
 		connector_status_connected : connector_status_disconnected;
 }
@@ -1502,11 +1533,23 @@ static void dw_hdmi_connector_destroy(struct drm_connector *connector)
 	drm_connector_cleanup(connector);
 }
 
+static void dw_hdmi_connector_force(struct drm_connector *connector)
+{
+	struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
+					     connector);
+
+	mutex_lock(&hdmi->mutex);
+	hdmi->force = connector->force;
+	dw_hdmi_update_power(hdmi);
+	mutex_unlock(&hdmi->mutex);
+}
+
 static struct drm_connector_funcs dw_hdmi_connector_funcs = {
 	.dpms = drm_helper_connector_dpms,
 	.fill_modes = drm_helper_probe_single_connector_modes,
 	.detect = dw_hdmi_connector_detect,
 	.destroy = dw_hdmi_connector_destroy,
+	.force = dw_hdmi_connector_force,
 };
 
 static struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs = {
@@ -1552,12 +1595,12 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
 		if (phy_int_pol & HDMI_PHY_HPD) {
 			dev_dbg(hdmi->dev, "EVENT=plugin\n");
 
-			if (!hdmi->disabled)
+			if (!hdmi->disabled && !hdmi->force)
 				dw_hdmi_poweron(hdmi);
 		} else {
 			dev_dbg(hdmi->dev, "EVENT=plugout\n");
 
-			if (!hdmi->disabled)
+			if (!hdmi->disabled && !hdmi->force)
 				dw_hdmi_poweroff(hdmi);
 		}
 		mutex_unlock(&hdmi->mutex);
-- 
2.1.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 11/12] drm: bridge/dw_hdmi: add connector mode forcing
@ 2015-08-08 16:04   ` Russell King
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:04 UTC (permalink / raw)
  To: linux-arm-kernel

When connected to HDMI sources, some DVI monitors de-assert their HPD
signal and TDMS loads for one seconds every four seconds when there is
no signal present on the connection.

Unfortunately, this behaviour is indistinguishable from a proper HDMI
setup with an AV receiver in the path to the display: the HDMI spec
requires us to detect HPD deassertions as short as 100ms, which indicate
that the EDID has changed.

Since it is possible to connect a DVI monitor to an AV receiver and then
to a HDMI source, merely working around this by detecting the lack of
HDMI vendor block in the EDID is insufficient - the AV receiver is at
liberty to modify the EDID as it sees fit, and it will place its own
parameters into the EDID including the HDMI vendor block.

DRM has support for forcing the state of a connector, which we should
implement to allow us to work around these broken DVI monitors - we can
tell DRM to force the connection state to indicate that there is always
a device connected to work around this problem.  Although this requires
manual configuration, it is better than nothing at all.

When a forced connection state has been set, there is no point handling
our RXSENSE interrupts, so disable them in this circumstance.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/dw_hdmi.c | 51 ++++++++++++++++++++++++++++++++++++----
 1 file changed, 47 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index 0ee188930d26..2d1c7e4ec086 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -126,7 +126,9 @@ struct dw_hdmi {
 	bool sink_has_audio;
 
 	struct mutex mutex;		/* for state below and previous_mode */
+	enum drm_connector_force force;	/* mutex-protected force state */
 	bool disabled;			/* DRM has disabled our bridge */
+	bool bridge_is_on;		/* indicates the bridge is on */
 
 	spinlock_t audio_lock;
 	struct mutex audio_mutex;
@@ -1378,12 +1380,36 @@ static void initialize_hdmi_ih_mutes(struct dw_hdmi *hdmi)
 
 static void dw_hdmi_poweron(struct dw_hdmi *hdmi)
 {
+	hdmi->bridge_is_on = true;
 	dw_hdmi_setup(hdmi, &hdmi->previous_mode);
 }
 
 static void dw_hdmi_poweroff(struct dw_hdmi *hdmi)
 {
 	dw_hdmi_phy_disable(hdmi);
+	hdmi->bridge_is_on = false;
+}
+
+static void dw_hdmi_update_power(struct dw_hdmi *hdmi)
+{
+	int force = hdmi->force;
+
+	if (hdmi->disabled) {
+		force = DRM_FORCE_OFF;
+	} else if (force == DRM_FORCE_UNSPECIFIED) {
+		if (hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD)
+			force = DRM_FORCE_ON;
+		else
+			force = DRM_FORCE_OFF;
+	}
+
+	if (force == DRM_FORCE_OFF) {
+		if (hdmi->bridge_is_on)
+			dw_hdmi_poweroff(hdmi);
+	} else {
+		if (!hdmi->bridge_is_on)
+			dw_hdmi_poweron(hdmi);
+	}
 }
 
 static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge,
@@ -1413,7 +1439,7 @@ static void dw_hdmi_bridge_disable(struct drm_bridge *bridge)
 
 	mutex_lock(&hdmi->mutex);
 	hdmi->disabled = true;
-	dw_hdmi_poweroff(hdmi);
+	dw_hdmi_update_power(hdmi);
 	mutex_unlock(&hdmi->mutex);
 }
 
@@ -1422,8 +1448,8 @@ static void dw_hdmi_bridge_enable(struct drm_bridge *bridge)
 	struct dw_hdmi *hdmi = bridge->driver_private;
 
 	mutex_lock(&hdmi->mutex);
-	dw_hdmi_poweron(hdmi);
 	hdmi->disabled = false;
+	dw_hdmi_update_power(hdmi);
 	mutex_unlock(&hdmi->mutex);
 }
 
@@ -1438,6 +1464,11 @@ dw_hdmi_connector_detect(struct drm_connector *connector, bool force)
 	struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
 					     connector);
 
+	mutex_lock(&hdmi->mutex);
+	hdmi->force = DRM_FORCE_UNSPECIFIED;
+	dw_hdmi_update_power(hdmi);
+	mutex_unlock(&hdmi->mutex);
+
 	return hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD ?
 		connector_status_connected : connector_status_disconnected;
 }
@@ -1502,11 +1533,23 @@ static void dw_hdmi_connector_destroy(struct drm_connector *connector)
 	drm_connector_cleanup(connector);
 }
 
+static void dw_hdmi_connector_force(struct drm_connector *connector)
+{
+	struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
+					     connector);
+
+	mutex_lock(&hdmi->mutex);
+	hdmi->force = connector->force;
+	dw_hdmi_update_power(hdmi);
+	mutex_unlock(&hdmi->mutex);
+}
+
 static struct drm_connector_funcs dw_hdmi_connector_funcs = {
 	.dpms = drm_helper_connector_dpms,
 	.fill_modes = drm_helper_probe_single_connector_modes,
 	.detect = dw_hdmi_connector_detect,
 	.destroy = dw_hdmi_connector_destroy,
+	.force = dw_hdmi_connector_force,
 };
 
 static struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs = {
@@ -1552,12 +1595,12 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
 		if (phy_int_pol & HDMI_PHY_HPD) {
 			dev_dbg(hdmi->dev, "EVENT=plugin\n");
 
-			if (!hdmi->disabled)
+			if (!hdmi->disabled && !hdmi->force)
 				dw_hdmi_poweron(hdmi);
 		} else {
 			dev_dbg(hdmi->dev, "EVENT=plugout\n");
 
-			if (!hdmi->disabled)
+			if (!hdmi->disabled && !hdmi->force)
 				dw_hdmi_poweroff(hdmi);
 		}
 		mutex_unlock(&hdmi->mutex);
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 12/12] drm: bridge/dw_hdmi: improve HDMI enable/disable handling
  2015-08-08 16:02 ` Russell King - ARM Linux
@ 2015-08-08 16:04   ` Russell King
  -1 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:04 UTC (permalink / raw)
  To: linux-rockchip, dri-devel, linux-kernel, linux-arm-kernel
  Cc: Philipp Zabel, Andy Yan, Yakir Yang, Fabio Estevam, Sascha Hauer,
	Jon Nettleton, David Airlie

HDMI sinks are permitted to de-assert and re-assert the HPD signal to
indicate that their EDID has been updated, which may not involve a
change of video information.

An example of where such a situation can arise is when an AV receiver
is connected between the source and the display device.  Events which
can cause the HPD to be deasserted include:

 * turning on or switching to standby the AV receiver.
 * turning on or switching to standby the display device.

Each of these can change the entire EDID data, or just a part of the
EDID data - it's up to the connected HDMI sink to do what they desire
here.  For example

 - with the AV receiver and display device both in standby, a source
   connected to the AV receiver may provide its own EDID to the source.
 - turning on the display device causes the display device's EDID to be
   made available in an unmodified form to the source.
 - subsequently turning on the AV receiver then provides a modified
   version of the display device's EDID.

Moreover, HPD doesn't tell us whether something is actually listening
on the HDMI TDMS signals.  The phy gives us a set of RXSENSE indications
which tell us whether there is a sink connected to the TMDS signals.

Currently, we use the HPD signal to enable or disable the HDMI block,
which is questionable when HPD is used in this manner.  Using the
RXSENSE would be more appropriate, but there is some bad behaviour
which needs to be coped with.  The iMX6 implementation lets the TMDS
signals float when the phy is "powered down", which cause spurious
interrupts.  Rather than just using RXSENSE, use RXSENSE and HPD
becoming both active to signal the presence of a device, but loss
of RXSENSE to indicate that the device has been unplugged.

The side effect of this change is that a sink deasserting the HPD
signal to cause a re-read of the EDID data will not cause the bridge
to immediately disable the video signal.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/dw_hdmi.c | 124 ++++++++++++++++++++++++++++++++-------
 1 file changed, 102 insertions(+), 22 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index 2d1c7e4ec086..fba25607ef88 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -129,6 +129,8 @@ struct dw_hdmi {
 	enum drm_connector_force force;	/* mutex-protected force state */
 	bool disabled;			/* DRM has disabled our bridge */
 	bool bridge_is_on;		/* indicates the bridge is on */
+	bool rxsense;			/* rxsense state */
+	u8 phy_mask;			/* desired phy int mask settings */
 
 	spinlock_t audio_lock;
 	struct mutex audio_mutex;
@@ -142,6 +144,14 @@ struct dw_hdmi {
 	u8 (*read)(struct dw_hdmi *hdmi, int offset);
 };
 
+#define HDMI_IH_PHY_STAT0_RX_SENSE \
+	(HDMI_IH_PHY_STAT0_RX_SENSE0 | HDMI_IH_PHY_STAT0_RX_SENSE1 | \
+	 HDMI_IH_PHY_STAT0_RX_SENSE2 | HDMI_IH_PHY_STAT0_RX_SENSE3)
+
+#define HDMI_PHY_RX_SENSE \
+	(HDMI_PHY_RX_SENSE0 | HDMI_PHY_RX_SENSE1 | \
+	 HDMI_PHY_RX_SENSE2 | HDMI_PHY_RX_SENSE3)
+
 static void dw_hdmi_writel(struct dw_hdmi *hdmi, u8 val, int offset)
 {
 	writel(val, hdmi->regs + (offset << 2));
@@ -1318,10 +1328,11 @@ static int dw_hdmi_fb_registered(struct dw_hdmi *hdmi)
 		    HDMI_PHY_I2CM_CTLINT_ADDR);
 
 	/* enable cable hot plug irq */
-	hdmi_writeb(hdmi, (u8)~HDMI_PHY_HPD, HDMI_PHY_MASK0);
+	hdmi_writeb(hdmi, hdmi->phy_mask, HDMI_PHY_MASK0);
 
 	/* Clear Hotplug interrupts */
-	hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0);
+	hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE,
+		    HDMI_IH_PHY_STAT0);
 
 	return 0;
 }
@@ -1397,7 +1408,7 @@ static void dw_hdmi_update_power(struct dw_hdmi *hdmi)
 	if (hdmi->disabled) {
 		force = DRM_FORCE_OFF;
 	} else if (force == DRM_FORCE_UNSPECIFIED) {
-		if (hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD)
+		if (hdmi->rxsense)
 			force = DRM_FORCE_ON;
 		else
 			force = DRM_FORCE_OFF;
@@ -1412,6 +1423,31 @@ static void dw_hdmi_update_power(struct dw_hdmi *hdmi)
 	}
 }
 
+/*
+ * Adjust the detection of RXSENSE according to whether we have a forced
+ * connection mode enabled, or whether we have been disabled.  There is
+ * no point processing RXSENSE interrupts if we have a forced connection
+ * state, or DRM has us disabled.
+ *
+ * We also disable rxsense interrupts when we think we're disconnected
+ * to avoid floating TDMS signals giving false rxsense interrupts.
+ *
+ * Note: we still need to listen for HPD interrupts even when DRM has us
+ * disabled so that we can detect a connect event.
+ */
+static void dw_hdmi_update_phy_mask(struct dw_hdmi *hdmi)
+{
+	u8 old_mask = hdmi->phy_mask;
+
+	if (hdmi->force || hdmi->disabled || !hdmi->rxsense)
+		hdmi->phy_mask |= HDMI_PHY_RX_SENSE;
+	else
+		hdmi->phy_mask &= ~HDMI_PHY_RX_SENSE;
+
+	if (old_mask != hdmi->phy_mask)
+		hdmi_writeb(hdmi, hdmi->phy_mask, HDMI_PHY_MASK0);
+}
+
 static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge,
 				    struct drm_display_mode *orig_mode,
 				    struct drm_display_mode *mode)
@@ -1440,6 +1476,7 @@ static void dw_hdmi_bridge_disable(struct drm_bridge *bridge)
 	mutex_lock(&hdmi->mutex);
 	hdmi->disabled = true;
 	dw_hdmi_update_power(hdmi);
+	dw_hdmi_update_phy_mask(hdmi);
 	mutex_unlock(&hdmi->mutex);
 }
 
@@ -1450,6 +1487,7 @@ static void dw_hdmi_bridge_enable(struct drm_bridge *bridge)
 	mutex_lock(&hdmi->mutex);
 	hdmi->disabled = false;
 	dw_hdmi_update_power(hdmi);
+	dw_hdmi_update_phy_mask(hdmi);
 	mutex_unlock(&hdmi->mutex);
 }
 
@@ -1467,6 +1505,7 @@ dw_hdmi_connector_detect(struct drm_connector *connector, bool force)
 	mutex_lock(&hdmi->mutex);
 	hdmi->force = DRM_FORCE_UNSPECIFIED;
 	dw_hdmi_update_power(hdmi);
+	dw_hdmi_update_phy_mask(hdmi);
 	mutex_unlock(&hdmi->mutex);
 
 	return hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD ?
@@ -1541,6 +1580,7 @@ static void dw_hdmi_connector_force(struct drm_connector *connector)
 	mutex_lock(&hdmi->mutex);
 	hdmi->force = connector->force;
 	dw_hdmi_update_power(hdmi);
+	dw_hdmi_update_phy_mask(hdmi);
 	mutex_unlock(&hdmi->mutex);
 }
 
@@ -1582,33 +1622,69 @@ static irqreturn_t dw_hdmi_hardirq(int irq, void *dev_id)
 static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
 {
 	struct dw_hdmi *hdmi = dev_id;
-	u8 intr_stat;
-	u8 phy_int_pol;
+	u8 intr_stat, phy_int_pol, phy_pol_mask, phy_stat;
 
 	intr_stat = hdmi_readb(hdmi, HDMI_IH_PHY_STAT0);
-
 	phy_int_pol = hdmi_readb(hdmi, HDMI_PHY_POL0);
+	phy_stat = hdmi_readb(hdmi, HDMI_PHY_STAT0);
+
+	phy_pol_mask = 0;
+	if (intr_stat & HDMI_IH_PHY_STAT0_HPD)
+		phy_pol_mask |= HDMI_PHY_HPD;
+	if (intr_stat & HDMI_IH_PHY_STAT0_RX_SENSE0)
+		phy_pol_mask |= HDMI_PHY_RX_SENSE0;
+	if (intr_stat & HDMI_IH_PHY_STAT0_RX_SENSE1)
+		phy_pol_mask |= HDMI_PHY_RX_SENSE1;
+	if (intr_stat & HDMI_IH_PHY_STAT0_RX_SENSE2)
+		phy_pol_mask |= HDMI_PHY_RX_SENSE2;
+	if (intr_stat & HDMI_IH_PHY_STAT0_RX_SENSE3)
+		phy_pol_mask |= HDMI_PHY_RX_SENSE3;
+
+	if (phy_pol_mask)
+		hdmi_modb(hdmi, ~phy_int_pol, phy_pol_mask, HDMI_PHY_POL0);
 
-	if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
-		hdmi_modb(hdmi, ~phy_int_pol, HDMI_PHY_HPD, HDMI_PHY_POL0);
+	/*
+	 * RX sense tells us whether the TDMS transmitters are detecting
+	 * load - in other words, there's something listening on the
+	 * other end of the link.  Use this to decide whether we should
+	 * power on the phy as HPD may be toggled by the sink to merely
+	 * ask the source to re-read the EDID.
+	 */
+	if (intr_stat &
+	    (HDMI_IH_PHY_STAT0_RX_SENSE | HDMI_IH_PHY_STAT0_HPD)) {
 		mutex_lock(&hdmi->mutex);
-		if (phy_int_pol & HDMI_PHY_HPD) {
-			dev_dbg(hdmi->dev, "EVENT=plugin\n");
-
-			if (!hdmi->disabled && !hdmi->force)
-				dw_hdmi_poweron(hdmi);
-		} else {
-			dev_dbg(hdmi->dev, "EVENT=plugout\n");
-
-			if (!hdmi->disabled && !hdmi->force)
-				dw_hdmi_poweroff(hdmi);
+		if (!hdmi->disabled && !hdmi->force) {
+			/*
+			 * If the RX sense status indicates we're disconnected,
+			 * clear the software rxsense status.
+			 */
+			if (!(phy_stat & HDMI_PHY_RX_SENSE))
+				hdmi->rxsense = false;
+
+			/*
+			 * Only set the software rxsense status when both
+			 * rxsense and hpd indicates we're connected.
+			 * This avoids what seems to be bad behaviour in
+			 * at least iMX6S versions of the phy.
+			 */
+			if (phy_stat & HDMI_PHY_HPD)
+				hdmi->rxsense = true;
+
+			dw_hdmi_update_power(hdmi);
+			dw_hdmi_update_phy_mask(hdmi);
 		}
 		mutex_unlock(&hdmi->mutex);
+	}
+
+	if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
+		dev_dbg(hdmi->dev, "EVENT=%s\n",
+			phy_int_pol & HDMI_PHY_HPD ? "plugin" : "plugout");
 		drm_helper_hpd_irq_event(hdmi->bridge->dev);
 	}
 
 	hdmi_writeb(hdmi, intr_stat, HDMI_IH_PHY_STAT0);
-	hdmi_writeb(hdmi, ~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0);
+	hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE),
+		    HDMI_IH_MUTE_PHY_STAT0);
 
 	return IRQ_HANDLED;
 }
@@ -1674,6 +1750,8 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 	hdmi->ratio = 100;
 	hdmi->encoder = encoder;
 	hdmi->disabled = true;
+	hdmi->rxsense = true;
+	hdmi->phy_mask = (u8)~(HDMI_PHY_HPD | HDMI_PHY_RX_SENSE);
 
 	mutex_init(&hdmi->mutex);
 	mutex_init(&hdmi->audio_mutex);
@@ -1764,10 +1842,11 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 	 * Configure registers related to HDMI interrupt
 	 * generation before registering IRQ.
 	 */
-	hdmi_writeb(hdmi, HDMI_PHY_HPD, HDMI_PHY_POL0);
+	hdmi_writeb(hdmi, HDMI_PHY_HPD | HDMI_PHY_RX_SENSE, HDMI_PHY_POL0);
 
 	/* Clear Hotplug interrupts */
-	hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0);
+	hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE,
+		    HDMI_IH_PHY_STAT0);
 
 	ret = dw_hdmi_fb_registered(hdmi);
 	if (ret)
@@ -1778,7 +1857,8 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 		goto err_iahb;
 
 	/* Unmute interrupts */
-	hdmi_writeb(hdmi, ~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0);
+	hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE),
+		    HDMI_IH_MUTE_PHY_STAT0);
 
 	dev_set_drvdata(dev, hdmi);
 
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 12/12] drm: bridge/dw_hdmi: improve HDMI enable/disable handling
@ 2015-08-08 16:04   ` Russell King
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:04 UTC (permalink / raw)
  To: linux-arm-kernel

HDMI sinks are permitted to de-assert and re-assert the HPD signal to
indicate that their EDID has been updated, which may not involve a
change of video information.

An example of where such a situation can arise is when an AV receiver
is connected between the source and the display device.  Events which
can cause the HPD to be deasserted include:

 * turning on or switching to standby the AV receiver.
 * turning on or switching to standby the display device.

Each of these can change the entire EDID data, or just a part of the
EDID data - it's up to the connected HDMI sink to do what they desire
here.  For example

 - with the AV receiver and display device both in standby, a source
   connected to the AV receiver may provide its own EDID to the source.
 - turning on the display device causes the display device's EDID to be
   made available in an unmodified form to the source.
 - subsequently turning on the AV receiver then provides a modified
   version of the display device's EDID.

Moreover, HPD doesn't tell us whether something is actually listening
on the HDMI TDMS signals.  The phy gives us a set of RXSENSE indications
which tell us whether there is a sink connected to the TMDS signals.

Currently, we use the HPD signal to enable or disable the HDMI block,
which is questionable when HPD is used in this manner.  Using the
RXSENSE would be more appropriate, but there is some bad behaviour
which needs to be coped with.  The iMX6 implementation lets the TMDS
signals float when the phy is "powered down", which cause spurious
interrupts.  Rather than just using RXSENSE, use RXSENSE and HPD
becoming both active to signal the presence of a device, but loss
of RXSENSE to indicate that the device has been unplugged.

The side effect of this change is that a sink deasserting the HPD
signal to cause a re-read of the EDID data will not cause the bridge
to immediately disable the video signal.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/dw_hdmi.c | 124 ++++++++++++++++++++++++++++++++-------
 1 file changed, 102 insertions(+), 22 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index 2d1c7e4ec086..fba25607ef88 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -129,6 +129,8 @@ struct dw_hdmi {
 	enum drm_connector_force force;	/* mutex-protected force state */
 	bool disabled;			/* DRM has disabled our bridge */
 	bool bridge_is_on;		/* indicates the bridge is on */
+	bool rxsense;			/* rxsense state */
+	u8 phy_mask;			/* desired phy int mask settings */
 
 	spinlock_t audio_lock;
 	struct mutex audio_mutex;
@@ -142,6 +144,14 @@ struct dw_hdmi {
 	u8 (*read)(struct dw_hdmi *hdmi, int offset);
 };
 
+#define HDMI_IH_PHY_STAT0_RX_SENSE \
+	(HDMI_IH_PHY_STAT0_RX_SENSE0 | HDMI_IH_PHY_STAT0_RX_SENSE1 | \
+	 HDMI_IH_PHY_STAT0_RX_SENSE2 | HDMI_IH_PHY_STAT0_RX_SENSE3)
+
+#define HDMI_PHY_RX_SENSE \
+	(HDMI_PHY_RX_SENSE0 | HDMI_PHY_RX_SENSE1 | \
+	 HDMI_PHY_RX_SENSE2 | HDMI_PHY_RX_SENSE3)
+
 static void dw_hdmi_writel(struct dw_hdmi *hdmi, u8 val, int offset)
 {
 	writel(val, hdmi->regs + (offset << 2));
@@ -1318,10 +1328,11 @@ static int dw_hdmi_fb_registered(struct dw_hdmi *hdmi)
 		    HDMI_PHY_I2CM_CTLINT_ADDR);
 
 	/* enable cable hot plug irq */
-	hdmi_writeb(hdmi, (u8)~HDMI_PHY_HPD, HDMI_PHY_MASK0);
+	hdmi_writeb(hdmi, hdmi->phy_mask, HDMI_PHY_MASK0);
 
 	/* Clear Hotplug interrupts */
-	hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0);
+	hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE,
+		    HDMI_IH_PHY_STAT0);
 
 	return 0;
 }
@@ -1397,7 +1408,7 @@ static void dw_hdmi_update_power(struct dw_hdmi *hdmi)
 	if (hdmi->disabled) {
 		force = DRM_FORCE_OFF;
 	} else if (force == DRM_FORCE_UNSPECIFIED) {
-		if (hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD)
+		if (hdmi->rxsense)
 			force = DRM_FORCE_ON;
 		else
 			force = DRM_FORCE_OFF;
@@ -1412,6 +1423,31 @@ static void dw_hdmi_update_power(struct dw_hdmi *hdmi)
 	}
 }
 
+/*
+ * Adjust the detection of RXSENSE according to whether we have a forced
+ * connection mode enabled, or whether we have been disabled.  There is
+ * no point processing RXSENSE interrupts if we have a forced connection
+ * state, or DRM has us disabled.
+ *
+ * We also disable rxsense interrupts when we think we're disconnected
+ * to avoid floating TDMS signals giving false rxsense interrupts.
+ *
+ * Note: we still need to listen for HPD interrupts even when DRM has us
+ * disabled so that we can detect a connect event.
+ */
+static void dw_hdmi_update_phy_mask(struct dw_hdmi *hdmi)
+{
+	u8 old_mask = hdmi->phy_mask;
+
+	if (hdmi->force || hdmi->disabled || !hdmi->rxsense)
+		hdmi->phy_mask |= HDMI_PHY_RX_SENSE;
+	else
+		hdmi->phy_mask &= ~HDMI_PHY_RX_SENSE;
+
+	if (old_mask != hdmi->phy_mask)
+		hdmi_writeb(hdmi, hdmi->phy_mask, HDMI_PHY_MASK0);
+}
+
 static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge,
 				    struct drm_display_mode *orig_mode,
 				    struct drm_display_mode *mode)
@@ -1440,6 +1476,7 @@ static void dw_hdmi_bridge_disable(struct drm_bridge *bridge)
 	mutex_lock(&hdmi->mutex);
 	hdmi->disabled = true;
 	dw_hdmi_update_power(hdmi);
+	dw_hdmi_update_phy_mask(hdmi);
 	mutex_unlock(&hdmi->mutex);
 }
 
@@ -1450,6 +1487,7 @@ static void dw_hdmi_bridge_enable(struct drm_bridge *bridge)
 	mutex_lock(&hdmi->mutex);
 	hdmi->disabled = false;
 	dw_hdmi_update_power(hdmi);
+	dw_hdmi_update_phy_mask(hdmi);
 	mutex_unlock(&hdmi->mutex);
 }
 
@@ -1467,6 +1505,7 @@ dw_hdmi_connector_detect(struct drm_connector *connector, bool force)
 	mutex_lock(&hdmi->mutex);
 	hdmi->force = DRM_FORCE_UNSPECIFIED;
 	dw_hdmi_update_power(hdmi);
+	dw_hdmi_update_phy_mask(hdmi);
 	mutex_unlock(&hdmi->mutex);
 
 	return hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD ?
@@ -1541,6 +1580,7 @@ static void dw_hdmi_connector_force(struct drm_connector *connector)
 	mutex_lock(&hdmi->mutex);
 	hdmi->force = connector->force;
 	dw_hdmi_update_power(hdmi);
+	dw_hdmi_update_phy_mask(hdmi);
 	mutex_unlock(&hdmi->mutex);
 }
 
@@ -1582,33 +1622,69 @@ static irqreturn_t dw_hdmi_hardirq(int irq, void *dev_id)
 static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
 {
 	struct dw_hdmi *hdmi = dev_id;
-	u8 intr_stat;
-	u8 phy_int_pol;
+	u8 intr_stat, phy_int_pol, phy_pol_mask, phy_stat;
 
 	intr_stat = hdmi_readb(hdmi, HDMI_IH_PHY_STAT0);
-
 	phy_int_pol = hdmi_readb(hdmi, HDMI_PHY_POL0);
+	phy_stat = hdmi_readb(hdmi, HDMI_PHY_STAT0);
+
+	phy_pol_mask = 0;
+	if (intr_stat & HDMI_IH_PHY_STAT0_HPD)
+		phy_pol_mask |= HDMI_PHY_HPD;
+	if (intr_stat & HDMI_IH_PHY_STAT0_RX_SENSE0)
+		phy_pol_mask |= HDMI_PHY_RX_SENSE0;
+	if (intr_stat & HDMI_IH_PHY_STAT0_RX_SENSE1)
+		phy_pol_mask |= HDMI_PHY_RX_SENSE1;
+	if (intr_stat & HDMI_IH_PHY_STAT0_RX_SENSE2)
+		phy_pol_mask |= HDMI_PHY_RX_SENSE2;
+	if (intr_stat & HDMI_IH_PHY_STAT0_RX_SENSE3)
+		phy_pol_mask |= HDMI_PHY_RX_SENSE3;
+
+	if (phy_pol_mask)
+		hdmi_modb(hdmi, ~phy_int_pol, phy_pol_mask, HDMI_PHY_POL0);
 
-	if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
-		hdmi_modb(hdmi, ~phy_int_pol, HDMI_PHY_HPD, HDMI_PHY_POL0);
+	/*
+	 * RX sense tells us whether the TDMS transmitters are detecting
+	 * load - in other words, there's something listening on the
+	 * other end of the link.  Use this to decide whether we should
+	 * power on the phy as HPD may be toggled by the sink to merely
+	 * ask the source to re-read the EDID.
+	 */
+	if (intr_stat &
+	    (HDMI_IH_PHY_STAT0_RX_SENSE | HDMI_IH_PHY_STAT0_HPD)) {
 		mutex_lock(&hdmi->mutex);
-		if (phy_int_pol & HDMI_PHY_HPD) {
-			dev_dbg(hdmi->dev, "EVENT=plugin\n");
-
-			if (!hdmi->disabled && !hdmi->force)
-				dw_hdmi_poweron(hdmi);
-		} else {
-			dev_dbg(hdmi->dev, "EVENT=plugout\n");
-
-			if (!hdmi->disabled && !hdmi->force)
-				dw_hdmi_poweroff(hdmi);
+		if (!hdmi->disabled && !hdmi->force) {
+			/*
+			 * If the RX sense status indicates we're disconnected,
+			 * clear the software rxsense status.
+			 */
+			if (!(phy_stat & HDMI_PHY_RX_SENSE))
+				hdmi->rxsense = false;
+
+			/*
+			 * Only set the software rxsense status when both
+			 * rxsense and hpd indicates we're connected.
+			 * This avoids what seems to be bad behaviour in
+			 * at least iMX6S versions of the phy.
+			 */
+			if (phy_stat & HDMI_PHY_HPD)
+				hdmi->rxsense = true;
+
+			dw_hdmi_update_power(hdmi);
+			dw_hdmi_update_phy_mask(hdmi);
 		}
 		mutex_unlock(&hdmi->mutex);
+	}
+
+	if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
+		dev_dbg(hdmi->dev, "EVENT=%s\n",
+			phy_int_pol & HDMI_PHY_HPD ? "plugin" : "plugout");
 		drm_helper_hpd_irq_event(hdmi->bridge->dev);
 	}
 
 	hdmi_writeb(hdmi, intr_stat, HDMI_IH_PHY_STAT0);
-	hdmi_writeb(hdmi, ~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0);
+	hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE),
+		    HDMI_IH_MUTE_PHY_STAT0);
 
 	return IRQ_HANDLED;
 }
@@ -1674,6 +1750,8 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 	hdmi->ratio = 100;
 	hdmi->encoder = encoder;
 	hdmi->disabled = true;
+	hdmi->rxsense = true;
+	hdmi->phy_mask = (u8)~(HDMI_PHY_HPD | HDMI_PHY_RX_SENSE);
 
 	mutex_init(&hdmi->mutex);
 	mutex_init(&hdmi->audio_mutex);
@@ -1764,10 +1842,11 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 	 * Configure registers related to HDMI interrupt
 	 * generation before registering IRQ.
 	 */
-	hdmi_writeb(hdmi, HDMI_PHY_HPD, HDMI_PHY_POL0);
+	hdmi_writeb(hdmi, HDMI_PHY_HPD | HDMI_PHY_RX_SENSE, HDMI_PHY_POL0);
 
 	/* Clear Hotplug interrupts */
-	hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0);
+	hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE,
+		    HDMI_IH_PHY_STAT0);
 
 	ret = dw_hdmi_fb_registered(hdmi);
 	if (ret)
@@ -1778,7 +1857,8 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 		goto err_iahb;
 
 	/* Unmute interrupts */
-	hdmi_writeb(hdmi, ~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0);
+	hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE),
+		    HDMI_IH_MUTE_PHY_STAT0);
 
 	dev_set_drvdata(dev, hdmi);
 
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 226+ messages in thread

* Re: [PATCH 0/9] dw-hdmi audio support
  2015-08-08 16:02 ` Russell King - ARM Linux
  (?)
@ 2015-08-08 16:09   ` Russell King - ARM Linux
  -1 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-08-08 16:09 UTC (permalink / raw)
  To: linux-rockchip, alsa-devel, dri-devel, linux-kernel, linux-arm-kernel
  Cc: Fabio Estevam, David Airlie, Jon Nettleton, Philipp Zabel,
	Yakir Yang, Andy Yan, Sascha Hauer

Following on from the previous sub-series, this sub-series adds audio
support to dw-hdmi.

The two different variants are now in this patch: AHB audio support
found on iMX6 platforms, and I2S support found on Rockchip patches.
Thanks to Yakir Yang for contributing the I2S support.

I suspect that there is still some discussion to be had on this
series, though I would like to see it moving forward so that we can
get something merged.

 drivers/gpu/drm/bridge/Kconfig             |  20 +
 drivers/gpu/drm/bridge/Makefile            |   2 +
 drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c | 635 +++++++++++++++++++++++++++++
 drivers/gpu/drm/bridge/dw_hdmi-audio.h     |  14 +
 drivers/gpu/drm/bridge/dw_hdmi-i2s-audio.c | 398 ++++++++++++++++++
 drivers/gpu/drm/bridge/dw_hdmi.c           | 202 ++++-----
 drivers/gpu/drm/bridge/dw_hdmi.h           |   6 +
 7 files changed, 1155 insertions(+), 122 deletions(-)

-- 
FTTC broadband for 0.8mile line: currently at 10.5Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 0/9] dw-hdmi audio support
@ 2015-08-08 16:09   ` Russell King - ARM Linux
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-08-08 16:09 UTC (permalink / raw)
  To: linux-rockchip, alsa-devel, dri-devel, linux-kernel, linux-arm-kernel
  Cc: Fabio Estevam, Yakir Yang, Andy Yan

Following on from the previous sub-series, this sub-series adds audio
support to dw-hdmi.

The two different variants are now in this patch: AHB audio support
found on iMX6 platforms, and I2S support found on Rockchip patches.
Thanks to Yakir Yang for contributing the I2S support.

I suspect that there is still some discussion to be had on this
series, though I would like to see it moving forward so that we can
get something merged.

 drivers/gpu/drm/bridge/Kconfig             |  20 +
 drivers/gpu/drm/bridge/Makefile            |   2 +
 drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c | 635 +++++++++++++++++++++++++++++
 drivers/gpu/drm/bridge/dw_hdmi-audio.h     |  14 +
 drivers/gpu/drm/bridge/dw_hdmi-i2s-audio.c | 398 ++++++++++++++++++
 drivers/gpu/drm/bridge/dw_hdmi.c           | 202 ++++-----
 drivers/gpu/drm/bridge/dw_hdmi.h           |   6 +
 7 files changed, 1155 insertions(+), 122 deletions(-)

-- 
FTTC broadband for 0.8mile line: currently at 10.5Mbps down 400kbps up
according to speedtest.net.
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 0/9] dw-hdmi audio support
@ 2015-08-08 16:09   ` Russell King - ARM Linux
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-08-08 16:09 UTC (permalink / raw)
  To: linux-arm-kernel

Following on from the previous sub-series, this sub-series adds audio
support to dw-hdmi.

The two different variants are now in this patch: AHB audio support
found on iMX6 platforms, and I2S support found on Rockchip patches.
Thanks to Yakir Yang for contributing the I2S support.

I suspect that there is still some discussion to be had on this
series, though I would like to see it moving forward so that we can
get something merged.

 drivers/gpu/drm/bridge/Kconfig             |  20 +
 drivers/gpu/drm/bridge/Makefile            |   2 +
 drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c | 635 +++++++++++++++++++++++++++++
 drivers/gpu/drm/bridge/dw_hdmi-audio.h     |  14 +
 drivers/gpu/drm/bridge/dw_hdmi-i2s-audio.c | 398 ++++++++++++++++++
 drivers/gpu/drm/bridge/dw_hdmi.c           | 202 ++++-----
 drivers/gpu/drm/bridge/dw_hdmi.h           |   6 +
 7 files changed, 1155 insertions(+), 122 deletions(-)

-- 
FTTC broadband for 0.8mile line: currently at 10.5Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver
  2015-08-08 16:09   ` Russell King - ARM Linux
  (?)
@ 2015-08-08 16:10     ` Russell King
  -1 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:10 UTC (permalink / raw)
  To: linux-rockchip, alsa-devel, dri-devel, linux-kernel, linux-arm-kernel
  Cc: Philipp Zabel, Andy Yan, Yakir Yang, Fabio Estevam, Mark Brown,
	Takashi Iwai, Jaroslav Kysela, Sascha Hauer, Jon Nettleton,
	David Airlie

Add ALSA based HDMI AHB audio driver for dw_hdmi.  The only buffer
format supported by the hardware is its own special IEC958 based format,
which is not compatible with any ALSA format.  To avoid doing too much
data manipulation within the driver, we support only ALSAs IEC958 LE and
24-bit PCM formats for 2 to 6 channels, which we convert to its hardware
format.

A more desirable solution would be to have this conversion in userspace,
but ALSA does not appear to allow such transformations outside of
libasound itself.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/Kconfig             |  10 +
 drivers/gpu/drm/bridge/Makefile            |   1 +
 drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c | 561 +++++++++++++++++++++++++++++
 drivers/gpu/drm/bridge/dw_hdmi-audio.h     |  13 +
 drivers/gpu/drm/bridge/dw_hdmi.c           |  24 ++
 drivers/gpu/drm/bridge/dw_hdmi.h           |   3 +
 6 files changed, 612 insertions(+)
 create mode 100644 drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
 create mode 100644 drivers/gpu/drm/bridge/dw_hdmi-audio.h

diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index acef3223772c..56ed35fe0734 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -3,6 +3,16 @@ config DRM_DW_HDMI
 	depends on DRM
 	select DRM_KMS_HELPER
 
+config DRM_DW_HDMI_AHB_AUDIO
+	tristate "Synopsis Designware AHB Audio interface"
+	depends on DRM_DW_HDMI && SND
+	select SND_PCM
+	select SND_PCM_IEC958
+	help
+	  Support the AHB Audio interface which is part of the Synopsis
+	  Designware HDMI block.  This is used in conjunction with
+	  the i.MX6 HDMI driver.
+
 config DRM_PTN3460
 	tristate "PTN3460 DP/LVDS bridge"
 	depends on DRM
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index 8dfebd984370..eb80dbbb8365 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -3,3 +3,4 @@ ccflags-y := -Iinclude/drm
 obj-$(CONFIG_DRM_PS8622) += ps8622.o
 obj-$(CONFIG_DRM_PTN3460) += ptn3460.o
 obj-$(CONFIG_DRM_DW_HDMI) += dw_hdmi.o
+obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw_hdmi-ahb-audio.o
diff --git a/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
new file mode 100644
index 000000000000..22bbbc5c2393
--- /dev/null
+++ b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
@@ -0,0 +1,561 @@
+/*
+ * DesignWare HDMI audio driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Written and tested against the Designware HDMI Tx found in iMX6.
+ */
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <drm/bridge/dw_hdmi.h>
+
+#include <sound/asoundef.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_iec958.h>
+
+#include "dw_hdmi-audio.h"
+
+#define DRIVER_NAME "dw-hdmi-ahb-audio"
+
+/* Provide some bits rather than bit offsets */
+enum {
+	HDMI_AHB_DMA_CONF0_SW_FIFO_RST = BIT(7),
+	HDMI_AHB_DMA_CONF0_EN_HLOCK = BIT(3),
+	HDMI_AHB_DMA_START_START = BIT(0),
+	HDMI_AHB_DMA_STOP_STOP = BIT(0),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR = BIT(5),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST = BIT(4),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY = BIT(3),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE = BIT(2),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL = BIT(1),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL =
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY,
+	HDMI_IH_AHBDMAAUD_STAT0_ERROR = BIT(5),
+	HDMI_IH_AHBDMAAUD_STAT0_LOST = BIT(4),
+	HDMI_IH_AHBDMAAUD_STAT0_RETRY = BIT(3),
+	HDMI_IH_AHBDMAAUD_STAT0_DONE = BIT(2),
+	HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL = BIT(1),
+	HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0),
+	HDMI_IH_AHBDMAAUD_STAT0_ALL =
+		HDMI_IH_AHBDMAAUD_STAT0_ERROR |
+		HDMI_IH_AHBDMAAUD_STAT0_LOST |
+		HDMI_IH_AHBDMAAUD_STAT0_RETRY |
+		HDMI_IH_AHBDMAAUD_STAT0_DONE |
+		HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL |
+		HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY,
+	HDMI_AHB_DMA_CONF0_INCR16 = 2 << 1,
+	HDMI_AHB_DMA_CONF0_INCR8 = 1 << 1,
+	HDMI_AHB_DMA_CONF0_INCR4 = 0,
+	HDMI_AHB_DMA_CONF0_BURST_MODE = BIT(0),
+	HDMI_AHB_DMA_MASK_DONE = BIT(7),
+	HDMI_REVISION_ID = 0x0001,
+	HDMI_IH_AHBDMAAUD_STAT0 = 0x0109,
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0 = 0x0189,
+	HDMI_AHB_DMA_CONF0 = 0x3600,
+	HDMI_AHB_DMA_START = 0x3601,
+	HDMI_AHB_DMA_STOP = 0x3602,
+	HDMI_AHB_DMA_THRSLD = 0x3603,
+	HDMI_AHB_DMA_STRADDR0 = 0x3604,
+	HDMI_AHB_DMA_STPADDR0 = 0x3608,
+	HDMI_AHB_DMA_MASK = 0x3614,
+	HDMI_AHB_DMA_POL = 0x3615,
+	HDMI_AHB_DMA_CONF1 = 0x3616,
+	HDMI_AHB_DMA_BUFFPOL = 0x361a,
+};
+
+struct snd_dw_hdmi {
+	struct snd_card *card;
+	struct snd_pcm *pcm;
+	struct dw_hdmi_audio_data data;
+	struct snd_pcm_substream *substream;
+	void (*reformat)(struct snd_dw_hdmi *, size_t, size_t);
+	void *buf_src;
+	void *buf_dst;
+	dma_addr_t buf_addr;
+	unsigned buf_offset;
+	unsigned buf_period;
+	unsigned buf_size;
+	unsigned channels;
+	u8 revision;
+	u8 iec_offset;
+	u8 cs[192][8];
+};
+
+static void dw_hdmi_writel(unsigned long val, void __iomem *ptr)
+{
+	writeb_relaxed(val, ptr);
+	writeb_relaxed(val >> 8, ptr + 1);
+	writeb_relaxed(val >> 16, ptr + 2);
+	writeb_relaxed(val >> 24, ptr + 3);
+}
+
+/*
+ * Convert to hardware format: The userspace buffer contains IEC958 samples,
+ * with the PCUV bits in bits 31..28 and audio samples in bits 27..4.  We
+ * need these to be in bits 27..24, with the IEC B bit in bit 28, and audio
+ * samples in 23..0.
+ *
+ * Default preamble in bits 3..0: 8 = block start, 4 = even 2 = odd
+ *
+ * Ideally, we could do with having the data properly formatted in userspace.
+ */
+static void dw_hdmi_reformat_iec958(struct snd_dw_hdmi *dw,
+	size_t offset, size_t bytes)
+{
+	u32 *src = dw->buf_src + offset;
+	u32 *dst = dw->buf_dst + offset;
+	u32 *end = dw->buf_src + offset + bytes;
+
+	do {
+		u32 b, sample = *src++;
+
+		b = (sample & 8) << (28 - 3);
+
+		sample >>= 4;
+
+		*dst++ = sample | b;
+	} while (src < end);
+}
+
+static u32 parity(u32 sample)
+{
+	sample ^= sample >> 16;
+	sample ^= sample >> 8;
+	sample ^= sample >> 4;
+	sample ^= sample >> 2;
+	sample ^= sample >> 1;
+	return (sample & 1) << 27;
+}
+
+static void dw_hdmi_reformat_s24(struct snd_dw_hdmi *dw,
+	size_t offset, size_t bytes)
+{
+	u32 *src = dw->buf_src + offset;
+	u32 *dst = dw->buf_dst + offset;
+	u32 *end = dw->buf_src + offset + bytes;
+
+	do {
+		unsigned i;
+		u8 *cs;
+
+		cs = dw->cs[dw->iec_offset++];
+		if (dw->iec_offset >= 192)
+			dw->iec_offset = 0;
+
+		i = dw->channels;
+		do {
+			u32 sample = *src++;
+
+			sample &= ~0xff000000;
+			sample |= *cs++ << 24;
+			sample |= parity(sample & ~0xf8000000);
+
+			*dst++ = sample;
+		} while (--i);
+	} while (src < end);
+}
+
+static void dw_hdmi_create_cs(struct snd_dw_hdmi *dw,
+	struct snd_pcm_runtime *runtime)
+{
+	u8 cs[4];
+	unsigned ch, i, j;
+
+	snd_pcm_create_iec958_consumer(runtime, cs, sizeof(cs));
+
+	memset(dw->cs, 0, sizeof(dw->cs));
+
+	for (ch = 0; ch < 8; ch++) {
+		cs[2] &= ~IEC958_AES2_CON_CHANNEL;
+		cs[2] |= (ch + 1) << 4;
+
+		for (i = 0; i < ARRAY_SIZE(cs); i++) {
+			unsigned c = cs[i];
+
+			for (j = 0; j < 8; j++, c >>= 1)
+				dw->cs[i * 8 + j][ch] = (c & 1) << 2;
+		}
+	}
+	dw->cs[0][0] |= BIT(4);
+}
+
+static void dw_hdmi_start_dma(struct snd_dw_hdmi *dw)
+{
+	void __iomem *base = dw->data.base;
+	unsigned offset = dw->buf_offset;
+	unsigned period = dw->buf_period;
+	u32 start, stop;
+
+	dw->reformat(dw, offset, period);
+
+	/* Clear all irqs before enabling irqs and starting DMA */
+	writeb_relaxed(HDMI_IH_AHBDMAAUD_STAT0_ALL,
+		       base + HDMI_IH_AHBDMAAUD_STAT0);
+
+	start = dw->buf_addr + offset;
+	stop = start + period - 1;
+
+	/* Setup the hardware start/stop addresses */
+	dw_hdmi_writel(start, base + HDMI_AHB_DMA_STRADDR0);
+	dw_hdmi_writel(stop, base + HDMI_AHB_DMA_STPADDR0);
+
+	writeb_relaxed((u8)~HDMI_AHB_DMA_MASK_DONE, base + HDMI_AHB_DMA_MASK);
+	writeb(HDMI_AHB_DMA_START_START, base + HDMI_AHB_DMA_START);
+
+	offset += period;
+	if (offset >= dw->buf_size)
+		offset = 0;
+	dw->buf_offset = offset;
+}
+
+static void dw_hdmi_stop_dma(struct snd_dw_hdmi *dw)
+{
+	dw->substream = NULL;
+
+	/* Disable interrupts before disabling DMA */
+	writeb_relaxed(~0, dw->data.base + HDMI_AHB_DMA_MASK);
+	writeb_relaxed(HDMI_AHB_DMA_STOP_STOP, dw->data.base + HDMI_AHB_DMA_STOP);
+}
+
+static irqreturn_t snd_dw_hdmi_irq(int irq, void *data)
+{
+	struct snd_dw_hdmi *dw = data;
+	struct snd_pcm_substream *substream;
+	unsigned stat;
+
+	stat = readb_relaxed(dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
+	if (!stat)
+		return IRQ_NONE;
+
+	writeb_relaxed(stat, dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
+
+	substream = dw->substream;
+	if (stat & HDMI_IH_AHBDMAAUD_STAT0_DONE && substream) {
+		snd_pcm_period_elapsed(substream);
+		if (dw->substream)
+			dw_hdmi_start_dma(dw);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static struct snd_pcm_hardware dw_hdmi_hw = {
+	.info = SNDRV_PCM_INFO_INTERLEAVED |
+		SNDRV_PCM_INFO_BLOCK_TRANSFER |
+		SNDRV_PCM_INFO_MMAP |
+		SNDRV_PCM_INFO_MMAP_VALID,
+	.formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE |
+		   SNDRV_PCM_FMTBIT_S24_LE,
+	.rates = SNDRV_PCM_RATE_32000 |
+		 SNDRV_PCM_RATE_44100 |
+		 SNDRV_PCM_RATE_48000 |
+		 SNDRV_PCM_RATE_88200 |
+		 SNDRV_PCM_RATE_96000 |
+		 SNDRV_PCM_RATE_176400 |
+		 SNDRV_PCM_RATE_192000,
+	.channels_min = 2,
+	.channels_max = 8,
+	.buffer_bytes_max = 64 * 1024,
+	.period_bytes_min = 256,
+	.period_bytes_max = 8192,	/* ERR004323: must limit to 8k */
+	.periods_min = 2,
+	.periods_max = 16,
+	.fifo_size = 0,
+};
+
+static int dw_hdmi_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_dw_hdmi *dw = substream->private_data;
+	void __iomem *base = dw->data.base;
+	int ret;
+
+	runtime->hw = dw_hdmi_hw;
+
+	ret = snd_pcm_limit_hw_rates(runtime);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0)
+		return ret;
+
+	/* Clear FIFO */
+	writeb_relaxed(HDMI_AHB_DMA_CONF0_SW_FIFO_RST,
+		       base + HDMI_AHB_DMA_CONF0);
+
+	/* Configure interrupt polarities */
+	writeb_relaxed(~0, base + HDMI_AHB_DMA_POL);
+	writeb_relaxed(~0, base + HDMI_AHB_DMA_BUFFPOL);
+
+	/* Keep interrupts masked, and clear any pending */
+	writeb_relaxed(~0, base + HDMI_AHB_DMA_MASK);
+	writeb_relaxed(~0, base + HDMI_IH_AHBDMAAUD_STAT0);
+
+	ret = request_irq(dw->data.irq, snd_dw_hdmi_irq, IRQF_SHARED,
+			  "dw-hdmi-audio", dw);
+	if (ret)
+		return ret;
+
+	/* Un-mute done interrupt */
+	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL &
+		       ~HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE,
+		       base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
+
+	return 0;
+}
+
+static int dw_hdmi_close(struct snd_pcm_substream *substream)
+{
+	struct snd_dw_hdmi *dw = substream->private_data;
+
+	/* Mute all interrupts */
+	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
+		       dw->data.base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
+
+	free_irq(dw->data.irq, dw);
+
+	return 0;
+}
+
+static int dw_hdmi_hw_free(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int dw_hdmi_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	return snd_pcm_lib_alloc_vmalloc_buffer(substream,
+						params_buffer_bytes(params));
+}
+
+static int dw_hdmi_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_dw_hdmi *dw = substream->private_data;
+	u8 threshold, conf0, conf1;
+
+	/* Setup as per 3.0.5 FSL 4.1.0 BSP */
+	switch (dw->revision) {
+	case 0x0a:
+		conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
+			HDMI_AHB_DMA_CONF0_INCR4;
+		if (runtime->channels == 2)
+			threshold = 126;
+		else
+			threshold = 124;
+		break;
+	case 0x1a:
+		conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
+			HDMI_AHB_DMA_CONF0_INCR8;
+		threshold = 128;
+		break;
+	default:
+		/* NOTREACHED */
+		return -EINVAL;
+	}
+
+	dw_hdmi_set_sample_rate(dw->data.hdmi, runtime->rate);
+
+	/* Minimum number of bytes in the fifo. */
+	runtime->hw.fifo_size = threshold * 32;
+
+	conf0 |= HDMI_AHB_DMA_CONF0_EN_HLOCK;
+	conf1 = (1 << runtime->channels) - 1;
+
+	writeb_relaxed(threshold, dw->data.base + HDMI_AHB_DMA_THRSLD);
+	writeb_relaxed(conf0, dw->data.base + HDMI_AHB_DMA_CONF0);
+	writeb_relaxed(conf1, dw->data.base + HDMI_AHB_DMA_CONF1);
+
+	switch (runtime->format) {
+	case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
+		dw->reformat = dw_hdmi_reformat_iec958;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		dw_hdmi_create_cs(dw, runtime);
+		dw->reformat = dw_hdmi_reformat_s24;
+		break;
+	}
+	dw->iec_offset = 0;
+	dw->channels = runtime->channels;
+	dw->buf_src  = runtime->dma_area;
+	dw->buf_dst  = substream->dma_buffer.area;
+	dw->buf_addr = substream->dma_buffer.addr;
+	dw->buf_period = snd_pcm_lib_period_bytes(substream);
+	dw->buf_size = snd_pcm_lib_buffer_bytes(substream);
+
+	return 0;
+}
+
+static int dw_hdmi_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_dw_hdmi *dw = substream->private_data;
+	int ret = 0;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		dw->buf_offset = 0;
+		dw->substream = substream;
+		dw_hdmi_start_dma(dw);
+		dw_hdmi_audio_enable(dw->data.hdmi);
+		substream->runtime->delay = substream->runtime->period_size;
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+		dw_hdmi_stop_dma(dw);
+		dw_hdmi_audio_disable(dw->data.hdmi);
+		break;
+
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static snd_pcm_uframes_t dw_hdmi_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_dw_hdmi *dw = substream->private_data;
+
+	return bytes_to_frames(runtime, dw->buf_offset);
+}
+
+static struct snd_pcm_ops snd_dw_hdmi_ops = {
+	.open = dw_hdmi_open,
+	.close = dw_hdmi_close,
+	.ioctl = snd_pcm_lib_ioctl,
+	.hw_params = dw_hdmi_hw_params,
+	.hw_free = dw_hdmi_hw_free,
+	.prepare = dw_hdmi_prepare,
+	.trigger = dw_hdmi_trigger,
+	.pointer = dw_hdmi_pointer,
+	.page = snd_pcm_lib_get_vmalloc_page,
+};
+
+static int snd_dw_hdmi_probe(struct platform_device *pdev)
+{
+	const struct dw_hdmi_audio_data *data = pdev->dev.platform_data;
+	struct device *dev = pdev->dev.parent;
+	struct snd_dw_hdmi *dw;
+	struct snd_card *card;
+	struct snd_pcm *pcm;
+	unsigned revision;
+	int ret;
+
+	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
+		       data->base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
+	revision = readb_relaxed(data->base + HDMI_REVISION_ID);
+	if (revision != 0x0a && revision != 0x1a) {
+		dev_err(dev, "dw-hdmi-audio: unknown revision 0x%02x\n",
+			revision);
+		return -ENXIO;
+	}
+
+	ret = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
+			      THIS_MODULE, sizeof(struct snd_dw_hdmi), &card);
+	if (ret < 0)
+		return ret;
+
+	strlcpy(card->driver, DRIVER_NAME, sizeof(card->driver));
+	strlcpy(card->shortname, "DW-HDMI", sizeof(card->shortname));
+	snprintf(card->longname, sizeof(card->longname),
+		 "%s rev 0x%02x, irq %d", card->shortname, revision,
+		 data->irq);
+
+	dw = card->private_data;
+	dw->card = card;
+	dw->data = *data;
+	dw->revision = revision;
+
+	ret = snd_pcm_new(card, "DW HDMI", 0, 1, 0, &pcm);
+	if (ret < 0)
+		goto err;
+
+	dw->pcm = pcm;
+	pcm->private_data = dw;
+	strlcpy(pcm->name, DRIVER_NAME, sizeof(pcm->name));
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_dw_hdmi_ops);
+
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+			dev, 64 * 1024, 64 * 1024);
+
+	ret = snd_card_register(card);
+	if (ret < 0)
+		goto err;
+
+	platform_set_drvdata(pdev, dw);
+
+	return 0;
+
+err:
+	snd_card_free(card);
+	return ret;
+}
+
+static int snd_dw_hdmi_remove(struct platform_device *pdev)
+{
+	struct snd_dw_hdmi *dw = platform_get_drvdata(pdev);
+
+	snd_card_free(dw->card);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int snd_dw_hdmi_suspend(struct device *dev)
+{
+	struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
+
+	snd_power_change_state(dw->card, SNDRV_CTL_POWER_D3cold);
+	snd_pcm_suspend_all(dw->pcm);
+
+	return 0;
+}
+
+static int snd_dw_hdmi_resume(struct device *dev)
+{
+	struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
+
+	snd_power_change_state(dw->card, SNDRV_CTL_POWER_D0);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(snd_dw_hdmi_pm, snd_dw_hdmi_suspend,
+			 snd_dw_hdmi_resume);
+#define PM_OPS &snd_dw_hdmi_pm
+#else
+#define PM_OPS NULL
+#endif
+
+static struct platform_driver snd_dw_hdmi_driver = {
+	.probe	= snd_dw_hdmi_probe,
+	.remove	= snd_dw_hdmi_remove,
+	.driver	= {
+		.name = DRIVER_NAME,
+		.owner = THIS_MODULE,
+		.pm = PM_OPS,
+	},
+};
+
+module_platform_driver(snd_dw_hdmi_driver);
+
+MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>");
+MODULE_DESCRIPTION("Synopsis Designware HDMI AHB ALSA interface");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/gpu/drm/bridge/dw_hdmi-audio.h b/drivers/gpu/drm/bridge/dw_hdmi-audio.h
new file mode 100644
index 000000000000..1e840118d90a
--- /dev/null
+++ b/drivers/gpu/drm/bridge/dw_hdmi-audio.h
@@ -0,0 +1,13 @@
+#ifndef DW_HDMI_AUDIO_H
+#define DW_HDMI_AUDIO_H
+
+struct dw_hdmi;
+
+struct dw_hdmi_audio_data {
+	phys_addr_t phys;
+	void __iomem *base;
+	int irq;
+	struct dw_hdmi *hdmi;
+};
+
+#endif
diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index fba25607ef88..b65464789fbd 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -28,6 +28,7 @@
 #include <drm/bridge/dw_hdmi.h>
 
 #include "dw_hdmi.h"
+#include "dw_hdmi-audio.h"
 
 #define HDMI_EDID_LEN		512
 
@@ -104,6 +105,7 @@ struct dw_hdmi {
 	struct drm_encoder *encoder;
 	struct drm_bridge *bridge;
 
+	struct platform_device *audio;
 	enum dw_hdmi_devtype dev_type;
 	struct device *dev;
 	struct clk *isfr_clk;
@@ -1732,7 +1734,9 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 {
 	struct drm_device *drm = data;
 	struct device_node *np = dev->of_node;
+	struct platform_device_info pdevinfo;
 	struct device_node *ddc_node;
+	struct dw_hdmi_audio_data audio;
 	struct dw_hdmi *hdmi;
 	int ret;
 	u32 val = 1;
@@ -1860,6 +1864,23 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 	hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE),
 		    HDMI_IH_MUTE_PHY_STAT0);
 
+	memset(&pdevinfo, 0, sizeof(pdevinfo));
+	pdevinfo.parent = dev;
+	pdevinfo.id = PLATFORM_DEVID_AUTO;
+
+	if (hdmi_readb(hdmi, HDMI_CONFIG1_ID) & HDMI_CONFIG1_AHB) {
+		audio.phys = iores->start;
+		audio.base = hdmi->regs;
+		audio.irq = irq;
+		audio.hdmi = hdmi;
+
+		pdevinfo.name = "dw-hdmi-ahb-audio";
+		pdevinfo.data = &audio;
+		pdevinfo.size_data = sizeof(audio);
+		pdevinfo.dma_mask = DMA_BIT_MASK(32);
+		hdmi->audio = platform_device_register_full(&pdevinfo);
+	}
+
 	dev_set_drvdata(dev, hdmi);
 
 	return 0;
@@ -1877,6 +1898,9 @@ void dw_hdmi_unbind(struct device *dev, struct device *master, void *data)
 {
 	struct dw_hdmi *hdmi = dev_get_drvdata(dev);
 
+	if (hdmi->audio && !IS_ERR(hdmi->audio))
+		platform_device_unregister(hdmi->audio);
+
 	/* Disable all interrupts */
 	hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
 
diff --git a/drivers/gpu/drm/bridge/dw_hdmi.h b/drivers/gpu/drm/bridge/dw_hdmi.h
index 175dbc89a824..78e54e813212 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.h
+++ b/drivers/gpu/drm/bridge/dw_hdmi.h
@@ -545,6 +545,9 @@
 #define HDMI_I2CM_FS_SCL_LCNT_0_ADDR            0x7E12
 
 enum {
+/* CONFIG1_ID field values */
+	HDMI_CONFIG1_AHB = 0x01,
+
 /* IH_FC_INT2 field values */
 	HDMI_IH_FC_INT2_OVERFLOW_MASK = 0x03,
 	HDMI_IH_FC_INT2_LOW_PRIORITY_OVERFLOW = 0x02,
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver
@ 2015-08-08 16:10     ` Russell King
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:10 UTC (permalink / raw)
  To: linux-rockchip, alsa-devel, dri-devel, linux-kernel, linux-arm-kernel
  Cc: Fabio Estevam, Jaroslav Kysela, Mark Brown, Yakir Yang, Andy Yan

Add ALSA based HDMI AHB audio driver for dw_hdmi.  The only buffer
format supported by the hardware is its own special IEC958 based format,
which is not compatible with any ALSA format.  To avoid doing too much
data manipulation within the driver, we support only ALSAs IEC958 LE and
24-bit PCM formats for 2 to 6 channels, which we convert to its hardware
format.

A more desirable solution would be to have this conversion in userspace,
but ALSA does not appear to allow such transformations outside of
libasound itself.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/Kconfig             |  10 +
 drivers/gpu/drm/bridge/Makefile            |   1 +
 drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c | 561 +++++++++++++++++++++++++++++
 drivers/gpu/drm/bridge/dw_hdmi-audio.h     |  13 +
 drivers/gpu/drm/bridge/dw_hdmi.c           |  24 ++
 drivers/gpu/drm/bridge/dw_hdmi.h           |   3 +
 6 files changed, 612 insertions(+)
 create mode 100644 drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
 create mode 100644 drivers/gpu/drm/bridge/dw_hdmi-audio.h

diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index acef3223772c..56ed35fe0734 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -3,6 +3,16 @@ config DRM_DW_HDMI
 	depends on DRM
 	select DRM_KMS_HELPER
 
+config DRM_DW_HDMI_AHB_AUDIO
+	tristate "Synopsis Designware AHB Audio interface"
+	depends on DRM_DW_HDMI && SND
+	select SND_PCM
+	select SND_PCM_IEC958
+	help
+	  Support the AHB Audio interface which is part of the Synopsis
+	  Designware HDMI block.  This is used in conjunction with
+	  the i.MX6 HDMI driver.
+
 config DRM_PTN3460
 	tristate "PTN3460 DP/LVDS bridge"
 	depends on DRM
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index 8dfebd984370..eb80dbbb8365 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -3,3 +3,4 @@ ccflags-y := -Iinclude/drm
 obj-$(CONFIG_DRM_PS8622) += ps8622.o
 obj-$(CONFIG_DRM_PTN3460) += ptn3460.o
 obj-$(CONFIG_DRM_DW_HDMI) += dw_hdmi.o
+obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw_hdmi-ahb-audio.o
diff --git a/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
new file mode 100644
index 000000000000..22bbbc5c2393
--- /dev/null
+++ b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
@@ -0,0 +1,561 @@
+/*
+ * DesignWare HDMI audio driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Written and tested against the Designware HDMI Tx found in iMX6.
+ */
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <drm/bridge/dw_hdmi.h>
+
+#include <sound/asoundef.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_iec958.h>
+
+#include "dw_hdmi-audio.h"
+
+#define DRIVER_NAME "dw-hdmi-ahb-audio"
+
+/* Provide some bits rather than bit offsets */
+enum {
+	HDMI_AHB_DMA_CONF0_SW_FIFO_RST = BIT(7),
+	HDMI_AHB_DMA_CONF0_EN_HLOCK = BIT(3),
+	HDMI_AHB_DMA_START_START = BIT(0),
+	HDMI_AHB_DMA_STOP_STOP = BIT(0),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR = BIT(5),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST = BIT(4),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY = BIT(3),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE = BIT(2),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL = BIT(1),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL =
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY,
+	HDMI_IH_AHBDMAAUD_STAT0_ERROR = BIT(5),
+	HDMI_IH_AHBDMAAUD_STAT0_LOST = BIT(4),
+	HDMI_IH_AHBDMAAUD_STAT0_RETRY = BIT(3),
+	HDMI_IH_AHBDMAAUD_STAT0_DONE = BIT(2),
+	HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL = BIT(1),
+	HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0),
+	HDMI_IH_AHBDMAAUD_STAT0_ALL =
+		HDMI_IH_AHBDMAAUD_STAT0_ERROR |
+		HDMI_IH_AHBDMAAUD_STAT0_LOST |
+		HDMI_IH_AHBDMAAUD_STAT0_RETRY |
+		HDMI_IH_AHBDMAAUD_STAT0_DONE |
+		HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL |
+		HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY,
+	HDMI_AHB_DMA_CONF0_INCR16 = 2 << 1,
+	HDMI_AHB_DMA_CONF0_INCR8 = 1 << 1,
+	HDMI_AHB_DMA_CONF0_INCR4 = 0,
+	HDMI_AHB_DMA_CONF0_BURST_MODE = BIT(0),
+	HDMI_AHB_DMA_MASK_DONE = BIT(7),
+	HDMI_REVISION_ID = 0x0001,
+	HDMI_IH_AHBDMAAUD_STAT0 = 0x0109,
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0 = 0x0189,
+	HDMI_AHB_DMA_CONF0 = 0x3600,
+	HDMI_AHB_DMA_START = 0x3601,
+	HDMI_AHB_DMA_STOP = 0x3602,
+	HDMI_AHB_DMA_THRSLD = 0x3603,
+	HDMI_AHB_DMA_STRADDR0 = 0x3604,
+	HDMI_AHB_DMA_STPADDR0 = 0x3608,
+	HDMI_AHB_DMA_MASK = 0x3614,
+	HDMI_AHB_DMA_POL = 0x3615,
+	HDMI_AHB_DMA_CONF1 = 0x3616,
+	HDMI_AHB_DMA_BUFFPOL = 0x361a,
+};
+
+struct snd_dw_hdmi {
+	struct snd_card *card;
+	struct snd_pcm *pcm;
+	struct dw_hdmi_audio_data data;
+	struct snd_pcm_substream *substream;
+	void (*reformat)(struct snd_dw_hdmi *, size_t, size_t);
+	void *buf_src;
+	void *buf_dst;
+	dma_addr_t buf_addr;
+	unsigned buf_offset;
+	unsigned buf_period;
+	unsigned buf_size;
+	unsigned channels;
+	u8 revision;
+	u8 iec_offset;
+	u8 cs[192][8];
+};
+
+static void dw_hdmi_writel(unsigned long val, void __iomem *ptr)
+{
+	writeb_relaxed(val, ptr);
+	writeb_relaxed(val >> 8, ptr + 1);
+	writeb_relaxed(val >> 16, ptr + 2);
+	writeb_relaxed(val >> 24, ptr + 3);
+}
+
+/*
+ * Convert to hardware format: The userspace buffer contains IEC958 samples,
+ * with the PCUV bits in bits 31..28 and audio samples in bits 27..4.  We
+ * need these to be in bits 27..24, with the IEC B bit in bit 28, and audio
+ * samples in 23..0.
+ *
+ * Default preamble in bits 3..0: 8 = block start, 4 = even 2 = odd
+ *
+ * Ideally, we could do with having the data properly formatted in userspace.
+ */
+static void dw_hdmi_reformat_iec958(struct snd_dw_hdmi *dw,
+	size_t offset, size_t bytes)
+{
+	u32 *src = dw->buf_src + offset;
+	u32 *dst = dw->buf_dst + offset;
+	u32 *end = dw->buf_src + offset + bytes;
+
+	do {
+		u32 b, sample = *src++;
+
+		b = (sample & 8) << (28 - 3);
+
+		sample >>= 4;
+
+		*dst++ = sample | b;
+	} while (src < end);
+}
+
+static u32 parity(u32 sample)
+{
+	sample ^= sample >> 16;
+	sample ^= sample >> 8;
+	sample ^= sample >> 4;
+	sample ^= sample >> 2;
+	sample ^= sample >> 1;
+	return (sample & 1) << 27;
+}
+
+static void dw_hdmi_reformat_s24(struct snd_dw_hdmi *dw,
+	size_t offset, size_t bytes)
+{
+	u32 *src = dw->buf_src + offset;
+	u32 *dst = dw->buf_dst + offset;
+	u32 *end = dw->buf_src + offset + bytes;
+
+	do {
+		unsigned i;
+		u8 *cs;
+
+		cs = dw->cs[dw->iec_offset++];
+		if (dw->iec_offset >= 192)
+			dw->iec_offset = 0;
+
+		i = dw->channels;
+		do {
+			u32 sample = *src++;
+
+			sample &= ~0xff000000;
+			sample |= *cs++ << 24;
+			sample |= parity(sample & ~0xf8000000);
+
+			*dst++ = sample;
+		} while (--i);
+	} while (src < end);
+}
+
+static void dw_hdmi_create_cs(struct snd_dw_hdmi *dw,
+	struct snd_pcm_runtime *runtime)
+{
+	u8 cs[4];
+	unsigned ch, i, j;
+
+	snd_pcm_create_iec958_consumer(runtime, cs, sizeof(cs));
+
+	memset(dw->cs, 0, sizeof(dw->cs));
+
+	for (ch = 0; ch < 8; ch++) {
+		cs[2] &= ~IEC958_AES2_CON_CHANNEL;
+		cs[2] |= (ch + 1) << 4;
+
+		for (i = 0; i < ARRAY_SIZE(cs); i++) {
+			unsigned c = cs[i];
+
+			for (j = 0; j < 8; j++, c >>= 1)
+				dw->cs[i * 8 + j][ch] = (c & 1) << 2;
+		}
+	}
+	dw->cs[0][0] |= BIT(4);
+}
+
+static void dw_hdmi_start_dma(struct snd_dw_hdmi *dw)
+{
+	void __iomem *base = dw->data.base;
+	unsigned offset = dw->buf_offset;
+	unsigned period = dw->buf_period;
+	u32 start, stop;
+
+	dw->reformat(dw, offset, period);
+
+	/* Clear all irqs before enabling irqs and starting DMA */
+	writeb_relaxed(HDMI_IH_AHBDMAAUD_STAT0_ALL,
+		       base + HDMI_IH_AHBDMAAUD_STAT0);
+
+	start = dw->buf_addr + offset;
+	stop = start + period - 1;
+
+	/* Setup the hardware start/stop addresses */
+	dw_hdmi_writel(start, base + HDMI_AHB_DMA_STRADDR0);
+	dw_hdmi_writel(stop, base + HDMI_AHB_DMA_STPADDR0);
+
+	writeb_relaxed((u8)~HDMI_AHB_DMA_MASK_DONE, base + HDMI_AHB_DMA_MASK);
+	writeb(HDMI_AHB_DMA_START_START, base + HDMI_AHB_DMA_START);
+
+	offset += period;
+	if (offset >= dw->buf_size)
+		offset = 0;
+	dw->buf_offset = offset;
+}
+
+static void dw_hdmi_stop_dma(struct snd_dw_hdmi *dw)
+{
+	dw->substream = NULL;
+
+	/* Disable interrupts before disabling DMA */
+	writeb_relaxed(~0, dw->data.base + HDMI_AHB_DMA_MASK);
+	writeb_relaxed(HDMI_AHB_DMA_STOP_STOP, dw->data.base + HDMI_AHB_DMA_STOP);
+}
+
+static irqreturn_t snd_dw_hdmi_irq(int irq, void *data)
+{
+	struct snd_dw_hdmi *dw = data;
+	struct snd_pcm_substream *substream;
+	unsigned stat;
+
+	stat = readb_relaxed(dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
+	if (!stat)
+		return IRQ_NONE;
+
+	writeb_relaxed(stat, dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
+
+	substream = dw->substream;
+	if (stat & HDMI_IH_AHBDMAAUD_STAT0_DONE && substream) {
+		snd_pcm_period_elapsed(substream);
+		if (dw->substream)
+			dw_hdmi_start_dma(dw);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static struct snd_pcm_hardware dw_hdmi_hw = {
+	.info = SNDRV_PCM_INFO_INTERLEAVED |
+		SNDRV_PCM_INFO_BLOCK_TRANSFER |
+		SNDRV_PCM_INFO_MMAP |
+		SNDRV_PCM_INFO_MMAP_VALID,
+	.formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE |
+		   SNDRV_PCM_FMTBIT_S24_LE,
+	.rates = SNDRV_PCM_RATE_32000 |
+		 SNDRV_PCM_RATE_44100 |
+		 SNDRV_PCM_RATE_48000 |
+		 SNDRV_PCM_RATE_88200 |
+		 SNDRV_PCM_RATE_96000 |
+		 SNDRV_PCM_RATE_176400 |
+		 SNDRV_PCM_RATE_192000,
+	.channels_min = 2,
+	.channels_max = 8,
+	.buffer_bytes_max = 64 * 1024,
+	.period_bytes_min = 256,
+	.period_bytes_max = 8192,	/* ERR004323: must limit to 8k */
+	.periods_min = 2,
+	.periods_max = 16,
+	.fifo_size = 0,
+};
+
+static int dw_hdmi_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_dw_hdmi *dw = substream->private_data;
+	void __iomem *base = dw->data.base;
+	int ret;
+
+	runtime->hw = dw_hdmi_hw;
+
+	ret = snd_pcm_limit_hw_rates(runtime);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0)
+		return ret;
+
+	/* Clear FIFO */
+	writeb_relaxed(HDMI_AHB_DMA_CONF0_SW_FIFO_RST,
+		       base + HDMI_AHB_DMA_CONF0);
+
+	/* Configure interrupt polarities */
+	writeb_relaxed(~0, base + HDMI_AHB_DMA_POL);
+	writeb_relaxed(~0, base + HDMI_AHB_DMA_BUFFPOL);
+
+	/* Keep interrupts masked, and clear any pending */
+	writeb_relaxed(~0, base + HDMI_AHB_DMA_MASK);
+	writeb_relaxed(~0, base + HDMI_IH_AHBDMAAUD_STAT0);
+
+	ret = request_irq(dw->data.irq, snd_dw_hdmi_irq, IRQF_SHARED,
+			  "dw-hdmi-audio", dw);
+	if (ret)
+		return ret;
+
+	/* Un-mute done interrupt */
+	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL &
+		       ~HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE,
+		       base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
+
+	return 0;
+}
+
+static int dw_hdmi_close(struct snd_pcm_substream *substream)
+{
+	struct snd_dw_hdmi *dw = substream->private_data;
+
+	/* Mute all interrupts */
+	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
+		       dw->data.base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
+
+	free_irq(dw->data.irq, dw);
+
+	return 0;
+}
+
+static int dw_hdmi_hw_free(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int dw_hdmi_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	return snd_pcm_lib_alloc_vmalloc_buffer(substream,
+						params_buffer_bytes(params));
+}
+
+static int dw_hdmi_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_dw_hdmi *dw = substream->private_data;
+	u8 threshold, conf0, conf1;
+
+	/* Setup as per 3.0.5 FSL 4.1.0 BSP */
+	switch (dw->revision) {
+	case 0x0a:
+		conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
+			HDMI_AHB_DMA_CONF0_INCR4;
+		if (runtime->channels == 2)
+			threshold = 126;
+		else
+			threshold = 124;
+		break;
+	case 0x1a:
+		conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
+			HDMI_AHB_DMA_CONF0_INCR8;
+		threshold = 128;
+		break;
+	default:
+		/* NOTREACHED */
+		return -EINVAL;
+	}
+
+	dw_hdmi_set_sample_rate(dw->data.hdmi, runtime->rate);
+
+	/* Minimum number of bytes in the fifo. */
+	runtime->hw.fifo_size = threshold * 32;
+
+	conf0 |= HDMI_AHB_DMA_CONF0_EN_HLOCK;
+	conf1 = (1 << runtime->channels) - 1;
+
+	writeb_relaxed(threshold, dw->data.base + HDMI_AHB_DMA_THRSLD);
+	writeb_relaxed(conf0, dw->data.base + HDMI_AHB_DMA_CONF0);
+	writeb_relaxed(conf1, dw->data.base + HDMI_AHB_DMA_CONF1);
+
+	switch (runtime->format) {
+	case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
+		dw->reformat = dw_hdmi_reformat_iec958;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		dw_hdmi_create_cs(dw, runtime);
+		dw->reformat = dw_hdmi_reformat_s24;
+		break;
+	}
+	dw->iec_offset = 0;
+	dw->channels = runtime->channels;
+	dw->buf_src  = runtime->dma_area;
+	dw->buf_dst  = substream->dma_buffer.area;
+	dw->buf_addr = substream->dma_buffer.addr;
+	dw->buf_period = snd_pcm_lib_period_bytes(substream);
+	dw->buf_size = snd_pcm_lib_buffer_bytes(substream);
+
+	return 0;
+}
+
+static int dw_hdmi_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_dw_hdmi *dw = substream->private_data;
+	int ret = 0;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		dw->buf_offset = 0;
+		dw->substream = substream;
+		dw_hdmi_start_dma(dw);
+		dw_hdmi_audio_enable(dw->data.hdmi);
+		substream->runtime->delay = substream->runtime->period_size;
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+		dw_hdmi_stop_dma(dw);
+		dw_hdmi_audio_disable(dw->data.hdmi);
+		break;
+
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static snd_pcm_uframes_t dw_hdmi_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_dw_hdmi *dw = substream->private_data;
+
+	return bytes_to_frames(runtime, dw->buf_offset);
+}
+
+static struct snd_pcm_ops snd_dw_hdmi_ops = {
+	.open = dw_hdmi_open,
+	.close = dw_hdmi_close,
+	.ioctl = snd_pcm_lib_ioctl,
+	.hw_params = dw_hdmi_hw_params,
+	.hw_free = dw_hdmi_hw_free,
+	.prepare = dw_hdmi_prepare,
+	.trigger = dw_hdmi_trigger,
+	.pointer = dw_hdmi_pointer,
+	.page = snd_pcm_lib_get_vmalloc_page,
+};
+
+static int snd_dw_hdmi_probe(struct platform_device *pdev)
+{
+	const struct dw_hdmi_audio_data *data = pdev->dev.platform_data;
+	struct device *dev = pdev->dev.parent;
+	struct snd_dw_hdmi *dw;
+	struct snd_card *card;
+	struct snd_pcm *pcm;
+	unsigned revision;
+	int ret;
+
+	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
+		       data->base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
+	revision = readb_relaxed(data->base + HDMI_REVISION_ID);
+	if (revision != 0x0a && revision != 0x1a) {
+		dev_err(dev, "dw-hdmi-audio: unknown revision 0x%02x\n",
+			revision);
+		return -ENXIO;
+	}
+
+	ret = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
+			      THIS_MODULE, sizeof(struct snd_dw_hdmi), &card);
+	if (ret < 0)
+		return ret;
+
+	strlcpy(card->driver, DRIVER_NAME, sizeof(card->driver));
+	strlcpy(card->shortname, "DW-HDMI", sizeof(card->shortname));
+	snprintf(card->longname, sizeof(card->longname),
+		 "%s rev 0x%02x, irq %d", card->shortname, revision,
+		 data->irq);
+
+	dw = card->private_data;
+	dw->card = card;
+	dw->data = *data;
+	dw->revision = revision;
+
+	ret = snd_pcm_new(card, "DW HDMI", 0, 1, 0, &pcm);
+	if (ret < 0)
+		goto err;
+
+	dw->pcm = pcm;
+	pcm->private_data = dw;
+	strlcpy(pcm->name, DRIVER_NAME, sizeof(pcm->name));
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_dw_hdmi_ops);
+
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+			dev, 64 * 1024, 64 * 1024);
+
+	ret = snd_card_register(card);
+	if (ret < 0)
+		goto err;
+
+	platform_set_drvdata(pdev, dw);
+
+	return 0;
+
+err:
+	snd_card_free(card);
+	return ret;
+}
+
+static int snd_dw_hdmi_remove(struct platform_device *pdev)
+{
+	struct snd_dw_hdmi *dw = platform_get_drvdata(pdev);
+
+	snd_card_free(dw->card);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int snd_dw_hdmi_suspend(struct device *dev)
+{
+	struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
+
+	snd_power_change_state(dw->card, SNDRV_CTL_POWER_D3cold);
+	snd_pcm_suspend_all(dw->pcm);
+
+	return 0;
+}
+
+static int snd_dw_hdmi_resume(struct device *dev)
+{
+	struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
+
+	snd_power_change_state(dw->card, SNDRV_CTL_POWER_D0);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(snd_dw_hdmi_pm, snd_dw_hdmi_suspend,
+			 snd_dw_hdmi_resume);
+#define PM_OPS &snd_dw_hdmi_pm
+#else
+#define PM_OPS NULL
+#endif
+
+static struct platform_driver snd_dw_hdmi_driver = {
+	.probe	= snd_dw_hdmi_probe,
+	.remove	= snd_dw_hdmi_remove,
+	.driver	= {
+		.name = DRIVER_NAME,
+		.owner = THIS_MODULE,
+		.pm = PM_OPS,
+	},
+};
+
+module_platform_driver(snd_dw_hdmi_driver);
+
+MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>");
+MODULE_DESCRIPTION("Synopsis Designware HDMI AHB ALSA interface");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/gpu/drm/bridge/dw_hdmi-audio.h b/drivers/gpu/drm/bridge/dw_hdmi-audio.h
new file mode 100644
index 000000000000..1e840118d90a
--- /dev/null
+++ b/drivers/gpu/drm/bridge/dw_hdmi-audio.h
@@ -0,0 +1,13 @@
+#ifndef DW_HDMI_AUDIO_H
+#define DW_HDMI_AUDIO_H
+
+struct dw_hdmi;
+
+struct dw_hdmi_audio_data {
+	phys_addr_t phys;
+	void __iomem *base;
+	int irq;
+	struct dw_hdmi *hdmi;
+};
+
+#endif
diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index fba25607ef88..b65464789fbd 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -28,6 +28,7 @@
 #include <drm/bridge/dw_hdmi.h>
 
 #include "dw_hdmi.h"
+#include "dw_hdmi-audio.h"
 
 #define HDMI_EDID_LEN		512
 
@@ -104,6 +105,7 @@ struct dw_hdmi {
 	struct drm_encoder *encoder;
 	struct drm_bridge *bridge;
 
+	struct platform_device *audio;
 	enum dw_hdmi_devtype dev_type;
 	struct device *dev;
 	struct clk *isfr_clk;
@@ -1732,7 +1734,9 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 {
 	struct drm_device *drm = data;
 	struct device_node *np = dev->of_node;
+	struct platform_device_info pdevinfo;
 	struct device_node *ddc_node;
+	struct dw_hdmi_audio_data audio;
 	struct dw_hdmi *hdmi;
 	int ret;
 	u32 val = 1;
@@ -1860,6 +1864,23 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 	hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE),
 		    HDMI_IH_MUTE_PHY_STAT0);
 
+	memset(&pdevinfo, 0, sizeof(pdevinfo));
+	pdevinfo.parent = dev;
+	pdevinfo.id = PLATFORM_DEVID_AUTO;
+
+	if (hdmi_readb(hdmi, HDMI_CONFIG1_ID) & HDMI_CONFIG1_AHB) {
+		audio.phys = iores->start;
+		audio.base = hdmi->regs;
+		audio.irq = irq;
+		audio.hdmi = hdmi;
+
+		pdevinfo.name = "dw-hdmi-ahb-audio";
+		pdevinfo.data = &audio;
+		pdevinfo.size_data = sizeof(audio);
+		pdevinfo.dma_mask = DMA_BIT_MASK(32);
+		hdmi->audio = platform_device_register_full(&pdevinfo);
+	}
+
 	dev_set_drvdata(dev, hdmi);
 
 	return 0;
@@ -1877,6 +1898,9 @@ void dw_hdmi_unbind(struct device *dev, struct device *master, void *data)
 {
 	struct dw_hdmi *hdmi = dev_get_drvdata(dev);
 
+	if (hdmi->audio && !IS_ERR(hdmi->audio))
+		platform_device_unregister(hdmi->audio);
+
 	/* Disable all interrupts */
 	hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
 
diff --git a/drivers/gpu/drm/bridge/dw_hdmi.h b/drivers/gpu/drm/bridge/dw_hdmi.h
index 175dbc89a824..78e54e813212 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.h
+++ b/drivers/gpu/drm/bridge/dw_hdmi.h
@@ -545,6 +545,9 @@
 #define HDMI_I2CM_FS_SCL_LCNT_0_ADDR            0x7E12
 
 enum {
+/* CONFIG1_ID field values */
+	HDMI_CONFIG1_AHB = 0x01,
+
 /* IH_FC_INT2 field values */
 	HDMI_IH_FC_INT2_OVERFLOW_MASK = 0x03,
 	HDMI_IH_FC_INT2_LOW_PRIORITY_OVERFLOW = 0x02,
-- 
2.1.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver
@ 2015-08-08 16:10     ` Russell King
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:10 UTC (permalink / raw)
  To: linux-arm-kernel

Add ALSA based HDMI AHB audio driver for dw_hdmi.  The only buffer
format supported by the hardware is its own special IEC958 based format,
which is not compatible with any ALSA format.  To avoid doing too much
data manipulation within the driver, we support only ALSAs IEC958 LE and
24-bit PCM formats for 2 to 6 channels, which we convert to its hardware
format.

A more desirable solution would be to have this conversion in userspace,
but ALSA does not appear to allow such transformations outside of
libasound itself.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/Kconfig             |  10 +
 drivers/gpu/drm/bridge/Makefile            |   1 +
 drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c | 561 +++++++++++++++++++++++++++++
 drivers/gpu/drm/bridge/dw_hdmi-audio.h     |  13 +
 drivers/gpu/drm/bridge/dw_hdmi.c           |  24 ++
 drivers/gpu/drm/bridge/dw_hdmi.h           |   3 +
 6 files changed, 612 insertions(+)
 create mode 100644 drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
 create mode 100644 drivers/gpu/drm/bridge/dw_hdmi-audio.h

diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index acef3223772c..56ed35fe0734 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -3,6 +3,16 @@ config DRM_DW_HDMI
 	depends on DRM
 	select DRM_KMS_HELPER
 
+config DRM_DW_HDMI_AHB_AUDIO
+	tristate "Synopsis Designware AHB Audio interface"
+	depends on DRM_DW_HDMI && SND
+	select SND_PCM
+	select SND_PCM_IEC958
+	help
+	  Support the AHB Audio interface which is part of the Synopsis
+	  Designware HDMI block.  This is used in conjunction with
+	  the i.MX6 HDMI driver.
+
 config DRM_PTN3460
 	tristate "PTN3460 DP/LVDS bridge"
 	depends on DRM
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index 8dfebd984370..eb80dbbb8365 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -3,3 +3,4 @@ ccflags-y := -Iinclude/drm
 obj-$(CONFIG_DRM_PS8622) += ps8622.o
 obj-$(CONFIG_DRM_PTN3460) += ptn3460.o
 obj-$(CONFIG_DRM_DW_HDMI) += dw_hdmi.o
+obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw_hdmi-ahb-audio.o
diff --git a/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
new file mode 100644
index 000000000000..22bbbc5c2393
--- /dev/null
+++ b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
@@ -0,0 +1,561 @@
+/*
+ * DesignWare HDMI audio driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Written and tested against the Designware HDMI Tx found in iMX6.
+ */
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <drm/bridge/dw_hdmi.h>
+
+#include <sound/asoundef.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_iec958.h>
+
+#include "dw_hdmi-audio.h"
+
+#define DRIVER_NAME "dw-hdmi-ahb-audio"
+
+/* Provide some bits rather than bit offsets */
+enum {
+	HDMI_AHB_DMA_CONF0_SW_FIFO_RST = BIT(7),
+	HDMI_AHB_DMA_CONF0_EN_HLOCK = BIT(3),
+	HDMI_AHB_DMA_START_START = BIT(0),
+	HDMI_AHB_DMA_STOP_STOP = BIT(0),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR = BIT(5),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST = BIT(4),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY = BIT(3),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE = BIT(2),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL = BIT(1),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL =
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY,
+	HDMI_IH_AHBDMAAUD_STAT0_ERROR = BIT(5),
+	HDMI_IH_AHBDMAAUD_STAT0_LOST = BIT(4),
+	HDMI_IH_AHBDMAAUD_STAT0_RETRY = BIT(3),
+	HDMI_IH_AHBDMAAUD_STAT0_DONE = BIT(2),
+	HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL = BIT(1),
+	HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0),
+	HDMI_IH_AHBDMAAUD_STAT0_ALL =
+		HDMI_IH_AHBDMAAUD_STAT0_ERROR |
+		HDMI_IH_AHBDMAAUD_STAT0_LOST |
+		HDMI_IH_AHBDMAAUD_STAT0_RETRY |
+		HDMI_IH_AHBDMAAUD_STAT0_DONE |
+		HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL |
+		HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY,
+	HDMI_AHB_DMA_CONF0_INCR16 = 2 << 1,
+	HDMI_AHB_DMA_CONF0_INCR8 = 1 << 1,
+	HDMI_AHB_DMA_CONF0_INCR4 = 0,
+	HDMI_AHB_DMA_CONF0_BURST_MODE = BIT(0),
+	HDMI_AHB_DMA_MASK_DONE = BIT(7),
+	HDMI_REVISION_ID = 0x0001,
+	HDMI_IH_AHBDMAAUD_STAT0 = 0x0109,
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0 = 0x0189,
+	HDMI_AHB_DMA_CONF0 = 0x3600,
+	HDMI_AHB_DMA_START = 0x3601,
+	HDMI_AHB_DMA_STOP = 0x3602,
+	HDMI_AHB_DMA_THRSLD = 0x3603,
+	HDMI_AHB_DMA_STRADDR0 = 0x3604,
+	HDMI_AHB_DMA_STPADDR0 = 0x3608,
+	HDMI_AHB_DMA_MASK = 0x3614,
+	HDMI_AHB_DMA_POL = 0x3615,
+	HDMI_AHB_DMA_CONF1 = 0x3616,
+	HDMI_AHB_DMA_BUFFPOL = 0x361a,
+};
+
+struct snd_dw_hdmi {
+	struct snd_card *card;
+	struct snd_pcm *pcm;
+	struct dw_hdmi_audio_data data;
+	struct snd_pcm_substream *substream;
+	void (*reformat)(struct snd_dw_hdmi *, size_t, size_t);
+	void *buf_src;
+	void *buf_dst;
+	dma_addr_t buf_addr;
+	unsigned buf_offset;
+	unsigned buf_period;
+	unsigned buf_size;
+	unsigned channels;
+	u8 revision;
+	u8 iec_offset;
+	u8 cs[192][8];
+};
+
+static void dw_hdmi_writel(unsigned long val, void __iomem *ptr)
+{
+	writeb_relaxed(val, ptr);
+	writeb_relaxed(val >> 8, ptr + 1);
+	writeb_relaxed(val >> 16, ptr + 2);
+	writeb_relaxed(val >> 24, ptr + 3);
+}
+
+/*
+ * Convert to hardware format: The userspace buffer contains IEC958 samples,
+ * with the PCUV bits in bits 31..28 and audio samples in bits 27..4.  We
+ * need these to be in bits 27..24, with the IEC B bit in bit 28, and audio
+ * samples in 23..0.
+ *
+ * Default preamble in bits 3..0: 8 = block start, 4 = even 2 = odd
+ *
+ * Ideally, we could do with having the data properly formatted in userspace.
+ */
+static void dw_hdmi_reformat_iec958(struct snd_dw_hdmi *dw,
+	size_t offset, size_t bytes)
+{
+	u32 *src = dw->buf_src + offset;
+	u32 *dst = dw->buf_dst + offset;
+	u32 *end = dw->buf_src + offset + bytes;
+
+	do {
+		u32 b, sample = *src++;
+
+		b = (sample & 8) << (28 - 3);
+
+		sample >>= 4;
+
+		*dst++ = sample | b;
+	} while (src < end);
+}
+
+static u32 parity(u32 sample)
+{
+	sample ^= sample >> 16;
+	sample ^= sample >> 8;
+	sample ^= sample >> 4;
+	sample ^= sample >> 2;
+	sample ^= sample >> 1;
+	return (sample & 1) << 27;
+}
+
+static void dw_hdmi_reformat_s24(struct snd_dw_hdmi *dw,
+	size_t offset, size_t bytes)
+{
+	u32 *src = dw->buf_src + offset;
+	u32 *dst = dw->buf_dst + offset;
+	u32 *end = dw->buf_src + offset + bytes;
+
+	do {
+		unsigned i;
+		u8 *cs;
+
+		cs = dw->cs[dw->iec_offset++];
+		if (dw->iec_offset >= 192)
+			dw->iec_offset = 0;
+
+		i = dw->channels;
+		do {
+			u32 sample = *src++;
+
+			sample &= ~0xff000000;
+			sample |= *cs++ << 24;
+			sample |= parity(sample & ~0xf8000000);
+
+			*dst++ = sample;
+		} while (--i);
+	} while (src < end);
+}
+
+static void dw_hdmi_create_cs(struct snd_dw_hdmi *dw,
+	struct snd_pcm_runtime *runtime)
+{
+	u8 cs[4];
+	unsigned ch, i, j;
+
+	snd_pcm_create_iec958_consumer(runtime, cs, sizeof(cs));
+
+	memset(dw->cs, 0, sizeof(dw->cs));
+
+	for (ch = 0; ch < 8; ch++) {
+		cs[2] &= ~IEC958_AES2_CON_CHANNEL;
+		cs[2] |= (ch + 1) << 4;
+
+		for (i = 0; i < ARRAY_SIZE(cs); i++) {
+			unsigned c = cs[i];
+
+			for (j = 0; j < 8; j++, c >>= 1)
+				dw->cs[i * 8 + j][ch] = (c & 1) << 2;
+		}
+	}
+	dw->cs[0][0] |= BIT(4);
+}
+
+static void dw_hdmi_start_dma(struct snd_dw_hdmi *dw)
+{
+	void __iomem *base = dw->data.base;
+	unsigned offset = dw->buf_offset;
+	unsigned period = dw->buf_period;
+	u32 start, stop;
+
+	dw->reformat(dw, offset, period);
+
+	/* Clear all irqs before enabling irqs and starting DMA */
+	writeb_relaxed(HDMI_IH_AHBDMAAUD_STAT0_ALL,
+		       base + HDMI_IH_AHBDMAAUD_STAT0);
+
+	start = dw->buf_addr + offset;
+	stop = start + period - 1;
+
+	/* Setup the hardware start/stop addresses */
+	dw_hdmi_writel(start, base + HDMI_AHB_DMA_STRADDR0);
+	dw_hdmi_writel(stop, base + HDMI_AHB_DMA_STPADDR0);
+
+	writeb_relaxed((u8)~HDMI_AHB_DMA_MASK_DONE, base + HDMI_AHB_DMA_MASK);
+	writeb(HDMI_AHB_DMA_START_START, base + HDMI_AHB_DMA_START);
+
+	offset += period;
+	if (offset >= dw->buf_size)
+		offset = 0;
+	dw->buf_offset = offset;
+}
+
+static void dw_hdmi_stop_dma(struct snd_dw_hdmi *dw)
+{
+	dw->substream = NULL;
+
+	/* Disable interrupts before disabling DMA */
+	writeb_relaxed(~0, dw->data.base + HDMI_AHB_DMA_MASK);
+	writeb_relaxed(HDMI_AHB_DMA_STOP_STOP, dw->data.base + HDMI_AHB_DMA_STOP);
+}
+
+static irqreturn_t snd_dw_hdmi_irq(int irq, void *data)
+{
+	struct snd_dw_hdmi *dw = data;
+	struct snd_pcm_substream *substream;
+	unsigned stat;
+
+	stat = readb_relaxed(dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
+	if (!stat)
+		return IRQ_NONE;
+
+	writeb_relaxed(stat, dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
+
+	substream = dw->substream;
+	if (stat & HDMI_IH_AHBDMAAUD_STAT0_DONE && substream) {
+		snd_pcm_period_elapsed(substream);
+		if (dw->substream)
+			dw_hdmi_start_dma(dw);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static struct snd_pcm_hardware dw_hdmi_hw = {
+	.info = SNDRV_PCM_INFO_INTERLEAVED |
+		SNDRV_PCM_INFO_BLOCK_TRANSFER |
+		SNDRV_PCM_INFO_MMAP |
+		SNDRV_PCM_INFO_MMAP_VALID,
+	.formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE |
+		   SNDRV_PCM_FMTBIT_S24_LE,
+	.rates = SNDRV_PCM_RATE_32000 |
+		 SNDRV_PCM_RATE_44100 |
+		 SNDRV_PCM_RATE_48000 |
+		 SNDRV_PCM_RATE_88200 |
+		 SNDRV_PCM_RATE_96000 |
+		 SNDRV_PCM_RATE_176400 |
+		 SNDRV_PCM_RATE_192000,
+	.channels_min = 2,
+	.channels_max = 8,
+	.buffer_bytes_max = 64 * 1024,
+	.period_bytes_min = 256,
+	.period_bytes_max = 8192,	/* ERR004323: must limit to 8k */
+	.periods_min = 2,
+	.periods_max = 16,
+	.fifo_size = 0,
+};
+
+static int dw_hdmi_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_dw_hdmi *dw = substream->private_data;
+	void __iomem *base = dw->data.base;
+	int ret;
+
+	runtime->hw = dw_hdmi_hw;
+
+	ret = snd_pcm_limit_hw_rates(runtime);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0)
+		return ret;
+
+	/* Clear FIFO */
+	writeb_relaxed(HDMI_AHB_DMA_CONF0_SW_FIFO_RST,
+		       base + HDMI_AHB_DMA_CONF0);
+
+	/* Configure interrupt polarities */
+	writeb_relaxed(~0, base + HDMI_AHB_DMA_POL);
+	writeb_relaxed(~0, base + HDMI_AHB_DMA_BUFFPOL);
+
+	/* Keep interrupts masked, and clear any pending */
+	writeb_relaxed(~0, base + HDMI_AHB_DMA_MASK);
+	writeb_relaxed(~0, base + HDMI_IH_AHBDMAAUD_STAT0);
+
+	ret = request_irq(dw->data.irq, snd_dw_hdmi_irq, IRQF_SHARED,
+			  "dw-hdmi-audio", dw);
+	if (ret)
+		return ret;
+
+	/* Un-mute done interrupt */
+	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL &
+		       ~HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE,
+		       base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
+
+	return 0;
+}
+
+static int dw_hdmi_close(struct snd_pcm_substream *substream)
+{
+	struct snd_dw_hdmi *dw = substream->private_data;
+
+	/* Mute all interrupts */
+	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
+		       dw->data.base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
+
+	free_irq(dw->data.irq, dw);
+
+	return 0;
+}
+
+static int dw_hdmi_hw_free(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int dw_hdmi_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	return snd_pcm_lib_alloc_vmalloc_buffer(substream,
+						params_buffer_bytes(params));
+}
+
+static int dw_hdmi_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_dw_hdmi *dw = substream->private_data;
+	u8 threshold, conf0, conf1;
+
+	/* Setup as per 3.0.5 FSL 4.1.0 BSP */
+	switch (dw->revision) {
+	case 0x0a:
+		conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
+			HDMI_AHB_DMA_CONF0_INCR4;
+		if (runtime->channels == 2)
+			threshold = 126;
+		else
+			threshold = 124;
+		break;
+	case 0x1a:
+		conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
+			HDMI_AHB_DMA_CONF0_INCR8;
+		threshold = 128;
+		break;
+	default:
+		/* NOTREACHED */
+		return -EINVAL;
+	}
+
+	dw_hdmi_set_sample_rate(dw->data.hdmi, runtime->rate);
+
+	/* Minimum number of bytes in the fifo. */
+	runtime->hw.fifo_size = threshold * 32;
+
+	conf0 |= HDMI_AHB_DMA_CONF0_EN_HLOCK;
+	conf1 = (1 << runtime->channels) - 1;
+
+	writeb_relaxed(threshold, dw->data.base + HDMI_AHB_DMA_THRSLD);
+	writeb_relaxed(conf0, dw->data.base + HDMI_AHB_DMA_CONF0);
+	writeb_relaxed(conf1, dw->data.base + HDMI_AHB_DMA_CONF1);
+
+	switch (runtime->format) {
+	case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
+		dw->reformat = dw_hdmi_reformat_iec958;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		dw_hdmi_create_cs(dw, runtime);
+		dw->reformat = dw_hdmi_reformat_s24;
+		break;
+	}
+	dw->iec_offset = 0;
+	dw->channels = runtime->channels;
+	dw->buf_src  = runtime->dma_area;
+	dw->buf_dst  = substream->dma_buffer.area;
+	dw->buf_addr = substream->dma_buffer.addr;
+	dw->buf_period = snd_pcm_lib_period_bytes(substream);
+	dw->buf_size = snd_pcm_lib_buffer_bytes(substream);
+
+	return 0;
+}
+
+static int dw_hdmi_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_dw_hdmi *dw = substream->private_data;
+	int ret = 0;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		dw->buf_offset = 0;
+		dw->substream = substream;
+		dw_hdmi_start_dma(dw);
+		dw_hdmi_audio_enable(dw->data.hdmi);
+		substream->runtime->delay = substream->runtime->period_size;
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+		dw_hdmi_stop_dma(dw);
+		dw_hdmi_audio_disable(dw->data.hdmi);
+		break;
+
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static snd_pcm_uframes_t dw_hdmi_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_dw_hdmi *dw = substream->private_data;
+
+	return bytes_to_frames(runtime, dw->buf_offset);
+}
+
+static struct snd_pcm_ops snd_dw_hdmi_ops = {
+	.open = dw_hdmi_open,
+	.close = dw_hdmi_close,
+	.ioctl = snd_pcm_lib_ioctl,
+	.hw_params = dw_hdmi_hw_params,
+	.hw_free = dw_hdmi_hw_free,
+	.prepare = dw_hdmi_prepare,
+	.trigger = dw_hdmi_trigger,
+	.pointer = dw_hdmi_pointer,
+	.page = snd_pcm_lib_get_vmalloc_page,
+};
+
+static int snd_dw_hdmi_probe(struct platform_device *pdev)
+{
+	const struct dw_hdmi_audio_data *data = pdev->dev.platform_data;
+	struct device *dev = pdev->dev.parent;
+	struct snd_dw_hdmi *dw;
+	struct snd_card *card;
+	struct snd_pcm *pcm;
+	unsigned revision;
+	int ret;
+
+	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
+		       data->base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
+	revision = readb_relaxed(data->base + HDMI_REVISION_ID);
+	if (revision != 0x0a && revision != 0x1a) {
+		dev_err(dev, "dw-hdmi-audio: unknown revision 0x%02x\n",
+			revision);
+		return -ENXIO;
+	}
+
+	ret = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
+			      THIS_MODULE, sizeof(struct snd_dw_hdmi), &card);
+	if (ret < 0)
+		return ret;
+
+	strlcpy(card->driver, DRIVER_NAME, sizeof(card->driver));
+	strlcpy(card->shortname, "DW-HDMI", sizeof(card->shortname));
+	snprintf(card->longname, sizeof(card->longname),
+		 "%s rev 0x%02x, irq %d", card->shortname, revision,
+		 data->irq);
+
+	dw = card->private_data;
+	dw->card = card;
+	dw->data = *data;
+	dw->revision = revision;
+
+	ret = snd_pcm_new(card, "DW HDMI", 0, 1, 0, &pcm);
+	if (ret < 0)
+		goto err;
+
+	dw->pcm = pcm;
+	pcm->private_data = dw;
+	strlcpy(pcm->name, DRIVER_NAME, sizeof(pcm->name));
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_dw_hdmi_ops);
+
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+			dev, 64 * 1024, 64 * 1024);
+
+	ret = snd_card_register(card);
+	if (ret < 0)
+		goto err;
+
+	platform_set_drvdata(pdev, dw);
+
+	return 0;
+
+err:
+	snd_card_free(card);
+	return ret;
+}
+
+static int snd_dw_hdmi_remove(struct platform_device *pdev)
+{
+	struct snd_dw_hdmi *dw = platform_get_drvdata(pdev);
+
+	snd_card_free(dw->card);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int snd_dw_hdmi_suspend(struct device *dev)
+{
+	struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
+
+	snd_power_change_state(dw->card, SNDRV_CTL_POWER_D3cold);
+	snd_pcm_suspend_all(dw->pcm);
+
+	return 0;
+}
+
+static int snd_dw_hdmi_resume(struct device *dev)
+{
+	struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
+
+	snd_power_change_state(dw->card, SNDRV_CTL_POWER_D0);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(snd_dw_hdmi_pm, snd_dw_hdmi_suspend,
+			 snd_dw_hdmi_resume);
+#define PM_OPS &snd_dw_hdmi_pm
+#else
+#define PM_OPS NULL
+#endif
+
+static struct platform_driver snd_dw_hdmi_driver = {
+	.probe	= snd_dw_hdmi_probe,
+	.remove	= snd_dw_hdmi_remove,
+	.driver	= {
+		.name = DRIVER_NAME,
+		.owner = THIS_MODULE,
+		.pm = PM_OPS,
+	},
+};
+
+module_platform_driver(snd_dw_hdmi_driver);
+
+MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>");
+MODULE_DESCRIPTION("Synopsis Designware HDMI AHB ALSA interface");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/gpu/drm/bridge/dw_hdmi-audio.h b/drivers/gpu/drm/bridge/dw_hdmi-audio.h
new file mode 100644
index 000000000000..1e840118d90a
--- /dev/null
+++ b/drivers/gpu/drm/bridge/dw_hdmi-audio.h
@@ -0,0 +1,13 @@
+#ifndef DW_HDMI_AUDIO_H
+#define DW_HDMI_AUDIO_H
+
+struct dw_hdmi;
+
+struct dw_hdmi_audio_data {
+	phys_addr_t phys;
+	void __iomem *base;
+	int irq;
+	struct dw_hdmi *hdmi;
+};
+
+#endif
diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index fba25607ef88..b65464789fbd 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -28,6 +28,7 @@
 #include <drm/bridge/dw_hdmi.h>
 
 #include "dw_hdmi.h"
+#include "dw_hdmi-audio.h"
 
 #define HDMI_EDID_LEN		512
 
@@ -104,6 +105,7 @@ struct dw_hdmi {
 	struct drm_encoder *encoder;
 	struct drm_bridge *bridge;
 
+	struct platform_device *audio;
 	enum dw_hdmi_devtype dev_type;
 	struct device *dev;
 	struct clk *isfr_clk;
@@ -1732,7 +1734,9 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 {
 	struct drm_device *drm = data;
 	struct device_node *np = dev->of_node;
+	struct platform_device_info pdevinfo;
 	struct device_node *ddc_node;
+	struct dw_hdmi_audio_data audio;
 	struct dw_hdmi *hdmi;
 	int ret;
 	u32 val = 1;
@@ -1860,6 +1864,23 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 	hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE),
 		    HDMI_IH_MUTE_PHY_STAT0);
 
+	memset(&pdevinfo, 0, sizeof(pdevinfo));
+	pdevinfo.parent = dev;
+	pdevinfo.id = PLATFORM_DEVID_AUTO;
+
+	if (hdmi_readb(hdmi, HDMI_CONFIG1_ID) & HDMI_CONFIG1_AHB) {
+		audio.phys = iores->start;
+		audio.base = hdmi->regs;
+		audio.irq = irq;
+		audio.hdmi = hdmi;
+
+		pdevinfo.name = "dw-hdmi-ahb-audio";
+		pdevinfo.data = &audio;
+		pdevinfo.size_data = sizeof(audio);
+		pdevinfo.dma_mask = DMA_BIT_MASK(32);
+		hdmi->audio = platform_device_register_full(&pdevinfo);
+	}
+
 	dev_set_drvdata(dev, hdmi);
 
 	return 0;
@@ -1877,6 +1898,9 @@ void dw_hdmi_unbind(struct device *dev, struct device *master, void *data)
 {
 	struct dw_hdmi *hdmi = dev_get_drvdata(dev);
 
+	if (hdmi->audio && !IS_ERR(hdmi->audio))
+		platform_device_unregister(hdmi->audio);
+
 	/* Disable all interrupts */
 	hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
 
diff --git a/drivers/gpu/drm/bridge/dw_hdmi.h b/drivers/gpu/drm/bridge/dw_hdmi.h
index 175dbc89a824..78e54e813212 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.h
+++ b/drivers/gpu/drm/bridge/dw_hdmi.h
@@ -545,6 +545,9 @@
 #define HDMI_I2CM_FS_SCL_LCNT_0_ADDR            0x7E12
 
 enum {
+/* CONFIG1_ID field values */
+	HDMI_CONFIG1_AHB = 0x01,
+
 /* IH_FC_INT2 field values */
 	HDMI_IH_FC_INT2_OVERFLOW_MASK = 0x03,
 	HDMI_IH_FC_INT2_LOW_PRIORITY_OVERFLOW = 0x02,
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 2/9] drm: bridge/dw_hdmi-ahb-audio: parse ELD from HDMI driver
  2015-08-08 16:09   ` Russell King - ARM Linux
  (?)
@ 2015-08-08 16:10     ` Russell King
  -1 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:10 UTC (permalink / raw)
  To: linux-rockchip, alsa-devel, dri-devel, linux-kernel, linux-arm-kernel
  Cc: Philipp Zabel, Andy Yan, Yakir Yang, Fabio Estevam, Mark Brown,
	Takashi Iwai, Jaroslav Kysela, Sascha Hauer, Jon Nettleton,
	David Airlie

Parse the ELD (EDID like data) stored from the HDMI driver to restrict
the sample rates and channels which are available to ALSA.  This causes
the ALSA device to reflect the capabilities of the overall audio path,
not just what is supported at the HDMI source interface level.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/Kconfig             | 1 +
 drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c | 6 ++++++
 drivers/gpu/drm/bridge/dw_hdmi-audio.h     | 1 +
 drivers/gpu/drm/bridge/dw_hdmi.c           | 3 +++
 4 files changed, 11 insertions(+)

diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index 56ed35fe0734..204861bfb867 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -7,6 +7,7 @@ config DRM_DW_HDMI_AHB_AUDIO
 	tristate "Synopsis Designware AHB Audio interface"
 	depends on DRM_DW_HDMI && SND
 	select SND_PCM
+	select SND_PCM_ELD
 	select SND_PCM_IEC958
 	help
 	  Support the AHB Audio interface which is part of the Synopsis
diff --git a/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
index 22bbbc5c2393..125b81306254 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
@@ -12,11 +12,13 @@
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <drm/bridge/dw_hdmi.h>
+#include <drm/drm_edid.h>
 
 #include <sound/asoundef.h>
 #include <sound/core.h>
 #include <sound/initval.h>
 #include <sound/pcm.h>
+#include <sound/pcm_drm_eld.h>
 #include <sound/pcm_iec958.h>
 
 #include "dw_hdmi-audio.h"
@@ -284,6 +286,10 @@ static int dw_hdmi_open(struct snd_pcm_substream *substream)
 
 	runtime->hw = dw_hdmi_hw;
 
+	ret = snd_pcm_hw_constraint_eld(runtime, dw->data.eld);
+	if (ret < 0)
+		return ret;
+
 	ret = snd_pcm_limit_hw_rates(runtime);
 	if (ret < 0)
 		return ret;
diff --git a/drivers/gpu/drm/bridge/dw_hdmi-audio.h b/drivers/gpu/drm/bridge/dw_hdmi-audio.h
index 1e840118d90a..91f631beecc7 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi-audio.h
+++ b/drivers/gpu/drm/bridge/dw_hdmi-audio.h
@@ -8,6 +8,7 @@ struct dw_hdmi_audio_data {
 	void __iomem *base;
 	int irq;
 	struct dw_hdmi *hdmi;
+	u8 *eld;
 };
 
 #endif
diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index b65464789fbd..a8b243278774 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -1533,6 +1533,8 @@ static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
 		hdmi->sink_has_audio = drm_detect_monitor_audio(edid);
 		drm_mode_connector_update_edid_property(connector, edid);
 		ret = drm_add_edid_modes(connector, edid);
+		/* Store the ELD */
+		drm_edid_to_eld(connector, edid);
 		kfree(edid);
 	} else {
 		dev_dbg(hdmi->dev, "failed to get edid\n");
@@ -1873,6 +1875,7 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 		audio.base = hdmi->regs;
 		audio.irq = irq;
 		audio.hdmi = hdmi;
+		audio.eld = hdmi->connector.eld;
 
 		pdevinfo.name = "dw-hdmi-ahb-audio";
 		pdevinfo.data = &audio;
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 2/9] drm: bridge/dw_hdmi-ahb-audio: parse ELD from HDMI driver
@ 2015-08-08 16:10     ` Russell King
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:10 UTC (permalink / raw)
  To: linux-rockchip, alsa-devel, dri-devel, linux-kernel, linux-arm-kernel
  Cc: Fabio Estevam, Jaroslav Kysela, Mark Brown, Yakir Yang, Andy Yan

Parse the ELD (EDID like data) stored from the HDMI driver to restrict
the sample rates and channels which are available to ALSA.  This causes
the ALSA device to reflect the capabilities of the overall audio path,
not just what is supported at the HDMI source interface level.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/Kconfig             | 1 +
 drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c | 6 ++++++
 drivers/gpu/drm/bridge/dw_hdmi-audio.h     | 1 +
 drivers/gpu/drm/bridge/dw_hdmi.c           | 3 +++
 4 files changed, 11 insertions(+)

diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index 56ed35fe0734..204861bfb867 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -7,6 +7,7 @@ config DRM_DW_HDMI_AHB_AUDIO
 	tristate "Synopsis Designware AHB Audio interface"
 	depends on DRM_DW_HDMI && SND
 	select SND_PCM
+	select SND_PCM_ELD
 	select SND_PCM_IEC958
 	help
 	  Support the AHB Audio interface which is part of the Synopsis
diff --git a/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
index 22bbbc5c2393..125b81306254 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
@@ -12,11 +12,13 @@
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <drm/bridge/dw_hdmi.h>
+#include <drm/drm_edid.h>
 
 #include <sound/asoundef.h>
 #include <sound/core.h>
 #include <sound/initval.h>
 #include <sound/pcm.h>
+#include <sound/pcm_drm_eld.h>
 #include <sound/pcm_iec958.h>
 
 #include "dw_hdmi-audio.h"
@@ -284,6 +286,10 @@ static int dw_hdmi_open(struct snd_pcm_substream *substream)
 
 	runtime->hw = dw_hdmi_hw;
 
+	ret = snd_pcm_hw_constraint_eld(runtime, dw->data.eld);
+	if (ret < 0)
+		return ret;
+
 	ret = snd_pcm_limit_hw_rates(runtime);
 	if (ret < 0)
 		return ret;
diff --git a/drivers/gpu/drm/bridge/dw_hdmi-audio.h b/drivers/gpu/drm/bridge/dw_hdmi-audio.h
index 1e840118d90a..91f631beecc7 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi-audio.h
+++ b/drivers/gpu/drm/bridge/dw_hdmi-audio.h
@@ -8,6 +8,7 @@ struct dw_hdmi_audio_data {
 	void __iomem *base;
 	int irq;
 	struct dw_hdmi *hdmi;
+	u8 *eld;
 };
 
 #endif
diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index b65464789fbd..a8b243278774 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -1533,6 +1533,8 @@ static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
 		hdmi->sink_has_audio = drm_detect_monitor_audio(edid);
 		drm_mode_connector_update_edid_property(connector, edid);
 		ret = drm_add_edid_modes(connector, edid);
+		/* Store the ELD */
+		drm_edid_to_eld(connector, edid);
 		kfree(edid);
 	} else {
 		dev_dbg(hdmi->dev, "failed to get edid\n");
@@ -1873,6 +1875,7 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 		audio.base = hdmi->regs;
 		audio.irq = irq;
 		audio.hdmi = hdmi;
+		audio.eld = hdmi->connector.eld;
 
 		pdevinfo.name = "dw-hdmi-ahb-audio";
 		pdevinfo.data = &audio;
-- 
2.1.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 2/9] drm: bridge/dw_hdmi-ahb-audio: parse ELD from HDMI driver
@ 2015-08-08 16:10     ` Russell King
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:10 UTC (permalink / raw)
  To: linux-arm-kernel

Parse the ELD (EDID like data) stored from the HDMI driver to restrict
the sample rates and channels which are available to ALSA.  This causes
the ALSA device to reflect the capabilities of the overall audio path,
not just what is supported at the HDMI source interface level.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/Kconfig             | 1 +
 drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c | 6 ++++++
 drivers/gpu/drm/bridge/dw_hdmi-audio.h     | 1 +
 drivers/gpu/drm/bridge/dw_hdmi.c           | 3 +++
 4 files changed, 11 insertions(+)

diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index 56ed35fe0734..204861bfb867 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -7,6 +7,7 @@ config DRM_DW_HDMI_AHB_AUDIO
 	tristate "Synopsis Designware AHB Audio interface"
 	depends on DRM_DW_HDMI && SND
 	select SND_PCM
+	select SND_PCM_ELD
 	select SND_PCM_IEC958
 	help
 	  Support the AHB Audio interface which is part of the Synopsis
diff --git a/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
index 22bbbc5c2393..125b81306254 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
@@ -12,11 +12,13 @@
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <drm/bridge/dw_hdmi.h>
+#include <drm/drm_edid.h>
 
 #include <sound/asoundef.h>
 #include <sound/core.h>
 #include <sound/initval.h>
 #include <sound/pcm.h>
+#include <sound/pcm_drm_eld.h>
 #include <sound/pcm_iec958.h>
 
 #include "dw_hdmi-audio.h"
@@ -284,6 +286,10 @@ static int dw_hdmi_open(struct snd_pcm_substream *substream)
 
 	runtime->hw = dw_hdmi_hw;
 
+	ret = snd_pcm_hw_constraint_eld(runtime, dw->data.eld);
+	if (ret < 0)
+		return ret;
+
 	ret = snd_pcm_limit_hw_rates(runtime);
 	if (ret < 0)
 		return ret;
diff --git a/drivers/gpu/drm/bridge/dw_hdmi-audio.h b/drivers/gpu/drm/bridge/dw_hdmi-audio.h
index 1e840118d90a..91f631beecc7 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi-audio.h
+++ b/drivers/gpu/drm/bridge/dw_hdmi-audio.h
@@ -8,6 +8,7 @@ struct dw_hdmi_audio_data {
 	void __iomem *base;
 	int irq;
 	struct dw_hdmi *hdmi;
+	u8 *eld;
 };
 
 #endif
diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index b65464789fbd..a8b243278774 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -1533,6 +1533,8 @@ static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
 		hdmi->sink_has_audio = drm_detect_monitor_audio(edid);
 		drm_mode_connector_update_edid_property(connector, edid);
 		ret = drm_add_edid_modes(connector, edid);
+		/* Store the ELD */
+		drm_edid_to_eld(connector, edid);
 		kfree(edid);
 	} else {
 		dev_dbg(hdmi->dev, "failed to get edid\n");
@@ -1873,6 +1875,7 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 		audio.base = hdmi->regs;
 		audio.irq = irq;
 		audio.hdmi = hdmi;
+		audio.eld = hdmi->connector.eld;
 
 		pdevinfo.name = "dw-hdmi-ahb-audio";
 		pdevinfo.data = &audio;
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 3/9] drm: bridge/dw_hdmi-ahb-audio: basic support for multi-channel PCM audio
  2015-08-08 16:09   ` Russell King - ARM Linux
@ 2015-08-08 16:10     ` Russell King
  -1 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:10 UTC (permalink / raw)
  To: linux-rockchip, alsa-devel, dri-devel, linux-kernel, linux-arm-kernel
  Cc: Philipp Zabel, Andy Yan, Yakir Yang, Fabio Estevam, Mark Brown,
	Takashi Iwai, Jaroslav Kysela, Sascha Hauer, Jon Nettleton,
	David Airlie

Add basic support for multi-channel PCM audio, with fixed speaker
mappings.  This has been tested with an AV receiver, and appears to
work for low sample rates up to 8 channels.

It should be noted that multi-channel mode using the IEC958 alsa-lib
conversion plugin requires correct AES channel status for the AV
receiver to recognise the stream, especially the sample rate bits.
"Not identified" does not work there.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c | 59 +++++++++++++++++++++++++++++-
 1 file changed, 57 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
index 125b81306254..3a8f32e04b63 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
@@ -62,9 +62,14 @@ enum {
 	HDMI_AHB_DMA_CONF0_INCR4 = 0,
 	HDMI_AHB_DMA_CONF0_BURST_MODE = BIT(0),
 	HDMI_AHB_DMA_MASK_DONE = BIT(7),
+
 	HDMI_REVISION_ID = 0x0001,
 	HDMI_IH_AHBDMAAUD_STAT0 = 0x0109,
 	HDMI_IH_MUTE_AHBDMAAUD_STAT0 = 0x0189,
+	HDMI_FC_AUDICONF2 = 0x1027,
+	HDMI_FC_AUDSCONF = 0x1063,
+	HDMI_FC_AUDSCONF_LAYOUT1 = 1 << 0,
+	HDMI_FC_AUDSCONF_LAYOUT0 = 0 << 0,
 	HDMI_AHB_DMA_CONF0 = 0x3600,
 	HDMI_AHB_DMA_START = 0x3601,
 	HDMI_AHB_DMA_STOP = 0x3602,
@@ -77,6 +82,44 @@ enum {
 	HDMI_AHB_DMA_BUFFPOL = 0x361a,
 };
 
+struct dw_hdmi_channel_conf {
+	u8 conf1;
+	u8 ca;
+};
+
+/*
+ * The default mapping of ALSA channels to HDMI channels and speaker
+ * allocation bits.  Note that we can't do channel remapping here -
+ * channels must be in the same order.
+ *
+ * Mappings for alsa-lib pcm/surround*.conf files:
+ *
+ *		Front	Sur4.0	Sur4.1	Sur5.0	Sur5.1	Sur7.1
+ * Channels	2	4	6	6	6	8
+ *
+ * Our mapping from ALSA channel to CEA686D speaker name and HDMI channel:
+ *
+ *				Number of ALSA channels
+ * ALSA Channel	2	3	4	5	6	7	8
+ * 0		FL:0	=	=	=	=	=	=
+ * 1		FR:1	=	=	=	=	=	=
+ * 2			FC:3	RL:4	LFE:2	=	=	=
+ * 3				RR:5	RL:4	FC:3	=	=
+ * 4					RR:5	RL:4	=	=
+ * 5						RR:5	=	=
+ * 6							RC:6	=
+ * 7							RLC/FRC	RLC/FRC
+ */
+static struct dw_hdmi_channel_conf default_hdmi_channel_config[7] = {
+	{ 0x03, 0x00 },	/* FL,FR */
+	{ 0x0b, 0x02 },	/* FL,FR,FC */
+	{ 0x33, 0x08 },	/* FL,FR,RL,RR */
+	{ 0x37, 0x09 },	/* FL,FR,LFE,RL,RR */
+	{ 0x3f, 0x0b },	/* FL,FR,LFE,FC,RL,RR */
+	{ 0x7f, 0x0f },	/* FL,FR,LFE,FC,RL,RR,RC */
+	{ 0xff, 0x13 },	/* FL,FR,LFE,FC,RL,RR,[FR]RC,[FR]LC */
+};
+
 struct snd_dw_hdmi {
 	struct snd_card *card;
 	struct snd_pcm *pcm;
@@ -352,7 +395,7 @@ static int dw_hdmi_prepare(struct snd_pcm_substream *substream)
 {
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct snd_dw_hdmi *dw = substream->private_data;
-	u8 threshold, conf0, conf1;
+	u8 threshold, conf0, conf1, layout, ca;
 
 	/* Setup as per 3.0.5 FSL 4.1.0 BSP */
 	switch (dw->revision) {
@@ -380,11 +423,23 @@ static int dw_hdmi_prepare(struct snd_pcm_substream *substream)
 	runtime->hw.fifo_size = threshold * 32;
 
 	conf0 |= HDMI_AHB_DMA_CONF0_EN_HLOCK;
-	conf1 = (1 << runtime->channels) - 1;
+	conf1 = default_hdmi_channel_config[runtime->channels - 2].conf1;
+	ca = default_hdmi_channel_config[runtime->channels - 2].ca;
+
+	/*
+	 * For >2 channel PCM audio, we need to select layout 1
+	 * and set an appropriate channel map.
+	 */
+	if (runtime->channels > 2)
+		layout = HDMI_FC_AUDSCONF_LAYOUT1;
+	else
+		layout = HDMI_FC_AUDSCONF_LAYOUT0;
 
 	writeb_relaxed(threshold, dw->data.base + HDMI_AHB_DMA_THRSLD);
 	writeb_relaxed(conf0, dw->data.base + HDMI_AHB_DMA_CONF0);
 	writeb_relaxed(conf1, dw->data.base + HDMI_AHB_DMA_CONF1);
+	writeb_relaxed(layout, dw->data.base + HDMI_FC_AUDSCONF);
+	writeb_relaxed(ca, dw->data.base + HDMI_FC_AUDICONF2);
 
 	switch (runtime->format) {
 	case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 3/9] drm: bridge/dw_hdmi-ahb-audio: basic support for multi-channel PCM audio
@ 2015-08-08 16:10     ` Russell King
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:10 UTC (permalink / raw)
  To: linux-arm-kernel

Add basic support for multi-channel PCM audio, with fixed speaker
mappings.  This has been tested with an AV receiver, and appears to
work for low sample rates up to 8 channels.

It should be noted that multi-channel mode using the IEC958 alsa-lib
conversion plugin requires correct AES channel status for the AV
receiver to recognise the stream, especially the sample rate bits.
"Not identified" does not work there.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c | 59 +++++++++++++++++++++++++++++-
 1 file changed, 57 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
index 125b81306254..3a8f32e04b63 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
@@ -62,9 +62,14 @@ enum {
 	HDMI_AHB_DMA_CONF0_INCR4 = 0,
 	HDMI_AHB_DMA_CONF0_BURST_MODE = BIT(0),
 	HDMI_AHB_DMA_MASK_DONE = BIT(7),
+
 	HDMI_REVISION_ID = 0x0001,
 	HDMI_IH_AHBDMAAUD_STAT0 = 0x0109,
 	HDMI_IH_MUTE_AHBDMAAUD_STAT0 = 0x0189,
+	HDMI_FC_AUDICONF2 = 0x1027,
+	HDMI_FC_AUDSCONF = 0x1063,
+	HDMI_FC_AUDSCONF_LAYOUT1 = 1 << 0,
+	HDMI_FC_AUDSCONF_LAYOUT0 = 0 << 0,
 	HDMI_AHB_DMA_CONF0 = 0x3600,
 	HDMI_AHB_DMA_START = 0x3601,
 	HDMI_AHB_DMA_STOP = 0x3602,
@@ -77,6 +82,44 @@ enum {
 	HDMI_AHB_DMA_BUFFPOL = 0x361a,
 };
 
+struct dw_hdmi_channel_conf {
+	u8 conf1;
+	u8 ca;
+};
+
+/*
+ * The default mapping of ALSA channels to HDMI channels and speaker
+ * allocation bits.  Note that we can't do channel remapping here -
+ * channels must be in the same order.
+ *
+ * Mappings for alsa-lib pcm/surround*.conf files:
+ *
+ *		Front	Sur4.0	Sur4.1	Sur5.0	Sur5.1	Sur7.1
+ * Channels	2	4	6	6	6	8
+ *
+ * Our mapping from ALSA channel to CEA686D speaker name and HDMI channel:
+ *
+ *				Number of ALSA channels
+ * ALSA Channel	2	3	4	5	6	7	8
+ * 0		FL:0	=	=	=	=	=	=
+ * 1		FR:1	=	=	=	=	=	=
+ * 2			FC:3	RL:4	LFE:2	=	=	=
+ * 3				RR:5	RL:4	FC:3	=	=
+ * 4					RR:5	RL:4	=	=
+ * 5						RR:5	=	=
+ * 6							RC:6	=
+ * 7							RLC/FRC	RLC/FRC
+ */
+static struct dw_hdmi_channel_conf default_hdmi_channel_config[7] = {
+	{ 0x03, 0x00 },	/* FL,FR */
+	{ 0x0b, 0x02 },	/* FL,FR,FC */
+	{ 0x33, 0x08 },	/* FL,FR,RL,RR */
+	{ 0x37, 0x09 },	/* FL,FR,LFE,RL,RR */
+	{ 0x3f, 0x0b },	/* FL,FR,LFE,FC,RL,RR */
+	{ 0x7f, 0x0f },	/* FL,FR,LFE,FC,RL,RR,RC */
+	{ 0xff, 0x13 },	/* FL,FR,LFE,FC,RL,RR,[FR]RC,[FR]LC */
+};
+
 struct snd_dw_hdmi {
 	struct snd_card *card;
 	struct snd_pcm *pcm;
@@ -352,7 +395,7 @@ static int dw_hdmi_prepare(struct snd_pcm_substream *substream)
 {
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct snd_dw_hdmi *dw = substream->private_data;
-	u8 threshold, conf0, conf1;
+	u8 threshold, conf0, conf1, layout, ca;
 
 	/* Setup as per 3.0.5 FSL 4.1.0 BSP */
 	switch (dw->revision) {
@@ -380,11 +423,23 @@ static int dw_hdmi_prepare(struct snd_pcm_substream *substream)
 	runtime->hw.fifo_size = threshold * 32;
 
 	conf0 |= HDMI_AHB_DMA_CONF0_EN_HLOCK;
-	conf1 = (1 << runtime->channels) - 1;
+	conf1 = default_hdmi_channel_config[runtime->channels - 2].conf1;
+	ca = default_hdmi_channel_config[runtime->channels - 2].ca;
+
+	/*
+	 * For >2 channel PCM audio, we need to select layout 1
+	 * and set an appropriate channel map.
+	 */
+	if (runtime->channels > 2)
+		layout = HDMI_FC_AUDSCONF_LAYOUT1;
+	else
+		layout = HDMI_FC_AUDSCONF_LAYOUT0;
 
 	writeb_relaxed(threshold, dw->data.base + HDMI_AHB_DMA_THRSLD);
 	writeb_relaxed(conf0, dw->data.base + HDMI_AHB_DMA_CONF0);
 	writeb_relaxed(conf1, dw->data.base + HDMI_AHB_DMA_CONF1);
+	writeb_relaxed(layout, dw->data.base + HDMI_FC_AUDSCONF);
+	writeb_relaxed(ca, dw->data.base + HDMI_FC_AUDICONF2);
 
 	switch (runtime->format) {
 	case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 4/9] drm: bridge/dw_hdmi-ahb-audio: allow larger buffer sizes
  2015-08-08 16:09   ` Russell King - ARM Linux
  (?)
@ 2015-08-08 16:10     ` Russell King
  -1 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:10 UTC (permalink / raw)
  To: linux-rockchip, alsa-devel, dri-devel, linux-kernel, linux-arm-kernel
  Cc: Philipp Zabel, Andy Yan, Yakir Yang, Fabio Estevam, Mark Brown,
	Takashi Iwai, Jaroslav Kysela, Sascha Hauer, Jon Nettleton,
	David Airlie

With multichannel audio, we need to allow larger buffer sizes to avoid
XRUNs during playback.  Push the buffer size up to 1024K, but as we
maintain two buffers, ensure that the vmalloc buffer does not exceed
the userspace buffer size.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c | 19 ++++++++++++++++---
 1 file changed, 16 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
index 3a8f32e04b63..4b537f9ce8f0 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
@@ -312,7 +312,7 @@ static struct snd_pcm_hardware dw_hdmi_hw = {
 		 SNDRV_PCM_RATE_192000,
 	.channels_min = 2,
 	.channels_max = 8,
-	.buffer_bytes_max = 64 * 1024,
+	.buffer_bytes_max = 1024 * 1024,
 	.period_bytes_min = 256,
 	.period_bytes_max = 8192,	/* ERR004323: must limit to 8k */
 	.periods_min = 2,
@@ -337,7 +337,15 @@ static int dw_hdmi_open(struct snd_pcm_substream *substream)
 	if (ret < 0)
 		return ret;
 
-	ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+	ret = snd_pcm_hw_constraint_integer(runtime,
+					    SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0)
+		return ret;
+
+	/* Limit the buffer size to the size of the preallocated buffer */
+	ret = snd_pcm_hw_constraint_minmax(runtime,
+					   SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+					   0, substream->dma_buffer.bytes);
 	if (ret < 0)
 		return ret;
 
@@ -387,6 +395,7 @@ static int dw_hdmi_hw_free(struct snd_pcm_substream *substream)
 static int dw_hdmi_hw_params(struct snd_pcm_substream *substream,
 	struct snd_pcm_hw_params *params)
 {
+	/* Allocate the PCM runtime buffer, which is exposed to userspace. */
 	return snd_pcm_lib_alloc_vmalloc_buffer(substream,
 						params_buffer_bytes(params));
 }
@@ -552,8 +561,12 @@ static int snd_dw_hdmi_probe(struct platform_device *pdev)
 	strlcpy(pcm->name, DRIVER_NAME, sizeof(pcm->name));
 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_dw_hdmi_ops);
 
+	/*
+	 * To support 8-channel 96kHz audio reliably, we need 512k
+	 * to satisfy alsa with our restricted period (ERR004323).
+	 */
 	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
-			dev, 64 * 1024, 64 * 1024);
+			dev, 128 * 1024, 1024 * 1024);
 
 	ret = snd_card_register(card);
 	if (ret < 0)
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 4/9] drm: bridge/dw_hdmi-ahb-audio: allow larger buffer sizes
@ 2015-08-08 16:10     ` Russell King
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:10 UTC (permalink / raw)
  To: linux-rockchip, alsa-devel, dri-devel, linux-kernel, linux-arm-kernel
  Cc: Fabio Estevam, Jaroslav Kysela, Mark Brown, Yakir Yang, Andy Yan

With multichannel audio, we need to allow larger buffer sizes to avoid
XRUNs during playback.  Push the buffer size up to 1024K, but as we
maintain two buffers, ensure that the vmalloc buffer does not exceed
the userspace buffer size.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c | 19 ++++++++++++++++---
 1 file changed, 16 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
index 3a8f32e04b63..4b537f9ce8f0 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
@@ -312,7 +312,7 @@ static struct snd_pcm_hardware dw_hdmi_hw = {
 		 SNDRV_PCM_RATE_192000,
 	.channels_min = 2,
 	.channels_max = 8,
-	.buffer_bytes_max = 64 * 1024,
+	.buffer_bytes_max = 1024 * 1024,
 	.period_bytes_min = 256,
 	.period_bytes_max = 8192,	/* ERR004323: must limit to 8k */
 	.periods_min = 2,
@@ -337,7 +337,15 @@ static int dw_hdmi_open(struct snd_pcm_substream *substream)
 	if (ret < 0)
 		return ret;
 
-	ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+	ret = snd_pcm_hw_constraint_integer(runtime,
+					    SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0)
+		return ret;
+
+	/* Limit the buffer size to the size of the preallocated buffer */
+	ret = snd_pcm_hw_constraint_minmax(runtime,
+					   SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+					   0, substream->dma_buffer.bytes);
 	if (ret < 0)
 		return ret;
 
@@ -387,6 +395,7 @@ static int dw_hdmi_hw_free(struct snd_pcm_substream *substream)
 static int dw_hdmi_hw_params(struct snd_pcm_substream *substream,
 	struct snd_pcm_hw_params *params)
 {
+	/* Allocate the PCM runtime buffer, which is exposed to userspace. */
 	return snd_pcm_lib_alloc_vmalloc_buffer(substream,
 						params_buffer_bytes(params));
 }
@@ -552,8 +561,12 @@ static int snd_dw_hdmi_probe(struct platform_device *pdev)
 	strlcpy(pcm->name, DRIVER_NAME, sizeof(pcm->name));
 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_dw_hdmi_ops);
 
+	/*
+	 * To support 8-channel 96kHz audio reliably, we need 512k
+	 * to satisfy alsa with our restricted period (ERR004323).
+	 */
 	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
-			dev, 64 * 1024, 64 * 1024);
+			dev, 128 * 1024, 1024 * 1024);
 
 	ret = snd_card_register(card);
 	if (ret < 0)
-- 
2.1.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 4/9] drm: bridge/dw_hdmi-ahb-audio: allow larger buffer sizes
@ 2015-08-08 16:10     ` Russell King
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:10 UTC (permalink / raw)
  To: linux-arm-kernel

With multichannel audio, we need to allow larger buffer sizes to avoid
XRUNs during playback.  Push the buffer size up to 1024K, but as we
maintain two buffers, ensure that the vmalloc buffer does not exceed
the userspace buffer size.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c | 19 ++++++++++++++++---
 1 file changed, 16 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
index 3a8f32e04b63..4b537f9ce8f0 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
@@ -312,7 +312,7 @@ static struct snd_pcm_hardware dw_hdmi_hw = {
 		 SNDRV_PCM_RATE_192000,
 	.channels_min = 2,
 	.channels_max = 8,
-	.buffer_bytes_max = 64 * 1024,
+	.buffer_bytes_max = 1024 * 1024,
 	.period_bytes_min = 256,
 	.period_bytes_max = 8192,	/* ERR004323: must limit to 8k */
 	.periods_min = 2,
@@ -337,7 +337,15 @@ static int dw_hdmi_open(struct snd_pcm_substream *substream)
 	if (ret < 0)
 		return ret;
 
-	ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+	ret = snd_pcm_hw_constraint_integer(runtime,
+					    SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0)
+		return ret;
+
+	/* Limit the buffer size to the size of the preallocated buffer */
+	ret = snd_pcm_hw_constraint_minmax(runtime,
+					   SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+					   0, substream->dma_buffer.bytes);
 	if (ret < 0)
 		return ret;
 
@@ -387,6 +395,7 @@ static int dw_hdmi_hw_free(struct snd_pcm_substream *substream)
 static int dw_hdmi_hw_params(struct snd_pcm_substream *substream,
 	struct snd_pcm_hw_params *params)
 {
+	/* Allocate the PCM runtime buffer, which is exposed to userspace. */
 	return snd_pcm_lib_alloc_vmalloc_buffer(substream,
 						params_buffer_bytes(params));
 }
@@ -552,8 +561,12 @@ static int snd_dw_hdmi_probe(struct platform_device *pdev)
 	strlcpy(pcm->name, DRIVER_NAME, sizeof(pcm->name));
 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_dw_hdmi_ops);
 
+	/*
+	 * To support 8-channel 96kHz audio reliably, we need 512k
+	 * to satisfy alsa with our restricted period (ERR004323).
+	 */
 	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
-			dev, 64 * 1024, 64 * 1024);
+			dev, 128 * 1024, 1024 * 1024);
 
 	ret = snd_card_register(card);
 	if (ret < 0)
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 5/9] drm: bridge/dw_hdmi: avoid being recursive in N calculation
  2015-08-08 16:09   ` Russell King - ARM Linux
  (?)
@ 2015-08-08 16:10     ` Russell King
  -1 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:10 UTC (permalink / raw)
  To: linux-rockchip, alsa-devel, dri-devel, linux-kernel, linux-arm-kernel
  Cc: Philipp Zabel, Andy Yan, Yakir Yang, Fabio Estevam, Mark Brown,
	Takashi Iwai, Jaroslav Kysela, Sascha Hauer, Jon Nettleton,
	David Airlie

There's no need to be recursive when computing the N value for the ACR
packet - we can instead calculate the multiplier prior to our switch()
based lookup, and multiply the N value appropriately afterwards.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/dw_hdmi.c | 25 +++++++++----------------
 1 file changed, 9 insertions(+), 16 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index a8b243278774..f0e6059f818a 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -221,6 +221,12 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk,
 				   unsigned int ratio)
 {
 	unsigned int n = (128 * freq) / 1000;
+	unsigned int mult = 1;
+
+	while (freq > 48000) {
+		mult *= 2;
+		freq /= 2;
+	}
 
 	switch (freq) {
 	case 32000:
@@ -232,6 +238,7 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk,
 			n = 11648;
 		else
 			n = 4096;
+		n *= mult;
 		break;
 
 	case 44100:
@@ -243,6 +250,7 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk,
 			n = (ratio == 150) ? 17836 : 8918;
 		else
 			n = 6272;
+		n *= mult;
 		break;
 
 	case 48000:
@@ -256,22 +264,7 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk,
 			n = (ratio == 150) ? 11648 : 5824;
 		else
 			n = 6144;
-		break;
-
-	case 88200:
-		n = hdmi_compute_n(44100, pixel_clk, ratio) * 2;
-		break;
-
-	case 96000:
-		n = hdmi_compute_n(48000, pixel_clk, ratio) * 2;
-		break;
-
-	case 176400:
-		n = hdmi_compute_n(44100, pixel_clk, ratio) * 4;
-		break;
-
-	case 192000:
-		n = hdmi_compute_n(48000, pixel_clk, ratio) * 4;
+		n *= mult;
 		break;
 
 	default:
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 5/9] drm: bridge/dw_hdmi: avoid being recursive in N calculation
@ 2015-08-08 16:10     ` Russell King
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:10 UTC (permalink / raw)
  To: linux-rockchip, alsa-devel, dri-devel, linux-kernel, linux-arm-kernel
  Cc: Fabio Estevam, Jaroslav Kysela, Mark Brown, Yakir Yang, Andy Yan

There's no need to be recursive when computing the N value for the ACR
packet - we can instead calculate the multiplier prior to our switch()
based lookup, and multiply the N value appropriately afterwards.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/dw_hdmi.c | 25 +++++++++----------------
 1 file changed, 9 insertions(+), 16 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index a8b243278774..f0e6059f818a 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -221,6 +221,12 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk,
 				   unsigned int ratio)
 {
 	unsigned int n = (128 * freq) / 1000;
+	unsigned int mult = 1;
+
+	while (freq > 48000) {
+		mult *= 2;
+		freq /= 2;
+	}
 
 	switch (freq) {
 	case 32000:
@@ -232,6 +238,7 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk,
 			n = 11648;
 		else
 			n = 4096;
+		n *= mult;
 		break;
 
 	case 44100:
@@ -243,6 +250,7 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk,
 			n = (ratio == 150) ? 17836 : 8918;
 		else
 			n = 6272;
+		n *= mult;
 		break;
 
 	case 48000:
@@ -256,22 +264,7 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk,
 			n = (ratio == 150) ? 11648 : 5824;
 		else
 			n = 6144;
-		break;
-
-	case 88200:
-		n = hdmi_compute_n(44100, pixel_clk, ratio) * 2;
-		break;
-
-	case 96000:
-		n = hdmi_compute_n(48000, pixel_clk, ratio) * 2;
-		break;
-
-	case 176400:
-		n = hdmi_compute_n(44100, pixel_clk, ratio) * 4;
-		break;
-
-	case 192000:
-		n = hdmi_compute_n(48000, pixel_clk, ratio) * 4;
+		n *= mult;
 		break;
 
 	default:
-- 
2.1.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 5/9] drm: bridge/dw_hdmi: avoid being recursive in N calculation
@ 2015-08-08 16:10     ` Russell King
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:10 UTC (permalink / raw)
  To: linux-arm-kernel

There's no need to be recursive when computing the N value for the ACR
packet - we can instead calculate the multiplier prior to our switch()
based lookup, and multiply the N value appropriately afterwards.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/dw_hdmi.c | 25 +++++++++----------------
 1 file changed, 9 insertions(+), 16 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index a8b243278774..f0e6059f818a 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -221,6 +221,12 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk,
 				   unsigned int ratio)
 {
 	unsigned int n = (128 * freq) / 1000;
+	unsigned int mult = 1;
+
+	while (freq > 48000) {
+		mult *= 2;
+		freq /= 2;
+	}
 
 	switch (freq) {
 	case 32000:
@@ -232,6 +238,7 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk,
 			n = 11648;
 		else
 			n = 4096;
+		n *= mult;
 		break;
 
 	case 44100:
@@ -243,6 +250,7 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk,
 			n = (ratio == 150) ? 17836 : 8918;
 		else
 			n = 6272;
+		n *= mult;
 		break;
 
 	case 48000:
@@ -256,22 +264,7 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk,
 			n = (ratio == 150) ? 11648 : 5824;
 		else
 			n = 6144;
-		break;
-
-	case 88200:
-		n = hdmi_compute_n(44100, pixel_clk, ratio) * 2;
-		break;
-
-	case 96000:
-		n = hdmi_compute_n(48000, pixel_clk, ratio) * 2;
-		break;
-
-	case 176400:
-		n = hdmi_compute_n(44100, pixel_clk, ratio) * 4;
-		break;
-
-	case 192000:
-		n = hdmi_compute_n(48000, pixel_clk, ratio) * 4;
+		n *= mult;
 		break;
 
 	default:
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 6/9] drm: bridge/dw_hdmi: adjust pixel clock values in N calculation
  2015-08-08 16:09   ` Russell King - ARM Linux
  (?)
@ 2015-08-08 16:10     ` Russell King
  -1 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:10 UTC (permalink / raw)
  To: linux-rockchip, alsa-devel, dri-devel, linux-kernel, linux-arm-kernel
  Cc: Philipp Zabel, Andy Yan, Yakir Yang, Fabio Estevam, Mark Brown,
	Takashi Iwai, Jaroslav Kysela, Sascha Hauer, Jon Nettleton,
	David Airlie

Adjust the pixel clock values in the N calculation to match the more
accurate clock values we're given by the DRM subsystem, which are the
kHz pixel rate, with any fractional kHz rounded down in the case of
the non-240, non-480 line modes, or rounded up for the others.  So,

	 25.20 / 1.001 =>  25175
	 27.00 * 1.001 =>  27027
	 74.25 / 1.001 =>  74176
	148.50 / 1.001 => 148352

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/dw_hdmi.c | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index f0e6059f818a..5576cd7d7abb 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -230,11 +230,11 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk,
 
 	switch (freq) {
 	case 32000:
-		if (pixel_clk == 25170000)
+		if (pixel_clk == 25175000)
 			n = (ratio == 150) ? 9152 : 4576;
-		else if (pixel_clk == 27020000)
+		else if (pixel_clk == 27027000)
 			n = (ratio == 150) ? 8192 : 4096;
-		else if (pixel_clk == 74170000 || pixel_clk == 148350000)
+		else if (pixel_clk == 74176000 || pixel_clk == 148352000)
 			n = 11648;
 		else
 			n = 4096;
@@ -242,11 +242,11 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk,
 		break;
 
 	case 44100:
-		if (pixel_clk == 25170000)
+		if (pixel_clk == 25175000)
 			n = 7007;
-		else if (pixel_clk == 74170000)
+		else if (pixel_clk == 74176000)
 			n = 17836;
-		else if (pixel_clk == 148350000)
+		else if (pixel_clk == 148352000)
 			n = (ratio == 150) ? 17836 : 8918;
 		else
 			n = 6272;
@@ -254,13 +254,13 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk,
 		break;
 
 	case 48000:
-		if (pixel_clk == 25170000)
+		if (pixel_clk == 25175000)
 			n = (ratio == 150) ? 9152 : 6864;
-		else if (pixel_clk == 27020000)
+		else if (pixel_clk == 27027000)
 			n = (ratio == 150) ? 8192 : 6144;
-		else if (pixel_clk == 74170000)
+		else if (pixel_clk == 74176000)
 			n = 11648;
-		else if (pixel_clk == 148350000)
+		else if (pixel_clk == 148352000)
 			n = (ratio == 150) ? 11648 : 5824;
 		else
 			n = 6144;
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 6/9] drm: bridge/dw_hdmi: adjust pixel clock values in N calculation
@ 2015-08-08 16:10     ` Russell King
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:10 UTC (permalink / raw)
  To: linux-rockchip, alsa-devel, dri-devel, linux-kernel, linux-arm-kernel
  Cc: Fabio Estevam, Jaroslav Kysela, Mark Brown, Yakir Yang, Andy Yan

Adjust the pixel clock values in the N calculation to match the more
accurate clock values we're given by the DRM subsystem, which are the
kHz pixel rate, with any fractional kHz rounded down in the case of
the non-240, non-480 line modes, or rounded up for the others.  So,

	 25.20 / 1.001 =>  25175
	 27.00 * 1.001 =>  27027
	 74.25 / 1.001 =>  74176
	148.50 / 1.001 => 148352

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/dw_hdmi.c | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index f0e6059f818a..5576cd7d7abb 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -230,11 +230,11 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk,
 
 	switch (freq) {
 	case 32000:
-		if (pixel_clk == 25170000)
+		if (pixel_clk == 25175000)
 			n = (ratio == 150) ? 9152 : 4576;
-		else if (pixel_clk == 27020000)
+		else if (pixel_clk == 27027000)
 			n = (ratio == 150) ? 8192 : 4096;
-		else if (pixel_clk == 74170000 || pixel_clk == 148350000)
+		else if (pixel_clk == 74176000 || pixel_clk == 148352000)
 			n = 11648;
 		else
 			n = 4096;
@@ -242,11 +242,11 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk,
 		break;
 
 	case 44100:
-		if (pixel_clk == 25170000)
+		if (pixel_clk == 25175000)
 			n = 7007;
-		else if (pixel_clk == 74170000)
+		else if (pixel_clk == 74176000)
 			n = 17836;
-		else if (pixel_clk == 148350000)
+		else if (pixel_clk == 148352000)
 			n = (ratio == 150) ? 17836 : 8918;
 		else
 			n = 6272;
@@ -254,13 +254,13 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk,
 		break;
 
 	case 48000:
-		if (pixel_clk == 25170000)
+		if (pixel_clk == 25175000)
 			n = (ratio == 150) ? 9152 : 6864;
-		else if (pixel_clk == 27020000)
+		else if (pixel_clk == 27027000)
 			n = (ratio == 150) ? 8192 : 6144;
-		else if (pixel_clk == 74170000)
+		else if (pixel_clk == 74176000)
 			n = 11648;
-		else if (pixel_clk == 148350000)
+		else if (pixel_clk == 148352000)
 			n = (ratio == 150) ? 11648 : 5824;
 		else
 			n = 6144;
-- 
2.1.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 6/9] drm: bridge/dw_hdmi: adjust pixel clock values in N calculation
@ 2015-08-08 16:10     ` Russell King
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:10 UTC (permalink / raw)
  To: linux-arm-kernel

Adjust the pixel clock values in the N calculation to match the more
accurate clock values we're given by the DRM subsystem, which are the
kHz pixel rate, with any fractional kHz rounded down in the case of
the non-240, non-480 line modes, or rounded up for the others.  So,

	 25.20 / 1.001 =>  25175
	 27.00 * 1.001 =>  27027
	 74.25 / 1.001 =>  74176
	148.50 / 1.001 => 148352

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/dw_hdmi.c | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index f0e6059f818a..5576cd7d7abb 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -230,11 +230,11 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk,
 
 	switch (freq) {
 	case 32000:
-		if (pixel_clk == 25170000)
+		if (pixel_clk == 25175000)
 			n = (ratio == 150) ? 9152 : 4576;
-		else if (pixel_clk == 27020000)
+		else if (pixel_clk == 27027000)
 			n = (ratio == 150) ? 8192 : 4096;
-		else if (pixel_clk == 74170000 || pixel_clk == 148350000)
+		else if (pixel_clk == 74176000 || pixel_clk == 148352000)
 			n = 11648;
 		else
 			n = 4096;
@@ -242,11 +242,11 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk,
 		break;
 
 	case 44100:
-		if (pixel_clk == 25170000)
+		if (pixel_clk == 25175000)
 			n = 7007;
-		else if (pixel_clk == 74170000)
+		else if (pixel_clk == 74176000)
 			n = 17836;
-		else if (pixel_clk == 148350000)
+		else if (pixel_clk == 148352000)
 			n = (ratio == 150) ? 17836 : 8918;
 		else
 			n = 6272;
@@ -254,13 +254,13 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk,
 		break;
 
 	case 48000:
-		if (pixel_clk == 25170000)
+		if (pixel_clk == 25175000)
 			n = (ratio == 150) ? 9152 : 6864;
-		else if (pixel_clk == 27020000)
+		else if (pixel_clk == 27027000)
 			n = (ratio == 150) ? 8192 : 6144;
-		else if (pixel_clk == 74170000)
+		else if (pixel_clk == 74176000)
 			n = 11648;
-		else if (pixel_clk == 148350000)
+		else if (pixel_clk == 148352000)
 			n = (ratio == 150) ? 11648 : 5824;
 		else
 			n = 6144;
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 7/9] drm: bridge/dw_hdmi: remove ratio support from ACR code
  2015-08-08 16:09   ` Russell King - ARM Linux
  (?)
@ 2015-08-08 16:10     ` Russell King
  -1 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:10 UTC (permalink / raw)
  To: linux-rockchip, alsa-devel, dri-devel, linux-kernel, linux-arm-kernel
  Cc: Philipp Zabel, Andy Yan, Yakir Yang, Fabio Estevam, Mark Brown,
	Takashi Iwai, Jaroslav Kysela, Sascha Hauer, Jon Nettleton,
	David Airlie

We never set the ratio for CTS/N calculation for the audio clock
regenerator (ACR) to anything but 100, so this adds pointless
complexity.  Should we support pixel repetition, we should update the
CTS/N calculation code to use those parameters or the actual TMDS clock
rate instead of a ratio.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/dw_hdmi.c | 44 ++++++++++++++++------------------------
 1 file changed, 18 insertions(+), 26 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index 5576cd7d7abb..60487bff48e3 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -140,7 +140,6 @@ struct dw_hdmi {
 	unsigned int audio_cts;
 	unsigned int audio_n;
 	bool audio_enable;
-	int ratio;
 
 	void (*write)(struct dw_hdmi *hdmi, u8 val, int offset);
 	u8 (*read)(struct dw_hdmi *hdmi, int offset);
@@ -217,8 +216,7 @@ static void hdmi_set_cts_n(struct dw_hdmi *hdmi, unsigned int cts,
 	hdmi_writeb(hdmi, n & 0xff, HDMI_AUD_N1);
 }
 
-static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk,
-				   unsigned int ratio)
+static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk)
 {
 	unsigned int n = (128 * freq) / 1000;
 	unsigned int mult = 1;
@@ -231,9 +229,9 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk,
 	switch (freq) {
 	case 32000:
 		if (pixel_clk == 25175000)
-			n = (ratio == 150) ? 9152 : 4576;
+			n = 4576;
 		else if (pixel_clk == 27027000)
-			n = (ratio == 150) ? 8192 : 4096;
+			n = 4096;
 		else if (pixel_clk == 74176000 || pixel_clk == 148352000)
 			n = 11648;
 		else
@@ -247,7 +245,7 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk,
 		else if (pixel_clk == 74176000)
 			n = 17836;
 		else if (pixel_clk == 148352000)
-			n = (ratio == 150) ? 17836 : 8918;
+			n = 8918;
 		else
 			n = 6272;
 		n *= mult;
@@ -255,13 +253,13 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk,
 
 	case 48000:
 		if (pixel_clk == 25175000)
-			n = (ratio == 150) ? 9152 : 6864;
+			n = 6864;
 		else if (pixel_clk == 27027000)
-			n = (ratio == 150) ? 8192 : 6144;
+			n = 6144;
 		else if (pixel_clk == 74176000)
 			n = 11648;
 		else if (pixel_clk == 148352000)
-			n = (ratio == 150) ? 11648 : 5824;
+			n = 5824;
 		else
 			n = 6144;
 		n *= mult;
@@ -274,13 +272,11 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk,
 	return n;
 }
 
-static unsigned int hdmi_compute_cts(unsigned int freq, unsigned long pixel_clk,
-				     unsigned int ratio)
+static unsigned int hdmi_compute_cts(unsigned int freq, unsigned long pixel_clk)
 {
 	unsigned int cts = 0;
 
-	pr_debug("%s: freq: %d pixel_clk: %ld ratio: %d\n", __func__, freq,
-		 pixel_clk, ratio);
+	pr_debug("%s: freq: %d pixel_clk: %ld\n", __func__, freq, pixel_clk);
 
 	switch (freq) {
 	case 32000:
@@ -341,26 +337,24 @@ static unsigned int hdmi_compute_cts(unsigned int freq, unsigned long pixel_clk,
 	default:
 		break;
 	}
-	if (ratio == 100)
-		return cts;
-	return (cts * ratio) / 100;
+	return cts;
 }
 
 static void hdmi_set_clk_regenerator(struct dw_hdmi *hdmi,
-	unsigned long pixel_clk, unsigned int sample_rate, unsigned int ratio)
+	unsigned long pixel_clk, unsigned int sample_rate)
 {
 	unsigned int n, cts;
 
-	n = hdmi_compute_n(sample_rate, pixel_clk, ratio);
-	cts = hdmi_compute_cts(sample_rate, pixel_clk, ratio);
+	n = hdmi_compute_n(sample_rate, pixel_clk);
+	cts = hdmi_compute_cts(sample_rate, pixel_clk);
 	if (!cts) {
 		dev_err(hdmi->dev,
 			"%s: pixel clock/sample rate not supported: %luMHz / %ukHz\n",
 			__func__, pixel_clk, sample_rate);
 	}
 
-	dev_dbg(hdmi->dev, "%s: samplerate=%ukHz ratio=%d pixelclk=%luMHz N=%d cts=%d\n",
-		__func__, sample_rate, ratio, pixel_clk, n, cts);
+	dev_dbg(hdmi->dev, "%s: samplerate=%ukHz pixelclk=%luMHz N=%d cts=%d\n",
+		__func__, sample_rate, pixel_clk, n, cts);
 
 	spin_lock_irq(&hdmi->audio_lock);
 	hdmi->audio_n = n;
@@ -372,8 +366,7 @@ static void hdmi_set_clk_regenerator(struct dw_hdmi *hdmi,
 static void hdmi_init_clk_regenerator(struct dw_hdmi *hdmi)
 {
 	mutex_lock(&hdmi->audio_mutex);
-	hdmi_set_clk_regenerator(hdmi, 74250000, hdmi->sample_rate,
-				 hdmi->ratio);
+	hdmi_set_clk_regenerator(hdmi, 74250000, hdmi->sample_rate);
 	mutex_unlock(&hdmi->audio_mutex);
 }
 
@@ -381,7 +374,7 @@ static void hdmi_clk_regenerator_update_pixel_clock(struct dw_hdmi *hdmi)
 {
 	mutex_lock(&hdmi->audio_mutex);
 	hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mpixelclock,
-				 hdmi->sample_rate, hdmi->ratio);
+				 hdmi->sample_rate);
 	mutex_unlock(&hdmi->audio_mutex);
 }
 
@@ -390,7 +383,7 @@ void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate)
 	mutex_lock(&hdmi->audio_mutex);
 	hdmi->sample_rate = rate;
 	hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mpixelclock,
-				 hdmi->sample_rate, hdmi->ratio);
+				 hdmi->sample_rate);
 	mutex_unlock(&hdmi->audio_mutex);
 }
 EXPORT_SYMBOL_GPL(dw_hdmi_set_sample_rate);
@@ -1746,7 +1739,6 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 	hdmi->dev = dev;
 	hdmi->dev_type = plat_data->dev_type;
 	hdmi->sample_rate = 48000;
-	hdmi->ratio = 100;
 	hdmi->encoder = encoder;
 	hdmi->disabled = true;
 	hdmi->rxsense = true;
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 7/9] drm: bridge/dw_hdmi: remove ratio support from ACR code
@ 2015-08-08 16:10     ` Russell King
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:10 UTC (permalink / raw)
  To: linux-rockchip, alsa-devel, dri-devel, linux-kernel, linux-arm-kernel
  Cc: Fabio Estevam, Jaroslav Kysela, Mark Brown, Yakir Yang, Andy Yan

We never set the ratio for CTS/N calculation for the audio clock
regenerator (ACR) to anything but 100, so this adds pointless
complexity.  Should we support pixel repetition, we should update the
CTS/N calculation code to use those parameters or the actual TMDS clock
rate instead of a ratio.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/dw_hdmi.c | 44 ++++++++++++++++------------------------
 1 file changed, 18 insertions(+), 26 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index 5576cd7d7abb..60487bff48e3 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -140,7 +140,6 @@ struct dw_hdmi {
 	unsigned int audio_cts;
 	unsigned int audio_n;
 	bool audio_enable;
-	int ratio;
 
 	void (*write)(struct dw_hdmi *hdmi, u8 val, int offset);
 	u8 (*read)(struct dw_hdmi *hdmi, int offset);
@@ -217,8 +216,7 @@ static void hdmi_set_cts_n(struct dw_hdmi *hdmi, unsigned int cts,
 	hdmi_writeb(hdmi, n & 0xff, HDMI_AUD_N1);
 }
 
-static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk,
-				   unsigned int ratio)
+static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk)
 {
 	unsigned int n = (128 * freq) / 1000;
 	unsigned int mult = 1;
@@ -231,9 +229,9 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk,
 	switch (freq) {
 	case 32000:
 		if (pixel_clk == 25175000)
-			n = (ratio == 150) ? 9152 : 4576;
+			n = 4576;
 		else if (pixel_clk == 27027000)
-			n = (ratio == 150) ? 8192 : 4096;
+			n = 4096;
 		else if (pixel_clk == 74176000 || pixel_clk == 148352000)
 			n = 11648;
 		else
@@ -247,7 +245,7 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk,
 		else if (pixel_clk == 74176000)
 			n = 17836;
 		else if (pixel_clk == 148352000)
-			n = (ratio == 150) ? 17836 : 8918;
+			n = 8918;
 		else
 			n = 6272;
 		n *= mult;
@@ -255,13 +253,13 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk,
 
 	case 48000:
 		if (pixel_clk == 25175000)
-			n = (ratio == 150) ? 9152 : 6864;
+			n = 6864;
 		else if (pixel_clk == 27027000)
-			n = (ratio == 150) ? 8192 : 6144;
+			n = 6144;
 		else if (pixel_clk == 74176000)
 			n = 11648;
 		else if (pixel_clk == 148352000)
-			n = (ratio == 150) ? 11648 : 5824;
+			n = 5824;
 		else
 			n = 6144;
 		n *= mult;
@@ -274,13 +272,11 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk,
 	return n;
 }
 
-static unsigned int hdmi_compute_cts(unsigned int freq, unsigned long pixel_clk,
-				     unsigned int ratio)
+static unsigned int hdmi_compute_cts(unsigned int freq, unsigned long pixel_clk)
 {
 	unsigned int cts = 0;
 
-	pr_debug("%s: freq: %d pixel_clk: %ld ratio: %d\n", __func__, freq,
-		 pixel_clk, ratio);
+	pr_debug("%s: freq: %d pixel_clk: %ld\n", __func__, freq, pixel_clk);
 
 	switch (freq) {
 	case 32000:
@@ -341,26 +337,24 @@ static unsigned int hdmi_compute_cts(unsigned int freq, unsigned long pixel_clk,
 	default:
 		break;
 	}
-	if (ratio == 100)
-		return cts;
-	return (cts * ratio) / 100;
+	return cts;
 }
 
 static void hdmi_set_clk_regenerator(struct dw_hdmi *hdmi,
-	unsigned long pixel_clk, unsigned int sample_rate, unsigned int ratio)
+	unsigned long pixel_clk, unsigned int sample_rate)
 {
 	unsigned int n, cts;
 
-	n = hdmi_compute_n(sample_rate, pixel_clk, ratio);
-	cts = hdmi_compute_cts(sample_rate, pixel_clk, ratio);
+	n = hdmi_compute_n(sample_rate, pixel_clk);
+	cts = hdmi_compute_cts(sample_rate, pixel_clk);
 	if (!cts) {
 		dev_err(hdmi->dev,
 			"%s: pixel clock/sample rate not supported: %luMHz / %ukHz\n",
 			__func__, pixel_clk, sample_rate);
 	}
 
-	dev_dbg(hdmi->dev, "%s: samplerate=%ukHz ratio=%d pixelclk=%luMHz N=%d cts=%d\n",
-		__func__, sample_rate, ratio, pixel_clk, n, cts);
+	dev_dbg(hdmi->dev, "%s: samplerate=%ukHz pixelclk=%luMHz N=%d cts=%d\n",
+		__func__, sample_rate, pixel_clk, n, cts);
 
 	spin_lock_irq(&hdmi->audio_lock);
 	hdmi->audio_n = n;
@@ -372,8 +366,7 @@ static void hdmi_set_clk_regenerator(struct dw_hdmi *hdmi,
 static void hdmi_init_clk_regenerator(struct dw_hdmi *hdmi)
 {
 	mutex_lock(&hdmi->audio_mutex);
-	hdmi_set_clk_regenerator(hdmi, 74250000, hdmi->sample_rate,
-				 hdmi->ratio);
+	hdmi_set_clk_regenerator(hdmi, 74250000, hdmi->sample_rate);
 	mutex_unlock(&hdmi->audio_mutex);
 }
 
@@ -381,7 +374,7 @@ static void hdmi_clk_regenerator_update_pixel_clock(struct dw_hdmi *hdmi)
 {
 	mutex_lock(&hdmi->audio_mutex);
 	hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mpixelclock,
-				 hdmi->sample_rate, hdmi->ratio);
+				 hdmi->sample_rate);
 	mutex_unlock(&hdmi->audio_mutex);
 }
 
@@ -390,7 +383,7 @@ void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate)
 	mutex_lock(&hdmi->audio_mutex);
 	hdmi->sample_rate = rate;
 	hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mpixelclock,
-				 hdmi->sample_rate, hdmi->ratio);
+				 hdmi->sample_rate);
 	mutex_unlock(&hdmi->audio_mutex);
 }
 EXPORT_SYMBOL_GPL(dw_hdmi_set_sample_rate);
@@ -1746,7 +1739,6 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 	hdmi->dev = dev;
 	hdmi->dev_type = plat_data->dev_type;
 	hdmi->sample_rate = 48000;
-	hdmi->ratio = 100;
 	hdmi->encoder = encoder;
 	hdmi->disabled = true;
 	hdmi->rxsense = true;
-- 
2.1.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 7/9] drm: bridge/dw_hdmi: remove ratio support from ACR code
@ 2015-08-08 16:10     ` Russell King
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:10 UTC (permalink / raw)
  To: linux-arm-kernel

We never set the ratio for CTS/N calculation for the audio clock
regenerator (ACR) to anything but 100, so this adds pointless
complexity.  Should we support pixel repetition, we should update the
CTS/N calculation code to use those parameters or the actual TMDS clock
rate instead of a ratio.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/dw_hdmi.c | 44 ++++++++++++++++------------------------
 1 file changed, 18 insertions(+), 26 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index 5576cd7d7abb..60487bff48e3 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -140,7 +140,6 @@ struct dw_hdmi {
 	unsigned int audio_cts;
 	unsigned int audio_n;
 	bool audio_enable;
-	int ratio;
 
 	void (*write)(struct dw_hdmi *hdmi, u8 val, int offset);
 	u8 (*read)(struct dw_hdmi *hdmi, int offset);
@@ -217,8 +216,7 @@ static void hdmi_set_cts_n(struct dw_hdmi *hdmi, unsigned int cts,
 	hdmi_writeb(hdmi, n & 0xff, HDMI_AUD_N1);
 }
 
-static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk,
-				   unsigned int ratio)
+static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk)
 {
 	unsigned int n = (128 * freq) / 1000;
 	unsigned int mult = 1;
@@ -231,9 +229,9 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk,
 	switch (freq) {
 	case 32000:
 		if (pixel_clk == 25175000)
-			n = (ratio == 150) ? 9152 : 4576;
+			n = 4576;
 		else if (pixel_clk == 27027000)
-			n = (ratio == 150) ? 8192 : 4096;
+			n = 4096;
 		else if (pixel_clk == 74176000 || pixel_clk == 148352000)
 			n = 11648;
 		else
@@ -247,7 +245,7 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk,
 		else if (pixel_clk == 74176000)
 			n = 17836;
 		else if (pixel_clk == 148352000)
-			n = (ratio == 150) ? 17836 : 8918;
+			n = 8918;
 		else
 			n = 6272;
 		n *= mult;
@@ -255,13 +253,13 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk,
 
 	case 48000:
 		if (pixel_clk == 25175000)
-			n = (ratio == 150) ? 9152 : 6864;
+			n = 6864;
 		else if (pixel_clk == 27027000)
-			n = (ratio == 150) ? 8192 : 6144;
+			n = 6144;
 		else if (pixel_clk == 74176000)
 			n = 11648;
 		else if (pixel_clk == 148352000)
-			n = (ratio == 150) ? 11648 : 5824;
+			n = 5824;
 		else
 			n = 6144;
 		n *= mult;
@@ -274,13 +272,11 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk,
 	return n;
 }
 
-static unsigned int hdmi_compute_cts(unsigned int freq, unsigned long pixel_clk,
-				     unsigned int ratio)
+static unsigned int hdmi_compute_cts(unsigned int freq, unsigned long pixel_clk)
 {
 	unsigned int cts = 0;
 
-	pr_debug("%s: freq: %d pixel_clk: %ld ratio: %d\n", __func__, freq,
-		 pixel_clk, ratio);
+	pr_debug("%s: freq: %d pixel_clk: %ld\n", __func__, freq, pixel_clk);
 
 	switch (freq) {
 	case 32000:
@@ -341,26 +337,24 @@ static unsigned int hdmi_compute_cts(unsigned int freq, unsigned long pixel_clk,
 	default:
 		break;
 	}
-	if (ratio == 100)
-		return cts;
-	return (cts * ratio) / 100;
+	return cts;
 }
 
 static void hdmi_set_clk_regenerator(struct dw_hdmi *hdmi,
-	unsigned long pixel_clk, unsigned int sample_rate, unsigned int ratio)
+	unsigned long pixel_clk, unsigned int sample_rate)
 {
 	unsigned int n, cts;
 
-	n = hdmi_compute_n(sample_rate, pixel_clk, ratio);
-	cts = hdmi_compute_cts(sample_rate, pixel_clk, ratio);
+	n = hdmi_compute_n(sample_rate, pixel_clk);
+	cts = hdmi_compute_cts(sample_rate, pixel_clk);
 	if (!cts) {
 		dev_err(hdmi->dev,
 			"%s: pixel clock/sample rate not supported: %luMHz / %ukHz\n",
 			__func__, pixel_clk, sample_rate);
 	}
 
-	dev_dbg(hdmi->dev, "%s: samplerate=%ukHz ratio=%d pixelclk=%luMHz N=%d cts=%d\n",
-		__func__, sample_rate, ratio, pixel_clk, n, cts);
+	dev_dbg(hdmi->dev, "%s: samplerate=%ukHz pixelclk=%luMHz N=%d cts=%d\n",
+		__func__, sample_rate, pixel_clk, n, cts);
 
 	spin_lock_irq(&hdmi->audio_lock);
 	hdmi->audio_n = n;
@@ -372,8 +366,7 @@ static void hdmi_set_clk_regenerator(struct dw_hdmi *hdmi,
 static void hdmi_init_clk_regenerator(struct dw_hdmi *hdmi)
 {
 	mutex_lock(&hdmi->audio_mutex);
-	hdmi_set_clk_regenerator(hdmi, 74250000, hdmi->sample_rate,
-				 hdmi->ratio);
+	hdmi_set_clk_regenerator(hdmi, 74250000, hdmi->sample_rate);
 	mutex_unlock(&hdmi->audio_mutex);
 }
 
@@ -381,7 +374,7 @@ static void hdmi_clk_regenerator_update_pixel_clock(struct dw_hdmi *hdmi)
 {
 	mutex_lock(&hdmi->audio_mutex);
 	hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mpixelclock,
-				 hdmi->sample_rate, hdmi->ratio);
+				 hdmi->sample_rate);
 	mutex_unlock(&hdmi->audio_mutex);
 }
 
@@ -390,7 +383,7 @@ void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate)
 	mutex_lock(&hdmi->audio_mutex);
 	hdmi->sample_rate = rate;
 	hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mpixelclock,
-				 hdmi->sample_rate, hdmi->ratio);
+				 hdmi->sample_rate);
 	mutex_unlock(&hdmi->audio_mutex);
 }
 EXPORT_SYMBOL_GPL(dw_hdmi_set_sample_rate);
@@ -1746,7 +1739,6 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 	hdmi->dev = dev;
 	hdmi->dev_type = plat_data->dev_type;
 	hdmi->sample_rate = 48000;
-	hdmi->ratio = 100;
 	hdmi->encoder = encoder;
 	hdmi->disabled = true;
 	hdmi->rxsense = true;
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 8/9] drm: bridge/dw_hdmi: replace CTS calculation for the ACR
  2015-08-08 16:09   ` Russell King - ARM Linux
@ 2015-08-08 16:10     ` Russell King
  -1 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:10 UTC (permalink / raw)
  To: linux-rockchip, alsa-devel, dri-devel, linux-kernel, linux-arm-kernel
  Cc: Philipp Zabel, Andy Yan, Yakir Yang, Fabio Estevam, Mark Brown,
	Takashi Iwai, Jaroslav Kysela, Sascha Hauer, Jon Nettleton,
	David Airlie

Given the TDMS clock, audio sample rate, and the N parameter, we can
calculate the CTS value for the audio clock regenerator (ACR) using the
following calculation given in the HDMI specification:

	CTS = ftdms * N / (128 * fs)

The specification says that the CTS value is an average value, which is
true if the source hardware measures it.  Where source hardware needs it
to be programmed, it is particularly difficult to alternate it between
two values correctly to ensure that we achieve a correct "average"
fractional value at the sink.

Also, there's the problem that our "ftdms" is not a fully accurate
value; it is rounded to a kHz value.  This introduces an unnecessary
(and harmless) fractional value into the above equation for combinations
like 148.5MHz/1.001 for 44100Hz - we still calculate the correct CTS
value.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/dw_hdmi.c | 92 +++++++---------------------------------
 1 file changed, 16 insertions(+), 76 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index 60487bff48e3..a4f9aecf1862 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -272,89 +272,29 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk)
 	return n;
 }
 
-static unsigned int hdmi_compute_cts(unsigned int freq, unsigned long pixel_clk)
-{
-	unsigned int cts = 0;
-
-	pr_debug("%s: freq: %d pixel_clk: %ld\n", __func__, freq, pixel_clk);
-
-	switch (freq) {
-	case 32000:
-		if (pixel_clk == 297000000) {
-			cts = 222750;
-			break;
-		}
-	case 48000:
-	case 96000:
-	case 192000:
-		switch (pixel_clk) {
-		case 25200000:
-		case 27000000:
-		case 54000000:
-		case 74250000:
-		case 148500000:
-			cts = pixel_clk / 1000;
-			break;
-		case 297000000:
-			cts = 247500;
-			break;
-		/*
-		 * All other TMDS clocks are not supported by
-		 * DWC_hdmi_tx. The TMDS clocks divided or
-		 * multiplied by 1,001 coefficients are not
-		 * supported.
-		 */
-		default:
-			break;
-		}
-		break;
-	case 44100:
-	case 88200:
-	case 176400:
-		switch (pixel_clk) {
-		case 25200000:
-			cts = 28000;
-			break;
-		case 27000000:
-			cts = 30000;
-			break;
-		case 54000000:
-			cts = 60000;
-			break;
-		case 74250000:
-			cts = 82500;
-			break;
-		case 148500000:
-			cts = 165000;
-			break;
-		case 297000000:
-			cts = 247500;
-			break;
-		default:
-			break;
-		}
-		break;
-	default:
-		break;
-	}
-	return cts;
-}
-
 static void hdmi_set_clk_regenerator(struct dw_hdmi *hdmi,
 	unsigned long pixel_clk, unsigned int sample_rate)
 {
+	unsigned long ftdms = pixel_clk;
 	unsigned int n, cts;
+	u64 tmp;
 
 	n = hdmi_compute_n(sample_rate, pixel_clk);
-	cts = hdmi_compute_cts(sample_rate, pixel_clk);
-	if (!cts) {
-		dev_err(hdmi->dev,
-			"%s: pixel clock/sample rate not supported: %luMHz / %ukHz\n",
-			__func__, pixel_clk, sample_rate);
-	}
 
-	dev_dbg(hdmi->dev, "%s: samplerate=%ukHz pixelclk=%luMHz N=%d cts=%d\n",
-		__func__, sample_rate, pixel_clk, n, cts);
+	/*
+	 * Compute the CTS value from the N value.  Note that CTS and N
+	 * can be up to 20 bits in total, so we need 64-bit math.  Also
+	 * note that our TDMS clock is not fully accurate; it is accurate
+	 * to kHz.  This can introduce an unnecessary remainder in the
+	 * calculation below, so we don't try to warn about that.
+	 */
+	tmp = (u64)ftdms * n;
+	do_div(tmp, 128 * sample_rate);
+	cts = tmp;
+
+	dev_dbg(hdmi->dev, "%s: fs=%uHz ftdms=%lu.%03luMHz N=%d cts=%d\n",
+		__func__, sample_rate, ftdms / 1000000, (ftdms / 1000) % 1000,
+		n, cts);
 
 	spin_lock_irq(&hdmi->audio_lock);
 	hdmi->audio_n = n;
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 8/9] drm: bridge/dw_hdmi: replace CTS calculation for the ACR
@ 2015-08-08 16:10     ` Russell King
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:10 UTC (permalink / raw)
  To: linux-arm-kernel

Given the TDMS clock, audio sample rate, and the N parameter, we can
calculate the CTS value for the audio clock regenerator (ACR) using the
following calculation given in the HDMI specification:

	CTS = ftdms * N / (128 * fs)

The specification says that the CTS value is an average value, which is
true if the source hardware measures it.  Where source hardware needs it
to be programmed, it is particularly difficult to alternate it between
two values correctly to ensure that we achieve a correct "average"
fractional value at the sink.

Also, there's the problem that our "ftdms" is not a fully accurate
value; it is rounded to a kHz value.  This introduces an unnecessary
(and harmless) fractional value into the above equation for combinations
like 148.5MHz/1.001 for 44100Hz - we still calculate the correct CTS
value.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/dw_hdmi.c | 92 +++++++---------------------------------
 1 file changed, 16 insertions(+), 76 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index 60487bff48e3..a4f9aecf1862 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -272,89 +272,29 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk)
 	return n;
 }
 
-static unsigned int hdmi_compute_cts(unsigned int freq, unsigned long pixel_clk)
-{
-	unsigned int cts = 0;
-
-	pr_debug("%s: freq: %d pixel_clk: %ld\n", __func__, freq, pixel_clk);
-
-	switch (freq) {
-	case 32000:
-		if (pixel_clk == 297000000) {
-			cts = 222750;
-			break;
-		}
-	case 48000:
-	case 96000:
-	case 192000:
-		switch (pixel_clk) {
-		case 25200000:
-		case 27000000:
-		case 54000000:
-		case 74250000:
-		case 148500000:
-			cts = pixel_clk / 1000;
-			break;
-		case 297000000:
-			cts = 247500;
-			break;
-		/*
-		 * All other TMDS clocks are not supported by
-		 * DWC_hdmi_tx. The TMDS clocks divided or
-		 * multiplied by 1,001 coefficients are not
-		 * supported.
-		 */
-		default:
-			break;
-		}
-		break;
-	case 44100:
-	case 88200:
-	case 176400:
-		switch (pixel_clk) {
-		case 25200000:
-			cts = 28000;
-			break;
-		case 27000000:
-			cts = 30000;
-			break;
-		case 54000000:
-			cts = 60000;
-			break;
-		case 74250000:
-			cts = 82500;
-			break;
-		case 148500000:
-			cts = 165000;
-			break;
-		case 297000000:
-			cts = 247500;
-			break;
-		default:
-			break;
-		}
-		break;
-	default:
-		break;
-	}
-	return cts;
-}
-
 static void hdmi_set_clk_regenerator(struct dw_hdmi *hdmi,
 	unsigned long pixel_clk, unsigned int sample_rate)
 {
+	unsigned long ftdms = pixel_clk;
 	unsigned int n, cts;
+	u64 tmp;
 
 	n = hdmi_compute_n(sample_rate, pixel_clk);
-	cts = hdmi_compute_cts(sample_rate, pixel_clk);
-	if (!cts) {
-		dev_err(hdmi->dev,
-			"%s: pixel clock/sample rate not supported: %luMHz / %ukHz\n",
-			__func__, pixel_clk, sample_rate);
-	}
 
-	dev_dbg(hdmi->dev, "%s: samplerate=%ukHz pixelclk=%luMHz N=%d cts=%d\n",
-		__func__, sample_rate, pixel_clk, n, cts);
+	/*
+	 * Compute the CTS value from the N value.  Note that CTS and N
+	 * can be up to 20 bits in total, so we need 64-bit math.  Also
+	 * note that our TDMS clock is not fully accurate; it is accurate
+	 * to kHz.  This can introduce an unnecessary remainder in the
+	 * calculation below, so we don't try to warn about that.
+	 */
+	tmp = (u64)ftdms * n;
+	do_div(tmp, 128 * sample_rate);
+	cts = tmp;
+
+	dev_dbg(hdmi->dev, "%s: fs=%uHz ftdms=%lu.%03luMHz N=%d cts=%d\n",
+		__func__, sample_rate, ftdms / 1000000, (ftdms / 1000) % 1000,
+		n, cts);
 
 	spin_lock_irq(&hdmi->audio_lock);
 	hdmi->audio_n = n;
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 9/9] drm: bridge/dw_hdmi-i2s-audio: add audio driver
  2015-08-08 16:09   ` Russell King - ARM Linux
@ 2015-08-08 16:10     ` Russell King
  -1 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:10 UTC (permalink / raw)
  To: linux-rockchip, alsa-devel, dri-devel, linux-kernel, linux-arm-kernel
  Cc: Philipp Zabel, Andy Yan, Yakir Yang, Fabio Estevam, Mark Brown,
	Takashi Iwai, Jaroslav Kysela, Sascha Hauer, Jon Nettleton,
	David Airlie

From: Yakir Yang <ykk@rock-chips.com>
To: linux-rockchip@lists.infradead.org,alsa-devel@alsa-project.org,dri-devel@lists.freedesktop.org,linux-kernel@vger.kernel.org,linux-arm-kernel@lists.infradead.org

Add ALSA based HDMI I2S audio driver for dw_hdmi. Sound card
driver could connect to this codec through the codec dai name
"dw-hdmi-i2s-audio".

[Fixed IRQ name, MODULE_DESCRIPTION, MODULE_ALIAS in
 dw-hdmi-i2s-audio.c, and platform device name in dw-hdmi.c --rmk]

Signed-off-by: Yakir Yang <ykk@rock-chips.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/Kconfig             |   9 +
 drivers/gpu/drm/bridge/Makefile            |   1 +
 drivers/gpu/drm/bridge/dw_hdmi-i2s-audio.c | 398 +++++++++++++++++++++++++++++
 drivers/gpu/drm/bridge/dw_hdmi.c           |  24 +-
 drivers/gpu/drm/bridge/dw_hdmi.h           |   3 +
 5 files changed, 426 insertions(+), 9 deletions(-)
 create mode 100644 drivers/gpu/drm/bridge/dw_hdmi-i2s-audio.c

diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index 204861bfb867..59e3f24c4918 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -14,6 +14,15 @@ config DRM_DW_HDMI_AHB_AUDIO
 	  Designware HDMI block.  This is used in conjunction with
 	  the i.MX6 HDMI driver.
 
+config DRM_DW_HDMI_I2S_AUDIO
+	tristate "Synopsis Designware I2S Audio interface"
+	depends on DRM_DW_HDMI && SND
+	select SND_PCM
+	help
+	  Support the I2S Audio interface which is part of the Synopsis
+	  Designware HDMI block.  This is used in conjunction with the
+	  RK3288 HDMI driver.
+
 config DRM_PTN3460
 	tristate "PTN3460 DP/LVDS bridge"
 	depends on DRM
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index eb80dbbb8365..65a12390844a 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_DRM_PS8622) += ps8622.o
 obj-$(CONFIG_DRM_PTN3460) += ptn3460.o
 obj-$(CONFIG_DRM_DW_HDMI) += dw_hdmi.o
 obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw_hdmi-ahb-audio.o
+obj-$(CONFIG_DRM_DW_HDMI_I2S_AUDIO) += dw_hdmi-i2s-audio.o
diff --git a/drivers/gpu/drm/bridge/dw_hdmi-i2s-audio.c b/drivers/gpu/drm/bridge/dw_hdmi-i2s-audio.c
new file mode 100644
index 000000000000..62d3d33642d0
--- /dev/null
+++ b/drivers/gpu/drm/bridge/dw_hdmi-i2s-audio.c
@@ -0,0 +1,398 @@
+/*
+ * DesignWare HDMI audio driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Written and tested against the Designware HDMI Tx found in RK3288.
+ */
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/moduleparam.h>
+
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/initval.h>
+#include <sound/pcm_params.h>
+#include <drm/bridge/dw_hdmi.h>
+
+#include "dw_hdmi-audio.h"
+
+#define DRIVER_NAME "dw-hdmi-i2s-audio"
+
+enum {
+	AUDIO_CONF1_DATWIDTH_MSK = 0x1F,
+	AUDIO_CONF1_DATAMODE_MSK = 0xE0,
+	AUDIO_DAIFMT_IIS = 0x0,
+	AUDIO_DAIFMT_RIGHT_J = 0x20,
+	AUDIO_DAIFMT_LEFT_J = 0x40,
+	AUDIO_DAIFMT_BURST_1 = 0x60,
+	AUDIO_DAIFMT_BURST_2 = 0x80,
+	AUDIO_CONF0_INTERFACE_MSK = BIT(5),
+	AUDIO_INPUTTYPE_IIS = 0x20,
+	AUDIO_INPUTTYPE_SPDIF = 0x00,
+	AUDIO_CONF0_I2SINEN_MSK = 0x0F,
+	AUDIO_CHANNELNUM_2 = 0x01,
+	AUDIO_CHANNELNUM_4 = 0x03,
+	AUDIO_CHANNELNUM_6 = 0x07,
+	AUDIO_CHANNELNUM_8 = 0x0F,
+	HDMI_PHY_HPD = BIT(1),
+	HDMI_PHY_STAT0 = 0x3004,
+	HDMI_AUD_CONF0 = 0x3100,
+	HDMI_AUD_CONF1 = 0x3101,
+	HDMI_AUD_INPUTCLKFS = 0x3206,
+};
+
+struct dw_audio_fmt {
+	int input_type;
+	int chan_num;
+	int sample_rate;
+	int word_length;
+	int dai_fmt;
+};
+
+struct snd_dw_hdmi {
+	struct device *dev;
+	struct dw_hdmi_audio_data data;
+
+	bool is_jack_ready;
+	struct snd_soc_jack jack;
+
+	bool is_playback_status;
+	struct dw_audio_fmt fmt;
+};
+
+static void hdmi_writel(struct snd_dw_hdmi *dw, u8 val, int offset)
+{
+	writel(val, dw->data.base + (offset << 2));
+}
+
+static u8 hdmi_readl(struct snd_dw_hdmi *dw, int offset)
+{
+	return readl(dw->data.base + (offset << 2));
+}
+
+static void hdmi_modl(struct snd_dw_hdmi *dw, u8 data,
+		      u8 mask, unsigned reg)
+{
+	u8 val = hdmi_readl(dw, reg) & ~mask;
+
+	val |= data & mask;
+	hdmi_writel(dw, val, reg);
+}
+
+int snd_dw_hdmi_jack_detect(struct snd_dw_hdmi *dw)
+{
+	u8 jack_status;
+
+	if (!dw->is_jack_ready)
+		return -EINVAL;
+
+	jack_status = !!(hdmi_readl(dw, HDMI_PHY_STAT0) & HDMI_PHY_HPD) ?
+			SND_JACK_LINEOUT : 0;
+
+	snd_soc_jack_report(&dw->jack, jack_status, SND_JACK_LINEOUT);
+
+	return 0;
+}
+
+static irqreturn_t snd_dw_hdmi_irq(int irq, void *dev_id)
+{
+	struct snd_dw_hdmi *dw = dev_id;
+
+	snd_dw_hdmi_jack_detect(dw);
+
+	return IRQ_HANDLED;
+}
+
+static void dw_hdmi_audio_set_fmt(struct snd_dw_hdmi *dw,
+				  const struct dw_audio_fmt *fmt)
+{
+	hdmi_modl(dw, fmt->input_type, AUDIO_CONF0_INTERFACE_MSK,
+		  HDMI_AUD_CONF0);
+
+	hdmi_modl(dw, fmt->chan_num, AUDIO_CONF0_I2SINEN_MSK,
+		  HDMI_AUD_CONF0);
+
+	hdmi_modl(dw, fmt->word_length, AUDIO_CONF1_DATWIDTH_MSK,
+		  HDMI_AUD_CONF1);
+
+	hdmi_modl(dw, fmt->dai_fmt, AUDIO_CONF1_DATAMODE_MSK,
+		  HDMI_AUD_CONF1);
+
+	hdmi_writel(dw, 0, HDMI_AUD_INPUTCLKFS);
+
+	dw_hdmi_set_sample_rate(dw->data.hdmi, fmt->sample_rate);
+}
+
+static void dw_audio_set_fmt(struct snd_dw_hdmi *dw,
+			     const struct dw_audio_fmt *fmt)
+{
+	if (fmt)
+		dw->fmt = *fmt;
+	dw_hdmi_audio_set_fmt(dw, &dw->fmt);
+}
+
+static int snd_dw_hdmi_dai_startup(struct snd_pcm_substream *substream,
+				   struct snd_soc_dai *codec_dai)
+{
+	struct snd_dw_hdmi *dw = snd_soc_dai_get_drvdata(codec_dai);
+
+	dw->is_playback_status = true;
+	dw_hdmi_audio_enable(dw->data.hdmi);
+
+	return 0;
+}
+
+static int snd_dw_hdmi_dai_hw_params(struct snd_pcm_substream *substream,
+				     struct snd_pcm_hw_params *params,
+				     struct snd_soc_dai *codec_dai)
+{
+	struct snd_dw_hdmi *dw = snd_soc_dai_get_drvdata(codec_dai);
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct dw_audio_fmt dw_fmt;
+	unsigned int fmt, rate, chan, width;
+
+	fmt = rtd->dai_link->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+	switch (fmt) {
+	case SND_SOC_DAIFMT_I2S:
+		dw_fmt.dai_fmt = AUDIO_DAIFMT_IIS;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		dw_fmt.dai_fmt = AUDIO_DAIFMT_LEFT_J;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		dw_fmt.dai_fmt = AUDIO_DAIFMT_RIGHT_J;
+		break;
+	default:
+		dev_err(codec_dai->dev, "DAI format unsupported");
+		return -EINVAL;
+	}
+
+	width = params_width(params);
+	switch (width) {
+	case 16:
+	case 24:
+		dw_fmt.word_length = width;
+		break;
+	default:
+		dev_err(codec_dai->dev, "width[%d] not support!\n", width);
+		return -EINVAL;
+	}
+
+	chan = params_channels(params);
+	switch (chan) {
+	case 2:
+		dw_fmt.chan_num = AUDIO_CHANNELNUM_2;
+		break;
+	case 4:
+		dw_fmt.chan_num = AUDIO_CHANNELNUM_4;
+		break;
+	case 6:
+		dw_fmt.chan_num = AUDIO_CHANNELNUM_6;
+		break;
+	case 8:
+		dw_fmt.chan_num = AUDIO_CHANNELNUM_8;
+		break;
+	default:
+		dev_err(codec_dai->dev, "channel[%d] not support!\n", chan);
+		return -EINVAL;
+	}
+
+	rate = params_rate(params);
+	switch (rate) {
+	case 32000:
+	case 44100:
+	case 48000:
+	case 88200:
+	case 96000:
+	case 176400:
+	case 192000:
+		dw_fmt.sample_rate = rate;
+		break;
+	default:
+		dev_err(codec_dai->dev, "rate[%d] not support!\n", rate);
+		return -EINVAL;
+	}
+
+	dw_fmt.input_type = AUDIO_INPUTTYPE_IIS;
+
+	dw_audio_set_fmt(dw, &dw_fmt);
+
+	return 0;
+}
+
+static int snd_dw_hdmi_dai_trigger(struct snd_pcm_substream *substream,
+				   int cmd, struct snd_soc_dai *codec_dai)
+{
+	struct snd_dw_hdmi *dw = snd_soc_dai_get_drvdata(codec_dai);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		dw_hdmi_audio_enable(dw->data.hdmi);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		dw_hdmi_audio_disable(dw->data.hdmi);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void snd_dw_hdmi_dai_shutdown(struct snd_pcm_substream *substream,
+				     struct snd_soc_dai *codec_dai)
+{
+	struct snd_dw_hdmi *dw = snd_soc_dai_get_drvdata(codec_dai);
+
+	dw->is_playback_status = false;
+	dw_hdmi_audio_disable(dw->data.hdmi);
+}
+
+static int snd_dw_hdmi_audio_probe(struct snd_soc_codec *codec)
+{
+	struct snd_dw_hdmi *dw = snd_soc_codec_get_drvdata(codec);
+	int ret;
+
+	ret = snd_soc_jack_new(codec, "dw Jack", SND_JACK_LINEOUT,
+			       &dw->jack);
+	if (ret) {
+		dev_err(dw->dev, "jack new failed (%d)\n", ret);
+		dw->is_jack_ready = false;
+		return ret;
+	}
+
+	dw->is_jack_ready = true;
+
+	return snd_dw_hdmi_jack_detect(dw);
+}
+
+static const struct snd_soc_dapm_widget snd_dw_hdmi_audio_widgets[] = {
+	SND_SOC_DAPM_OUTPUT("TX"),
+};
+
+static const struct snd_soc_dapm_route snd_dw_hdmi_audio_routes[] = {
+	{ "TX", NULL, "Playback" },
+};
+
+static const struct snd_soc_dai_ops dw_hdmi_dai_ops = {
+	.startup = snd_dw_hdmi_dai_startup,
+	.hw_params = snd_dw_hdmi_dai_hw_params,
+	.trigger = snd_dw_hdmi_dai_trigger,
+	.shutdown = snd_dw_hdmi_dai_shutdown,
+};
+
+static struct snd_soc_dai_driver dw_hdmi_audio_dai = {
+	.name = "dw-hdmi-i2s-hifi",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 2,
+		.channels_max = 8,
+		.rates = SNDRV_PCM_RATE_32000 |
+			 SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
+			 SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
+			 SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+	},
+	.ops = &dw_hdmi_dai_ops,
+};
+
+static const struct snd_soc_codec_driver dw_hdmi_audio = {
+	.probe = snd_dw_hdmi_audio_probe,
+	.dapm_widgets = snd_dw_hdmi_audio_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(snd_dw_hdmi_audio_widgets),
+	.dapm_routes = snd_dw_hdmi_audio_routes,
+	.num_dapm_routes = ARRAY_SIZE(snd_dw_hdmi_audio_routes),
+};
+
+static int dw_hdmi_audio_probe(struct platform_device *pdev)
+{
+	struct dw_hdmi_audio_data *data = pdev->dev.platform_data;
+	struct snd_dw_hdmi *dw;
+	int ret;
+
+	dw = devm_kzalloc(&pdev->dev, sizeof(*dw), GFP_KERNEL);
+	if (!dw)
+		return -ENOMEM;
+
+	dw->data = *data;
+	dw->dev = &pdev->dev;
+	dw->is_jack_ready = false;
+	platform_set_drvdata(pdev, dw);
+
+	ret = request_irq(dw->data.irq, snd_dw_hdmi_irq, IRQF_SHARED,
+			  DRIVER_NAME, dw);
+	if (ret) {
+		dev_err(&pdev->dev, "request irq failed (%d)\n", ret);
+		return -EINVAL;
+	}
+
+	ret = snd_soc_register_codec(&pdev->dev, &dw_hdmi_audio,
+				     &dw_hdmi_audio_dai, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "register codec failed (%d)\n", ret);
+		return -EINVAL;
+	}
+
+	dev_dbg(&pdev->dev, "dw audio init success.\n");
+
+	return 0;
+}
+
+static int dw_hdmi_audio_remove(struct platform_device *pdev)
+{
+	struct snd_dw_hdmi *dw = platform_get_drvdata(pdev);
+
+	snd_soc_unregister_codec(&pdev->dev);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int dw_hdmi_audio_resume(struct device *dev)
+{
+	struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
+
+	snd_dw_hdmi_jack_detect(dw);
+
+	if (dw->is_playback_status)
+		dw_hdmi_audio_set_fmt(dw, &dw->fmt);
+
+	return 0;
+}
+
+static int dw_hdmi_audio_suspend(struct device *dev)
+{
+	return 0;
+}
+#endif
+
+static const struct dev_pm_ops dw_hdmi_audio_pm = {
+	SET_SYSTEM_SLEEP_PM_OPS(dw_hdmi_audio_suspend, dw_hdmi_audio_resume)
+};
+
+static struct platform_driver dw_hdmi_audio_driver = {
+	.driver = {
+		.name = DRIVER_NAME,
+		.pm = &dw_hdmi_audio_pm,
+	},
+	.probe = dw_hdmi_audio_probe,
+	.remove = dw_hdmi_audio_remove,
+};
+module_platform_driver(dw_hdmi_audio_driver);
+
+MODULE_AUTHOR("Yakir Yang <ykk@rock-chips.com>");
+MODULE_DESCRIPTION("Synopsis DesignWare HDMI I2S ASoC Interface");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index a4f9aecf1862..b08311be90d9 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -1793,19 +1793,25 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 
 	memset(&pdevinfo, 0, sizeof(pdevinfo));
 	pdevinfo.parent = dev;
-	pdevinfo.id = PLATFORM_DEVID_AUTO;
+
+	audio.phys = iores->start;
+	audio.base = hdmi->regs;
+	audio.irq = irq;
+	audio.hdmi = hdmi;
+	audio.eld = hdmi->connector.eld;
+
+	pdevinfo.data = &audio;
+	pdevinfo.size_data = sizeof(audio);
 
 	if (hdmi_readb(hdmi, HDMI_CONFIG1_ID) & HDMI_CONFIG1_AHB) {
-		audio.phys = iores->start;
-		audio.base = hdmi->regs;
-		audio.irq = irq;
-		audio.hdmi = hdmi;
-		audio.eld = hdmi->connector.eld;
+		pdevinfo.name = "dw-hdmi-i2s-audio";
+		pdevinfo.id = PLATFORM_DEVID_AUTO;
+		pdevinfo.dma_mask = DMA_BIT_MASK(32);
+		hdmi->audio = platform_device_register_full(&pdevinfo);
 
+	} else if (hdmi_readb(hdmi, HDMI_CONFIG0_ID) & HDMI_CONFIG0_I2S) {
 		pdevinfo.name = "dw-hdmi-ahb-audio";
-		pdevinfo.data = &audio;
-		pdevinfo.size_data = sizeof(audio);
-		pdevinfo.dma_mask = DMA_BIT_MASK(32);
+		pdevinfo.id = PLATFORM_DEVID_NONE;
 		hdmi->audio = platform_device_register_full(&pdevinfo);
 	}
 
diff --git a/drivers/gpu/drm/bridge/dw_hdmi.h b/drivers/gpu/drm/bridge/dw_hdmi.h
index 78e54e813212..9c2237753f0a 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.h
+++ b/drivers/gpu/drm/bridge/dw_hdmi.h
@@ -545,6 +545,9 @@
 #define HDMI_I2CM_FS_SCL_LCNT_0_ADDR            0x7E12
 
 enum {
+/* CONFIG0_ID field values */
+	HDMI_CONFIG0_I2S = 0x01,
+
 /* CONFIG1_ID field values */
 	HDMI_CONFIG1_AHB = 0x01,
 
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH 9/9] drm: bridge/dw_hdmi-i2s-audio: add audio driver
@ 2015-08-08 16:10     ` Russell King
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-08 16:10 UTC (permalink / raw)
  To: linux-arm-kernel

From: Yakir Yang <ykk@rock-chips.com>
To: linux-rockchip at lists.infradead.org,alsa-devel at alsa-project.org,dri-devel at lists.freedesktop.org,linux-kernel at vger.kernel.org,linux-arm-kernel at lists.infradead.org

Add ALSA based HDMI I2S audio driver for dw_hdmi. Sound card
driver could connect to this codec through the codec dai name
"dw-hdmi-i2s-audio".

[Fixed IRQ name, MODULE_DESCRIPTION, MODULE_ALIAS in
 dw-hdmi-i2s-audio.c, and platform device name in dw-hdmi.c --rmk]

Signed-off-by: Yakir Yang <ykk@rock-chips.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/Kconfig             |   9 +
 drivers/gpu/drm/bridge/Makefile            |   1 +
 drivers/gpu/drm/bridge/dw_hdmi-i2s-audio.c | 398 +++++++++++++++++++++++++++++
 drivers/gpu/drm/bridge/dw_hdmi.c           |  24 +-
 drivers/gpu/drm/bridge/dw_hdmi.h           |   3 +
 5 files changed, 426 insertions(+), 9 deletions(-)
 create mode 100644 drivers/gpu/drm/bridge/dw_hdmi-i2s-audio.c

diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index 204861bfb867..59e3f24c4918 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -14,6 +14,15 @@ config DRM_DW_HDMI_AHB_AUDIO
 	  Designware HDMI block.  This is used in conjunction with
 	  the i.MX6 HDMI driver.
 
+config DRM_DW_HDMI_I2S_AUDIO
+	tristate "Synopsis Designware I2S Audio interface"
+	depends on DRM_DW_HDMI && SND
+	select SND_PCM
+	help
+	  Support the I2S Audio interface which is part of the Synopsis
+	  Designware HDMI block.  This is used in conjunction with the
+	  RK3288 HDMI driver.
+
 config DRM_PTN3460
 	tristate "PTN3460 DP/LVDS bridge"
 	depends on DRM
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index eb80dbbb8365..65a12390844a 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_DRM_PS8622) += ps8622.o
 obj-$(CONFIG_DRM_PTN3460) += ptn3460.o
 obj-$(CONFIG_DRM_DW_HDMI) += dw_hdmi.o
 obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw_hdmi-ahb-audio.o
+obj-$(CONFIG_DRM_DW_HDMI_I2S_AUDIO) += dw_hdmi-i2s-audio.o
diff --git a/drivers/gpu/drm/bridge/dw_hdmi-i2s-audio.c b/drivers/gpu/drm/bridge/dw_hdmi-i2s-audio.c
new file mode 100644
index 000000000000..62d3d33642d0
--- /dev/null
+++ b/drivers/gpu/drm/bridge/dw_hdmi-i2s-audio.c
@@ -0,0 +1,398 @@
+/*
+ * DesignWare HDMI audio driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Written and tested against the Designware HDMI Tx found in RK3288.
+ */
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/moduleparam.h>
+
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/initval.h>
+#include <sound/pcm_params.h>
+#include <drm/bridge/dw_hdmi.h>
+
+#include "dw_hdmi-audio.h"
+
+#define DRIVER_NAME "dw-hdmi-i2s-audio"
+
+enum {
+	AUDIO_CONF1_DATWIDTH_MSK = 0x1F,
+	AUDIO_CONF1_DATAMODE_MSK = 0xE0,
+	AUDIO_DAIFMT_IIS = 0x0,
+	AUDIO_DAIFMT_RIGHT_J = 0x20,
+	AUDIO_DAIFMT_LEFT_J = 0x40,
+	AUDIO_DAIFMT_BURST_1 = 0x60,
+	AUDIO_DAIFMT_BURST_2 = 0x80,
+	AUDIO_CONF0_INTERFACE_MSK = BIT(5),
+	AUDIO_INPUTTYPE_IIS = 0x20,
+	AUDIO_INPUTTYPE_SPDIF = 0x00,
+	AUDIO_CONF0_I2SINEN_MSK = 0x0F,
+	AUDIO_CHANNELNUM_2 = 0x01,
+	AUDIO_CHANNELNUM_4 = 0x03,
+	AUDIO_CHANNELNUM_6 = 0x07,
+	AUDIO_CHANNELNUM_8 = 0x0F,
+	HDMI_PHY_HPD = BIT(1),
+	HDMI_PHY_STAT0 = 0x3004,
+	HDMI_AUD_CONF0 = 0x3100,
+	HDMI_AUD_CONF1 = 0x3101,
+	HDMI_AUD_INPUTCLKFS = 0x3206,
+};
+
+struct dw_audio_fmt {
+	int input_type;
+	int chan_num;
+	int sample_rate;
+	int word_length;
+	int dai_fmt;
+};
+
+struct snd_dw_hdmi {
+	struct device *dev;
+	struct dw_hdmi_audio_data data;
+
+	bool is_jack_ready;
+	struct snd_soc_jack jack;
+
+	bool is_playback_status;
+	struct dw_audio_fmt fmt;
+};
+
+static void hdmi_writel(struct snd_dw_hdmi *dw, u8 val, int offset)
+{
+	writel(val, dw->data.base + (offset << 2));
+}
+
+static u8 hdmi_readl(struct snd_dw_hdmi *dw, int offset)
+{
+	return readl(dw->data.base + (offset << 2));
+}
+
+static void hdmi_modl(struct snd_dw_hdmi *dw, u8 data,
+		      u8 mask, unsigned reg)
+{
+	u8 val = hdmi_readl(dw, reg) & ~mask;
+
+	val |= data & mask;
+	hdmi_writel(dw, val, reg);
+}
+
+int snd_dw_hdmi_jack_detect(struct snd_dw_hdmi *dw)
+{
+	u8 jack_status;
+
+	if (!dw->is_jack_ready)
+		return -EINVAL;
+
+	jack_status = !!(hdmi_readl(dw, HDMI_PHY_STAT0) & HDMI_PHY_HPD) ?
+			SND_JACK_LINEOUT : 0;
+
+	snd_soc_jack_report(&dw->jack, jack_status, SND_JACK_LINEOUT);
+
+	return 0;
+}
+
+static irqreturn_t snd_dw_hdmi_irq(int irq, void *dev_id)
+{
+	struct snd_dw_hdmi *dw = dev_id;
+
+	snd_dw_hdmi_jack_detect(dw);
+
+	return IRQ_HANDLED;
+}
+
+static void dw_hdmi_audio_set_fmt(struct snd_dw_hdmi *dw,
+				  const struct dw_audio_fmt *fmt)
+{
+	hdmi_modl(dw, fmt->input_type, AUDIO_CONF0_INTERFACE_MSK,
+		  HDMI_AUD_CONF0);
+
+	hdmi_modl(dw, fmt->chan_num, AUDIO_CONF0_I2SINEN_MSK,
+		  HDMI_AUD_CONF0);
+
+	hdmi_modl(dw, fmt->word_length, AUDIO_CONF1_DATWIDTH_MSK,
+		  HDMI_AUD_CONF1);
+
+	hdmi_modl(dw, fmt->dai_fmt, AUDIO_CONF1_DATAMODE_MSK,
+		  HDMI_AUD_CONF1);
+
+	hdmi_writel(dw, 0, HDMI_AUD_INPUTCLKFS);
+
+	dw_hdmi_set_sample_rate(dw->data.hdmi, fmt->sample_rate);
+}
+
+static void dw_audio_set_fmt(struct snd_dw_hdmi *dw,
+			     const struct dw_audio_fmt *fmt)
+{
+	if (fmt)
+		dw->fmt = *fmt;
+	dw_hdmi_audio_set_fmt(dw, &dw->fmt);
+}
+
+static int snd_dw_hdmi_dai_startup(struct snd_pcm_substream *substream,
+				   struct snd_soc_dai *codec_dai)
+{
+	struct snd_dw_hdmi *dw = snd_soc_dai_get_drvdata(codec_dai);
+
+	dw->is_playback_status = true;
+	dw_hdmi_audio_enable(dw->data.hdmi);
+
+	return 0;
+}
+
+static int snd_dw_hdmi_dai_hw_params(struct snd_pcm_substream *substream,
+				     struct snd_pcm_hw_params *params,
+				     struct snd_soc_dai *codec_dai)
+{
+	struct snd_dw_hdmi *dw = snd_soc_dai_get_drvdata(codec_dai);
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct dw_audio_fmt dw_fmt;
+	unsigned int fmt, rate, chan, width;
+
+	fmt = rtd->dai_link->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+	switch (fmt) {
+	case SND_SOC_DAIFMT_I2S:
+		dw_fmt.dai_fmt = AUDIO_DAIFMT_IIS;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		dw_fmt.dai_fmt = AUDIO_DAIFMT_LEFT_J;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		dw_fmt.dai_fmt = AUDIO_DAIFMT_RIGHT_J;
+		break;
+	default:
+		dev_err(codec_dai->dev, "DAI format unsupported");
+		return -EINVAL;
+	}
+
+	width = params_width(params);
+	switch (width) {
+	case 16:
+	case 24:
+		dw_fmt.word_length = width;
+		break;
+	default:
+		dev_err(codec_dai->dev, "width[%d] not support!\n", width);
+		return -EINVAL;
+	}
+
+	chan = params_channels(params);
+	switch (chan) {
+	case 2:
+		dw_fmt.chan_num = AUDIO_CHANNELNUM_2;
+		break;
+	case 4:
+		dw_fmt.chan_num = AUDIO_CHANNELNUM_4;
+		break;
+	case 6:
+		dw_fmt.chan_num = AUDIO_CHANNELNUM_6;
+		break;
+	case 8:
+		dw_fmt.chan_num = AUDIO_CHANNELNUM_8;
+		break;
+	default:
+		dev_err(codec_dai->dev, "channel[%d] not support!\n", chan);
+		return -EINVAL;
+	}
+
+	rate = params_rate(params);
+	switch (rate) {
+	case 32000:
+	case 44100:
+	case 48000:
+	case 88200:
+	case 96000:
+	case 176400:
+	case 192000:
+		dw_fmt.sample_rate = rate;
+		break;
+	default:
+		dev_err(codec_dai->dev, "rate[%d] not support!\n", rate);
+		return -EINVAL;
+	}
+
+	dw_fmt.input_type = AUDIO_INPUTTYPE_IIS;
+
+	dw_audio_set_fmt(dw, &dw_fmt);
+
+	return 0;
+}
+
+static int snd_dw_hdmi_dai_trigger(struct snd_pcm_substream *substream,
+				   int cmd, struct snd_soc_dai *codec_dai)
+{
+	struct snd_dw_hdmi *dw = snd_soc_dai_get_drvdata(codec_dai);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		dw_hdmi_audio_enable(dw->data.hdmi);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		dw_hdmi_audio_disable(dw->data.hdmi);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void snd_dw_hdmi_dai_shutdown(struct snd_pcm_substream *substream,
+				     struct snd_soc_dai *codec_dai)
+{
+	struct snd_dw_hdmi *dw = snd_soc_dai_get_drvdata(codec_dai);
+
+	dw->is_playback_status = false;
+	dw_hdmi_audio_disable(dw->data.hdmi);
+}
+
+static int snd_dw_hdmi_audio_probe(struct snd_soc_codec *codec)
+{
+	struct snd_dw_hdmi *dw = snd_soc_codec_get_drvdata(codec);
+	int ret;
+
+	ret = snd_soc_jack_new(codec, "dw Jack", SND_JACK_LINEOUT,
+			       &dw->jack);
+	if (ret) {
+		dev_err(dw->dev, "jack new failed (%d)\n", ret);
+		dw->is_jack_ready = false;
+		return ret;
+	}
+
+	dw->is_jack_ready = true;
+
+	return snd_dw_hdmi_jack_detect(dw);
+}
+
+static const struct snd_soc_dapm_widget snd_dw_hdmi_audio_widgets[] = {
+	SND_SOC_DAPM_OUTPUT("TX"),
+};
+
+static const struct snd_soc_dapm_route snd_dw_hdmi_audio_routes[] = {
+	{ "TX", NULL, "Playback" },
+};
+
+static const struct snd_soc_dai_ops dw_hdmi_dai_ops = {
+	.startup = snd_dw_hdmi_dai_startup,
+	.hw_params = snd_dw_hdmi_dai_hw_params,
+	.trigger = snd_dw_hdmi_dai_trigger,
+	.shutdown = snd_dw_hdmi_dai_shutdown,
+};
+
+static struct snd_soc_dai_driver dw_hdmi_audio_dai = {
+	.name = "dw-hdmi-i2s-hifi",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 2,
+		.channels_max = 8,
+		.rates = SNDRV_PCM_RATE_32000 |
+			 SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
+			 SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
+			 SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+	},
+	.ops = &dw_hdmi_dai_ops,
+};
+
+static const struct snd_soc_codec_driver dw_hdmi_audio = {
+	.probe = snd_dw_hdmi_audio_probe,
+	.dapm_widgets = snd_dw_hdmi_audio_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(snd_dw_hdmi_audio_widgets),
+	.dapm_routes = snd_dw_hdmi_audio_routes,
+	.num_dapm_routes = ARRAY_SIZE(snd_dw_hdmi_audio_routes),
+};
+
+static int dw_hdmi_audio_probe(struct platform_device *pdev)
+{
+	struct dw_hdmi_audio_data *data = pdev->dev.platform_data;
+	struct snd_dw_hdmi *dw;
+	int ret;
+
+	dw = devm_kzalloc(&pdev->dev, sizeof(*dw), GFP_KERNEL);
+	if (!dw)
+		return -ENOMEM;
+
+	dw->data = *data;
+	dw->dev = &pdev->dev;
+	dw->is_jack_ready = false;
+	platform_set_drvdata(pdev, dw);
+
+	ret = request_irq(dw->data.irq, snd_dw_hdmi_irq, IRQF_SHARED,
+			  DRIVER_NAME, dw);
+	if (ret) {
+		dev_err(&pdev->dev, "request irq failed (%d)\n", ret);
+		return -EINVAL;
+	}
+
+	ret = snd_soc_register_codec(&pdev->dev, &dw_hdmi_audio,
+				     &dw_hdmi_audio_dai, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "register codec failed (%d)\n", ret);
+		return -EINVAL;
+	}
+
+	dev_dbg(&pdev->dev, "dw audio init success.\n");
+
+	return 0;
+}
+
+static int dw_hdmi_audio_remove(struct platform_device *pdev)
+{
+	struct snd_dw_hdmi *dw = platform_get_drvdata(pdev);
+
+	snd_soc_unregister_codec(&pdev->dev);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int dw_hdmi_audio_resume(struct device *dev)
+{
+	struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
+
+	snd_dw_hdmi_jack_detect(dw);
+
+	if (dw->is_playback_status)
+		dw_hdmi_audio_set_fmt(dw, &dw->fmt);
+
+	return 0;
+}
+
+static int dw_hdmi_audio_suspend(struct device *dev)
+{
+	return 0;
+}
+#endif
+
+static const struct dev_pm_ops dw_hdmi_audio_pm = {
+	SET_SYSTEM_SLEEP_PM_OPS(dw_hdmi_audio_suspend, dw_hdmi_audio_resume)
+};
+
+static struct platform_driver dw_hdmi_audio_driver = {
+	.driver = {
+		.name = DRIVER_NAME,
+		.pm = &dw_hdmi_audio_pm,
+	},
+	.probe = dw_hdmi_audio_probe,
+	.remove = dw_hdmi_audio_remove,
+};
+module_platform_driver(dw_hdmi_audio_driver);
+
+MODULE_AUTHOR("Yakir Yang <ykk@rock-chips.com>");
+MODULE_DESCRIPTION("Synopsis DesignWare HDMI I2S ASoC Interface");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index a4f9aecf1862..b08311be90d9 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -1793,19 +1793,25 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 
 	memset(&pdevinfo, 0, sizeof(pdevinfo));
 	pdevinfo.parent = dev;
-	pdevinfo.id = PLATFORM_DEVID_AUTO;
+
+	audio.phys = iores->start;
+	audio.base = hdmi->regs;
+	audio.irq = irq;
+	audio.hdmi = hdmi;
+	audio.eld = hdmi->connector.eld;
+
+	pdevinfo.data = &audio;
+	pdevinfo.size_data = sizeof(audio);
 
 	if (hdmi_readb(hdmi, HDMI_CONFIG1_ID) & HDMI_CONFIG1_AHB) {
-		audio.phys = iores->start;
-		audio.base = hdmi->regs;
-		audio.irq = irq;
-		audio.hdmi = hdmi;
-		audio.eld = hdmi->connector.eld;
+		pdevinfo.name = "dw-hdmi-i2s-audio";
+		pdevinfo.id = PLATFORM_DEVID_AUTO;
+		pdevinfo.dma_mask = DMA_BIT_MASK(32);
+		hdmi->audio = platform_device_register_full(&pdevinfo);
 
+	} else if (hdmi_readb(hdmi, HDMI_CONFIG0_ID) & HDMI_CONFIG0_I2S) {
 		pdevinfo.name = "dw-hdmi-ahb-audio";
-		pdevinfo.data = &audio;
-		pdevinfo.size_data = sizeof(audio);
-		pdevinfo.dma_mask = DMA_BIT_MASK(32);
+		pdevinfo.id = PLATFORM_DEVID_NONE;
 		hdmi->audio = platform_device_register_full(&pdevinfo);
 	}
 
diff --git a/drivers/gpu/drm/bridge/dw_hdmi.h b/drivers/gpu/drm/bridge/dw_hdmi.h
index 78e54e813212..9c2237753f0a 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.h
+++ b/drivers/gpu/drm/bridge/dw_hdmi.h
@@ -545,6 +545,9 @@
 #define HDMI_I2CM_FS_SCL_LCNT_0_ADDR            0x7E12
 
 enum {
+/* CONFIG0_ID field values */
+	HDMI_CONFIG0_I2S = 0x01,
+
 /* CONFIG1_ID field values */
 	HDMI_CONFIG1_AHB = 0x01,
 
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 226+ messages in thread

* Re: [PATCH 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver
  2015-08-08 16:10     ` Russell King
  (?)
@ 2015-08-10 10:05       ` Takashi Iwai
  -1 siblings, 0 replies; 226+ messages in thread
From: Takashi Iwai @ 2015-08-10 10:05 UTC (permalink / raw)
  To: Russell King
  Cc: linux-rockchip, alsa-devel, dri-devel, linux-kernel,
	linux-arm-kernel, Philipp Zabel, Andy Yan, Yakir Yang,
	Fabio Estevam, Mark Brown, Jaroslav Kysela, Sascha Hauer,
	Jon Nettleton, David Airlie

On Sat, 08 Aug 2015 18:10:06 +0200,
Russell King wrote:
> 
> Add ALSA based HDMI AHB audio driver for dw_hdmi.  The only buffer
> format supported by the hardware is its own special IEC958 based format,
> which is not compatible with any ALSA format.  To avoid doing too much
> data manipulation within the driver, we support only ALSAs IEC958 LE and
> 24-bit PCM formats for 2 to 6 channels, which we convert to its hardware
> format.
> 
> A more desirable solution would be to have this conversion in userspace,
> but ALSA does not appear to allow such transformations outside of
> libasound itself.
> 
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> ---
>  drivers/gpu/drm/bridge/Kconfig             |  10 +
>  drivers/gpu/drm/bridge/Makefile            |   1 +
>  drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c | 561 +++++++++++++++++++++++++++++
>  drivers/gpu/drm/bridge/dw_hdmi-audio.h     |  13 +
>  drivers/gpu/drm/bridge/dw_hdmi.c           |  24 ++
>  drivers/gpu/drm/bridge/dw_hdmi.h           |   3 +
>  6 files changed, 612 insertions(+)
>  create mode 100644 drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
>  create mode 100644 drivers/gpu/drm/bridge/dw_hdmi-audio.h
> 
> diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
> index acef3223772c..56ed35fe0734 100644
> --- a/drivers/gpu/drm/bridge/Kconfig
> +++ b/drivers/gpu/drm/bridge/Kconfig
> @@ -3,6 +3,16 @@ config DRM_DW_HDMI
>  	depends on DRM
>  	select DRM_KMS_HELPER
>  
> +config DRM_DW_HDMI_AHB_AUDIO
> +	tristate "Synopsis Designware AHB Audio interface"
> +	depends on DRM_DW_HDMI && SND
> +	select SND_PCM
> +	select SND_PCM_IEC958
> +	help
> +	  Support the AHB Audio interface which is part of the Synopsis
> +	  Designware HDMI block.  This is used in conjunction with
> +	  the i.MX6 HDMI driver.
> +
>  config DRM_PTN3460
>  	tristate "PTN3460 DP/LVDS bridge"
>  	depends on DRM
> diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
> index 8dfebd984370..eb80dbbb8365 100644
> --- a/drivers/gpu/drm/bridge/Makefile
> +++ b/drivers/gpu/drm/bridge/Makefile
> @@ -3,3 +3,4 @@ ccflags-y := -Iinclude/drm
>  obj-$(CONFIG_DRM_PS8622) += ps8622.o
>  obj-$(CONFIG_DRM_PTN3460) += ptn3460.o
>  obj-$(CONFIG_DRM_DW_HDMI) += dw_hdmi.o
> +obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw_hdmi-ahb-audio.o
> diff --git a/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
> new file mode 100644
> index 000000000000..22bbbc5c2393
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
> @@ -0,0 +1,561 @@
> +/*
> + * DesignWare HDMI audio driver
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * Written and tested against the Designware HDMI Tx found in iMX6.
> + */
> +#include <linux/io.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <drm/bridge/dw_hdmi.h>
> +
> +#include <sound/asoundef.h>
> +#include <sound/core.h>
> +#include <sound/initval.h>
> +#include <sound/pcm.h>
> +#include <sound/pcm_iec958.h>
> +
> +#include "dw_hdmi-audio.h"
> +
> +#define DRIVER_NAME "dw-hdmi-ahb-audio"
> +
> +/* Provide some bits rather than bit offsets */
> +enum {
> +	HDMI_AHB_DMA_CONF0_SW_FIFO_RST = BIT(7),
> +	HDMI_AHB_DMA_CONF0_EN_HLOCK = BIT(3),
> +	HDMI_AHB_DMA_START_START = BIT(0),
> +	HDMI_AHB_DMA_STOP_STOP = BIT(0),
> +	HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR = BIT(5),
> +	HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST = BIT(4),
> +	HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY = BIT(3),
> +	HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE = BIT(2),
> +	HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL = BIT(1),
> +	HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0),
> +	HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL =
> +		HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR |
> +		HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST |
> +		HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY |
> +		HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE |
> +		HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL |
> +		HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY,
> +	HDMI_IH_AHBDMAAUD_STAT0_ERROR = BIT(5),
> +	HDMI_IH_AHBDMAAUD_STAT0_LOST = BIT(4),
> +	HDMI_IH_AHBDMAAUD_STAT0_RETRY = BIT(3),
> +	HDMI_IH_AHBDMAAUD_STAT0_DONE = BIT(2),
> +	HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL = BIT(1),
> +	HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0),
> +	HDMI_IH_AHBDMAAUD_STAT0_ALL =
> +		HDMI_IH_AHBDMAAUD_STAT0_ERROR |
> +		HDMI_IH_AHBDMAAUD_STAT0_LOST |
> +		HDMI_IH_AHBDMAAUD_STAT0_RETRY |
> +		HDMI_IH_AHBDMAAUD_STAT0_DONE |
> +		HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL |
> +		HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY,
> +	HDMI_AHB_DMA_CONF0_INCR16 = 2 << 1,
> +	HDMI_AHB_DMA_CONF0_INCR8 = 1 << 1,
> +	HDMI_AHB_DMA_CONF0_INCR4 = 0,
> +	HDMI_AHB_DMA_CONF0_BURST_MODE = BIT(0),
> +	HDMI_AHB_DMA_MASK_DONE = BIT(7),
> +	HDMI_REVISION_ID = 0x0001,
> +	HDMI_IH_AHBDMAAUD_STAT0 = 0x0109,
> +	HDMI_IH_MUTE_AHBDMAAUD_STAT0 = 0x0189,
> +	HDMI_AHB_DMA_CONF0 = 0x3600,
> +	HDMI_AHB_DMA_START = 0x3601,
> +	HDMI_AHB_DMA_STOP = 0x3602,
> +	HDMI_AHB_DMA_THRSLD = 0x3603,
> +	HDMI_AHB_DMA_STRADDR0 = 0x3604,
> +	HDMI_AHB_DMA_STPADDR0 = 0x3608,
> +	HDMI_AHB_DMA_MASK = 0x3614,
> +	HDMI_AHB_DMA_POL = 0x3615,
> +	HDMI_AHB_DMA_CONF1 = 0x3616,
> +	HDMI_AHB_DMA_BUFFPOL = 0x361a,
> +};
> +
> +struct snd_dw_hdmi {
> +	struct snd_card *card;
> +	struct snd_pcm *pcm;
> +	struct dw_hdmi_audio_data data;
> +	struct snd_pcm_substream *substream;
> +	void (*reformat)(struct snd_dw_hdmi *, size_t, size_t);
> +	void *buf_src;
> +	void *buf_dst;
> +	dma_addr_t buf_addr;
> +	unsigned buf_offset;
> +	unsigned buf_period;
> +	unsigned buf_size;
> +	unsigned channels;
> +	u8 revision;
> +	u8 iec_offset;
> +	u8 cs[192][8];
> +};
> +
> +static void dw_hdmi_writel(unsigned long val, void __iomem *ptr)

Better to be u32 instead of unsigned long in general.

> +{
> +	writeb_relaxed(val, ptr);
> +	writeb_relaxed(val >> 8, ptr + 1);
> +	writeb_relaxed(val >> 16, ptr + 2);
> +	writeb_relaxed(val >> 24, ptr + 3);
> +}
> +
> +/*
> + * Convert to hardware format: The userspace buffer contains IEC958 samples,
> + * with the PCUV bits in bits 31..28 and audio samples in bits 27..4.  We
> + * need these to be in bits 27..24, with the IEC B bit in bit 28, and audio
> + * samples in 23..0.
> + *
> + * Default preamble in bits 3..0: 8 = block start, 4 = even 2 = odd
> + *
> + * Ideally, we could do with having the data properly formatted in userspace.
> + */
> +static void dw_hdmi_reformat_iec958(struct snd_dw_hdmi *dw,
> +	size_t offset, size_t bytes)
> +{
> +	u32 *src = dw->buf_src + offset;
> +	u32 *dst = dw->buf_dst + offset;
> +	u32 *end = dw->buf_src + offset + bytes;
> +
> +	do {
> +		u32 b, sample = *src++;
> +
> +		b = (sample & 8) << (28 - 3);
> +
> +		sample >>= 4;
> +
> +		*dst++ = sample | b;
> +	} while (src < end);
> +}
> +
> +static u32 parity(u32 sample)
> +{
> +	sample ^= sample >> 16;
> +	sample ^= sample >> 8;
> +	sample ^= sample >> 4;
> +	sample ^= sample >> 2;
> +	sample ^= sample >> 1;
> +	return (sample & 1) << 27;
> +}
> +
> +static void dw_hdmi_reformat_s24(struct snd_dw_hdmi *dw,
> +	size_t offset, size_t bytes)
> +{
> +	u32 *src = dw->buf_src + offset;
> +	u32 *dst = dw->buf_dst + offset;
> +	u32 *end = dw->buf_src + offset + bytes;
> +
> +	do {
> +		unsigned i;
> +		u8 *cs;
> +
> +		cs = dw->cs[dw->iec_offset++];
> +		if (dw->iec_offset >= 192)
> +			dw->iec_offset = 0;
> +
> +		i = dw->channels;
> +		do {
> +			u32 sample = *src++;
> +
> +			sample &= ~0xff000000;
> +			sample |= *cs++ << 24;
> +			sample |= parity(sample & ~0xf8000000);
> +
> +			*dst++ = sample;
> +		} while (--i);
> +	} while (src < end);
> +}
> +
> +static void dw_hdmi_create_cs(struct snd_dw_hdmi *dw,
> +	struct snd_pcm_runtime *runtime)
> +{
> +	u8 cs[4];
> +	unsigned ch, i, j;
> +
> +	snd_pcm_create_iec958_consumer(runtime, cs, sizeof(cs));
> +
> +	memset(dw->cs, 0, sizeof(dw->cs));
> +
> +	for (ch = 0; ch < 8; ch++) {
> +		cs[2] &= ~IEC958_AES2_CON_CHANNEL;
> +		cs[2] |= (ch + 1) << 4;
> +
> +		for (i = 0; i < ARRAY_SIZE(cs); i++) {
> +			unsigned c = cs[i];
> +
> +			for (j = 0; j < 8; j++, c >>= 1)
> +				dw->cs[i * 8 + j][ch] = (c & 1) << 2;
> +		}
> +	}
> +	dw->cs[0][0] |= BIT(4);
> +}
> +
> +static void dw_hdmi_start_dma(struct snd_dw_hdmi *dw)
> +{
> +	void __iomem *base = dw->data.base;
> +	unsigned offset = dw->buf_offset;
> +	unsigned period = dw->buf_period;
> +	u32 start, stop;
> +
> +	dw->reformat(dw, offset, period);
> +
> +	/* Clear all irqs before enabling irqs and starting DMA */
> +	writeb_relaxed(HDMI_IH_AHBDMAAUD_STAT0_ALL,
> +		       base + HDMI_IH_AHBDMAAUD_STAT0);
> +
> +	start = dw->buf_addr + offset;
> +	stop = start + period - 1;
> +
> +	/* Setup the hardware start/stop addresses */
> +	dw_hdmi_writel(start, base + HDMI_AHB_DMA_STRADDR0);
> +	dw_hdmi_writel(stop, base + HDMI_AHB_DMA_STPADDR0);
> +
> +	writeb_relaxed((u8)~HDMI_AHB_DMA_MASK_DONE, base + HDMI_AHB_DMA_MASK);
> +	writeb(HDMI_AHB_DMA_START_START, base + HDMI_AHB_DMA_START);
> +
> +	offset += period;
> +	if (offset >= dw->buf_size)
> +		offset = 0;
> +	dw->buf_offset = offset;
> +}
> +
> +static void dw_hdmi_stop_dma(struct snd_dw_hdmi *dw)
> +{
> +	dw->substream = NULL;
> +
> +	/* Disable interrupts before disabling DMA */
> +	writeb_relaxed(~0, dw->data.base + HDMI_AHB_DMA_MASK);
> +	writeb_relaxed(HDMI_AHB_DMA_STOP_STOP, dw->data.base + HDMI_AHB_DMA_STOP);
> +}
> +
> +static irqreturn_t snd_dw_hdmi_irq(int irq, void *data)
> +{
> +	struct snd_dw_hdmi *dw = data;
> +	struct snd_pcm_substream *substream;
> +	unsigned stat;
> +
> +	stat = readb_relaxed(dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
> +	if (!stat)
> +		return IRQ_NONE;
> +
> +	writeb_relaxed(stat, dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
> +
> +	substream = dw->substream;
> +	if (stat & HDMI_IH_AHBDMAAUD_STAT0_DONE && substream) {
> +		snd_pcm_period_elapsed(substream);
> +		if (dw->substream)
> +			dw_hdmi_start_dma(dw);
> +	}

Don't we need locking?  In theory, the trigger can be issued while the
irq is being handled.

> +
> +	return IRQ_HANDLED;
> +}
> +
> +static struct snd_pcm_hardware dw_hdmi_hw = {
> +	.info = SNDRV_PCM_INFO_INTERLEAVED |
> +		SNDRV_PCM_INFO_BLOCK_TRANSFER |
> +		SNDRV_PCM_INFO_MMAP |
> +		SNDRV_PCM_INFO_MMAP_VALID,
> +	.formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE |
> +		   SNDRV_PCM_FMTBIT_S24_LE,
> +	.rates = SNDRV_PCM_RATE_32000 |
> +		 SNDRV_PCM_RATE_44100 |
> +		 SNDRV_PCM_RATE_48000 |
> +		 SNDRV_PCM_RATE_88200 |
> +		 SNDRV_PCM_RATE_96000 |
> +		 SNDRV_PCM_RATE_176400 |
> +		 SNDRV_PCM_RATE_192000,
> +	.channels_min = 2,
> +	.channels_max = 8,
> +	.buffer_bytes_max = 64 * 1024,
> +	.period_bytes_min = 256,
> +	.period_bytes_max = 8192,	/* ERR004323: must limit to 8k */
> +	.periods_min = 2,
> +	.periods_max = 16,
> +	.fifo_size = 0,
> +};
> +
> +static int dw_hdmi_open(struct snd_pcm_substream *substream)
> +{
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +	struct snd_dw_hdmi *dw = substream->private_data;
> +	void __iomem *base = dw->data.base;
> +	int ret;
> +
> +	runtime->hw = dw_hdmi_hw;
> +
> +	ret = snd_pcm_limit_hw_rates(runtime);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Clear FIFO */
> +	writeb_relaxed(HDMI_AHB_DMA_CONF0_SW_FIFO_RST,
> +		       base + HDMI_AHB_DMA_CONF0);
> +
> +	/* Configure interrupt polarities */
> +	writeb_relaxed(~0, base + HDMI_AHB_DMA_POL);
> +	writeb_relaxed(~0, base + HDMI_AHB_DMA_BUFFPOL);
> +
> +	/* Keep interrupts masked, and clear any pending */
> +	writeb_relaxed(~0, base + HDMI_AHB_DMA_MASK);
> +	writeb_relaxed(~0, base + HDMI_IH_AHBDMAAUD_STAT0);
> +
> +	ret = request_irq(dw->data.irq, snd_dw_hdmi_irq, IRQF_SHARED,
> +			  "dw-hdmi-audio", dw);
> +	if (ret)
> +		return ret;
> +
> +	/* Un-mute done interrupt */
> +	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL &
> +		       ~HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE,
> +		       base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
> +
> +	return 0;
> +}
> +
> +static int dw_hdmi_close(struct snd_pcm_substream *substream)
> +{
> +	struct snd_dw_hdmi *dw = substream->private_data;
> +
> +	/* Mute all interrupts */
> +	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
> +		       dw->data.base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
> +
> +	free_irq(dw->data.irq, dw);
> +
> +	return 0;
> +}
> +
> +static int dw_hdmi_hw_free(struct snd_pcm_substream *substream)
> +{
> +	return snd_pcm_lib_free_vmalloc_buffer(substream);
> +}
> +
> +static int dw_hdmi_hw_params(struct snd_pcm_substream *substream,
> +	struct snd_pcm_hw_params *params)
> +{
> +	return snd_pcm_lib_alloc_vmalloc_buffer(substream,
> +						params_buffer_bytes(params));
> +}
> +
> +static int dw_hdmi_prepare(struct snd_pcm_substream *substream)
> +{
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +	struct snd_dw_hdmi *dw = substream->private_data;
> +	u8 threshold, conf0, conf1;
> +
> +	/* Setup as per 3.0.5 FSL 4.1.0 BSP */
> +	switch (dw->revision) {
> +	case 0x0a:
> +		conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
> +			HDMI_AHB_DMA_CONF0_INCR4;
> +		if (runtime->channels == 2)
> +			threshold = 126;
> +		else
> +			threshold = 124;
> +		break;
> +	case 0x1a:
> +		conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
> +			HDMI_AHB_DMA_CONF0_INCR8;
> +		threshold = 128;
> +		break;
> +	default:
> +		/* NOTREACHED */
> +		return -EINVAL;
> +	}
> +
> +	dw_hdmi_set_sample_rate(dw->data.hdmi, runtime->rate);
> +
> +	/* Minimum number of bytes in the fifo. */
> +	runtime->hw.fifo_size = threshold * 32;
> +
> +	conf0 |= HDMI_AHB_DMA_CONF0_EN_HLOCK;
> +	conf1 = (1 << runtime->channels) - 1;
> +
> +	writeb_relaxed(threshold, dw->data.base + HDMI_AHB_DMA_THRSLD);
> +	writeb_relaxed(conf0, dw->data.base + HDMI_AHB_DMA_CONF0);
> +	writeb_relaxed(conf1, dw->data.base + HDMI_AHB_DMA_CONF1);
> +
> +	switch (runtime->format) {
> +	case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
> +		dw->reformat = dw_hdmi_reformat_iec958;
> +		break;
> +	case SNDRV_PCM_FORMAT_S24_LE:
> +		dw_hdmi_create_cs(dw, runtime);
> +		dw->reformat = dw_hdmi_reformat_s24;
> +		break;
> +	}
> +	dw->iec_offset = 0;
> +	dw->channels = runtime->channels;
> +	dw->buf_src  = runtime->dma_area;
> +	dw->buf_dst  = substream->dma_buffer.area;
> +	dw->buf_addr = substream->dma_buffer.addr;
> +	dw->buf_period = snd_pcm_lib_period_bytes(substream);
> +	dw->buf_size = snd_pcm_lib_buffer_bytes(substream);
> +
> +	return 0;
> +}
> +
> +static int dw_hdmi_trigger(struct snd_pcm_substream *substream, int cmd)
> +{
> +	struct snd_dw_hdmi *dw = substream->private_data;
> +	int ret = 0;
> +
> +	switch (cmd) {
> +	case SNDRV_PCM_TRIGGER_START:
> +		dw->buf_offset = 0;
> +		dw->substream = substream;
> +		dw_hdmi_start_dma(dw);
> +		dw_hdmi_audio_enable(dw->data.hdmi);
> +		substream->runtime->delay = substream->runtime->period_size;
> +		break;
> +
> +	case SNDRV_PCM_TRIGGER_STOP:
> +		dw_hdmi_stop_dma(dw);
> +		dw_hdmi_audio_disable(dw->data.hdmi);
> +		break;
> +
> +	default:
> +		ret = -EINVAL;
> +		break;

SNDRV_PCM_TRIGGER_SUSPEND may be passed at suspend, too.


> +	}
> +
> +	return ret;
> +}
> +
> +static snd_pcm_uframes_t dw_hdmi_pointer(struct snd_pcm_substream *substream)
> +{
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +	struct snd_dw_hdmi *dw = substream->private_data;
> +
> +	return bytes_to_frames(runtime, dw->buf_offset);

So, this returns the offset that has been reformatted.  Does the
hardware support any better position reporting?  We may give the delay
from the driver if possible.


thanks,

Takashi


> +}
> +
> +static struct snd_pcm_ops snd_dw_hdmi_ops = {
> +	.open = dw_hdmi_open,
> +	.close = dw_hdmi_close,
> +	.ioctl = snd_pcm_lib_ioctl,
> +	.hw_params = dw_hdmi_hw_params,
> +	.hw_free = dw_hdmi_hw_free,
> +	.prepare = dw_hdmi_prepare,
> +	.trigger = dw_hdmi_trigger,
> +	.pointer = dw_hdmi_pointer,
> +	.page = snd_pcm_lib_get_vmalloc_page,
> +};
> +
> +static int snd_dw_hdmi_probe(struct platform_device *pdev)
> +{
> +	const struct dw_hdmi_audio_data *data = pdev->dev.platform_data;
> +	struct device *dev = pdev->dev.parent;
> +	struct snd_dw_hdmi *dw;
> +	struct snd_card *card;
> +	struct snd_pcm *pcm;
> +	unsigned revision;
> +	int ret;
> +
> +	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
> +		       data->base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
> +	revision = readb_relaxed(data->base + HDMI_REVISION_ID);
> +	if (revision != 0x0a && revision != 0x1a) {
> +		dev_err(dev, "dw-hdmi-audio: unknown revision 0x%02x\n",
> +			revision);
> +		return -ENXIO;
> +	}
> +
> +	ret = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
> +			      THIS_MODULE, sizeof(struct snd_dw_hdmi), &card);
> +	if (ret < 0)
> +		return ret;
> +
> +	strlcpy(card->driver, DRIVER_NAME, sizeof(card->driver));
> +	strlcpy(card->shortname, "DW-HDMI", sizeof(card->shortname));
> +	snprintf(card->longname, sizeof(card->longname),
> +		 "%s rev 0x%02x, irq %d", card->shortname, revision,
> +		 data->irq);
> +
> +	dw = card->private_data;
> +	dw->card = card;
> +	dw->data = *data;
> +	dw->revision = revision;
> +
> +	ret = snd_pcm_new(card, "DW HDMI", 0, 1, 0, &pcm);
> +	if (ret < 0)
> +		goto err;
> +
> +	dw->pcm = pcm;
> +	pcm->private_data = dw;
> +	strlcpy(pcm->name, DRIVER_NAME, sizeof(pcm->name));
> +	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_dw_hdmi_ops);
> +
> +	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
> +			dev, 64 * 1024, 64 * 1024);
> +
> +	ret = snd_card_register(card);
> +	if (ret < 0)
> +		goto err;
> +
> +	platform_set_drvdata(pdev, dw);
> +
> +	return 0;
> +
> +err:
> +	snd_card_free(card);
> +	return ret;
> +}
> +
> +static int snd_dw_hdmi_remove(struct platform_device *pdev)
> +{
> +	struct snd_dw_hdmi *dw = platform_get_drvdata(pdev);
> +
> +	snd_card_free(dw->card);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int snd_dw_hdmi_suspend(struct device *dev)
> +{
> +	struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
> +
> +	snd_power_change_state(dw->card, SNDRV_CTL_POWER_D3cold);
> +	snd_pcm_suspend_all(dw->pcm);
> +
> +	return 0;
> +}
> +
> +static int snd_dw_hdmi_resume(struct device *dev)
> +{
> +	struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
> +
> +	snd_power_change_state(dw->card, SNDRV_CTL_POWER_D0);
> +
> +	return 0;
> +}
> +
> +static SIMPLE_DEV_PM_OPS(snd_dw_hdmi_pm, snd_dw_hdmi_suspend,
> +			 snd_dw_hdmi_resume);
> +#define PM_OPS &snd_dw_hdmi_pm
> +#else
> +#define PM_OPS NULL
> +#endif
> +
> +static struct platform_driver snd_dw_hdmi_driver = {
> +	.probe	= snd_dw_hdmi_probe,
> +	.remove	= snd_dw_hdmi_remove,
> +	.driver	= {
> +		.name = DRIVER_NAME,
> +		.owner = THIS_MODULE,
> +		.pm = PM_OPS,
> +	},
> +};
> +
> +module_platform_driver(snd_dw_hdmi_driver);
> +
> +MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>");
> +MODULE_DESCRIPTION("Synopsis Designware HDMI AHB ALSA interface");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:" DRIVER_NAME);
> diff --git a/drivers/gpu/drm/bridge/dw_hdmi-audio.h b/drivers/gpu/drm/bridge/dw_hdmi-audio.h
> new file mode 100644
> index 000000000000..1e840118d90a
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/dw_hdmi-audio.h
> @@ -0,0 +1,13 @@
> +#ifndef DW_HDMI_AUDIO_H
> +#define DW_HDMI_AUDIO_H
> +
> +struct dw_hdmi;
> +
> +struct dw_hdmi_audio_data {
> +	phys_addr_t phys;
> +	void __iomem *base;
> +	int irq;
> +	struct dw_hdmi *hdmi;
> +};
> +
> +#endif
> diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
> index fba25607ef88..b65464789fbd 100644
> --- a/drivers/gpu/drm/bridge/dw_hdmi.c
> +++ b/drivers/gpu/drm/bridge/dw_hdmi.c
> @@ -28,6 +28,7 @@
>  #include <drm/bridge/dw_hdmi.h>
>  
>  #include "dw_hdmi.h"
> +#include "dw_hdmi-audio.h"
>  
>  #define HDMI_EDID_LEN		512
>  
> @@ -104,6 +105,7 @@ struct dw_hdmi {
>  	struct drm_encoder *encoder;
>  	struct drm_bridge *bridge;
>  
> +	struct platform_device *audio;
>  	enum dw_hdmi_devtype dev_type;
>  	struct device *dev;
>  	struct clk *isfr_clk;
> @@ -1732,7 +1734,9 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
>  {
>  	struct drm_device *drm = data;
>  	struct device_node *np = dev->of_node;
> +	struct platform_device_info pdevinfo;
>  	struct device_node *ddc_node;
> +	struct dw_hdmi_audio_data audio;
>  	struct dw_hdmi *hdmi;
>  	int ret;
>  	u32 val = 1;
> @@ -1860,6 +1864,23 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
>  	hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE),
>  		    HDMI_IH_MUTE_PHY_STAT0);
>  
> +	memset(&pdevinfo, 0, sizeof(pdevinfo));
> +	pdevinfo.parent = dev;
> +	pdevinfo.id = PLATFORM_DEVID_AUTO;
> +
> +	if (hdmi_readb(hdmi, HDMI_CONFIG1_ID) & HDMI_CONFIG1_AHB) {
> +		audio.phys = iores->start;
> +		audio.base = hdmi->regs;
> +		audio.irq = irq;
> +		audio.hdmi = hdmi;
> +
> +		pdevinfo.name = "dw-hdmi-ahb-audio";
> +		pdevinfo.data = &audio;
> +		pdevinfo.size_data = sizeof(audio);
> +		pdevinfo.dma_mask = DMA_BIT_MASK(32);
> +		hdmi->audio = platform_device_register_full(&pdevinfo);
> +	}
> +
>  	dev_set_drvdata(dev, hdmi);
>  
>  	return 0;
> @@ -1877,6 +1898,9 @@ void dw_hdmi_unbind(struct device *dev, struct device *master, void *data)
>  {
>  	struct dw_hdmi *hdmi = dev_get_drvdata(dev);
>  
> +	if (hdmi->audio && !IS_ERR(hdmi->audio))
> +		platform_device_unregister(hdmi->audio);
> +
>  	/* Disable all interrupts */
>  	hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
>  
> diff --git a/drivers/gpu/drm/bridge/dw_hdmi.h b/drivers/gpu/drm/bridge/dw_hdmi.h
> index 175dbc89a824..78e54e813212 100644
> --- a/drivers/gpu/drm/bridge/dw_hdmi.h
> +++ b/drivers/gpu/drm/bridge/dw_hdmi.h
> @@ -545,6 +545,9 @@
>  #define HDMI_I2CM_FS_SCL_LCNT_0_ADDR            0x7E12
>  
>  enum {
> +/* CONFIG1_ID field values */
> +	HDMI_CONFIG1_AHB = 0x01,
> +
>  /* IH_FC_INT2 field values */
>  	HDMI_IH_FC_INT2_OVERFLOW_MASK = 0x03,
>  	HDMI_IH_FC_INT2_LOW_PRIORITY_OVERFLOW = 0x02,
> -- 
> 2.1.0
> 

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver
@ 2015-08-10 10:05       ` Takashi Iwai
  0 siblings, 0 replies; 226+ messages in thread
From: Takashi Iwai @ 2015-08-10 10:05 UTC (permalink / raw)
  To: Russell King
  Cc: Fabio Estevam, alsa-devel, linux-kernel, dri-devel,
	Jaroslav Kysela, linux-rockchip, Mark Brown, Yakir Yang,
	Andy Yan, linux-arm-kernel

On Sat, 08 Aug 2015 18:10:06 +0200,
Russell King wrote:
> 
> Add ALSA based HDMI AHB audio driver for dw_hdmi.  The only buffer
> format supported by the hardware is its own special IEC958 based format,
> which is not compatible with any ALSA format.  To avoid doing too much
> data manipulation within the driver, we support only ALSAs IEC958 LE and
> 24-bit PCM formats for 2 to 6 channels, which we convert to its hardware
> format.
> 
> A more desirable solution would be to have this conversion in userspace,
> but ALSA does not appear to allow such transformations outside of
> libasound itself.
> 
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> ---
>  drivers/gpu/drm/bridge/Kconfig             |  10 +
>  drivers/gpu/drm/bridge/Makefile            |   1 +
>  drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c | 561 +++++++++++++++++++++++++++++
>  drivers/gpu/drm/bridge/dw_hdmi-audio.h     |  13 +
>  drivers/gpu/drm/bridge/dw_hdmi.c           |  24 ++
>  drivers/gpu/drm/bridge/dw_hdmi.h           |   3 +
>  6 files changed, 612 insertions(+)
>  create mode 100644 drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
>  create mode 100644 drivers/gpu/drm/bridge/dw_hdmi-audio.h
> 
> diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
> index acef3223772c..56ed35fe0734 100644
> --- a/drivers/gpu/drm/bridge/Kconfig
> +++ b/drivers/gpu/drm/bridge/Kconfig
> @@ -3,6 +3,16 @@ config DRM_DW_HDMI
>  	depends on DRM
>  	select DRM_KMS_HELPER
>  
> +config DRM_DW_HDMI_AHB_AUDIO
> +	tristate "Synopsis Designware AHB Audio interface"
> +	depends on DRM_DW_HDMI && SND
> +	select SND_PCM
> +	select SND_PCM_IEC958
> +	help
> +	  Support the AHB Audio interface which is part of the Synopsis
> +	  Designware HDMI block.  This is used in conjunction with
> +	  the i.MX6 HDMI driver.
> +
>  config DRM_PTN3460
>  	tristate "PTN3460 DP/LVDS bridge"
>  	depends on DRM
> diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
> index 8dfebd984370..eb80dbbb8365 100644
> --- a/drivers/gpu/drm/bridge/Makefile
> +++ b/drivers/gpu/drm/bridge/Makefile
> @@ -3,3 +3,4 @@ ccflags-y := -Iinclude/drm
>  obj-$(CONFIG_DRM_PS8622) += ps8622.o
>  obj-$(CONFIG_DRM_PTN3460) += ptn3460.o
>  obj-$(CONFIG_DRM_DW_HDMI) += dw_hdmi.o
> +obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw_hdmi-ahb-audio.o
> diff --git a/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
> new file mode 100644
> index 000000000000..22bbbc5c2393
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
> @@ -0,0 +1,561 @@
> +/*
> + * DesignWare HDMI audio driver
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * Written and tested against the Designware HDMI Tx found in iMX6.
> + */
> +#include <linux/io.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <drm/bridge/dw_hdmi.h>
> +
> +#include <sound/asoundef.h>
> +#include <sound/core.h>
> +#include <sound/initval.h>
> +#include <sound/pcm.h>
> +#include <sound/pcm_iec958.h>
> +
> +#include "dw_hdmi-audio.h"
> +
> +#define DRIVER_NAME "dw-hdmi-ahb-audio"
> +
> +/* Provide some bits rather than bit offsets */
> +enum {
> +	HDMI_AHB_DMA_CONF0_SW_FIFO_RST = BIT(7),
> +	HDMI_AHB_DMA_CONF0_EN_HLOCK = BIT(3),
> +	HDMI_AHB_DMA_START_START = BIT(0),
> +	HDMI_AHB_DMA_STOP_STOP = BIT(0),
> +	HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR = BIT(5),
> +	HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST = BIT(4),
> +	HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY = BIT(3),
> +	HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE = BIT(2),
> +	HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL = BIT(1),
> +	HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0),
> +	HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL =
> +		HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR |
> +		HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST |
> +		HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY |
> +		HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE |
> +		HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL |
> +		HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY,
> +	HDMI_IH_AHBDMAAUD_STAT0_ERROR = BIT(5),
> +	HDMI_IH_AHBDMAAUD_STAT0_LOST = BIT(4),
> +	HDMI_IH_AHBDMAAUD_STAT0_RETRY = BIT(3),
> +	HDMI_IH_AHBDMAAUD_STAT0_DONE = BIT(2),
> +	HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL = BIT(1),
> +	HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0),
> +	HDMI_IH_AHBDMAAUD_STAT0_ALL =
> +		HDMI_IH_AHBDMAAUD_STAT0_ERROR |
> +		HDMI_IH_AHBDMAAUD_STAT0_LOST |
> +		HDMI_IH_AHBDMAAUD_STAT0_RETRY |
> +		HDMI_IH_AHBDMAAUD_STAT0_DONE |
> +		HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL |
> +		HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY,
> +	HDMI_AHB_DMA_CONF0_INCR16 = 2 << 1,
> +	HDMI_AHB_DMA_CONF0_INCR8 = 1 << 1,
> +	HDMI_AHB_DMA_CONF0_INCR4 = 0,
> +	HDMI_AHB_DMA_CONF0_BURST_MODE = BIT(0),
> +	HDMI_AHB_DMA_MASK_DONE = BIT(7),
> +	HDMI_REVISION_ID = 0x0001,
> +	HDMI_IH_AHBDMAAUD_STAT0 = 0x0109,
> +	HDMI_IH_MUTE_AHBDMAAUD_STAT0 = 0x0189,
> +	HDMI_AHB_DMA_CONF0 = 0x3600,
> +	HDMI_AHB_DMA_START = 0x3601,
> +	HDMI_AHB_DMA_STOP = 0x3602,
> +	HDMI_AHB_DMA_THRSLD = 0x3603,
> +	HDMI_AHB_DMA_STRADDR0 = 0x3604,
> +	HDMI_AHB_DMA_STPADDR0 = 0x3608,
> +	HDMI_AHB_DMA_MASK = 0x3614,
> +	HDMI_AHB_DMA_POL = 0x3615,
> +	HDMI_AHB_DMA_CONF1 = 0x3616,
> +	HDMI_AHB_DMA_BUFFPOL = 0x361a,
> +};
> +
> +struct snd_dw_hdmi {
> +	struct snd_card *card;
> +	struct snd_pcm *pcm;
> +	struct dw_hdmi_audio_data data;
> +	struct snd_pcm_substream *substream;
> +	void (*reformat)(struct snd_dw_hdmi *, size_t, size_t);
> +	void *buf_src;
> +	void *buf_dst;
> +	dma_addr_t buf_addr;
> +	unsigned buf_offset;
> +	unsigned buf_period;
> +	unsigned buf_size;
> +	unsigned channels;
> +	u8 revision;
> +	u8 iec_offset;
> +	u8 cs[192][8];
> +};
> +
> +static void dw_hdmi_writel(unsigned long val, void __iomem *ptr)

Better to be u32 instead of unsigned long in general.

> +{
> +	writeb_relaxed(val, ptr);
> +	writeb_relaxed(val >> 8, ptr + 1);
> +	writeb_relaxed(val >> 16, ptr + 2);
> +	writeb_relaxed(val >> 24, ptr + 3);
> +}
> +
> +/*
> + * Convert to hardware format: The userspace buffer contains IEC958 samples,
> + * with the PCUV bits in bits 31..28 and audio samples in bits 27..4.  We
> + * need these to be in bits 27..24, with the IEC B bit in bit 28, and audio
> + * samples in 23..0.
> + *
> + * Default preamble in bits 3..0: 8 = block start, 4 = even 2 = odd
> + *
> + * Ideally, we could do with having the data properly formatted in userspace.
> + */
> +static void dw_hdmi_reformat_iec958(struct snd_dw_hdmi *dw,
> +	size_t offset, size_t bytes)
> +{
> +	u32 *src = dw->buf_src + offset;
> +	u32 *dst = dw->buf_dst + offset;
> +	u32 *end = dw->buf_src + offset + bytes;
> +
> +	do {
> +		u32 b, sample = *src++;
> +
> +		b = (sample & 8) << (28 - 3);
> +
> +		sample >>= 4;
> +
> +		*dst++ = sample | b;
> +	} while (src < end);
> +}
> +
> +static u32 parity(u32 sample)
> +{
> +	sample ^= sample >> 16;
> +	sample ^= sample >> 8;
> +	sample ^= sample >> 4;
> +	sample ^= sample >> 2;
> +	sample ^= sample >> 1;
> +	return (sample & 1) << 27;
> +}
> +
> +static void dw_hdmi_reformat_s24(struct snd_dw_hdmi *dw,
> +	size_t offset, size_t bytes)
> +{
> +	u32 *src = dw->buf_src + offset;
> +	u32 *dst = dw->buf_dst + offset;
> +	u32 *end = dw->buf_src + offset + bytes;
> +
> +	do {
> +		unsigned i;
> +		u8 *cs;
> +
> +		cs = dw->cs[dw->iec_offset++];
> +		if (dw->iec_offset >= 192)
> +			dw->iec_offset = 0;
> +
> +		i = dw->channels;
> +		do {
> +			u32 sample = *src++;
> +
> +			sample &= ~0xff000000;
> +			sample |= *cs++ << 24;
> +			sample |= parity(sample & ~0xf8000000);
> +
> +			*dst++ = sample;
> +		} while (--i);
> +	} while (src < end);
> +}
> +
> +static void dw_hdmi_create_cs(struct snd_dw_hdmi *dw,
> +	struct snd_pcm_runtime *runtime)
> +{
> +	u8 cs[4];
> +	unsigned ch, i, j;
> +
> +	snd_pcm_create_iec958_consumer(runtime, cs, sizeof(cs));
> +
> +	memset(dw->cs, 0, sizeof(dw->cs));
> +
> +	for (ch = 0; ch < 8; ch++) {
> +		cs[2] &= ~IEC958_AES2_CON_CHANNEL;
> +		cs[2] |= (ch + 1) << 4;
> +
> +		for (i = 0; i < ARRAY_SIZE(cs); i++) {
> +			unsigned c = cs[i];
> +
> +			for (j = 0; j < 8; j++, c >>= 1)
> +				dw->cs[i * 8 + j][ch] = (c & 1) << 2;
> +		}
> +	}
> +	dw->cs[0][0] |= BIT(4);
> +}
> +
> +static void dw_hdmi_start_dma(struct snd_dw_hdmi *dw)
> +{
> +	void __iomem *base = dw->data.base;
> +	unsigned offset = dw->buf_offset;
> +	unsigned period = dw->buf_period;
> +	u32 start, stop;
> +
> +	dw->reformat(dw, offset, period);
> +
> +	/* Clear all irqs before enabling irqs and starting DMA */
> +	writeb_relaxed(HDMI_IH_AHBDMAAUD_STAT0_ALL,
> +		       base + HDMI_IH_AHBDMAAUD_STAT0);
> +
> +	start = dw->buf_addr + offset;
> +	stop = start + period - 1;
> +
> +	/* Setup the hardware start/stop addresses */
> +	dw_hdmi_writel(start, base + HDMI_AHB_DMA_STRADDR0);
> +	dw_hdmi_writel(stop, base + HDMI_AHB_DMA_STPADDR0);
> +
> +	writeb_relaxed((u8)~HDMI_AHB_DMA_MASK_DONE, base + HDMI_AHB_DMA_MASK);
> +	writeb(HDMI_AHB_DMA_START_START, base + HDMI_AHB_DMA_START);
> +
> +	offset += period;
> +	if (offset >= dw->buf_size)
> +		offset = 0;
> +	dw->buf_offset = offset;
> +}
> +
> +static void dw_hdmi_stop_dma(struct snd_dw_hdmi *dw)
> +{
> +	dw->substream = NULL;
> +
> +	/* Disable interrupts before disabling DMA */
> +	writeb_relaxed(~0, dw->data.base + HDMI_AHB_DMA_MASK);
> +	writeb_relaxed(HDMI_AHB_DMA_STOP_STOP, dw->data.base + HDMI_AHB_DMA_STOP);
> +}
> +
> +static irqreturn_t snd_dw_hdmi_irq(int irq, void *data)
> +{
> +	struct snd_dw_hdmi *dw = data;
> +	struct snd_pcm_substream *substream;
> +	unsigned stat;
> +
> +	stat = readb_relaxed(dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
> +	if (!stat)
> +		return IRQ_NONE;
> +
> +	writeb_relaxed(stat, dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
> +
> +	substream = dw->substream;
> +	if (stat & HDMI_IH_AHBDMAAUD_STAT0_DONE && substream) {
> +		snd_pcm_period_elapsed(substream);
> +		if (dw->substream)
> +			dw_hdmi_start_dma(dw);
> +	}

Don't we need locking?  In theory, the trigger can be issued while the
irq is being handled.

> +
> +	return IRQ_HANDLED;
> +}
> +
> +static struct snd_pcm_hardware dw_hdmi_hw = {
> +	.info = SNDRV_PCM_INFO_INTERLEAVED |
> +		SNDRV_PCM_INFO_BLOCK_TRANSFER |
> +		SNDRV_PCM_INFO_MMAP |
> +		SNDRV_PCM_INFO_MMAP_VALID,
> +	.formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE |
> +		   SNDRV_PCM_FMTBIT_S24_LE,
> +	.rates = SNDRV_PCM_RATE_32000 |
> +		 SNDRV_PCM_RATE_44100 |
> +		 SNDRV_PCM_RATE_48000 |
> +		 SNDRV_PCM_RATE_88200 |
> +		 SNDRV_PCM_RATE_96000 |
> +		 SNDRV_PCM_RATE_176400 |
> +		 SNDRV_PCM_RATE_192000,
> +	.channels_min = 2,
> +	.channels_max = 8,
> +	.buffer_bytes_max = 64 * 1024,
> +	.period_bytes_min = 256,
> +	.period_bytes_max = 8192,	/* ERR004323: must limit to 8k */
> +	.periods_min = 2,
> +	.periods_max = 16,
> +	.fifo_size = 0,
> +};
> +
> +static int dw_hdmi_open(struct snd_pcm_substream *substream)
> +{
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +	struct snd_dw_hdmi *dw = substream->private_data;
> +	void __iomem *base = dw->data.base;
> +	int ret;
> +
> +	runtime->hw = dw_hdmi_hw;
> +
> +	ret = snd_pcm_limit_hw_rates(runtime);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Clear FIFO */
> +	writeb_relaxed(HDMI_AHB_DMA_CONF0_SW_FIFO_RST,
> +		       base + HDMI_AHB_DMA_CONF0);
> +
> +	/* Configure interrupt polarities */
> +	writeb_relaxed(~0, base + HDMI_AHB_DMA_POL);
> +	writeb_relaxed(~0, base + HDMI_AHB_DMA_BUFFPOL);
> +
> +	/* Keep interrupts masked, and clear any pending */
> +	writeb_relaxed(~0, base + HDMI_AHB_DMA_MASK);
> +	writeb_relaxed(~0, base + HDMI_IH_AHBDMAAUD_STAT0);
> +
> +	ret = request_irq(dw->data.irq, snd_dw_hdmi_irq, IRQF_SHARED,
> +			  "dw-hdmi-audio", dw);
> +	if (ret)
> +		return ret;
> +
> +	/* Un-mute done interrupt */
> +	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL &
> +		       ~HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE,
> +		       base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
> +
> +	return 0;
> +}
> +
> +static int dw_hdmi_close(struct snd_pcm_substream *substream)
> +{
> +	struct snd_dw_hdmi *dw = substream->private_data;
> +
> +	/* Mute all interrupts */
> +	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
> +		       dw->data.base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
> +
> +	free_irq(dw->data.irq, dw);
> +
> +	return 0;
> +}
> +
> +static int dw_hdmi_hw_free(struct snd_pcm_substream *substream)
> +{
> +	return snd_pcm_lib_free_vmalloc_buffer(substream);
> +}
> +
> +static int dw_hdmi_hw_params(struct snd_pcm_substream *substream,
> +	struct snd_pcm_hw_params *params)
> +{
> +	return snd_pcm_lib_alloc_vmalloc_buffer(substream,
> +						params_buffer_bytes(params));
> +}
> +
> +static int dw_hdmi_prepare(struct snd_pcm_substream *substream)
> +{
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +	struct snd_dw_hdmi *dw = substream->private_data;
> +	u8 threshold, conf0, conf1;
> +
> +	/* Setup as per 3.0.5 FSL 4.1.0 BSP */
> +	switch (dw->revision) {
> +	case 0x0a:
> +		conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
> +			HDMI_AHB_DMA_CONF0_INCR4;
> +		if (runtime->channels == 2)
> +			threshold = 126;
> +		else
> +			threshold = 124;
> +		break;
> +	case 0x1a:
> +		conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
> +			HDMI_AHB_DMA_CONF0_INCR8;
> +		threshold = 128;
> +		break;
> +	default:
> +		/* NOTREACHED */
> +		return -EINVAL;
> +	}
> +
> +	dw_hdmi_set_sample_rate(dw->data.hdmi, runtime->rate);
> +
> +	/* Minimum number of bytes in the fifo. */
> +	runtime->hw.fifo_size = threshold * 32;
> +
> +	conf0 |= HDMI_AHB_DMA_CONF0_EN_HLOCK;
> +	conf1 = (1 << runtime->channels) - 1;
> +
> +	writeb_relaxed(threshold, dw->data.base + HDMI_AHB_DMA_THRSLD);
> +	writeb_relaxed(conf0, dw->data.base + HDMI_AHB_DMA_CONF0);
> +	writeb_relaxed(conf1, dw->data.base + HDMI_AHB_DMA_CONF1);
> +
> +	switch (runtime->format) {
> +	case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
> +		dw->reformat = dw_hdmi_reformat_iec958;
> +		break;
> +	case SNDRV_PCM_FORMAT_S24_LE:
> +		dw_hdmi_create_cs(dw, runtime);
> +		dw->reformat = dw_hdmi_reformat_s24;
> +		break;
> +	}
> +	dw->iec_offset = 0;
> +	dw->channels = runtime->channels;
> +	dw->buf_src  = runtime->dma_area;
> +	dw->buf_dst  = substream->dma_buffer.area;
> +	dw->buf_addr = substream->dma_buffer.addr;
> +	dw->buf_period = snd_pcm_lib_period_bytes(substream);
> +	dw->buf_size = snd_pcm_lib_buffer_bytes(substream);
> +
> +	return 0;
> +}
> +
> +static int dw_hdmi_trigger(struct snd_pcm_substream *substream, int cmd)
> +{
> +	struct snd_dw_hdmi *dw = substream->private_data;
> +	int ret = 0;
> +
> +	switch (cmd) {
> +	case SNDRV_PCM_TRIGGER_START:
> +		dw->buf_offset = 0;
> +		dw->substream = substream;
> +		dw_hdmi_start_dma(dw);
> +		dw_hdmi_audio_enable(dw->data.hdmi);
> +		substream->runtime->delay = substream->runtime->period_size;
> +		break;
> +
> +	case SNDRV_PCM_TRIGGER_STOP:
> +		dw_hdmi_stop_dma(dw);
> +		dw_hdmi_audio_disable(dw->data.hdmi);
> +		break;
> +
> +	default:
> +		ret = -EINVAL;
> +		break;

SNDRV_PCM_TRIGGER_SUSPEND may be passed at suspend, too.


> +	}
> +
> +	return ret;
> +}
> +
> +static snd_pcm_uframes_t dw_hdmi_pointer(struct snd_pcm_substream *substream)
> +{
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +	struct snd_dw_hdmi *dw = substream->private_data;
> +
> +	return bytes_to_frames(runtime, dw->buf_offset);

So, this returns the offset that has been reformatted.  Does the
hardware support any better position reporting?  We may give the delay
from the driver if possible.


thanks,

Takashi


> +}
> +
> +static struct snd_pcm_ops snd_dw_hdmi_ops = {
> +	.open = dw_hdmi_open,
> +	.close = dw_hdmi_close,
> +	.ioctl = snd_pcm_lib_ioctl,
> +	.hw_params = dw_hdmi_hw_params,
> +	.hw_free = dw_hdmi_hw_free,
> +	.prepare = dw_hdmi_prepare,
> +	.trigger = dw_hdmi_trigger,
> +	.pointer = dw_hdmi_pointer,
> +	.page = snd_pcm_lib_get_vmalloc_page,
> +};
> +
> +static int snd_dw_hdmi_probe(struct platform_device *pdev)
> +{
> +	const struct dw_hdmi_audio_data *data = pdev->dev.platform_data;
> +	struct device *dev = pdev->dev.parent;
> +	struct snd_dw_hdmi *dw;
> +	struct snd_card *card;
> +	struct snd_pcm *pcm;
> +	unsigned revision;
> +	int ret;
> +
> +	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
> +		       data->base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
> +	revision = readb_relaxed(data->base + HDMI_REVISION_ID);
> +	if (revision != 0x0a && revision != 0x1a) {
> +		dev_err(dev, "dw-hdmi-audio: unknown revision 0x%02x\n",
> +			revision);
> +		return -ENXIO;
> +	}
> +
> +	ret = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
> +			      THIS_MODULE, sizeof(struct snd_dw_hdmi), &card);
> +	if (ret < 0)
> +		return ret;
> +
> +	strlcpy(card->driver, DRIVER_NAME, sizeof(card->driver));
> +	strlcpy(card->shortname, "DW-HDMI", sizeof(card->shortname));
> +	snprintf(card->longname, sizeof(card->longname),
> +		 "%s rev 0x%02x, irq %d", card->shortname, revision,
> +		 data->irq);
> +
> +	dw = card->private_data;
> +	dw->card = card;
> +	dw->data = *data;
> +	dw->revision = revision;
> +
> +	ret = snd_pcm_new(card, "DW HDMI", 0, 1, 0, &pcm);
> +	if (ret < 0)
> +		goto err;
> +
> +	dw->pcm = pcm;
> +	pcm->private_data = dw;
> +	strlcpy(pcm->name, DRIVER_NAME, sizeof(pcm->name));
> +	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_dw_hdmi_ops);
> +
> +	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
> +			dev, 64 * 1024, 64 * 1024);
> +
> +	ret = snd_card_register(card);
> +	if (ret < 0)
> +		goto err;
> +
> +	platform_set_drvdata(pdev, dw);
> +
> +	return 0;
> +
> +err:
> +	snd_card_free(card);
> +	return ret;
> +}
> +
> +static int snd_dw_hdmi_remove(struct platform_device *pdev)
> +{
> +	struct snd_dw_hdmi *dw = platform_get_drvdata(pdev);
> +
> +	snd_card_free(dw->card);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int snd_dw_hdmi_suspend(struct device *dev)
> +{
> +	struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
> +
> +	snd_power_change_state(dw->card, SNDRV_CTL_POWER_D3cold);
> +	snd_pcm_suspend_all(dw->pcm);
> +
> +	return 0;
> +}
> +
> +static int snd_dw_hdmi_resume(struct device *dev)
> +{
> +	struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
> +
> +	snd_power_change_state(dw->card, SNDRV_CTL_POWER_D0);
> +
> +	return 0;
> +}
> +
> +static SIMPLE_DEV_PM_OPS(snd_dw_hdmi_pm, snd_dw_hdmi_suspend,
> +			 snd_dw_hdmi_resume);
> +#define PM_OPS &snd_dw_hdmi_pm
> +#else
> +#define PM_OPS NULL
> +#endif
> +
> +static struct platform_driver snd_dw_hdmi_driver = {
> +	.probe	= snd_dw_hdmi_probe,
> +	.remove	= snd_dw_hdmi_remove,
> +	.driver	= {
> +		.name = DRIVER_NAME,
> +		.owner = THIS_MODULE,
> +		.pm = PM_OPS,
> +	},
> +};
> +
> +module_platform_driver(snd_dw_hdmi_driver);
> +
> +MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>");
> +MODULE_DESCRIPTION("Synopsis Designware HDMI AHB ALSA interface");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:" DRIVER_NAME);
> diff --git a/drivers/gpu/drm/bridge/dw_hdmi-audio.h b/drivers/gpu/drm/bridge/dw_hdmi-audio.h
> new file mode 100644
> index 000000000000..1e840118d90a
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/dw_hdmi-audio.h
> @@ -0,0 +1,13 @@
> +#ifndef DW_HDMI_AUDIO_H
> +#define DW_HDMI_AUDIO_H
> +
> +struct dw_hdmi;
> +
> +struct dw_hdmi_audio_data {
> +	phys_addr_t phys;
> +	void __iomem *base;
> +	int irq;
> +	struct dw_hdmi *hdmi;
> +};
> +
> +#endif
> diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
> index fba25607ef88..b65464789fbd 100644
> --- a/drivers/gpu/drm/bridge/dw_hdmi.c
> +++ b/drivers/gpu/drm/bridge/dw_hdmi.c
> @@ -28,6 +28,7 @@
>  #include <drm/bridge/dw_hdmi.h>
>  
>  #include "dw_hdmi.h"
> +#include "dw_hdmi-audio.h"
>  
>  #define HDMI_EDID_LEN		512
>  
> @@ -104,6 +105,7 @@ struct dw_hdmi {
>  	struct drm_encoder *encoder;
>  	struct drm_bridge *bridge;
>  
> +	struct platform_device *audio;
>  	enum dw_hdmi_devtype dev_type;
>  	struct device *dev;
>  	struct clk *isfr_clk;
> @@ -1732,7 +1734,9 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
>  {
>  	struct drm_device *drm = data;
>  	struct device_node *np = dev->of_node;
> +	struct platform_device_info pdevinfo;
>  	struct device_node *ddc_node;
> +	struct dw_hdmi_audio_data audio;
>  	struct dw_hdmi *hdmi;
>  	int ret;
>  	u32 val = 1;
> @@ -1860,6 +1864,23 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
>  	hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE),
>  		    HDMI_IH_MUTE_PHY_STAT0);
>  
> +	memset(&pdevinfo, 0, sizeof(pdevinfo));
> +	pdevinfo.parent = dev;
> +	pdevinfo.id = PLATFORM_DEVID_AUTO;
> +
> +	if (hdmi_readb(hdmi, HDMI_CONFIG1_ID) & HDMI_CONFIG1_AHB) {
> +		audio.phys = iores->start;
> +		audio.base = hdmi->regs;
> +		audio.irq = irq;
> +		audio.hdmi = hdmi;
> +
> +		pdevinfo.name = "dw-hdmi-ahb-audio";
> +		pdevinfo.data = &audio;
> +		pdevinfo.size_data = sizeof(audio);
> +		pdevinfo.dma_mask = DMA_BIT_MASK(32);
> +		hdmi->audio = platform_device_register_full(&pdevinfo);
> +	}
> +
>  	dev_set_drvdata(dev, hdmi);
>  
>  	return 0;
> @@ -1877,6 +1898,9 @@ void dw_hdmi_unbind(struct device *dev, struct device *master, void *data)
>  {
>  	struct dw_hdmi *hdmi = dev_get_drvdata(dev);
>  
> +	if (hdmi->audio && !IS_ERR(hdmi->audio))
> +		platform_device_unregister(hdmi->audio);
> +
>  	/* Disable all interrupts */
>  	hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
>  
> diff --git a/drivers/gpu/drm/bridge/dw_hdmi.h b/drivers/gpu/drm/bridge/dw_hdmi.h
> index 175dbc89a824..78e54e813212 100644
> --- a/drivers/gpu/drm/bridge/dw_hdmi.h
> +++ b/drivers/gpu/drm/bridge/dw_hdmi.h
> @@ -545,6 +545,9 @@
>  #define HDMI_I2CM_FS_SCL_LCNT_0_ADDR            0x7E12
>  
>  enum {
> +/* CONFIG1_ID field values */
> +	HDMI_CONFIG1_AHB = 0x01,
> +
>  /* IH_FC_INT2 field values */
>  	HDMI_IH_FC_INT2_OVERFLOW_MASK = 0x03,
>  	HDMI_IH_FC_INT2_LOW_PRIORITY_OVERFLOW = 0x02,
> -- 
> 2.1.0
> 
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver
@ 2015-08-10 10:05       ` Takashi Iwai
  0 siblings, 0 replies; 226+ messages in thread
From: Takashi Iwai @ 2015-08-10 10:05 UTC (permalink / raw)
  To: linux-arm-kernel

On Sat, 08 Aug 2015 18:10:06 +0200,
Russell King wrote:
> 
> Add ALSA based HDMI AHB audio driver for dw_hdmi.  The only buffer
> format supported by the hardware is its own special IEC958 based format,
> which is not compatible with any ALSA format.  To avoid doing too much
> data manipulation within the driver, we support only ALSAs IEC958 LE and
> 24-bit PCM formats for 2 to 6 channels, which we convert to its hardware
> format.
> 
> A more desirable solution would be to have this conversion in userspace,
> but ALSA does not appear to allow such transformations outside of
> libasound itself.
> 
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> ---
>  drivers/gpu/drm/bridge/Kconfig             |  10 +
>  drivers/gpu/drm/bridge/Makefile            |   1 +
>  drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c | 561 +++++++++++++++++++++++++++++
>  drivers/gpu/drm/bridge/dw_hdmi-audio.h     |  13 +
>  drivers/gpu/drm/bridge/dw_hdmi.c           |  24 ++
>  drivers/gpu/drm/bridge/dw_hdmi.h           |   3 +
>  6 files changed, 612 insertions(+)
>  create mode 100644 drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
>  create mode 100644 drivers/gpu/drm/bridge/dw_hdmi-audio.h
> 
> diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
> index acef3223772c..56ed35fe0734 100644
> --- a/drivers/gpu/drm/bridge/Kconfig
> +++ b/drivers/gpu/drm/bridge/Kconfig
> @@ -3,6 +3,16 @@ config DRM_DW_HDMI
>  	depends on DRM
>  	select DRM_KMS_HELPER
>  
> +config DRM_DW_HDMI_AHB_AUDIO
> +	tristate "Synopsis Designware AHB Audio interface"
> +	depends on DRM_DW_HDMI && SND
> +	select SND_PCM
> +	select SND_PCM_IEC958
> +	help
> +	  Support the AHB Audio interface which is part of the Synopsis
> +	  Designware HDMI block.  This is used in conjunction with
> +	  the i.MX6 HDMI driver.
> +
>  config DRM_PTN3460
>  	tristate "PTN3460 DP/LVDS bridge"
>  	depends on DRM
> diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
> index 8dfebd984370..eb80dbbb8365 100644
> --- a/drivers/gpu/drm/bridge/Makefile
> +++ b/drivers/gpu/drm/bridge/Makefile
> @@ -3,3 +3,4 @@ ccflags-y := -Iinclude/drm
>  obj-$(CONFIG_DRM_PS8622) += ps8622.o
>  obj-$(CONFIG_DRM_PTN3460) += ptn3460.o
>  obj-$(CONFIG_DRM_DW_HDMI) += dw_hdmi.o
> +obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw_hdmi-ahb-audio.o
> diff --git a/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
> new file mode 100644
> index 000000000000..22bbbc5c2393
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
> @@ -0,0 +1,561 @@
> +/*
> + * DesignWare HDMI audio driver
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * Written and tested against the Designware HDMI Tx found in iMX6.
> + */
> +#include <linux/io.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <drm/bridge/dw_hdmi.h>
> +
> +#include <sound/asoundef.h>
> +#include <sound/core.h>
> +#include <sound/initval.h>
> +#include <sound/pcm.h>
> +#include <sound/pcm_iec958.h>
> +
> +#include "dw_hdmi-audio.h"
> +
> +#define DRIVER_NAME "dw-hdmi-ahb-audio"
> +
> +/* Provide some bits rather than bit offsets */
> +enum {
> +	HDMI_AHB_DMA_CONF0_SW_FIFO_RST = BIT(7),
> +	HDMI_AHB_DMA_CONF0_EN_HLOCK = BIT(3),
> +	HDMI_AHB_DMA_START_START = BIT(0),
> +	HDMI_AHB_DMA_STOP_STOP = BIT(0),
> +	HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR = BIT(5),
> +	HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST = BIT(4),
> +	HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY = BIT(3),
> +	HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE = BIT(2),
> +	HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL = BIT(1),
> +	HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0),
> +	HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL =
> +		HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR |
> +		HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST |
> +		HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY |
> +		HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE |
> +		HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL |
> +		HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY,
> +	HDMI_IH_AHBDMAAUD_STAT0_ERROR = BIT(5),
> +	HDMI_IH_AHBDMAAUD_STAT0_LOST = BIT(4),
> +	HDMI_IH_AHBDMAAUD_STAT0_RETRY = BIT(3),
> +	HDMI_IH_AHBDMAAUD_STAT0_DONE = BIT(2),
> +	HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL = BIT(1),
> +	HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0),
> +	HDMI_IH_AHBDMAAUD_STAT0_ALL =
> +		HDMI_IH_AHBDMAAUD_STAT0_ERROR |
> +		HDMI_IH_AHBDMAAUD_STAT0_LOST |
> +		HDMI_IH_AHBDMAAUD_STAT0_RETRY |
> +		HDMI_IH_AHBDMAAUD_STAT0_DONE |
> +		HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL |
> +		HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY,
> +	HDMI_AHB_DMA_CONF0_INCR16 = 2 << 1,
> +	HDMI_AHB_DMA_CONF0_INCR8 = 1 << 1,
> +	HDMI_AHB_DMA_CONF0_INCR4 = 0,
> +	HDMI_AHB_DMA_CONF0_BURST_MODE = BIT(0),
> +	HDMI_AHB_DMA_MASK_DONE = BIT(7),
> +	HDMI_REVISION_ID = 0x0001,
> +	HDMI_IH_AHBDMAAUD_STAT0 = 0x0109,
> +	HDMI_IH_MUTE_AHBDMAAUD_STAT0 = 0x0189,
> +	HDMI_AHB_DMA_CONF0 = 0x3600,
> +	HDMI_AHB_DMA_START = 0x3601,
> +	HDMI_AHB_DMA_STOP = 0x3602,
> +	HDMI_AHB_DMA_THRSLD = 0x3603,
> +	HDMI_AHB_DMA_STRADDR0 = 0x3604,
> +	HDMI_AHB_DMA_STPADDR0 = 0x3608,
> +	HDMI_AHB_DMA_MASK = 0x3614,
> +	HDMI_AHB_DMA_POL = 0x3615,
> +	HDMI_AHB_DMA_CONF1 = 0x3616,
> +	HDMI_AHB_DMA_BUFFPOL = 0x361a,
> +};
> +
> +struct snd_dw_hdmi {
> +	struct snd_card *card;
> +	struct snd_pcm *pcm;
> +	struct dw_hdmi_audio_data data;
> +	struct snd_pcm_substream *substream;
> +	void (*reformat)(struct snd_dw_hdmi *, size_t, size_t);
> +	void *buf_src;
> +	void *buf_dst;
> +	dma_addr_t buf_addr;
> +	unsigned buf_offset;
> +	unsigned buf_period;
> +	unsigned buf_size;
> +	unsigned channels;
> +	u8 revision;
> +	u8 iec_offset;
> +	u8 cs[192][8];
> +};
> +
> +static void dw_hdmi_writel(unsigned long val, void __iomem *ptr)

Better to be u32 instead of unsigned long in general.

> +{
> +	writeb_relaxed(val, ptr);
> +	writeb_relaxed(val >> 8, ptr + 1);
> +	writeb_relaxed(val >> 16, ptr + 2);
> +	writeb_relaxed(val >> 24, ptr + 3);
> +}
> +
> +/*
> + * Convert to hardware format: The userspace buffer contains IEC958 samples,
> + * with the PCUV bits in bits 31..28 and audio samples in bits 27..4.  We
> + * need these to be in bits 27..24, with the IEC B bit in bit 28, and audio
> + * samples in 23..0.
> + *
> + * Default preamble in bits 3..0: 8 = block start, 4 = even 2 = odd
> + *
> + * Ideally, we could do with having the data properly formatted in userspace.
> + */
> +static void dw_hdmi_reformat_iec958(struct snd_dw_hdmi *dw,
> +	size_t offset, size_t bytes)
> +{
> +	u32 *src = dw->buf_src + offset;
> +	u32 *dst = dw->buf_dst + offset;
> +	u32 *end = dw->buf_src + offset + bytes;
> +
> +	do {
> +		u32 b, sample = *src++;
> +
> +		b = (sample & 8) << (28 - 3);
> +
> +		sample >>= 4;
> +
> +		*dst++ = sample | b;
> +	} while (src < end);
> +}
> +
> +static u32 parity(u32 sample)
> +{
> +	sample ^= sample >> 16;
> +	sample ^= sample >> 8;
> +	sample ^= sample >> 4;
> +	sample ^= sample >> 2;
> +	sample ^= sample >> 1;
> +	return (sample & 1) << 27;
> +}
> +
> +static void dw_hdmi_reformat_s24(struct snd_dw_hdmi *dw,
> +	size_t offset, size_t bytes)
> +{
> +	u32 *src = dw->buf_src + offset;
> +	u32 *dst = dw->buf_dst + offset;
> +	u32 *end = dw->buf_src + offset + bytes;
> +
> +	do {
> +		unsigned i;
> +		u8 *cs;
> +
> +		cs = dw->cs[dw->iec_offset++];
> +		if (dw->iec_offset >= 192)
> +			dw->iec_offset = 0;
> +
> +		i = dw->channels;
> +		do {
> +			u32 sample = *src++;
> +
> +			sample &= ~0xff000000;
> +			sample |= *cs++ << 24;
> +			sample |= parity(sample & ~0xf8000000);
> +
> +			*dst++ = sample;
> +		} while (--i);
> +	} while (src < end);
> +}
> +
> +static void dw_hdmi_create_cs(struct snd_dw_hdmi *dw,
> +	struct snd_pcm_runtime *runtime)
> +{
> +	u8 cs[4];
> +	unsigned ch, i, j;
> +
> +	snd_pcm_create_iec958_consumer(runtime, cs, sizeof(cs));
> +
> +	memset(dw->cs, 0, sizeof(dw->cs));
> +
> +	for (ch = 0; ch < 8; ch++) {
> +		cs[2] &= ~IEC958_AES2_CON_CHANNEL;
> +		cs[2] |= (ch + 1) << 4;
> +
> +		for (i = 0; i < ARRAY_SIZE(cs); i++) {
> +			unsigned c = cs[i];
> +
> +			for (j = 0; j < 8; j++, c >>= 1)
> +				dw->cs[i * 8 + j][ch] = (c & 1) << 2;
> +		}
> +	}
> +	dw->cs[0][0] |= BIT(4);
> +}
> +
> +static void dw_hdmi_start_dma(struct snd_dw_hdmi *dw)
> +{
> +	void __iomem *base = dw->data.base;
> +	unsigned offset = dw->buf_offset;
> +	unsigned period = dw->buf_period;
> +	u32 start, stop;
> +
> +	dw->reformat(dw, offset, period);
> +
> +	/* Clear all irqs before enabling irqs and starting DMA */
> +	writeb_relaxed(HDMI_IH_AHBDMAAUD_STAT0_ALL,
> +		       base + HDMI_IH_AHBDMAAUD_STAT0);
> +
> +	start = dw->buf_addr + offset;
> +	stop = start + period - 1;
> +
> +	/* Setup the hardware start/stop addresses */
> +	dw_hdmi_writel(start, base + HDMI_AHB_DMA_STRADDR0);
> +	dw_hdmi_writel(stop, base + HDMI_AHB_DMA_STPADDR0);
> +
> +	writeb_relaxed((u8)~HDMI_AHB_DMA_MASK_DONE, base + HDMI_AHB_DMA_MASK);
> +	writeb(HDMI_AHB_DMA_START_START, base + HDMI_AHB_DMA_START);
> +
> +	offset += period;
> +	if (offset >= dw->buf_size)
> +		offset = 0;
> +	dw->buf_offset = offset;
> +}
> +
> +static void dw_hdmi_stop_dma(struct snd_dw_hdmi *dw)
> +{
> +	dw->substream = NULL;
> +
> +	/* Disable interrupts before disabling DMA */
> +	writeb_relaxed(~0, dw->data.base + HDMI_AHB_DMA_MASK);
> +	writeb_relaxed(HDMI_AHB_DMA_STOP_STOP, dw->data.base + HDMI_AHB_DMA_STOP);
> +}
> +
> +static irqreturn_t snd_dw_hdmi_irq(int irq, void *data)
> +{
> +	struct snd_dw_hdmi *dw = data;
> +	struct snd_pcm_substream *substream;
> +	unsigned stat;
> +
> +	stat = readb_relaxed(dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
> +	if (!stat)
> +		return IRQ_NONE;
> +
> +	writeb_relaxed(stat, dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
> +
> +	substream = dw->substream;
> +	if (stat & HDMI_IH_AHBDMAAUD_STAT0_DONE && substream) {
> +		snd_pcm_period_elapsed(substream);
> +		if (dw->substream)
> +			dw_hdmi_start_dma(dw);
> +	}

Don't we need locking?  In theory, the trigger can be issued while the
irq is being handled.

> +
> +	return IRQ_HANDLED;
> +}
> +
> +static struct snd_pcm_hardware dw_hdmi_hw = {
> +	.info = SNDRV_PCM_INFO_INTERLEAVED |
> +		SNDRV_PCM_INFO_BLOCK_TRANSFER |
> +		SNDRV_PCM_INFO_MMAP |
> +		SNDRV_PCM_INFO_MMAP_VALID,
> +	.formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE |
> +		   SNDRV_PCM_FMTBIT_S24_LE,
> +	.rates = SNDRV_PCM_RATE_32000 |
> +		 SNDRV_PCM_RATE_44100 |
> +		 SNDRV_PCM_RATE_48000 |
> +		 SNDRV_PCM_RATE_88200 |
> +		 SNDRV_PCM_RATE_96000 |
> +		 SNDRV_PCM_RATE_176400 |
> +		 SNDRV_PCM_RATE_192000,
> +	.channels_min = 2,
> +	.channels_max = 8,
> +	.buffer_bytes_max = 64 * 1024,
> +	.period_bytes_min = 256,
> +	.period_bytes_max = 8192,	/* ERR004323: must limit to 8k */
> +	.periods_min = 2,
> +	.periods_max = 16,
> +	.fifo_size = 0,
> +};
> +
> +static int dw_hdmi_open(struct snd_pcm_substream *substream)
> +{
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +	struct snd_dw_hdmi *dw = substream->private_data;
> +	void __iomem *base = dw->data.base;
> +	int ret;
> +
> +	runtime->hw = dw_hdmi_hw;
> +
> +	ret = snd_pcm_limit_hw_rates(runtime);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Clear FIFO */
> +	writeb_relaxed(HDMI_AHB_DMA_CONF0_SW_FIFO_RST,
> +		       base + HDMI_AHB_DMA_CONF0);
> +
> +	/* Configure interrupt polarities */
> +	writeb_relaxed(~0, base + HDMI_AHB_DMA_POL);
> +	writeb_relaxed(~0, base + HDMI_AHB_DMA_BUFFPOL);
> +
> +	/* Keep interrupts masked, and clear any pending */
> +	writeb_relaxed(~0, base + HDMI_AHB_DMA_MASK);
> +	writeb_relaxed(~0, base + HDMI_IH_AHBDMAAUD_STAT0);
> +
> +	ret = request_irq(dw->data.irq, snd_dw_hdmi_irq, IRQF_SHARED,
> +			  "dw-hdmi-audio", dw);
> +	if (ret)
> +		return ret;
> +
> +	/* Un-mute done interrupt */
> +	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL &
> +		       ~HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE,
> +		       base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
> +
> +	return 0;
> +}
> +
> +static int dw_hdmi_close(struct snd_pcm_substream *substream)
> +{
> +	struct snd_dw_hdmi *dw = substream->private_data;
> +
> +	/* Mute all interrupts */
> +	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
> +		       dw->data.base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
> +
> +	free_irq(dw->data.irq, dw);
> +
> +	return 0;
> +}
> +
> +static int dw_hdmi_hw_free(struct snd_pcm_substream *substream)
> +{
> +	return snd_pcm_lib_free_vmalloc_buffer(substream);
> +}
> +
> +static int dw_hdmi_hw_params(struct snd_pcm_substream *substream,
> +	struct snd_pcm_hw_params *params)
> +{
> +	return snd_pcm_lib_alloc_vmalloc_buffer(substream,
> +						params_buffer_bytes(params));
> +}
> +
> +static int dw_hdmi_prepare(struct snd_pcm_substream *substream)
> +{
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +	struct snd_dw_hdmi *dw = substream->private_data;
> +	u8 threshold, conf0, conf1;
> +
> +	/* Setup as per 3.0.5 FSL 4.1.0 BSP */
> +	switch (dw->revision) {
> +	case 0x0a:
> +		conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
> +			HDMI_AHB_DMA_CONF0_INCR4;
> +		if (runtime->channels == 2)
> +			threshold = 126;
> +		else
> +			threshold = 124;
> +		break;
> +	case 0x1a:
> +		conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
> +			HDMI_AHB_DMA_CONF0_INCR8;
> +		threshold = 128;
> +		break;
> +	default:
> +		/* NOTREACHED */
> +		return -EINVAL;
> +	}
> +
> +	dw_hdmi_set_sample_rate(dw->data.hdmi, runtime->rate);
> +
> +	/* Minimum number of bytes in the fifo. */
> +	runtime->hw.fifo_size = threshold * 32;
> +
> +	conf0 |= HDMI_AHB_DMA_CONF0_EN_HLOCK;
> +	conf1 = (1 << runtime->channels) - 1;
> +
> +	writeb_relaxed(threshold, dw->data.base + HDMI_AHB_DMA_THRSLD);
> +	writeb_relaxed(conf0, dw->data.base + HDMI_AHB_DMA_CONF0);
> +	writeb_relaxed(conf1, dw->data.base + HDMI_AHB_DMA_CONF1);
> +
> +	switch (runtime->format) {
> +	case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
> +		dw->reformat = dw_hdmi_reformat_iec958;
> +		break;
> +	case SNDRV_PCM_FORMAT_S24_LE:
> +		dw_hdmi_create_cs(dw, runtime);
> +		dw->reformat = dw_hdmi_reformat_s24;
> +		break;
> +	}
> +	dw->iec_offset = 0;
> +	dw->channels = runtime->channels;
> +	dw->buf_src  = runtime->dma_area;
> +	dw->buf_dst  = substream->dma_buffer.area;
> +	dw->buf_addr = substream->dma_buffer.addr;
> +	dw->buf_period = snd_pcm_lib_period_bytes(substream);
> +	dw->buf_size = snd_pcm_lib_buffer_bytes(substream);
> +
> +	return 0;
> +}
> +
> +static int dw_hdmi_trigger(struct snd_pcm_substream *substream, int cmd)
> +{
> +	struct snd_dw_hdmi *dw = substream->private_data;
> +	int ret = 0;
> +
> +	switch (cmd) {
> +	case SNDRV_PCM_TRIGGER_START:
> +		dw->buf_offset = 0;
> +		dw->substream = substream;
> +		dw_hdmi_start_dma(dw);
> +		dw_hdmi_audio_enable(dw->data.hdmi);
> +		substream->runtime->delay = substream->runtime->period_size;
> +		break;
> +
> +	case SNDRV_PCM_TRIGGER_STOP:
> +		dw_hdmi_stop_dma(dw);
> +		dw_hdmi_audio_disable(dw->data.hdmi);
> +		break;
> +
> +	default:
> +		ret = -EINVAL;
> +		break;

SNDRV_PCM_TRIGGER_SUSPEND may be passed at suspend, too.


> +	}
> +
> +	return ret;
> +}
> +
> +static snd_pcm_uframes_t dw_hdmi_pointer(struct snd_pcm_substream *substream)
> +{
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +	struct snd_dw_hdmi *dw = substream->private_data;
> +
> +	return bytes_to_frames(runtime, dw->buf_offset);

So, this returns the offset that has been reformatted.  Does the
hardware support any better position reporting?  We may give the delay
from the driver if possible.


thanks,

Takashi


> +}
> +
> +static struct snd_pcm_ops snd_dw_hdmi_ops = {
> +	.open = dw_hdmi_open,
> +	.close = dw_hdmi_close,
> +	.ioctl = snd_pcm_lib_ioctl,
> +	.hw_params = dw_hdmi_hw_params,
> +	.hw_free = dw_hdmi_hw_free,
> +	.prepare = dw_hdmi_prepare,
> +	.trigger = dw_hdmi_trigger,
> +	.pointer = dw_hdmi_pointer,
> +	.page = snd_pcm_lib_get_vmalloc_page,
> +};
> +
> +static int snd_dw_hdmi_probe(struct platform_device *pdev)
> +{
> +	const struct dw_hdmi_audio_data *data = pdev->dev.platform_data;
> +	struct device *dev = pdev->dev.parent;
> +	struct snd_dw_hdmi *dw;
> +	struct snd_card *card;
> +	struct snd_pcm *pcm;
> +	unsigned revision;
> +	int ret;
> +
> +	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
> +		       data->base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
> +	revision = readb_relaxed(data->base + HDMI_REVISION_ID);
> +	if (revision != 0x0a && revision != 0x1a) {
> +		dev_err(dev, "dw-hdmi-audio: unknown revision 0x%02x\n",
> +			revision);
> +		return -ENXIO;
> +	}
> +
> +	ret = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
> +			      THIS_MODULE, sizeof(struct snd_dw_hdmi), &card);
> +	if (ret < 0)
> +		return ret;
> +
> +	strlcpy(card->driver, DRIVER_NAME, sizeof(card->driver));
> +	strlcpy(card->shortname, "DW-HDMI", sizeof(card->shortname));
> +	snprintf(card->longname, sizeof(card->longname),
> +		 "%s rev 0x%02x, irq %d", card->shortname, revision,
> +		 data->irq);
> +
> +	dw = card->private_data;
> +	dw->card = card;
> +	dw->data = *data;
> +	dw->revision = revision;
> +
> +	ret = snd_pcm_new(card, "DW HDMI", 0, 1, 0, &pcm);
> +	if (ret < 0)
> +		goto err;
> +
> +	dw->pcm = pcm;
> +	pcm->private_data = dw;
> +	strlcpy(pcm->name, DRIVER_NAME, sizeof(pcm->name));
> +	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_dw_hdmi_ops);
> +
> +	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
> +			dev, 64 * 1024, 64 * 1024);
> +
> +	ret = snd_card_register(card);
> +	if (ret < 0)
> +		goto err;
> +
> +	platform_set_drvdata(pdev, dw);
> +
> +	return 0;
> +
> +err:
> +	snd_card_free(card);
> +	return ret;
> +}
> +
> +static int snd_dw_hdmi_remove(struct platform_device *pdev)
> +{
> +	struct snd_dw_hdmi *dw = platform_get_drvdata(pdev);
> +
> +	snd_card_free(dw->card);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int snd_dw_hdmi_suspend(struct device *dev)
> +{
> +	struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
> +
> +	snd_power_change_state(dw->card, SNDRV_CTL_POWER_D3cold);
> +	snd_pcm_suspend_all(dw->pcm);
> +
> +	return 0;
> +}
> +
> +static int snd_dw_hdmi_resume(struct device *dev)
> +{
> +	struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
> +
> +	snd_power_change_state(dw->card, SNDRV_CTL_POWER_D0);
> +
> +	return 0;
> +}
> +
> +static SIMPLE_DEV_PM_OPS(snd_dw_hdmi_pm, snd_dw_hdmi_suspend,
> +			 snd_dw_hdmi_resume);
> +#define PM_OPS &snd_dw_hdmi_pm
> +#else
> +#define PM_OPS NULL
> +#endif
> +
> +static struct platform_driver snd_dw_hdmi_driver = {
> +	.probe	= snd_dw_hdmi_probe,
> +	.remove	= snd_dw_hdmi_remove,
> +	.driver	= {
> +		.name = DRIVER_NAME,
> +		.owner = THIS_MODULE,
> +		.pm = PM_OPS,
> +	},
> +};
> +
> +module_platform_driver(snd_dw_hdmi_driver);
> +
> +MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>");
> +MODULE_DESCRIPTION("Synopsis Designware HDMI AHB ALSA interface");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:" DRIVER_NAME);
> diff --git a/drivers/gpu/drm/bridge/dw_hdmi-audio.h b/drivers/gpu/drm/bridge/dw_hdmi-audio.h
> new file mode 100644
> index 000000000000..1e840118d90a
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/dw_hdmi-audio.h
> @@ -0,0 +1,13 @@
> +#ifndef DW_HDMI_AUDIO_H
> +#define DW_HDMI_AUDIO_H
> +
> +struct dw_hdmi;
> +
> +struct dw_hdmi_audio_data {
> +	phys_addr_t phys;
> +	void __iomem *base;
> +	int irq;
> +	struct dw_hdmi *hdmi;
> +};
> +
> +#endif
> diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
> index fba25607ef88..b65464789fbd 100644
> --- a/drivers/gpu/drm/bridge/dw_hdmi.c
> +++ b/drivers/gpu/drm/bridge/dw_hdmi.c
> @@ -28,6 +28,7 @@
>  #include <drm/bridge/dw_hdmi.h>
>  
>  #include "dw_hdmi.h"
> +#include "dw_hdmi-audio.h"
>  
>  #define HDMI_EDID_LEN		512
>  
> @@ -104,6 +105,7 @@ struct dw_hdmi {
>  	struct drm_encoder *encoder;
>  	struct drm_bridge *bridge;
>  
> +	struct platform_device *audio;
>  	enum dw_hdmi_devtype dev_type;
>  	struct device *dev;
>  	struct clk *isfr_clk;
> @@ -1732,7 +1734,9 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
>  {
>  	struct drm_device *drm = data;
>  	struct device_node *np = dev->of_node;
> +	struct platform_device_info pdevinfo;
>  	struct device_node *ddc_node;
> +	struct dw_hdmi_audio_data audio;
>  	struct dw_hdmi *hdmi;
>  	int ret;
>  	u32 val = 1;
> @@ -1860,6 +1864,23 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
>  	hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE),
>  		    HDMI_IH_MUTE_PHY_STAT0);
>  
> +	memset(&pdevinfo, 0, sizeof(pdevinfo));
> +	pdevinfo.parent = dev;
> +	pdevinfo.id = PLATFORM_DEVID_AUTO;
> +
> +	if (hdmi_readb(hdmi, HDMI_CONFIG1_ID) & HDMI_CONFIG1_AHB) {
> +		audio.phys = iores->start;
> +		audio.base = hdmi->regs;
> +		audio.irq = irq;
> +		audio.hdmi = hdmi;
> +
> +		pdevinfo.name = "dw-hdmi-ahb-audio";
> +		pdevinfo.data = &audio;
> +		pdevinfo.size_data = sizeof(audio);
> +		pdevinfo.dma_mask = DMA_BIT_MASK(32);
> +		hdmi->audio = platform_device_register_full(&pdevinfo);
> +	}
> +
>  	dev_set_drvdata(dev, hdmi);
>  
>  	return 0;
> @@ -1877,6 +1898,9 @@ void dw_hdmi_unbind(struct device *dev, struct device *master, void *data)
>  {
>  	struct dw_hdmi *hdmi = dev_get_drvdata(dev);
>  
> +	if (hdmi->audio && !IS_ERR(hdmi->audio))
> +		platform_device_unregister(hdmi->audio);
> +
>  	/* Disable all interrupts */
>  	hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
>  
> diff --git a/drivers/gpu/drm/bridge/dw_hdmi.h b/drivers/gpu/drm/bridge/dw_hdmi.h
> index 175dbc89a824..78e54e813212 100644
> --- a/drivers/gpu/drm/bridge/dw_hdmi.h
> +++ b/drivers/gpu/drm/bridge/dw_hdmi.h
> @@ -545,6 +545,9 @@
>  #define HDMI_I2CM_FS_SCL_LCNT_0_ADDR            0x7E12
>  
>  enum {
> +/* CONFIG1_ID field values */
> +	HDMI_CONFIG1_AHB = 0x01,
> +
>  /* IH_FC_INT2 field values */
>  	HDMI_IH_FC_INT2_OVERFLOW_MASK = 0x03,
>  	HDMI_IH_FC_INT2_LOW_PRIORITY_OVERFLOW = 0x02,
> -- 
> 2.1.0
> 

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver
  2015-08-10 10:05       ` Takashi Iwai
  (?)
@ 2015-08-10 10:39         ` Russell King - ARM Linux
  -1 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-08-10 10:39 UTC (permalink / raw)
  To: Takashi Iwai
  Cc: linux-rockchip, alsa-devel, dri-devel, linux-kernel,
	linux-arm-kernel, Philipp Zabel, Andy Yan, Yakir Yang,
	Fabio Estevam, Mark Brown, Jaroslav Kysela, Sascha Hauer,
	Jon Nettleton, David Airlie

On Mon, Aug 10, 2015 at 12:05:07PM +0200, Takashi Iwai wrote:
> On Sat, 08 Aug 2015 18:10:06 +0200,
> Russell King wrote:
> > +static irqreturn_t snd_dw_hdmi_irq(int irq, void *data)
> > +{
> > +	struct snd_dw_hdmi *dw = data;
> > +	struct snd_pcm_substream *substream;
> > +	unsigned stat;
> > +
> > +	stat = readb_relaxed(dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
> > +	if (!stat)
> > +		return IRQ_NONE;
> > +
> > +	writeb_relaxed(stat, dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
> > +
> > +	substream = dw->substream;
> > +	if (stat & HDMI_IH_AHBDMAAUD_STAT0_DONE && substream) {
> > +		snd_pcm_period_elapsed(substream);
> > +		if (dw->substream)
> > +			dw_hdmi_start_dma(dw);
> > +	}
> 
> Don't we need locking?

Possibly.

> In theory, the trigger can be issued while the irq is being handled.

Well, we can't have a lock around the whole of the above, because that
results in deadlock (as snd_pcm_period_elapsed() can end up calling into
the trigger method.)  I'm not happy to throw a spinlock around this
because of the in-built format conversion (something else I'm really not
happy about - which has to exist here because alsalib is soo painful
to add custom sample reformatting to - such modules have to be built
as part of alsalib itself rather than an add-on module.)

> > +static int dw_hdmi_trigger(struct snd_pcm_substream *substream, int cmd)
> > +{
> > +	struct snd_dw_hdmi *dw = substream->private_data;
> > +	int ret = 0;
> > +
> > +	switch (cmd) {
> > +	case SNDRV_PCM_TRIGGER_START:
> > +		dw->buf_offset = 0;
> > +		dw->substream = substream;
> > +		dw_hdmi_start_dma(dw);
> > +		dw_hdmi_audio_enable(dw->data.hdmi);
> > +		substream->runtime->delay = substream->runtime->period_size;
> > +		break;
> > +
> > +	case SNDRV_PCM_TRIGGER_STOP:
> > +		dw_hdmi_stop_dma(dw);
> > +		dw_hdmi_audio_disable(dw->data.hdmi);
> > +		break;
> > +
> > +	default:
> > +		ret = -EINVAL;
> > +		break;
> 
> SNDRV_PCM_TRIGGER_SUSPEND may be passed at suspend, too.

I think rather than adding code which would be difficult for me to test,
I'd instead remove the suspend/resume callbacks, or at least disable them
until someone can test that feature, or is willing to implement it.

> > +static snd_pcm_uframes_t dw_hdmi_pointer(struct snd_pcm_substream *substream)
> > +{
> > +	struct snd_pcm_runtime *runtime = substream->runtime;
> > +	struct snd_dw_hdmi *dw = substream->private_data;
> > +
> > +	return bytes_to_frames(runtime, dw->buf_offset);
> 
> So, this returns the offset that has been reformatted.  Does the
> hardware support any better position reporting?  We may give the delay
> from the driver if possible.

Basically, no.  Reading a 32-bit DMA position as separate bytes while
DMA is active is racy.

This is the best we can do, and the way we report the position has been
arrived at after what's getting on for two years of testing with
pulseaudio, vlc direct access & spdif pass-through, aplay, etc:

Author: Russell King <rmk+kernel@arm.linux.org.uk>
Date:   Thu Nov 7 16:01:45 2013 +0000

    drm: bridge/dw_hdmi-ahb-audio: add audio driver

-- 
FTTC broadband for 0.8mile line: currently at 10.5Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver
@ 2015-08-10 10:39         ` Russell King - ARM Linux
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-08-10 10:39 UTC (permalink / raw)
  To: Takashi Iwai
  Cc: Fabio Estevam, alsa-devel, linux-kernel, dri-devel,
	Jaroslav Kysela, linux-rockchip, Mark Brown, Yakir Yang,
	Andy Yan, linux-arm-kernel

On Mon, Aug 10, 2015 at 12:05:07PM +0200, Takashi Iwai wrote:
> On Sat, 08 Aug 2015 18:10:06 +0200,
> Russell King wrote:
> > +static irqreturn_t snd_dw_hdmi_irq(int irq, void *data)
> > +{
> > +	struct snd_dw_hdmi *dw = data;
> > +	struct snd_pcm_substream *substream;
> > +	unsigned stat;
> > +
> > +	stat = readb_relaxed(dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
> > +	if (!stat)
> > +		return IRQ_NONE;
> > +
> > +	writeb_relaxed(stat, dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
> > +
> > +	substream = dw->substream;
> > +	if (stat & HDMI_IH_AHBDMAAUD_STAT0_DONE && substream) {
> > +		snd_pcm_period_elapsed(substream);
> > +		if (dw->substream)
> > +			dw_hdmi_start_dma(dw);
> > +	}
> 
> Don't we need locking?

Possibly.

> In theory, the trigger can be issued while the irq is being handled.

Well, we can't have a lock around the whole of the above, because that
results in deadlock (as snd_pcm_period_elapsed() can end up calling into
the trigger method.)  I'm not happy to throw a spinlock around this
because of the in-built format conversion (something else I'm really not
happy about - which has to exist here because alsalib is soo painful
to add custom sample reformatting to - such modules have to be built
as part of alsalib itself rather than an add-on module.)

> > +static int dw_hdmi_trigger(struct snd_pcm_substream *substream, int cmd)
> > +{
> > +	struct snd_dw_hdmi *dw = substream->private_data;
> > +	int ret = 0;
> > +
> > +	switch (cmd) {
> > +	case SNDRV_PCM_TRIGGER_START:
> > +		dw->buf_offset = 0;
> > +		dw->substream = substream;
> > +		dw_hdmi_start_dma(dw);
> > +		dw_hdmi_audio_enable(dw->data.hdmi);
> > +		substream->runtime->delay = substream->runtime->period_size;
> > +		break;
> > +
> > +	case SNDRV_PCM_TRIGGER_STOP:
> > +		dw_hdmi_stop_dma(dw);
> > +		dw_hdmi_audio_disable(dw->data.hdmi);
> > +		break;
> > +
> > +	default:
> > +		ret = -EINVAL;
> > +		break;
> 
> SNDRV_PCM_TRIGGER_SUSPEND may be passed at suspend, too.

I think rather than adding code which would be difficult for me to test,
I'd instead remove the suspend/resume callbacks, or at least disable them
until someone can test that feature, or is willing to implement it.

> > +static snd_pcm_uframes_t dw_hdmi_pointer(struct snd_pcm_substream *substream)
> > +{
> > +	struct snd_pcm_runtime *runtime = substream->runtime;
> > +	struct snd_dw_hdmi *dw = substream->private_data;
> > +
> > +	return bytes_to_frames(runtime, dw->buf_offset);
> 
> So, this returns the offset that has been reformatted.  Does the
> hardware support any better position reporting?  We may give the delay
> from the driver if possible.

Basically, no.  Reading a 32-bit DMA position as separate bytes while
DMA is active is racy.

This is the best we can do, and the way we report the position has been
arrived at after what's getting on for two years of testing with
pulseaudio, vlc direct access & spdif pass-through, aplay, etc:

Author: Russell King <rmk+kernel@arm.linux.org.uk>
Date:   Thu Nov 7 16:01:45 2013 +0000

    drm: bridge/dw_hdmi-ahb-audio: add audio driver

-- 
FTTC broadband for 0.8mile line: currently at 10.5Mbps down 400kbps up
according to speedtest.net.
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver
@ 2015-08-10 10:39         ` Russell King - ARM Linux
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-08-10 10:39 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Aug 10, 2015 at 12:05:07PM +0200, Takashi Iwai wrote:
> On Sat, 08 Aug 2015 18:10:06 +0200,
> Russell King wrote:
> > +static irqreturn_t snd_dw_hdmi_irq(int irq, void *data)
> > +{
> > +	struct snd_dw_hdmi *dw = data;
> > +	struct snd_pcm_substream *substream;
> > +	unsigned stat;
> > +
> > +	stat = readb_relaxed(dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
> > +	if (!stat)
> > +		return IRQ_NONE;
> > +
> > +	writeb_relaxed(stat, dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
> > +
> > +	substream = dw->substream;
> > +	if (stat & HDMI_IH_AHBDMAAUD_STAT0_DONE && substream) {
> > +		snd_pcm_period_elapsed(substream);
> > +		if (dw->substream)
> > +			dw_hdmi_start_dma(dw);
> > +	}
> 
> Don't we need locking?

Possibly.

> In theory, the trigger can be issued while the irq is being handled.

Well, we can't have a lock around the whole of the above, because that
results in deadlock (as snd_pcm_period_elapsed() can end up calling into
the trigger method.)  I'm not happy to throw a spinlock around this
because of the in-built format conversion (something else I'm really not
happy about - which has to exist here because alsalib is soo painful
to add custom sample reformatting to - such modules have to be built
as part of alsalib itself rather than an add-on module.)

> > +static int dw_hdmi_trigger(struct snd_pcm_substream *substream, int cmd)
> > +{
> > +	struct snd_dw_hdmi *dw = substream->private_data;
> > +	int ret = 0;
> > +
> > +	switch (cmd) {
> > +	case SNDRV_PCM_TRIGGER_START:
> > +		dw->buf_offset = 0;
> > +		dw->substream = substream;
> > +		dw_hdmi_start_dma(dw);
> > +		dw_hdmi_audio_enable(dw->data.hdmi);
> > +		substream->runtime->delay = substream->runtime->period_size;
> > +		break;
> > +
> > +	case SNDRV_PCM_TRIGGER_STOP:
> > +		dw_hdmi_stop_dma(dw);
> > +		dw_hdmi_audio_disable(dw->data.hdmi);
> > +		break;
> > +
> > +	default:
> > +		ret = -EINVAL;
> > +		break;
> 
> SNDRV_PCM_TRIGGER_SUSPEND may be passed at suspend, too.

I think rather than adding code which would be difficult for me to test,
I'd instead remove the suspend/resume callbacks, or at least disable them
until someone can test that feature, or is willing to implement it.

> > +static snd_pcm_uframes_t dw_hdmi_pointer(struct snd_pcm_substream *substream)
> > +{
> > +	struct snd_pcm_runtime *runtime = substream->runtime;
> > +	struct snd_dw_hdmi *dw = substream->private_data;
> > +
> > +	return bytes_to_frames(runtime, dw->buf_offset);
> 
> So, this returns the offset that has been reformatted.  Does the
> hardware support any better position reporting?  We may give the delay
> from the driver if possible.

Basically, no.  Reading a 32-bit DMA position as separate bytes while
DMA is active is racy.

This is the best we can do, and the way we report the position has been
arrived at after what's getting on for two years of testing with
pulseaudio, vlc direct access & spdif pass-through, aplay, etc:

Author: Russell King <rmk+kernel@arm.linux.org.uk>
Date:   Thu Nov 7 16:01:45 2013 +0000

    drm: bridge/dw_hdmi-ahb-audio: add audio driver

-- 
FTTC broadband for 0.8mile line: currently at 10.5Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 00/12] dw-hdmi development
  2015-08-08 16:02 ` Russell King - ARM Linux
  (?)
@ 2015-08-10 12:21   ` Thierry Reding
  -1 siblings, 0 replies; 226+ messages in thread
From: Thierry Reding @ 2015-08-10 12:21 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: linux-rockchip, alsa-devel, dri-devel, linux-kernel,
	linux-arm-kernel, Fabio Estevam, Yakir Yang, Andy Yan

[-- Attachment #1: Type: text/plain, Size: 1597 bytes --]

On Sat, Aug 08, 2015 at 05:02:51PM +0100, Russell King - ARM Linux wrote:
> This sub-series is a mixture of development:
> 
> * Removing the incorrect pixel repetition configuration code
> * Preventing pixel-doubled modes from being used
> * Adding interlaced video support
> * Implementing the sink_is_hdmi/sink_has_audio flags I suggested a few
>   months ago
> * Only enabling audio support if the sink indicates it has audio
> * Avoiding double-enabling the HDMI interface
> * Fixing the mis-leading name of "dw_hdmi_phy_enable_power"
> * Adding connector mode forcing (important if your monitor bounces
>   RXSENSE and HPD signals while in low-power mode.)
> * Improving the HDMI enable/disabling on sink status
> 
> For review (and testing if people feel like it).  Acks/tested-bys etc
> welcome.  It applies on top of my drm-dwhdmi-devel branch, which is
> waiting for David Airlie to pull (see pull request on dri-devel, 15th
> July.)

Hi Russell,

I have in the past merged patches for the bridge subdirectory via the
drm/panel tree, though lately much of the dw-hdmi patches have gone in
via Philipp or you directly. This seems to have worked fine so far, but
this time around I carry a patch to clean up Kconfig and Makefile a
little and bring more consistency to the subdirectory and I think it's
going to conflict with your series here (and potentially any ongoing
work you have).

Would you be open to me picking up these patches into the drm/panel
tree? It feeds into linux-next, so the code would get some exposure
before Dave's return.

Thierry

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 00/12] dw-hdmi development
@ 2015-08-10 12:21   ` Thierry Reding
  0 siblings, 0 replies; 226+ messages in thread
From: Thierry Reding @ 2015-08-10 12:21 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Fabio Estevam, alsa-devel, linux-kernel, dri-devel,
	linux-rockchip, Yakir Yang, Andy Yan, linux-arm-kernel


[-- Attachment #1.1: Type: text/plain, Size: 1597 bytes --]

On Sat, Aug 08, 2015 at 05:02:51PM +0100, Russell King - ARM Linux wrote:
> This sub-series is a mixture of development:
> 
> * Removing the incorrect pixel repetition configuration code
> * Preventing pixel-doubled modes from being used
> * Adding interlaced video support
> * Implementing the sink_is_hdmi/sink_has_audio flags I suggested a few
>   months ago
> * Only enabling audio support if the sink indicates it has audio
> * Avoiding double-enabling the HDMI interface
> * Fixing the mis-leading name of "dw_hdmi_phy_enable_power"
> * Adding connector mode forcing (important if your monitor bounces
>   RXSENSE and HPD signals while in low-power mode.)
> * Improving the HDMI enable/disabling on sink status
> 
> For review (and testing if people feel like it).  Acks/tested-bys etc
> welcome.  It applies on top of my drm-dwhdmi-devel branch, which is
> waiting for David Airlie to pull (see pull request on dri-devel, 15th
> July.)

Hi Russell,

I have in the past merged patches for the bridge subdirectory via the
drm/panel tree, though lately much of the dw-hdmi patches have gone in
via Philipp or you directly. This seems to have worked fine so far, but
this time around I carry a patch to clean up Kconfig and Makefile a
little and bring more consistency to the subdirectory and I think it's
going to conflict with your series here (and potentially any ongoing
work you have).

Would you be open to me picking up these patches into the drm/panel
tree? It feeds into linux-next, so the code would get some exposure
before Dave's return.

Thierry

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 00/12] dw-hdmi development
@ 2015-08-10 12:21   ` Thierry Reding
  0 siblings, 0 replies; 226+ messages in thread
From: Thierry Reding @ 2015-08-10 12:21 UTC (permalink / raw)
  To: linux-arm-kernel

On Sat, Aug 08, 2015 at 05:02:51PM +0100, Russell King - ARM Linux wrote:
> This sub-series is a mixture of development:
> 
> * Removing the incorrect pixel repetition configuration code
> * Preventing pixel-doubled modes from being used
> * Adding interlaced video support
> * Implementing the sink_is_hdmi/sink_has_audio flags I suggested a few
>   months ago
> * Only enabling audio support if the sink indicates it has audio
> * Avoiding double-enabling the HDMI interface
> * Fixing the mis-leading name of "dw_hdmi_phy_enable_power"
> * Adding connector mode forcing (important if your monitor bounces
>   RXSENSE and HPD signals while in low-power mode.)
> * Improving the HDMI enable/disabling on sink status
> 
> For review (and testing if people feel like it).  Acks/tested-bys etc
> welcome.  It applies on top of my drm-dwhdmi-devel branch, which is
> waiting for David Airlie to pull (see pull request on dri-devel, 15th
> July.)

Hi Russell,

I have in the past merged patches for the bridge subdirectory via the
drm/panel tree, though lately much of the dw-hdmi patches have gone in
via Philipp or you directly. This seems to have worked fine so far, but
this time around I carry a patch to clean up Kconfig and Makefile a
little and bring more consistency to the subdirectory and I think it's
going to conflict with your series here (and potentially any ongoing
work you have).

Would you be open to me picking up these patches into the drm/panel
tree? It feeds into linux-next, so the code would get some exposure
before Dave's return.

Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20150810/867cb263/attachment.sig>

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver
  2015-08-10 10:39         ` Russell King - ARM Linux
  (?)
@ 2015-08-10 12:23           ` Takashi Iwai
  -1 siblings, 0 replies; 226+ messages in thread
From: Takashi Iwai @ 2015-08-10 12:23 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: linux-rockchip, alsa-devel, dri-devel, linux-kernel,
	linux-arm-kernel, Philipp Zabel, Andy Yan, Yakir Yang,
	Fabio Estevam, Mark Brown, Jaroslav Kysela, Sascha Hauer,
	Jon Nettleton, David Airlie

On Mon, 10 Aug 2015 12:39:21 +0200,
Russell King - ARM Linux wrote:
> 
> On Mon, Aug 10, 2015 at 12:05:07PM +0200, Takashi Iwai wrote:
> > On Sat, 08 Aug 2015 18:10:06 +0200,
> > Russell King wrote:
> > > +static irqreturn_t snd_dw_hdmi_irq(int irq, void *data)
> > > +{
> > > +	struct snd_dw_hdmi *dw = data;
> > > +	struct snd_pcm_substream *substream;
> > > +	unsigned stat;
> > > +
> > > +	stat = readb_relaxed(dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
> > > +	if (!stat)
> > > +		return IRQ_NONE;
> > > +
> > > +	writeb_relaxed(stat, dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
> > > +
> > > +	substream = dw->substream;
> > > +	if (stat & HDMI_IH_AHBDMAAUD_STAT0_DONE && substream) {
> > > +		snd_pcm_period_elapsed(substream);
> > > +		if (dw->substream)
> > > +			dw_hdmi_start_dma(dw);
> > > +	}
> > 
> > Don't we need locking?
> 
> Possibly.
> 
> > In theory, the trigger can be issued while the irq is being handled.
> 
> Well, we can't have a lock around the whole of the above, because that
> results in deadlock (as snd_pcm_period_elapsed() can end up calling into
> the trigger method.)

Yes, and a usual workaround is to unlock temporarily at calling
snd_pcm_period_elapsed(), then relock or call it at the end of 
handler.

> I'm not happy to throw a spinlock around this
> because of the in-built format conversion (something else I'm really not
> happy about - which has to exist here because alsalib is soo painful
> to add custom sample reformatting to - such modules have to be built
> as part of alsalib itself rather than an add-on module.)

I admit that alsa-lib code is very horrible to follow -- but I guess
the change you'd need for iec958 plugin would be fairly small.  We can
add a config option and let iec958 behaving slightly differently
depending on it.

Meanwhile, having an in-kernel workaround makes it much easier to
deploy, so I think it's OK to have this in driver for now.

> > > +static int dw_hdmi_trigger(struct snd_pcm_substream *substream, int cmd)
> > > +{
> > > +	struct snd_dw_hdmi *dw = substream->private_data;
> > > +	int ret = 0;
> > > +
> > > +	switch (cmd) {
> > > +	case SNDRV_PCM_TRIGGER_START:
> > > +		dw->buf_offset = 0;
> > > +		dw->substream = substream;
> > > +		dw_hdmi_start_dma(dw);
> > > +		dw_hdmi_audio_enable(dw->data.hdmi);
> > > +		substream->runtime->delay = substream->runtime->period_size;
> > > +		break;
> > > +
> > > +	case SNDRV_PCM_TRIGGER_STOP:
> > > +		dw_hdmi_stop_dma(dw);
> > > +		dw_hdmi_audio_disable(dw->data.hdmi);
> > > +		break;
> > > +
> > > +	default:
> > > +		ret = -EINVAL;
> > > +		break;
> > 
> > SNDRV_PCM_TRIGGER_SUSPEND may be passed at suspend, too.
> 
> I think rather than adding code which would be difficult for me to test,
> I'd instead remove the suspend/resume callbacks, or at least disable them
> until someone can test that feature, or is willing to implement it.

That's fine.

> > > +static snd_pcm_uframes_t dw_hdmi_pointer(struct snd_pcm_substream *substream)
> > > +{
> > > +	struct snd_pcm_runtime *runtime = substream->runtime;
> > > +	struct snd_dw_hdmi *dw = substream->private_data;
> > > +
> > > +	return bytes_to_frames(runtime, dw->buf_offset);
> > 
> > So, this returns the offset that has been reformatted.  Does the
> > hardware support any better position reporting?  We may give the delay
> > from the driver if possible.
> 
> Basically, no.  Reading a 32-bit DMA position as separate bytes while
> DMA is active is racy.
> 
> This is the best we can do, and the way we report the position has been
> arrived at after what's getting on for two years of testing with
> pulseaudio, vlc direct access & spdif pass-through, aplay, etc:
> 
> Author: Russell King <rmk+kernel@arm.linux.org.uk>
> Date:   Thu Nov 7 16:01:45 2013 +0000
> 
>     drm: bridge/dw_hdmi-ahb-audio: add audio driver

OK, then this is a driver with the low update granularity.  Hopefully
we'll get some good API to indicate that in near future, as we've been
discussing about it for a while.


thanks,

Takashi

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver
@ 2015-08-10 12:23           ` Takashi Iwai
  0 siblings, 0 replies; 226+ messages in thread
From: Takashi Iwai @ 2015-08-10 12:23 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Fabio Estevam, alsa-devel, linux-kernel, dri-devel,
	Jaroslav Kysela, linux-rockchip, Mark Brown, Yakir Yang,
	Andy Yan, linux-arm-kernel

On Mon, 10 Aug 2015 12:39:21 +0200,
Russell King - ARM Linux wrote:
> 
> On Mon, Aug 10, 2015 at 12:05:07PM +0200, Takashi Iwai wrote:
> > On Sat, 08 Aug 2015 18:10:06 +0200,
> > Russell King wrote:
> > > +static irqreturn_t snd_dw_hdmi_irq(int irq, void *data)
> > > +{
> > > +	struct snd_dw_hdmi *dw = data;
> > > +	struct snd_pcm_substream *substream;
> > > +	unsigned stat;
> > > +
> > > +	stat = readb_relaxed(dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
> > > +	if (!stat)
> > > +		return IRQ_NONE;
> > > +
> > > +	writeb_relaxed(stat, dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
> > > +
> > > +	substream = dw->substream;
> > > +	if (stat & HDMI_IH_AHBDMAAUD_STAT0_DONE && substream) {
> > > +		snd_pcm_period_elapsed(substream);
> > > +		if (dw->substream)
> > > +			dw_hdmi_start_dma(dw);
> > > +	}
> > 
> > Don't we need locking?
> 
> Possibly.
> 
> > In theory, the trigger can be issued while the irq is being handled.
> 
> Well, we can't have a lock around the whole of the above, because that
> results in deadlock (as snd_pcm_period_elapsed() can end up calling into
> the trigger method.)

Yes, and a usual workaround is to unlock temporarily at calling
snd_pcm_period_elapsed(), then relock or call it at the end of 
handler.

> I'm not happy to throw a spinlock around this
> because of the in-built format conversion (something else I'm really not
> happy about - which has to exist here because alsalib is soo painful
> to add custom sample reformatting to - such modules have to be built
> as part of alsalib itself rather than an add-on module.)

I admit that alsa-lib code is very horrible to follow -- but I guess
the change you'd need for iec958 plugin would be fairly small.  We can
add a config option and let iec958 behaving slightly differently
depending on it.

Meanwhile, having an in-kernel workaround makes it much easier to
deploy, so I think it's OK to have this in driver for now.

> > > +static int dw_hdmi_trigger(struct snd_pcm_substream *substream, int cmd)
> > > +{
> > > +	struct snd_dw_hdmi *dw = substream->private_data;
> > > +	int ret = 0;
> > > +
> > > +	switch (cmd) {
> > > +	case SNDRV_PCM_TRIGGER_START:
> > > +		dw->buf_offset = 0;
> > > +		dw->substream = substream;
> > > +		dw_hdmi_start_dma(dw);
> > > +		dw_hdmi_audio_enable(dw->data.hdmi);
> > > +		substream->runtime->delay = substream->runtime->period_size;
> > > +		break;
> > > +
> > > +	case SNDRV_PCM_TRIGGER_STOP:
> > > +		dw_hdmi_stop_dma(dw);
> > > +		dw_hdmi_audio_disable(dw->data.hdmi);
> > > +		break;
> > > +
> > > +	default:
> > > +		ret = -EINVAL;
> > > +		break;
> > 
> > SNDRV_PCM_TRIGGER_SUSPEND may be passed at suspend, too.
> 
> I think rather than adding code which would be difficult for me to test,
> I'd instead remove the suspend/resume callbacks, or at least disable them
> until someone can test that feature, or is willing to implement it.

That's fine.

> > > +static snd_pcm_uframes_t dw_hdmi_pointer(struct snd_pcm_substream *substream)
> > > +{
> > > +	struct snd_pcm_runtime *runtime = substream->runtime;
> > > +	struct snd_dw_hdmi *dw = substream->private_data;
> > > +
> > > +	return bytes_to_frames(runtime, dw->buf_offset);
> > 
> > So, this returns the offset that has been reformatted.  Does the
> > hardware support any better position reporting?  We may give the delay
> > from the driver if possible.
> 
> Basically, no.  Reading a 32-bit DMA position as separate bytes while
> DMA is active is racy.
> 
> This is the best we can do, and the way we report the position has been
> arrived at after what's getting on for two years of testing with
> pulseaudio, vlc direct access & spdif pass-through, aplay, etc:
> 
> Author: Russell King <rmk+kernel@arm.linux.org.uk>
> Date:   Thu Nov 7 16:01:45 2013 +0000
> 
>     drm: bridge/dw_hdmi-ahb-audio: add audio driver

OK, then this is a driver with the low update granularity.  Hopefully
we'll get some good API to indicate that in near future, as we've been
discussing about it for a while.


thanks,

Takashi
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver
@ 2015-08-10 12:23           ` Takashi Iwai
  0 siblings, 0 replies; 226+ messages in thread
From: Takashi Iwai @ 2015-08-10 12:23 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, 10 Aug 2015 12:39:21 +0200,
Russell King - ARM Linux wrote:
> 
> On Mon, Aug 10, 2015 at 12:05:07PM +0200, Takashi Iwai wrote:
> > On Sat, 08 Aug 2015 18:10:06 +0200,
> > Russell King wrote:
> > > +static irqreturn_t snd_dw_hdmi_irq(int irq, void *data)
> > > +{
> > > +	struct snd_dw_hdmi *dw = data;
> > > +	struct snd_pcm_substream *substream;
> > > +	unsigned stat;
> > > +
> > > +	stat = readb_relaxed(dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
> > > +	if (!stat)
> > > +		return IRQ_NONE;
> > > +
> > > +	writeb_relaxed(stat, dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
> > > +
> > > +	substream = dw->substream;
> > > +	if (stat & HDMI_IH_AHBDMAAUD_STAT0_DONE && substream) {
> > > +		snd_pcm_period_elapsed(substream);
> > > +		if (dw->substream)
> > > +			dw_hdmi_start_dma(dw);
> > > +	}
> > 
> > Don't we need locking?
> 
> Possibly.
> 
> > In theory, the trigger can be issued while the irq is being handled.
> 
> Well, we can't have a lock around the whole of the above, because that
> results in deadlock (as snd_pcm_period_elapsed() can end up calling into
> the trigger method.)

Yes, and a usual workaround is to unlock temporarily at calling
snd_pcm_period_elapsed(), then relock or call it at the end of 
handler.

> I'm not happy to throw a spinlock around this
> because of the in-built format conversion (something else I'm really not
> happy about - which has to exist here because alsalib is soo painful
> to add custom sample reformatting to - such modules have to be built
> as part of alsalib itself rather than an add-on module.)

I admit that alsa-lib code is very horrible to follow -- but I guess
the change you'd need for iec958 plugin would be fairly small.  We can
add a config option and let iec958 behaving slightly differently
depending on it.

Meanwhile, having an in-kernel workaround makes it much easier to
deploy, so I think it's OK to have this in driver for now.

> > > +static int dw_hdmi_trigger(struct snd_pcm_substream *substream, int cmd)
> > > +{
> > > +	struct snd_dw_hdmi *dw = substream->private_data;
> > > +	int ret = 0;
> > > +
> > > +	switch (cmd) {
> > > +	case SNDRV_PCM_TRIGGER_START:
> > > +		dw->buf_offset = 0;
> > > +		dw->substream = substream;
> > > +		dw_hdmi_start_dma(dw);
> > > +		dw_hdmi_audio_enable(dw->data.hdmi);
> > > +		substream->runtime->delay = substream->runtime->period_size;
> > > +		break;
> > > +
> > > +	case SNDRV_PCM_TRIGGER_STOP:
> > > +		dw_hdmi_stop_dma(dw);
> > > +		dw_hdmi_audio_disable(dw->data.hdmi);
> > > +		break;
> > > +
> > > +	default:
> > > +		ret = -EINVAL;
> > > +		break;
> > 
> > SNDRV_PCM_TRIGGER_SUSPEND may be passed at suspend, too.
> 
> I think rather than adding code which would be difficult for me to test,
> I'd instead remove the suspend/resume callbacks, or at least disable them
> until someone can test that feature, or is willing to implement it.

That's fine.

> > > +static snd_pcm_uframes_t dw_hdmi_pointer(struct snd_pcm_substream *substream)
> > > +{
> > > +	struct snd_pcm_runtime *runtime = substream->runtime;
> > > +	struct snd_dw_hdmi *dw = substream->private_data;
> > > +
> > > +	return bytes_to_frames(runtime, dw->buf_offset);
> > 
> > So, this returns the offset that has been reformatted.  Does the
> > hardware support any better position reporting?  We may give the delay
> > from the driver if possible.
> 
> Basically, no.  Reading a 32-bit DMA position as separate bytes while
> DMA is active is racy.
> 
> This is the best we can do, and the way we report the position has been
> arrived at after what's getting on for two years of testing with
> pulseaudio, vlc direct access & spdif pass-through, aplay, etc:
> 
> Author: Russell King <rmk+kernel@arm.linux.org.uk>
> Date:   Thu Nov 7 16:01:45 2013 +0000
> 
>     drm: bridge/dw_hdmi-ahb-audio: add audio driver

OK, then this is a driver with the low update granularity.  Hopefully
we'll get some good API to indicate that in near future, as we've been
discussing about it for a while.


thanks,

Takashi

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 9/9] drm: bridge/dw_hdmi-i2s-audio: add audio driver
  2015-08-08 16:10     ` Russell King
  (?)
@ 2015-08-10 15:48       ` Russell King - ARM Linux
  -1 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-08-10 15:48 UTC (permalink / raw)
  To: linux-rockchip, alsa-devel, dri-devel, linux-kernel, linux-arm-kernel
  Cc: Fabio Estevam, Takashi Iwai, Sascha Hauer, Jaroslav Kysela,
	David Airlie, Mark Brown, Philipp Zabel, Yakir Yang, Andy Yan,
	Jon Nettleton

On Sat, Aug 08, 2015 at 05:10:47PM +0100, Russell King wrote:
> From: Yakir Yang <ykk@rock-chips.com>
> 
> Add ALSA based HDMI I2S audio driver for dw_hdmi. Sound card
> driver could connect to this codec through the codec dai name
> "dw-hdmi-i2s-audio".
> 
> [Fixed IRQ name, MODULE_DESCRIPTION, MODULE_ALIAS in
>  dw-hdmi-i2s-audio.c, and platform device name in dw-hdmi.c --rmk]
> 
> Signed-off-by: Yakir Yang <ykk@rock-chips.com>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

I'm dropping this patch after all as it no longer builds against modern
kernels due to the reference to the removed snd_soc_jack_new().  Its
replacement is at card level, and I don't think it's a simple case of
replacing it here.

> +static int snd_dw_hdmi_audio_probe(struct snd_soc_codec *codec)
> +{
> +	struct snd_dw_hdmi *dw = snd_soc_codec_get_drvdata(codec);
> +	int ret;
> +
> +	ret = snd_soc_jack_new(codec, "dw Jack", SND_JACK_LINEOUT,
> +			       &dw->jack);
...
> +static const struct snd_soc_codec_driver dw_hdmi_audio = {
> +	.probe = snd_dw_hdmi_audio_probe,
> +	.dapm_widgets = snd_dw_hdmi_audio_widgets,
> +	.num_dapm_widgets = ARRAY_SIZE(snd_dw_hdmi_audio_widgets),
> +	.dapm_routes = snd_dw_hdmi_audio_routes,
> +	.num_dapm_routes = ARRAY_SIZE(snd_dw_hdmi_audio_routes),
> +};
> +
> +static int dw_hdmi_audio_probe(struct platform_device *pdev)
> +{
> +	struct dw_hdmi_audio_data *data = pdev->dev.platform_data;
> +	struct snd_dw_hdmi *dw;
> +	int ret;
> +
> +	dw = devm_kzalloc(&pdev->dev, sizeof(*dw), GFP_KERNEL);
> +	if (!dw)
> +		return -ENOMEM;
> +
> +	dw->data = *data;
> +	dw->dev = &pdev->dev;
> +	dw->is_jack_ready = false;
> +	platform_set_drvdata(pdev, dw);
> +
> +	ret = request_irq(dw->data.irq, snd_dw_hdmi_irq, IRQF_SHARED,
> +			  DRIVER_NAME, dw);
> +	if (ret) {
> +		dev_err(&pdev->dev, "request irq failed (%d)\n", ret);
> +		return -EINVAL;
> +	}
> +
> +	ret = snd_soc_register_codec(&pdev->dev, &dw_hdmi_audio,
> +				     &dw_hdmi_audio_dai, 1);

-- 
FTTC broadband for 0.8mile line: currently at 10.5Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 9/9] drm: bridge/dw_hdmi-i2s-audio: add audio driver
@ 2015-08-10 15:48       ` Russell King - ARM Linux
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-08-10 15:48 UTC (permalink / raw)
  To: linux-rockchip, alsa-devel, dri-devel, linux-kernel, linux-arm-kernel
  Cc: Fabio Estevam, Jaroslav Kysela, Mark Brown, Yakir Yang, Andy Yan

On Sat, Aug 08, 2015 at 05:10:47PM +0100, Russell King wrote:
> From: Yakir Yang <ykk@rock-chips.com>
> 
> Add ALSA based HDMI I2S audio driver for dw_hdmi. Sound card
> driver could connect to this codec through the codec dai name
> "dw-hdmi-i2s-audio".
> 
> [Fixed IRQ name, MODULE_DESCRIPTION, MODULE_ALIAS in
>  dw-hdmi-i2s-audio.c, and platform device name in dw-hdmi.c --rmk]
> 
> Signed-off-by: Yakir Yang <ykk@rock-chips.com>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

I'm dropping this patch after all as it no longer builds against modern
kernels due to the reference to the removed snd_soc_jack_new().  Its
replacement is at card level, and I don't think it's a simple case of
replacing it here.

> +static int snd_dw_hdmi_audio_probe(struct snd_soc_codec *codec)
> +{
> +	struct snd_dw_hdmi *dw = snd_soc_codec_get_drvdata(codec);
> +	int ret;
> +
> +	ret = snd_soc_jack_new(codec, "dw Jack", SND_JACK_LINEOUT,
> +			       &dw->jack);
...
> +static const struct snd_soc_codec_driver dw_hdmi_audio = {
> +	.probe = snd_dw_hdmi_audio_probe,
> +	.dapm_widgets = snd_dw_hdmi_audio_widgets,
> +	.num_dapm_widgets = ARRAY_SIZE(snd_dw_hdmi_audio_widgets),
> +	.dapm_routes = snd_dw_hdmi_audio_routes,
> +	.num_dapm_routes = ARRAY_SIZE(snd_dw_hdmi_audio_routes),
> +};
> +
> +static int dw_hdmi_audio_probe(struct platform_device *pdev)
> +{
> +	struct dw_hdmi_audio_data *data = pdev->dev.platform_data;
> +	struct snd_dw_hdmi *dw;
> +	int ret;
> +
> +	dw = devm_kzalloc(&pdev->dev, sizeof(*dw), GFP_KERNEL);
> +	if (!dw)
> +		return -ENOMEM;
> +
> +	dw->data = *data;
> +	dw->dev = &pdev->dev;
> +	dw->is_jack_ready = false;
> +	platform_set_drvdata(pdev, dw);
> +
> +	ret = request_irq(dw->data.irq, snd_dw_hdmi_irq, IRQF_SHARED,
> +			  DRIVER_NAME, dw);
> +	if (ret) {
> +		dev_err(&pdev->dev, "request irq failed (%d)\n", ret);
> +		return -EINVAL;
> +	}
> +
> +	ret = snd_soc_register_codec(&pdev->dev, &dw_hdmi_audio,
> +				     &dw_hdmi_audio_dai, 1);

-- 
FTTC broadband for 0.8mile line: currently at 10.5Mbps down 400kbps up
according to speedtest.net.
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 9/9] drm: bridge/dw_hdmi-i2s-audio: add audio driver
@ 2015-08-10 15:48       ` Russell King - ARM Linux
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-08-10 15:48 UTC (permalink / raw)
  To: linux-arm-kernel

On Sat, Aug 08, 2015 at 05:10:47PM +0100, Russell King wrote:
> From: Yakir Yang <ykk@rock-chips.com>
> 
> Add ALSA based HDMI I2S audio driver for dw_hdmi. Sound card
> driver could connect to this codec through the codec dai name
> "dw-hdmi-i2s-audio".
> 
> [Fixed IRQ name, MODULE_DESCRIPTION, MODULE_ALIAS in
>  dw-hdmi-i2s-audio.c, and platform device name in dw-hdmi.c --rmk]
> 
> Signed-off-by: Yakir Yang <ykk@rock-chips.com>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

I'm dropping this patch after all as it no longer builds against modern
kernels due to the reference to the removed snd_soc_jack_new().  Its
replacement is at card level, and I don't think it's a simple case of
replacing it here.

> +static int snd_dw_hdmi_audio_probe(struct snd_soc_codec *codec)
> +{
> +	struct snd_dw_hdmi *dw = snd_soc_codec_get_drvdata(codec);
> +	int ret;
> +
> +	ret = snd_soc_jack_new(codec, "dw Jack", SND_JACK_LINEOUT,
> +			       &dw->jack);
...
> +static const struct snd_soc_codec_driver dw_hdmi_audio = {
> +	.probe = snd_dw_hdmi_audio_probe,
> +	.dapm_widgets = snd_dw_hdmi_audio_widgets,
> +	.num_dapm_widgets = ARRAY_SIZE(snd_dw_hdmi_audio_widgets),
> +	.dapm_routes = snd_dw_hdmi_audio_routes,
> +	.num_dapm_routes = ARRAY_SIZE(snd_dw_hdmi_audio_routes),
> +};
> +
> +static int dw_hdmi_audio_probe(struct platform_device *pdev)
> +{
> +	struct dw_hdmi_audio_data *data = pdev->dev.platform_data;
> +	struct snd_dw_hdmi *dw;
> +	int ret;
> +
> +	dw = devm_kzalloc(&pdev->dev, sizeof(*dw), GFP_KERNEL);
> +	if (!dw)
> +		return -ENOMEM;
> +
> +	dw->data = *data;
> +	dw->dev = &pdev->dev;
> +	dw->is_jack_ready = false;
> +	platform_set_drvdata(pdev, dw);
> +
> +	ret = request_irq(dw->data.irq, snd_dw_hdmi_irq, IRQF_SHARED,
> +			  DRIVER_NAME, dw);
> +	if (ret) {
> +		dev_err(&pdev->dev, "request irq failed (%d)\n", ret);
> +		return -EINVAL;
> +	}
> +
> +	ret = snd_soc_register_codec(&pdev->dev, &dw_hdmi_audio,
> +				     &dw_hdmi_audio_dai, 1);

-- 
FTTC broadband for 0.8mile line: currently at 10.5Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 9/9] drm: bridge/dw_hdmi-i2s-audio: add audio driver
  2015-08-10 15:48       ` Russell King - ARM Linux
  (?)
@ 2015-08-10 16:26         ` Yakir Yang
  -1 siblings, 0 replies; 226+ messages in thread
From: Yakir Yang @ 2015-08-10 16:26 UTC (permalink / raw)
  To: Russell King - ARM Linux, linux-rockchip, alsa-devel, dri-devel,
	linux-kernel, linux-arm-kernel
  Cc: Fabio Estevam, Takashi Iwai, Sascha Hauer, Jaroslav Kysela,
	David Airlie, Mark Brown, Philipp Zabel, Andy Yan, Jon Nettleton

Hi Russell,

在 2015/8/10 23:48, Russell King - ARM Linux 写道:
> On Sat, Aug 08, 2015 at 05:10:47PM +0100, Russell King wrote:
>> From: Yakir Yang <ykk@rock-chips.com>
>>
>> Add ALSA based HDMI I2S audio driver for dw_hdmi. Sound card
>> driver could connect to this codec through the codec dai name
>> "dw-hdmi-i2s-audio".
>>
>> [Fixed IRQ name, MODULE_DESCRIPTION, MODULE_ALIAS in
>>   dw-hdmi-i2s-audio.c, and platform device name in dw-hdmi.c --rmk]
>>
>> Signed-off-by: Yakir Yang <ykk@rock-chips.com>
>> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> I'm dropping this patch after all as it no longer builds against modern
> kernels due to the reference to the removed snd_soc_jack_new().  Its
> replacement is at card level, and I don't think it's a simple case of
> replacing it here.

Hmm... I would rather to fix it in my side, and then I could rebase on 
your series, is it okay ?

- Yakir

>> +static int snd_dw_hdmi_audio_probe(struct snd_soc_codec *codec)
>> +{
>> +	struct snd_dw_hdmi *dw = snd_soc_codec_get_drvdata(codec);
>> +	int ret;
>> +
>> +	ret = snd_soc_jack_new(codec, "dw Jack", SND_JACK_LINEOUT,
>> +			       &dw->jack);
> ...
>> +static const struct snd_soc_codec_driver dw_hdmi_audio = {
>> +	.probe = snd_dw_hdmi_audio_probe,
>> +	.dapm_widgets = snd_dw_hdmi_audio_widgets,
>> +	.num_dapm_widgets = ARRAY_SIZE(snd_dw_hdmi_audio_widgets),
>> +	.dapm_routes = snd_dw_hdmi_audio_routes,
>> +	.num_dapm_routes = ARRAY_SIZE(snd_dw_hdmi_audio_routes),
>> +};
>> +
>> +static int dw_hdmi_audio_probe(struct platform_device *pdev)
>> +{
>> +	struct dw_hdmi_audio_data *data = pdev->dev.platform_data;
>> +	struct snd_dw_hdmi *dw;
>> +	int ret;
>> +
>> +	dw = devm_kzalloc(&pdev->dev, sizeof(*dw), GFP_KERNEL);
>> +	if (!dw)
>> +		return -ENOMEM;
>> +
>> +	dw->data = *data;
>> +	dw->dev = &pdev->dev;
>> +	dw->is_jack_ready = false;
>> +	platform_set_drvdata(pdev, dw);
>> +
>> +	ret = request_irq(dw->data.irq, snd_dw_hdmi_irq, IRQF_SHARED,
>> +			  DRIVER_NAME, dw);
>> +	if (ret) {
>> +		dev_err(&pdev->dev, "request irq failed (%d)\n", ret);
>> +		return -EINVAL;
>> +	}
>> +
>> +	ret = snd_soc_register_codec(&pdev->dev, &dw_hdmi_audio,
>> +				     &dw_hdmi_audio_dai, 1);



^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 9/9] drm: bridge/dw_hdmi-i2s-audio: add audio driver
@ 2015-08-10 16:26         ` Yakir Yang
  0 siblings, 0 replies; 226+ messages in thread
From: Yakir Yang @ 2015-08-10 16:26 UTC (permalink / raw)
  To: Russell King - ARM Linux, linux-rockchip, alsa-devel, dri-devel,
	linux-kernel, linux-arm-kernel
  Cc: Fabio Estevam, Jaroslav Kysela, Mark Brown, Andy Yan

Hi Russell,

在 2015/8/10 23:48, Russell King - ARM Linux 写道:
> On Sat, Aug 08, 2015 at 05:10:47PM +0100, Russell King wrote:
>> From: Yakir Yang <ykk@rock-chips.com>
>>
>> Add ALSA based HDMI I2S audio driver for dw_hdmi. Sound card
>> driver could connect to this codec through the codec dai name
>> "dw-hdmi-i2s-audio".
>>
>> [Fixed IRQ name, MODULE_DESCRIPTION, MODULE_ALIAS in
>>   dw-hdmi-i2s-audio.c, and platform device name in dw-hdmi.c --rmk]
>>
>> Signed-off-by: Yakir Yang <ykk@rock-chips.com>
>> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> I'm dropping this patch after all as it no longer builds against modern
> kernels due to the reference to the removed snd_soc_jack_new().  Its
> replacement is at card level, and I don't think it's a simple case of
> replacing it here.

Hmm... I would rather to fix it in my side, and then I could rebase on 
your series, is it okay ?

- Yakir

>> +static int snd_dw_hdmi_audio_probe(struct snd_soc_codec *codec)
>> +{
>> +	struct snd_dw_hdmi *dw = snd_soc_codec_get_drvdata(codec);
>> +	int ret;
>> +
>> +	ret = snd_soc_jack_new(codec, "dw Jack", SND_JACK_LINEOUT,
>> +			       &dw->jack);
> ...
>> +static const struct snd_soc_codec_driver dw_hdmi_audio = {
>> +	.probe = snd_dw_hdmi_audio_probe,
>> +	.dapm_widgets = snd_dw_hdmi_audio_widgets,
>> +	.num_dapm_widgets = ARRAY_SIZE(snd_dw_hdmi_audio_widgets),
>> +	.dapm_routes = snd_dw_hdmi_audio_routes,
>> +	.num_dapm_routes = ARRAY_SIZE(snd_dw_hdmi_audio_routes),
>> +};
>> +
>> +static int dw_hdmi_audio_probe(struct platform_device *pdev)
>> +{
>> +	struct dw_hdmi_audio_data *data = pdev->dev.platform_data;
>> +	struct snd_dw_hdmi *dw;
>> +	int ret;
>> +
>> +	dw = devm_kzalloc(&pdev->dev, sizeof(*dw), GFP_KERNEL);
>> +	if (!dw)
>> +		return -ENOMEM;
>> +
>> +	dw->data = *data;
>> +	dw->dev = &pdev->dev;
>> +	dw->is_jack_ready = false;
>> +	platform_set_drvdata(pdev, dw);
>> +
>> +	ret = request_irq(dw->data.irq, snd_dw_hdmi_irq, IRQF_SHARED,
>> +			  DRIVER_NAME, dw);
>> +	if (ret) {
>> +		dev_err(&pdev->dev, "request irq failed (%d)\n", ret);
>> +		return -EINVAL;
>> +	}
>> +
>> +	ret = snd_soc_register_codec(&pdev->dev, &dw_hdmi_audio,
>> +				     &dw_hdmi_audio_dai, 1);


_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 9/9] drm: bridge/dw_hdmi-i2s-audio: add audio driver
@ 2015-08-10 16:26         ` Yakir Yang
  0 siblings, 0 replies; 226+ messages in thread
From: Yakir Yang @ 2015-08-10 16:26 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Russell,

? 2015/8/10 23:48, Russell King - ARM Linux ??:
> On Sat, Aug 08, 2015 at 05:10:47PM +0100, Russell King wrote:
>> From: Yakir Yang <ykk@rock-chips.com>
>>
>> Add ALSA based HDMI I2S audio driver for dw_hdmi. Sound card
>> driver could connect to this codec through the codec dai name
>> "dw-hdmi-i2s-audio".
>>
>> [Fixed IRQ name, MODULE_DESCRIPTION, MODULE_ALIAS in
>>   dw-hdmi-i2s-audio.c, and platform device name in dw-hdmi.c --rmk]
>>
>> Signed-off-by: Yakir Yang <ykk@rock-chips.com>
>> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> I'm dropping this patch after all as it no longer builds against modern
> kernels due to the reference to the removed snd_soc_jack_new().  Its
> replacement is at card level, and I don't think it's a simple case of
> replacing it here.

Hmm... I would rather to fix it in my side, and then I could rebase on 
your series, is it okay ?

- Yakir

>> +static int snd_dw_hdmi_audio_probe(struct snd_soc_codec *codec)
>> +{
>> +	struct snd_dw_hdmi *dw = snd_soc_codec_get_drvdata(codec);
>> +	int ret;
>> +
>> +	ret = snd_soc_jack_new(codec, "dw Jack", SND_JACK_LINEOUT,
>> +			       &dw->jack);
> ...
>> +static const struct snd_soc_codec_driver dw_hdmi_audio = {
>> +	.probe = snd_dw_hdmi_audio_probe,
>> +	.dapm_widgets = snd_dw_hdmi_audio_widgets,
>> +	.num_dapm_widgets = ARRAY_SIZE(snd_dw_hdmi_audio_widgets),
>> +	.dapm_routes = snd_dw_hdmi_audio_routes,
>> +	.num_dapm_routes = ARRAY_SIZE(snd_dw_hdmi_audio_routes),
>> +};
>> +
>> +static int dw_hdmi_audio_probe(struct platform_device *pdev)
>> +{
>> +	struct dw_hdmi_audio_data *data = pdev->dev.platform_data;
>> +	struct snd_dw_hdmi *dw;
>> +	int ret;
>> +
>> +	dw = devm_kzalloc(&pdev->dev, sizeof(*dw), GFP_KERNEL);
>> +	if (!dw)
>> +		return -ENOMEM;
>> +
>> +	dw->data = *data;
>> +	dw->dev = &pdev->dev;
>> +	dw->is_jack_ready = false;
>> +	platform_set_drvdata(pdev, dw);
>> +
>> +	ret = request_irq(dw->data.irq, snd_dw_hdmi_irq, IRQF_SHARED,
>> +			  DRIVER_NAME, dw);
>> +	if (ret) {
>> +		dev_err(&pdev->dev, "request irq failed (%d)\n", ret);
>> +		return -EINVAL;
>> +	}
>> +
>> +	ret = snd_soc_register_codec(&pdev->dev, &dw_hdmi_audio,
>> +				     &dw_hdmi_audio_dai, 1);

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver
  2015-08-10 12:23           ` Takashi Iwai
  (?)
@ 2015-08-10 16:49             ` Russell King - ARM Linux
  -1 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-08-10 16:49 UTC (permalink / raw)
  To: Takashi Iwai
  Cc: linux-rockchip, alsa-devel, dri-devel, linux-kernel,
	linux-arm-kernel, Philipp Zabel, Andy Yan, Yakir Yang,
	Fabio Estevam, Mark Brown, Jaroslav Kysela, Sascha Hauer,
	Jon Nettleton, David Airlie

On Mon, Aug 10, 2015 at 02:23:07PM +0200, Takashi Iwai wrote:
> I admit that alsa-lib code is very horrible to follow -- but I guess
> the change you'd need for iec958 plugin would be fairly small.  We can
> add a config option and let iec958 behaving slightly differently
> depending on it.

Yes, but there's other problems there as well.

The IEC958 plugin does the job of adding the 4 AES bytes and formatting
fairly well, but the problem when the 'default' bytes specified in the
ALSA configuration files are used.

Let's take the old chestnut of PulseAudio, or even aplay, or the miriad
of other audio-only players out there.

Most of them do not supply the AES bytes to be used, so we end up with
the default.

The default is... 0x04 0x82 0x00 0x02, which specifies a sample rate
of 48kHz.  However, the actual sample rate may not be 48kHz.  At least
the HDMI specifications say that the channel status data must be correct,
and there are AV receivers out there which do make use of this, and if
the channel status does not agree with the actual sample rate, they
either refuse to recognise the audio stream (saying there's nothing
there) or they intermittently mute the audio.  Yamaha RX-V677 is one
example which has this behaviour.

The only compliant program that I've found so far is VLC in SPDIF
pass-through mode, which is the only case where VLC passes the
channel status information.  Everything else seems broken in this
regard, by falling back to the default.

Obviously, aplay can be made to work by setting the AES bytes
manually when specifying the device for it to use, but this is not
really user-friendly or programmer friendly - especially as the
current use model expects things to "just work" (the common case
being PCM output on a PC which doesn't care about channel status.)

I'm not sure what the right solution is here: modifying every audio
player out there to make HDMI work sanely is crazy.  Having alsalib
automatically generate the correct AES channel status bytes for
linear audio formats seems to be sensible, but difficult given its
present structure with the defaults - the iec958 plugin has no idea
if the defaults are being used or not.

The advantage of having the horrid conversion in the kernel is that
we can choose to generate proper AES channel status data without
regard to userspace for standard linear PCM, and when the iec958 plugin
is being used with proper channel status (eg, in compressed audio
pass-through mode by VLC) then that works too.

-- 
FTTC broadband for 0.8mile line: currently at 10.5Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver
@ 2015-08-10 16:49             ` Russell King - ARM Linux
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-08-10 16:49 UTC (permalink / raw)
  To: Takashi Iwai
  Cc: Fabio Estevam, alsa-devel, linux-kernel, dri-devel,
	Jaroslav Kysela, linux-rockchip, Mark Brown, Yakir Yang,
	Andy Yan, linux-arm-kernel

On Mon, Aug 10, 2015 at 02:23:07PM +0200, Takashi Iwai wrote:
> I admit that alsa-lib code is very horrible to follow -- but I guess
> the change you'd need for iec958 plugin would be fairly small.  We can
> add a config option and let iec958 behaving slightly differently
> depending on it.

Yes, but there's other problems there as well.

The IEC958 plugin does the job of adding the 4 AES bytes and formatting
fairly well, but the problem when the 'default' bytes specified in the
ALSA configuration files are used.

Let's take the old chestnut of PulseAudio, or even aplay, or the miriad
of other audio-only players out there.

Most of them do not supply the AES bytes to be used, so we end up with
the default.

The default is... 0x04 0x82 0x00 0x02, which specifies a sample rate
of 48kHz.  However, the actual sample rate may not be 48kHz.  At least
the HDMI specifications say that the channel status data must be correct,
and there are AV receivers out there which do make use of this, and if
the channel status does not agree with the actual sample rate, they
either refuse to recognise the audio stream (saying there's nothing
there) or they intermittently mute the audio.  Yamaha RX-V677 is one
example which has this behaviour.

The only compliant program that I've found so far is VLC in SPDIF
pass-through mode, which is the only case where VLC passes the
channel status information.  Everything else seems broken in this
regard, by falling back to the default.

Obviously, aplay can be made to work by setting the AES bytes
manually when specifying the device for it to use, but this is not
really user-friendly or programmer friendly - especially as the
current use model expects things to "just work" (the common case
being PCM output on a PC which doesn't care about channel status.)

I'm not sure what the right solution is here: modifying every audio
player out there to make HDMI work sanely is crazy.  Having alsalib
automatically generate the correct AES channel status bytes for
linear audio formats seems to be sensible, but difficult given its
present structure with the defaults - the iec958 plugin has no idea
if the defaults are being used or not.

The advantage of having the horrid conversion in the kernel is that
we can choose to generate proper AES channel status data without
regard to userspace for standard linear PCM, and when the iec958 plugin
is being used with proper channel status (eg, in compressed audio
pass-through mode by VLC) then that works too.

-- 
FTTC broadband for 0.8mile line: currently at 10.5Mbps down 400kbps up
according to speedtest.net.
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver
@ 2015-08-10 16:49             ` Russell King - ARM Linux
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-08-10 16:49 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Aug 10, 2015 at 02:23:07PM +0200, Takashi Iwai wrote:
> I admit that alsa-lib code is very horrible to follow -- but I guess
> the change you'd need for iec958 plugin would be fairly small.  We can
> add a config option and let iec958 behaving slightly differently
> depending on it.

Yes, but there's other problems there as well.

The IEC958 plugin does the job of adding the 4 AES bytes and formatting
fairly well, but the problem when the 'default' bytes specified in the
ALSA configuration files are used.

Let's take the old chestnut of PulseAudio, or even aplay, or the miriad
of other audio-only players out there.

Most of them do not supply the AES bytes to be used, so we end up with
the default.

The default is... 0x04 0x82 0x00 0x02, which specifies a sample rate
of 48kHz.  However, the actual sample rate may not be 48kHz.  At least
the HDMI specifications say that the channel status data must be correct,
and there are AV receivers out there which do make use of this, and if
the channel status does not agree with the actual sample rate, they
either refuse to recognise the audio stream (saying there's nothing
there) or they intermittently mute the audio.  Yamaha RX-V677 is one
example which has this behaviour.

The only compliant program that I've found so far is VLC in SPDIF
pass-through mode, which is the only case where VLC passes the
channel status information.  Everything else seems broken in this
regard, by falling back to the default.

Obviously, aplay can be made to work by setting the AES bytes
manually when specifying the device for it to use, but this is not
really user-friendly or programmer friendly - especially as the
current use model expects things to "just work" (the common case
being PCM output on a PC which doesn't care about channel status.)

I'm not sure what the right solution is here: modifying every audio
player out there to make HDMI work sanely is crazy.  Having alsalib
automatically generate the correct AES channel status bytes for
linear audio formats seems to be sensible, but difficult given its
present structure with the defaults - the iec958 plugin has no idea
if the defaults are being used or not.

The advantage of having the horrid conversion in the kernel is that
we can choose to generate proper AES channel status data without
regard to userspace for standard linear PCM, and when the iec958 plugin
is being used with proper channel status (eg, in compressed audio
pass-through mode by VLC) then that works too.

-- 
FTTC broadband for 0.8mile line: currently at 10.5Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver
  2015-08-10 16:49             ` Russell King - ARM Linux
@ 2015-08-10 18:16               ` Mark Brown
  -1 siblings, 0 replies; 226+ messages in thread
From: Mark Brown @ 2015-08-10 18:16 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Takashi Iwai, linux-rockchip, alsa-devel, dri-devel,
	linux-kernel, linux-arm-kernel, Philipp Zabel, Andy Yan,
	Yakir Yang, Fabio Estevam, Jaroslav Kysela, Sascha Hauer,
	Jon Nettleton, David Airlie

[-- Attachment #1: Type: text/plain, Size: 939 bytes --]

On Mon, Aug 10, 2015 at 05:49:41PM +0100, Russell King - ARM Linux wrote:

> I'm not sure what the right solution is here: modifying every audio
> player out there to make HDMI work sanely is crazy.  Having alsalib
> automatically generate the correct AES channel status bytes for
> linear audio formats seems to be sensible, but difficult given its
> present structure with the defaults - the iec958 plugin has no idea
> if the defaults are being used or not.

> The advantage of having the horrid conversion in the kernel is that
> we can choose to generate proper AES channel status data without
> regard to userspace for standard linear PCM, and when the iec958 plugin
> is being used with proper channel status (eg, in compressed audio
> pass-through mode by VLC) then that works too.

The other advantage of doing it in kernel is that it also fixes tinyalsa
applications (which mainly means Android systems) by default for PCM
data.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 473 bytes --]

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver
@ 2015-08-10 18:16               ` Mark Brown
  0 siblings, 0 replies; 226+ messages in thread
From: Mark Brown @ 2015-08-10 18:16 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Aug 10, 2015 at 05:49:41PM +0100, Russell King - ARM Linux wrote:

> I'm not sure what the right solution is here: modifying every audio
> player out there to make HDMI work sanely is crazy.  Having alsalib
> automatically generate the correct AES channel status bytes for
> linear audio formats seems to be sensible, but difficult given its
> present structure with the defaults - the iec958 plugin has no idea
> if the defaults are being used or not.

> The advantage of having the horrid conversion in the kernel is that
> we can choose to generate proper AES channel status data without
> regard to userspace for standard linear PCM, and when the iec958 plugin
> is being used with proper channel status (eg, in compressed audio
> pass-through mode by VLC) then that works too.

The other advantage of doing it in kernel is that it also fixes tinyalsa
applications (which mainly means Android systems) by default for PCM
data.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 473 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20150810/8d801c69/attachment.sig>

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH v2 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver David Airlie <airlied@linux.ie>, Sascha Hauer <s.hauer@pengutronix.de>, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, Jaroslav Kysela <perex@perex.cz>, linux-rockchip@lists.infradead.org, Mark Brown <broonie@kernel.org>, Philipp Zabel <p.zabel@pengutronix.de>, Yakir Yang <ykk@rock-chips.com>, Andy Yan <andy.yan@rock-chips.com>, Jon Nettleton <jon.nettleton@gmail.com>, linux-arm-kernel@lists.infradead.org
  2015-08-10 10:05       ` Takashi Iwai
  (?)
@ 2015-08-14 13:54         ` Russell King
  -1 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-14 13:54 UTC (permalink / raw)
  To: Takashi Iwai
  Cc: Fabio Estevam, alsa-devel, David Airlie, Sascha Hauer,
	linux-kernel, dri-devel, Jaroslav Kysela, linux-rockchip,
	Mark Brown, Philipp Zabel, Yakir Yang, Andy Yan, Jon Nettleton,
	linux-arm-kernel

Add ALSA based HDMI AHB audio driver for dw_hdmi.  The only buffer
format supported by the hardware is its own special IEC958 based format,
which is not compatible with any ALSA format.  To avoid doing too much
data manipulation within the driver, we support only ALSAs IEC958 LE and
24-bit PCM formats for 2 to 6 channels, which we convert to its hardware
format.

A more desirable solution would be to have this conversion in userspace,
but ALSA does not appear to allow such transformations outside of
libasound itself.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
v2: updated with Takashi Iwai's comments.

 drivers/gpu/drm/bridge/Kconfig             |  10 +
 drivers/gpu/drm/bridge/Makefile            |   1 +
 drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c | 579 +++++++++++++++++++++++++++++
 drivers/gpu/drm/bridge/dw_hdmi-audio.h     |  13 +
 drivers/gpu/drm/bridge/dw_hdmi.c           |  24 ++
 drivers/gpu/drm/bridge/dw_hdmi.h           |   3 +
 6 files changed, 630 insertions(+)
 create mode 100644 drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
 create mode 100644 drivers/gpu/drm/bridge/dw_hdmi-audio.h

diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index acef3223772c..56ed35fe0734 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -3,6 +3,16 @@ config DRM_DW_HDMI
 	depends on DRM
 	select DRM_KMS_HELPER
 
+config DRM_DW_HDMI_AHB_AUDIO
+	tristate "Synopsis Designware AHB Audio interface"
+	depends on DRM_DW_HDMI && SND
+	select SND_PCM
+	select SND_PCM_IEC958
+	help
+	  Support the AHB Audio interface which is part of the Synopsis
+	  Designware HDMI block.  This is used in conjunction with
+	  the i.MX6 HDMI driver.
+
 config DRM_PTN3460
 	tristate "PTN3460 DP/LVDS bridge"
 	depends on DRM
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index 8dfebd984370..eb80dbbb8365 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -3,3 +3,4 @@ ccflags-y := -Iinclude/drm
 obj-$(CONFIG_DRM_PS8622) += ps8622.o
 obj-$(CONFIG_DRM_PTN3460) += ptn3460.o
 obj-$(CONFIG_DRM_DW_HDMI) += dw_hdmi.o
+obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw_hdmi-ahb-audio.o
diff --git a/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
new file mode 100644
index 000000000000..bf379310008a
--- /dev/null
+++ b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
@@ -0,0 +1,579 @@
+/*
+ * DesignWare HDMI audio driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Written and tested against the Designware HDMI Tx found in iMX6.
+ */
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <drm/bridge/dw_hdmi.h>
+
+#include <sound/asoundef.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_iec958.h>
+
+#include "dw_hdmi-audio.h"
+
+#define DRIVER_NAME "dw-hdmi-ahb-audio"
+
+/* Provide some bits rather than bit offsets */
+enum {
+	HDMI_AHB_DMA_CONF0_SW_FIFO_RST = BIT(7),
+	HDMI_AHB_DMA_CONF0_EN_HLOCK = BIT(3),
+	HDMI_AHB_DMA_START_START = BIT(0),
+	HDMI_AHB_DMA_STOP_STOP = BIT(0),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR = BIT(5),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST = BIT(4),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY = BIT(3),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE = BIT(2),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL = BIT(1),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL =
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY,
+	HDMI_IH_AHBDMAAUD_STAT0_ERROR = BIT(5),
+	HDMI_IH_AHBDMAAUD_STAT0_LOST = BIT(4),
+	HDMI_IH_AHBDMAAUD_STAT0_RETRY = BIT(3),
+	HDMI_IH_AHBDMAAUD_STAT0_DONE = BIT(2),
+	HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL = BIT(1),
+	HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0),
+	HDMI_IH_AHBDMAAUD_STAT0_ALL =
+		HDMI_IH_AHBDMAAUD_STAT0_ERROR |
+		HDMI_IH_AHBDMAAUD_STAT0_LOST |
+		HDMI_IH_AHBDMAAUD_STAT0_RETRY |
+		HDMI_IH_AHBDMAAUD_STAT0_DONE |
+		HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL |
+		HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY,
+	HDMI_AHB_DMA_CONF0_INCR16 = 2 << 1,
+	HDMI_AHB_DMA_CONF0_INCR8 = 1 << 1,
+	HDMI_AHB_DMA_CONF0_INCR4 = 0,
+	HDMI_AHB_DMA_CONF0_BURST_MODE = BIT(0),
+	HDMI_AHB_DMA_MASK_DONE = BIT(7),
+	HDMI_REVISION_ID = 0x0001,
+	HDMI_IH_AHBDMAAUD_STAT0 = 0x0109,
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0 = 0x0189,
+	HDMI_AHB_DMA_CONF0 = 0x3600,
+	HDMI_AHB_DMA_START = 0x3601,
+	HDMI_AHB_DMA_STOP = 0x3602,
+	HDMI_AHB_DMA_THRSLD = 0x3603,
+	HDMI_AHB_DMA_STRADDR0 = 0x3604,
+	HDMI_AHB_DMA_STPADDR0 = 0x3608,
+	HDMI_AHB_DMA_MASK = 0x3614,
+	HDMI_AHB_DMA_POL = 0x3615,
+	HDMI_AHB_DMA_CONF1 = 0x3616,
+	HDMI_AHB_DMA_BUFFPOL = 0x361a,
+};
+
+struct snd_dw_hdmi {
+	struct snd_card *card;
+	struct snd_pcm *pcm;
+	spinlock_t lock;
+	struct dw_hdmi_audio_data data;
+	struct snd_pcm_substream *substream;
+	void (*reformat)(struct snd_dw_hdmi *, size_t, size_t);
+	void *buf_src;
+	void *buf_dst;
+	dma_addr_t buf_addr;
+	unsigned buf_offset;
+	unsigned buf_period;
+	unsigned buf_size;
+	unsigned channels;
+	u8 revision;
+	u8 iec_offset;
+	u8 cs[192][8];
+};
+
+static void dw_hdmi_writel(u32 val, void __iomem *ptr)
+{
+	writeb_relaxed(val, ptr);
+	writeb_relaxed(val >> 8, ptr + 1);
+	writeb_relaxed(val >> 16, ptr + 2);
+	writeb_relaxed(val >> 24, ptr + 3);
+}
+
+/*
+ * Convert to hardware format: The userspace buffer contains IEC958 samples,
+ * with the PCUV bits in bits 31..28 and audio samples in bits 27..4.  We
+ * need these to be in bits 27..24, with the IEC B bit in bit 28, and audio
+ * samples in 23..0.
+ *
+ * Default preamble in bits 3..0: 8 = block start, 4 = even 2 = odd
+ *
+ * Ideally, we could do with having the data properly formatted in userspace.
+ */
+static void dw_hdmi_reformat_iec958(struct snd_dw_hdmi *dw,
+	size_t offset, size_t bytes)
+{
+	u32 *src = dw->buf_src + offset;
+	u32 *dst = dw->buf_dst + offset;
+	u32 *end = dw->buf_src + offset + bytes;
+
+	do {
+		u32 b, sample = *src++;
+
+		b = (sample & 8) << (28 - 3);
+
+		sample >>= 4;
+
+		*dst++ = sample | b;
+	} while (src < end);
+}
+
+static u32 parity(u32 sample)
+{
+	sample ^= sample >> 16;
+	sample ^= sample >> 8;
+	sample ^= sample >> 4;
+	sample ^= sample >> 2;
+	sample ^= sample >> 1;
+	return (sample & 1) << 27;
+}
+
+static void dw_hdmi_reformat_s24(struct snd_dw_hdmi *dw,
+	size_t offset, size_t bytes)
+{
+	u32 *src = dw->buf_src + offset;
+	u32 *dst = dw->buf_dst + offset;
+	u32 *end = dw->buf_src + offset + bytes;
+
+	do {
+		unsigned i;
+		u8 *cs;
+
+		cs = dw->cs[dw->iec_offset++];
+		if (dw->iec_offset >= 192)
+			dw->iec_offset = 0;
+
+		i = dw->channels;
+		do {
+			u32 sample = *src++;
+
+			sample &= ~0xff000000;
+			sample |= *cs++ << 24;
+			sample |= parity(sample & ~0xf8000000);
+
+			*dst++ = sample;
+		} while (--i);
+	} while (src < end);
+}
+
+static void dw_hdmi_create_cs(struct snd_dw_hdmi *dw,
+	struct snd_pcm_runtime *runtime)
+{
+	u8 cs[4];
+	unsigned ch, i, j;
+
+	snd_pcm_create_iec958_consumer(runtime, cs, sizeof(cs));
+
+	memset(dw->cs, 0, sizeof(dw->cs));
+
+	for (ch = 0; ch < 8; ch++) {
+		cs[2] &= ~IEC958_AES2_CON_CHANNEL;
+		cs[2] |= (ch + 1) << 4;
+
+		for (i = 0; i < ARRAY_SIZE(cs); i++) {
+			unsigned c = cs[i];
+
+			for (j = 0; j < 8; j++, c >>= 1)
+				dw->cs[i * 8 + j][ch] = (c & 1) << 2;
+		}
+	}
+	dw->cs[0][0] |= BIT(4);
+}
+
+static void dw_hdmi_start_dma(struct snd_dw_hdmi *dw)
+{
+	void __iomem *base = dw->data.base;
+	unsigned offset = dw->buf_offset;
+	unsigned period = dw->buf_period;
+	u32 start, stop;
+
+	dw->reformat(dw, offset, period);
+
+	/* Clear all irqs before enabling irqs and starting DMA */
+	writeb_relaxed(HDMI_IH_AHBDMAAUD_STAT0_ALL,
+		       base + HDMI_IH_AHBDMAAUD_STAT0);
+
+	start = dw->buf_addr + offset;
+	stop = start + period - 1;
+
+	/* Setup the hardware start/stop addresses */
+	dw_hdmi_writel(start, base + HDMI_AHB_DMA_STRADDR0);
+	dw_hdmi_writel(stop, base + HDMI_AHB_DMA_STPADDR0);
+
+	writeb_relaxed((u8)~HDMI_AHB_DMA_MASK_DONE, base + HDMI_AHB_DMA_MASK);
+	writeb(HDMI_AHB_DMA_START_START, base + HDMI_AHB_DMA_START);
+
+	offset += period;
+	if (offset >= dw->buf_size)
+		offset = 0;
+	dw->buf_offset = offset;
+}
+
+static void dw_hdmi_stop_dma(struct snd_dw_hdmi *dw)
+{
+	/* Disable interrupts before disabling DMA */
+	writeb_relaxed(~0, dw->data.base + HDMI_AHB_DMA_MASK);
+	writeb_relaxed(HDMI_AHB_DMA_STOP_STOP, dw->data.base + HDMI_AHB_DMA_STOP);
+}
+
+static irqreturn_t snd_dw_hdmi_irq(int irq, void *data)
+{
+	struct snd_dw_hdmi *dw = data;
+	struct snd_pcm_substream *substream;
+	unsigned stat;
+
+	stat = readb_relaxed(dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
+	if (!stat)
+		return IRQ_NONE;
+
+	writeb_relaxed(stat, dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
+
+	substream = dw->substream;
+	if (stat & HDMI_IH_AHBDMAAUD_STAT0_DONE && substream) {
+		snd_pcm_period_elapsed(substream);
+
+		spin_lock(&dw->lock);
+		if (dw->substream)
+			dw_hdmi_start_dma(dw);
+		spin_unlock(&dw->lock);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static struct snd_pcm_hardware dw_hdmi_hw = {
+	.info = SNDRV_PCM_INFO_INTERLEAVED |
+		SNDRV_PCM_INFO_BLOCK_TRANSFER |
+		SNDRV_PCM_INFO_MMAP |
+		SNDRV_PCM_INFO_MMAP_VALID,
+	.formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE |
+		   SNDRV_PCM_FMTBIT_S24_LE,
+	.rates = SNDRV_PCM_RATE_32000 |
+		 SNDRV_PCM_RATE_44100 |
+		 SNDRV_PCM_RATE_48000 |
+		 SNDRV_PCM_RATE_88200 |
+		 SNDRV_PCM_RATE_96000 |
+		 SNDRV_PCM_RATE_176400 |
+		 SNDRV_PCM_RATE_192000,
+	.channels_min = 2,
+	.channels_max = 8,
+	.buffer_bytes_max = 64 * 1024,
+	.period_bytes_min = 256,
+	.period_bytes_max = 8192,	/* ERR004323: must limit to 8k */
+	.periods_min = 2,
+	.periods_max = 16,
+	.fifo_size = 0,
+};
+
+static int dw_hdmi_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_dw_hdmi *dw = substream->private_data;
+	void __iomem *base = dw->data.base;
+	int ret;
+
+	runtime->hw = dw_hdmi_hw;
+
+	ret = snd_pcm_limit_hw_rates(runtime);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0)
+		return ret;
+
+	/* Clear FIFO */
+	writeb_relaxed(HDMI_AHB_DMA_CONF0_SW_FIFO_RST,
+		       base + HDMI_AHB_DMA_CONF0);
+
+	/* Configure interrupt polarities */
+	writeb_relaxed(~0, base + HDMI_AHB_DMA_POL);
+	writeb_relaxed(~0, base + HDMI_AHB_DMA_BUFFPOL);
+
+	/* Keep interrupts masked, and clear any pending */
+	writeb_relaxed(~0, base + HDMI_AHB_DMA_MASK);
+	writeb_relaxed(~0, base + HDMI_IH_AHBDMAAUD_STAT0);
+
+	ret = request_irq(dw->data.irq, snd_dw_hdmi_irq, IRQF_SHARED,
+			  "dw-hdmi-audio", dw);
+	if (ret)
+		return ret;
+
+	/* Un-mute done interrupt */
+	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL &
+		       ~HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE,
+		       base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
+
+	return 0;
+}
+
+static int dw_hdmi_close(struct snd_pcm_substream *substream)
+{
+	struct snd_dw_hdmi *dw = substream->private_data;
+
+	/* Mute all interrupts */
+	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
+		       dw->data.base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
+
+	free_irq(dw->data.irq, dw);
+
+	return 0;
+}
+
+static int dw_hdmi_hw_free(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int dw_hdmi_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	return snd_pcm_lib_alloc_vmalloc_buffer(substream,
+						params_buffer_bytes(params));
+}
+
+static int dw_hdmi_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_dw_hdmi *dw = substream->private_data;
+	u8 threshold, conf0, conf1;
+
+	/* Setup as per 3.0.5 FSL 4.1.0 BSP */
+	switch (dw->revision) {
+	case 0x0a:
+		conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
+			HDMI_AHB_DMA_CONF0_INCR4;
+		if (runtime->channels == 2)
+			threshold = 126;
+		else
+			threshold = 124;
+		break;
+	case 0x1a:
+		conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
+			HDMI_AHB_DMA_CONF0_INCR8;
+		threshold = 128;
+		break;
+	default:
+		/* NOTREACHED */
+		return -EINVAL;
+	}
+
+	dw_hdmi_set_sample_rate(dw->data.hdmi, runtime->rate);
+
+	/* Minimum number of bytes in the fifo. */
+	runtime->hw.fifo_size = threshold * 32;
+
+	conf0 |= HDMI_AHB_DMA_CONF0_EN_HLOCK;
+	conf1 = (1 << runtime->channels) - 1;
+
+	writeb_relaxed(threshold, dw->data.base + HDMI_AHB_DMA_THRSLD);
+	writeb_relaxed(conf0, dw->data.base + HDMI_AHB_DMA_CONF0);
+	writeb_relaxed(conf1, dw->data.base + HDMI_AHB_DMA_CONF1);
+
+	switch (runtime->format) {
+	case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
+		dw->reformat = dw_hdmi_reformat_iec958;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		dw_hdmi_create_cs(dw, runtime);
+		dw->reformat = dw_hdmi_reformat_s24;
+		break;
+	}
+	dw->iec_offset = 0;
+	dw->channels = runtime->channels;
+	dw->buf_src  = runtime->dma_area;
+	dw->buf_dst  = substream->dma_buffer.area;
+	dw->buf_addr = substream->dma_buffer.addr;
+	dw->buf_period = snd_pcm_lib_period_bytes(substream);
+	dw->buf_size = snd_pcm_lib_buffer_bytes(substream);
+
+	return 0;
+}
+
+static int dw_hdmi_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_dw_hdmi *dw = substream->private_data;
+	unsigned long flags;
+	int ret = 0;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		spin_lock_irqsave(&dw->lock, flags);
+		dw->buf_offset = 0;
+		dw->substream = substream;
+		dw_hdmi_start_dma(dw);
+		dw_hdmi_audio_enable(dw->data.hdmi);
+		spin_unlock_irqrestore(&dw->lock, flags);
+		substream->runtime->delay = substream->runtime->period_size;
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+		spin_lock_irqsave(&dw->lock, flags);
+		dw->substream = NULL;
+		dw_hdmi_stop_dma(dw);
+		dw_hdmi_audio_disable(dw->data.hdmi);
+		spin_unlock_irqrestore(&dw->lock, flags);
+		break;
+
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static snd_pcm_uframes_t dw_hdmi_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_dw_hdmi *dw = substream->private_data;
+
+	/*
+	 * We are unable to report the exact hardware position as
+	 * reading the 32-bit DMA position using 8-bit reads is racy.
+	 */
+	return bytes_to_frames(runtime, dw->buf_offset);
+}
+
+static struct snd_pcm_ops snd_dw_hdmi_ops = {
+	.open = dw_hdmi_open,
+	.close = dw_hdmi_close,
+	.ioctl = snd_pcm_lib_ioctl,
+	.hw_params = dw_hdmi_hw_params,
+	.hw_free = dw_hdmi_hw_free,
+	.prepare = dw_hdmi_prepare,
+	.trigger = dw_hdmi_trigger,
+	.pointer = dw_hdmi_pointer,
+	.page = snd_pcm_lib_get_vmalloc_page,
+};
+
+static int snd_dw_hdmi_probe(struct platform_device *pdev)
+{
+	const struct dw_hdmi_audio_data *data = pdev->dev.platform_data;
+	struct device *dev = pdev->dev.parent;
+	struct snd_dw_hdmi *dw;
+	struct snd_card *card;
+	struct snd_pcm *pcm;
+	unsigned revision;
+	int ret;
+
+	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
+		       data->base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
+	revision = readb_relaxed(data->base + HDMI_REVISION_ID);
+	if (revision != 0x0a && revision != 0x1a) {
+		dev_err(dev, "dw-hdmi-audio: unknown revision 0x%02x\n",
+			revision);
+		return -ENXIO;
+	}
+
+	ret = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
+			      THIS_MODULE, sizeof(struct snd_dw_hdmi), &card);
+	if (ret < 0)
+		return ret;
+
+	strlcpy(card->driver, DRIVER_NAME, sizeof(card->driver));
+	strlcpy(card->shortname, "DW-HDMI", sizeof(card->shortname));
+	snprintf(card->longname, sizeof(card->longname),
+		 "%s rev 0x%02x, irq %d", card->shortname, revision,
+		 data->irq);
+
+	dw = card->private_data;
+	dw->card = card;
+	dw->data = *data;
+	dw->revision = revision;
+
+	spin_lock_init(&dw->lock);
+
+	ret = snd_pcm_new(card, "DW HDMI", 0, 1, 0, &pcm);
+	if (ret < 0)
+		goto err;
+
+	dw->pcm = pcm;
+	pcm->private_data = dw;
+	strlcpy(pcm->name, DRIVER_NAME, sizeof(pcm->name));
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_dw_hdmi_ops);
+
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+			dev, 64 * 1024, 64 * 1024);
+
+	ret = snd_card_register(card);
+	if (ret < 0)
+		goto err;
+
+	platform_set_drvdata(pdev, dw);
+
+	return 0;
+
+err:
+	snd_card_free(card);
+	return ret;
+}
+
+static int snd_dw_hdmi_remove(struct platform_device *pdev)
+{
+	struct snd_dw_hdmi *dw = platform_get_drvdata(pdev);
+
+	snd_card_free(dw->card);
+
+	return 0;
+}
+
+#if defined(CONFIG_PM_SLEEP) && defined(IS_NOT_BROKEN)
+/*
+ * This code is fine, but requires implementation in the dw_hdmi_trigger()
+ * method which is currently missing as I have no way to test this.
+ */
+static int snd_dw_hdmi_suspend(struct device *dev)
+{
+	struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
+
+	snd_power_change_state(dw->card, SNDRV_CTL_POWER_D3cold);
+	snd_pcm_suspend_all(dw->pcm);
+
+	return 0;
+}
+
+static int snd_dw_hdmi_resume(struct device *dev)
+{
+	struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
+
+	snd_power_change_state(dw->card, SNDRV_CTL_POWER_D0);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(snd_dw_hdmi_pm, snd_dw_hdmi_suspend,
+			 snd_dw_hdmi_resume);
+#define PM_OPS &snd_dw_hdmi_pm
+#else
+#define PM_OPS NULL
+#endif
+
+static struct platform_driver snd_dw_hdmi_driver = {
+	.probe	= snd_dw_hdmi_probe,
+	.remove	= snd_dw_hdmi_remove,
+	.driver	= {
+		.name = DRIVER_NAME,
+		.owner = THIS_MODULE,
+		.pm = PM_OPS,
+	},
+};
+
+module_platform_driver(snd_dw_hdmi_driver);
+
+MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>");
+MODULE_DESCRIPTION("Synopsis Designware HDMI AHB ALSA interface");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/gpu/drm/bridge/dw_hdmi-audio.h b/drivers/gpu/drm/bridge/dw_hdmi-audio.h
new file mode 100644
index 000000000000..1e840118d90a
--- /dev/null
+++ b/drivers/gpu/drm/bridge/dw_hdmi-audio.h
@@ -0,0 +1,13 @@
+#ifndef DW_HDMI_AUDIO_H
+#define DW_HDMI_AUDIO_H
+
+struct dw_hdmi;
+
+struct dw_hdmi_audio_data {
+	phys_addr_t phys;
+	void __iomem *base;
+	int irq;
+	struct dw_hdmi *hdmi;
+};
+
+#endif
diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index fba25607ef88..b65464789fbd 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -28,6 +28,7 @@
 #include <drm/bridge/dw_hdmi.h>
 
 #include "dw_hdmi.h"
+#include "dw_hdmi-audio.h"
 
 #define HDMI_EDID_LEN		512
 
@@ -104,6 +105,7 @@ struct dw_hdmi {
 	struct drm_encoder *encoder;
 	struct drm_bridge *bridge;
 
+	struct platform_device *audio;
 	enum dw_hdmi_devtype dev_type;
 	struct device *dev;
 	struct clk *isfr_clk;
@@ -1732,7 +1734,9 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 {
 	struct drm_device *drm = data;
 	struct device_node *np = dev->of_node;
+	struct platform_device_info pdevinfo;
 	struct device_node *ddc_node;
+	struct dw_hdmi_audio_data audio;
 	struct dw_hdmi *hdmi;
 	int ret;
 	u32 val = 1;
@@ -1860,6 +1864,23 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 	hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE),
 		    HDMI_IH_MUTE_PHY_STAT0);
 
+	memset(&pdevinfo, 0, sizeof(pdevinfo));
+	pdevinfo.parent = dev;
+	pdevinfo.id = PLATFORM_DEVID_AUTO;
+
+	if (hdmi_readb(hdmi, HDMI_CONFIG1_ID) & HDMI_CONFIG1_AHB) {
+		audio.phys = iores->start;
+		audio.base = hdmi->regs;
+		audio.irq = irq;
+		audio.hdmi = hdmi;
+
+		pdevinfo.name = "dw-hdmi-ahb-audio";
+		pdevinfo.data = &audio;
+		pdevinfo.size_data = sizeof(audio);
+		pdevinfo.dma_mask = DMA_BIT_MASK(32);
+		hdmi->audio = platform_device_register_full(&pdevinfo);
+	}
+
 	dev_set_drvdata(dev, hdmi);
 
 	return 0;
@@ -1877,6 +1898,9 @@ void dw_hdmi_unbind(struct device *dev, struct device *master, void *data)
 {
 	struct dw_hdmi *hdmi = dev_get_drvdata(dev);
 
+	if (hdmi->audio && !IS_ERR(hdmi->audio))
+		platform_device_unregister(hdmi->audio);
+
 	/* Disable all interrupts */
 	hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
 
diff --git a/drivers/gpu/drm/bridge/dw_hdmi.h b/drivers/gpu/drm/bridge/dw_hdmi.h
index 175dbc89a824..78e54e813212 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.h
+++ b/drivers/gpu/drm/bridge/dw_hdmi.h
@@ -545,6 +545,9 @@
 #define HDMI_I2CM_FS_SCL_LCNT_0_ADDR            0x7E12
 
 enum {
+/* CONFIG1_ID field values */
+	HDMI_CONFIG1_AHB = 0x01,
+
 /* IH_FC_INT2 field values */
 	HDMI_IH_FC_INT2_OVERFLOW_MASK = 0x03,
 	HDMI_IH_FC_INT2_LOW_PRIORITY_OVERFLOW = 0x02,
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 226+ messages in thread

* Re: [PATCH v2 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver David Airlie <airlied@linux.ie>, Sascha Hauer <s.hauer@pengutronix.de>, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, Jaroslav        Kysela <perex@perex.cz>, linux-rockchip@lists.infradead.org, Mark Brown        <broonie@kernel.org>, Philipp Zabel <p.zabel@pengutronix.de>, Yakir        Yang <ykk@rock-chips.com>, Andy Yan <andy.yan@rock-chips.com>, Jon        Nettleton <jon.nettleton@gmail.com>, linux-arm-kernel@lists.infradead.org
@ 2015-08-14 13:54         ` Russell King
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-14 13:54 UTC (permalink / raw)
  To: Takashi Iwai
  Cc: Fabio Estevam, alsa-devel, David Airlie, Sascha Hauer,
	linux-kernel, dri-devel, linux-rockchip, Mark Brown,
	Philipp Zabel, Yakir Yang, Andy Yan, Jon Nettleton,
	linux-arm-kernel

Add ALSA based HDMI AHB audio driver for dw_hdmi.  The only buffer
format supported by the hardware is its own special IEC958 based format,
which is not compatible with any ALSA format.  To avoid doing too much
data manipulation within the driver, we support only ALSAs IEC958 LE and
24-bit PCM formats for 2 to 6 channels, which we convert to its hardware
format.

A more desirable solution would be to have this conversion in userspace,
but ALSA does not appear to allow such transformations outside of
libasound itself.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
v2: updated with Takashi Iwai's comments.

 drivers/gpu/drm/bridge/Kconfig             |  10 +
 drivers/gpu/drm/bridge/Makefile            |   1 +
 drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c | 579 +++++++++++++++++++++++++++++
 drivers/gpu/drm/bridge/dw_hdmi-audio.h     |  13 +
 drivers/gpu/drm/bridge/dw_hdmi.c           |  24 ++
 drivers/gpu/drm/bridge/dw_hdmi.h           |   3 +
 6 files changed, 630 insertions(+)
 create mode 100644 drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
 create mode 100644 drivers/gpu/drm/bridge/dw_hdmi-audio.h

diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index acef3223772c..56ed35fe0734 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -3,6 +3,16 @@ config DRM_DW_HDMI
 	depends on DRM
 	select DRM_KMS_HELPER
 
+config DRM_DW_HDMI_AHB_AUDIO
+	tristate "Synopsis Designware AHB Audio interface"
+	depends on DRM_DW_HDMI && SND
+	select SND_PCM
+	select SND_PCM_IEC958
+	help
+	  Support the AHB Audio interface which is part of the Synopsis
+	  Designware HDMI block.  This is used in conjunction with
+	  the i.MX6 HDMI driver.
+
 config DRM_PTN3460
 	tristate "PTN3460 DP/LVDS bridge"
 	depends on DRM
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index 8dfebd984370..eb80dbbb8365 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -3,3 +3,4 @@ ccflags-y := -Iinclude/drm
 obj-$(CONFIG_DRM_PS8622) += ps8622.o
 obj-$(CONFIG_DRM_PTN3460) += ptn3460.o
 obj-$(CONFIG_DRM_DW_HDMI) += dw_hdmi.o
+obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw_hdmi-ahb-audio.o
diff --git a/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
new file mode 100644
index 000000000000..bf379310008a
--- /dev/null
+++ b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
@@ -0,0 +1,579 @@
+/*
+ * DesignWare HDMI audio driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Written and tested against the Designware HDMI Tx found in iMX6.
+ */
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <drm/bridge/dw_hdmi.h>
+
+#include <sound/asoundef.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_iec958.h>
+
+#include "dw_hdmi-audio.h"
+
+#define DRIVER_NAME "dw-hdmi-ahb-audio"
+
+/* Provide some bits rather than bit offsets */
+enum {
+	HDMI_AHB_DMA_CONF0_SW_FIFO_RST = BIT(7),
+	HDMI_AHB_DMA_CONF0_EN_HLOCK = BIT(3),
+	HDMI_AHB_DMA_START_START = BIT(0),
+	HDMI_AHB_DMA_STOP_STOP = BIT(0),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR = BIT(5),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST = BIT(4),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY = BIT(3),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE = BIT(2),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL = BIT(1),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL =
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY,
+	HDMI_IH_AHBDMAAUD_STAT0_ERROR = BIT(5),
+	HDMI_IH_AHBDMAAUD_STAT0_LOST = BIT(4),
+	HDMI_IH_AHBDMAAUD_STAT0_RETRY = BIT(3),
+	HDMI_IH_AHBDMAAUD_STAT0_DONE = BIT(2),
+	HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL = BIT(1),
+	HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0),
+	HDMI_IH_AHBDMAAUD_STAT0_ALL =
+		HDMI_IH_AHBDMAAUD_STAT0_ERROR |
+		HDMI_IH_AHBDMAAUD_STAT0_LOST |
+		HDMI_IH_AHBDMAAUD_STAT0_RETRY |
+		HDMI_IH_AHBDMAAUD_STAT0_DONE |
+		HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL |
+		HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY,
+	HDMI_AHB_DMA_CONF0_INCR16 = 2 << 1,
+	HDMI_AHB_DMA_CONF0_INCR8 = 1 << 1,
+	HDMI_AHB_DMA_CONF0_INCR4 = 0,
+	HDMI_AHB_DMA_CONF0_BURST_MODE = BIT(0),
+	HDMI_AHB_DMA_MASK_DONE = BIT(7),
+	HDMI_REVISION_ID = 0x0001,
+	HDMI_IH_AHBDMAAUD_STAT0 = 0x0109,
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0 = 0x0189,
+	HDMI_AHB_DMA_CONF0 = 0x3600,
+	HDMI_AHB_DMA_START = 0x3601,
+	HDMI_AHB_DMA_STOP = 0x3602,
+	HDMI_AHB_DMA_THRSLD = 0x3603,
+	HDMI_AHB_DMA_STRADDR0 = 0x3604,
+	HDMI_AHB_DMA_STPADDR0 = 0x3608,
+	HDMI_AHB_DMA_MASK = 0x3614,
+	HDMI_AHB_DMA_POL = 0x3615,
+	HDMI_AHB_DMA_CONF1 = 0x3616,
+	HDMI_AHB_DMA_BUFFPOL = 0x361a,
+};
+
+struct snd_dw_hdmi {
+	struct snd_card *card;
+	struct snd_pcm *pcm;
+	spinlock_t lock;
+	struct dw_hdmi_audio_data data;
+	struct snd_pcm_substream *substream;
+	void (*reformat)(struct snd_dw_hdmi *, size_t, size_t);
+	void *buf_src;
+	void *buf_dst;
+	dma_addr_t buf_addr;
+	unsigned buf_offset;
+	unsigned buf_period;
+	unsigned buf_size;
+	unsigned channels;
+	u8 revision;
+	u8 iec_offset;
+	u8 cs[192][8];
+};
+
+static void dw_hdmi_writel(u32 val, void __iomem *ptr)
+{
+	writeb_relaxed(val, ptr);
+	writeb_relaxed(val >> 8, ptr + 1);
+	writeb_relaxed(val >> 16, ptr + 2);
+	writeb_relaxed(val >> 24, ptr + 3);
+}
+
+/*
+ * Convert to hardware format: The userspace buffer contains IEC958 samples,
+ * with the PCUV bits in bits 31..28 and audio samples in bits 27..4.  We
+ * need these to be in bits 27..24, with the IEC B bit in bit 28, and audio
+ * samples in 23..0.
+ *
+ * Default preamble in bits 3..0: 8 = block start, 4 = even 2 = odd
+ *
+ * Ideally, we could do with having the data properly formatted in userspace.
+ */
+static void dw_hdmi_reformat_iec958(struct snd_dw_hdmi *dw,
+	size_t offset, size_t bytes)
+{
+	u32 *src = dw->buf_src + offset;
+	u32 *dst = dw->buf_dst + offset;
+	u32 *end = dw->buf_src + offset + bytes;
+
+	do {
+		u32 b, sample = *src++;
+
+		b = (sample & 8) << (28 - 3);
+
+		sample >>= 4;
+
+		*dst++ = sample | b;
+	} while (src < end);
+}
+
+static u32 parity(u32 sample)
+{
+	sample ^= sample >> 16;
+	sample ^= sample >> 8;
+	sample ^= sample >> 4;
+	sample ^= sample >> 2;
+	sample ^= sample >> 1;
+	return (sample & 1) << 27;
+}
+
+static void dw_hdmi_reformat_s24(struct snd_dw_hdmi *dw,
+	size_t offset, size_t bytes)
+{
+	u32 *src = dw->buf_src + offset;
+	u32 *dst = dw->buf_dst + offset;
+	u32 *end = dw->buf_src + offset + bytes;
+
+	do {
+		unsigned i;
+		u8 *cs;
+
+		cs = dw->cs[dw->iec_offset++];
+		if (dw->iec_offset >= 192)
+			dw->iec_offset = 0;
+
+		i = dw->channels;
+		do {
+			u32 sample = *src++;
+
+			sample &= ~0xff000000;
+			sample |= *cs++ << 24;
+			sample |= parity(sample & ~0xf8000000);
+
+			*dst++ = sample;
+		} while (--i);
+	} while (src < end);
+}
+
+static void dw_hdmi_create_cs(struct snd_dw_hdmi *dw,
+	struct snd_pcm_runtime *runtime)
+{
+	u8 cs[4];
+	unsigned ch, i, j;
+
+	snd_pcm_create_iec958_consumer(runtime, cs, sizeof(cs));
+
+	memset(dw->cs, 0, sizeof(dw->cs));
+
+	for (ch = 0; ch < 8; ch++) {
+		cs[2] &= ~IEC958_AES2_CON_CHANNEL;
+		cs[2] |= (ch + 1) << 4;
+
+		for (i = 0; i < ARRAY_SIZE(cs); i++) {
+			unsigned c = cs[i];
+
+			for (j = 0; j < 8; j++, c >>= 1)
+				dw->cs[i * 8 + j][ch] = (c & 1) << 2;
+		}
+	}
+	dw->cs[0][0] |= BIT(4);
+}
+
+static void dw_hdmi_start_dma(struct snd_dw_hdmi *dw)
+{
+	void __iomem *base = dw->data.base;
+	unsigned offset = dw->buf_offset;
+	unsigned period = dw->buf_period;
+	u32 start, stop;
+
+	dw->reformat(dw, offset, period);
+
+	/* Clear all irqs before enabling irqs and starting DMA */
+	writeb_relaxed(HDMI_IH_AHBDMAAUD_STAT0_ALL,
+		       base + HDMI_IH_AHBDMAAUD_STAT0);
+
+	start = dw->buf_addr + offset;
+	stop = start + period - 1;
+
+	/* Setup the hardware start/stop addresses */
+	dw_hdmi_writel(start, base + HDMI_AHB_DMA_STRADDR0);
+	dw_hdmi_writel(stop, base + HDMI_AHB_DMA_STPADDR0);
+
+	writeb_relaxed((u8)~HDMI_AHB_DMA_MASK_DONE, base + HDMI_AHB_DMA_MASK);
+	writeb(HDMI_AHB_DMA_START_START, base + HDMI_AHB_DMA_START);
+
+	offset += period;
+	if (offset >= dw->buf_size)
+		offset = 0;
+	dw->buf_offset = offset;
+}
+
+static void dw_hdmi_stop_dma(struct snd_dw_hdmi *dw)
+{
+	/* Disable interrupts before disabling DMA */
+	writeb_relaxed(~0, dw->data.base + HDMI_AHB_DMA_MASK);
+	writeb_relaxed(HDMI_AHB_DMA_STOP_STOP, dw->data.base + HDMI_AHB_DMA_STOP);
+}
+
+static irqreturn_t snd_dw_hdmi_irq(int irq, void *data)
+{
+	struct snd_dw_hdmi *dw = data;
+	struct snd_pcm_substream *substream;
+	unsigned stat;
+
+	stat = readb_relaxed(dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
+	if (!stat)
+		return IRQ_NONE;
+
+	writeb_relaxed(stat, dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
+
+	substream = dw->substream;
+	if (stat & HDMI_IH_AHBDMAAUD_STAT0_DONE && substream) {
+		snd_pcm_period_elapsed(substream);
+
+		spin_lock(&dw->lock);
+		if (dw->substream)
+			dw_hdmi_start_dma(dw);
+		spin_unlock(&dw->lock);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static struct snd_pcm_hardware dw_hdmi_hw = {
+	.info = SNDRV_PCM_INFO_INTERLEAVED |
+		SNDRV_PCM_INFO_BLOCK_TRANSFER |
+		SNDRV_PCM_INFO_MMAP |
+		SNDRV_PCM_INFO_MMAP_VALID,
+	.formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE |
+		   SNDRV_PCM_FMTBIT_S24_LE,
+	.rates = SNDRV_PCM_RATE_32000 |
+		 SNDRV_PCM_RATE_44100 |
+		 SNDRV_PCM_RATE_48000 |
+		 SNDRV_PCM_RATE_88200 |
+		 SNDRV_PCM_RATE_96000 |
+		 SNDRV_PCM_RATE_176400 |
+		 SNDRV_PCM_RATE_192000,
+	.channels_min = 2,
+	.channels_max = 8,
+	.buffer_bytes_max = 64 * 1024,
+	.period_bytes_min = 256,
+	.period_bytes_max = 8192,	/* ERR004323: must limit to 8k */
+	.periods_min = 2,
+	.periods_max = 16,
+	.fifo_size = 0,
+};
+
+static int dw_hdmi_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_dw_hdmi *dw = substream->private_data;
+	void __iomem *base = dw->data.base;
+	int ret;
+
+	runtime->hw = dw_hdmi_hw;
+
+	ret = snd_pcm_limit_hw_rates(runtime);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0)
+		return ret;
+
+	/* Clear FIFO */
+	writeb_relaxed(HDMI_AHB_DMA_CONF0_SW_FIFO_RST,
+		       base + HDMI_AHB_DMA_CONF0);
+
+	/* Configure interrupt polarities */
+	writeb_relaxed(~0, base + HDMI_AHB_DMA_POL);
+	writeb_relaxed(~0, base + HDMI_AHB_DMA_BUFFPOL);
+
+	/* Keep interrupts masked, and clear any pending */
+	writeb_relaxed(~0, base + HDMI_AHB_DMA_MASK);
+	writeb_relaxed(~0, base + HDMI_IH_AHBDMAAUD_STAT0);
+
+	ret = request_irq(dw->data.irq, snd_dw_hdmi_irq, IRQF_SHARED,
+			  "dw-hdmi-audio", dw);
+	if (ret)
+		return ret;
+
+	/* Un-mute done interrupt */
+	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL &
+		       ~HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE,
+		       base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
+
+	return 0;
+}
+
+static int dw_hdmi_close(struct snd_pcm_substream *substream)
+{
+	struct snd_dw_hdmi *dw = substream->private_data;
+
+	/* Mute all interrupts */
+	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
+		       dw->data.base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
+
+	free_irq(dw->data.irq, dw);
+
+	return 0;
+}
+
+static int dw_hdmi_hw_free(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int dw_hdmi_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	return snd_pcm_lib_alloc_vmalloc_buffer(substream,
+						params_buffer_bytes(params));
+}
+
+static int dw_hdmi_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_dw_hdmi *dw = substream->private_data;
+	u8 threshold, conf0, conf1;
+
+	/* Setup as per 3.0.5 FSL 4.1.0 BSP */
+	switch (dw->revision) {
+	case 0x0a:
+		conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
+			HDMI_AHB_DMA_CONF0_INCR4;
+		if (runtime->channels == 2)
+			threshold = 126;
+		else
+			threshold = 124;
+		break;
+	case 0x1a:
+		conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
+			HDMI_AHB_DMA_CONF0_INCR8;
+		threshold = 128;
+		break;
+	default:
+		/* NOTREACHED */
+		return -EINVAL;
+	}
+
+	dw_hdmi_set_sample_rate(dw->data.hdmi, runtime->rate);
+
+	/* Minimum number of bytes in the fifo. */
+	runtime->hw.fifo_size = threshold * 32;
+
+	conf0 |= HDMI_AHB_DMA_CONF0_EN_HLOCK;
+	conf1 = (1 << runtime->channels) - 1;
+
+	writeb_relaxed(threshold, dw->data.base + HDMI_AHB_DMA_THRSLD);
+	writeb_relaxed(conf0, dw->data.base + HDMI_AHB_DMA_CONF0);
+	writeb_relaxed(conf1, dw->data.base + HDMI_AHB_DMA_CONF1);
+
+	switch (runtime->format) {
+	case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
+		dw->reformat = dw_hdmi_reformat_iec958;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		dw_hdmi_create_cs(dw, runtime);
+		dw->reformat = dw_hdmi_reformat_s24;
+		break;
+	}
+	dw->iec_offset = 0;
+	dw->channels = runtime->channels;
+	dw->buf_src  = runtime->dma_area;
+	dw->buf_dst  = substream->dma_buffer.area;
+	dw->buf_addr = substream->dma_buffer.addr;
+	dw->buf_period = snd_pcm_lib_period_bytes(substream);
+	dw->buf_size = snd_pcm_lib_buffer_bytes(substream);
+
+	return 0;
+}
+
+static int dw_hdmi_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_dw_hdmi *dw = substream->private_data;
+	unsigned long flags;
+	int ret = 0;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		spin_lock_irqsave(&dw->lock, flags);
+		dw->buf_offset = 0;
+		dw->substream = substream;
+		dw_hdmi_start_dma(dw);
+		dw_hdmi_audio_enable(dw->data.hdmi);
+		spin_unlock_irqrestore(&dw->lock, flags);
+		substream->runtime->delay = substream->runtime->period_size;
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+		spin_lock_irqsave(&dw->lock, flags);
+		dw->substream = NULL;
+		dw_hdmi_stop_dma(dw);
+		dw_hdmi_audio_disable(dw->data.hdmi);
+		spin_unlock_irqrestore(&dw->lock, flags);
+		break;
+
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static snd_pcm_uframes_t dw_hdmi_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_dw_hdmi *dw = substream->private_data;
+
+	/*
+	 * We are unable to report the exact hardware position as
+	 * reading the 32-bit DMA position using 8-bit reads is racy.
+	 */
+	return bytes_to_frames(runtime, dw->buf_offset);
+}
+
+static struct snd_pcm_ops snd_dw_hdmi_ops = {
+	.open = dw_hdmi_open,
+	.close = dw_hdmi_close,
+	.ioctl = snd_pcm_lib_ioctl,
+	.hw_params = dw_hdmi_hw_params,
+	.hw_free = dw_hdmi_hw_free,
+	.prepare = dw_hdmi_prepare,
+	.trigger = dw_hdmi_trigger,
+	.pointer = dw_hdmi_pointer,
+	.page = snd_pcm_lib_get_vmalloc_page,
+};
+
+static int snd_dw_hdmi_probe(struct platform_device *pdev)
+{
+	const struct dw_hdmi_audio_data *data = pdev->dev.platform_data;
+	struct device *dev = pdev->dev.parent;
+	struct snd_dw_hdmi *dw;
+	struct snd_card *card;
+	struct snd_pcm *pcm;
+	unsigned revision;
+	int ret;
+
+	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
+		       data->base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
+	revision = readb_relaxed(data->base + HDMI_REVISION_ID);
+	if (revision != 0x0a && revision != 0x1a) {
+		dev_err(dev, "dw-hdmi-audio: unknown revision 0x%02x\n",
+			revision);
+		return -ENXIO;
+	}
+
+	ret = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
+			      THIS_MODULE, sizeof(struct snd_dw_hdmi), &card);
+	if (ret < 0)
+		return ret;
+
+	strlcpy(card->driver, DRIVER_NAME, sizeof(card->driver));
+	strlcpy(card->shortname, "DW-HDMI", sizeof(card->shortname));
+	snprintf(card->longname, sizeof(card->longname),
+		 "%s rev 0x%02x, irq %d", card->shortname, revision,
+		 data->irq);
+
+	dw = card->private_data;
+	dw->card = card;
+	dw->data = *data;
+	dw->revision = revision;
+
+	spin_lock_init(&dw->lock);
+
+	ret = snd_pcm_new(card, "DW HDMI", 0, 1, 0, &pcm);
+	if (ret < 0)
+		goto err;
+
+	dw->pcm = pcm;
+	pcm->private_data = dw;
+	strlcpy(pcm->name, DRIVER_NAME, sizeof(pcm->name));
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_dw_hdmi_ops);
+
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+			dev, 64 * 1024, 64 * 1024);
+
+	ret = snd_card_register(card);
+	if (ret < 0)
+		goto err;
+
+	platform_set_drvdata(pdev, dw);
+
+	return 0;
+
+err:
+	snd_card_free(card);
+	return ret;
+}
+
+static int snd_dw_hdmi_remove(struct platform_device *pdev)
+{
+	struct snd_dw_hdmi *dw = platform_get_drvdata(pdev);
+
+	snd_card_free(dw->card);
+
+	return 0;
+}
+
+#if defined(CONFIG_PM_SLEEP) && defined(IS_NOT_BROKEN)
+/*
+ * This code is fine, but requires implementation in the dw_hdmi_trigger()
+ * method which is currently missing as I have no way to test this.
+ */
+static int snd_dw_hdmi_suspend(struct device *dev)
+{
+	struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
+
+	snd_power_change_state(dw->card, SNDRV_CTL_POWER_D3cold);
+	snd_pcm_suspend_all(dw->pcm);
+
+	return 0;
+}
+
+static int snd_dw_hdmi_resume(struct device *dev)
+{
+	struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
+
+	snd_power_change_state(dw->card, SNDRV_CTL_POWER_D0);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(snd_dw_hdmi_pm, snd_dw_hdmi_suspend,
+			 snd_dw_hdmi_resume);
+#define PM_OPS &snd_dw_hdmi_pm
+#else
+#define PM_OPS NULL
+#endif
+
+static struct platform_driver snd_dw_hdmi_driver = {
+	.probe	= snd_dw_hdmi_probe,
+	.remove	= snd_dw_hdmi_remove,
+	.driver	= {
+		.name = DRIVER_NAME,
+		.owner = THIS_MODULE,
+		.pm = PM_OPS,
+	},
+};
+
+module_platform_driver(snd_dw_hdmi_driver);
+
+MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>");
+MODULE_DESCRIPTION("Synopsis Designware HDMI AHB ALSA interface");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/gpu/drm/bridge/dw_hdmi-audio.h b/drivers/gpu/drm/bridge/dw_hdmi-audio.h
new file mode 100644
index 000000000000..1e840118d90a
--- /dev/null
+++ b/drivers/gpu/drm/bridge/dw_hdmi-audio.h
@@ -0,0 +1,13 @@
+#ifndef DW_HDMI_AUDIO_H
+#define DW_HDMI_AUDIO_H
+
+struct dw_hdmi;
+
+struct dw_hdmi_audio_data {
+	phys_addr_t phys;
+	void __iomem *base;
+	int irq;
+	struct dw_hdmi *hdmi;
+};
+
+#endif
diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index fba25607ef88..b65464789fbd 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -28,6 +28,7 @@
 #include <drm/bridge/dw_hdmi.h>
 
 #include "dw_hdmi.h"
+#include "dw_hdmi-audio.h"
 
 #define HDMI_EDID_LEN		512
 
@@ -104,6 +105,7 @@ struct dw_hdmi {
 	struct drm_encoder *encoder;
 	struct drm_bridge *bridge;
 
+	struct platform_device *audio;
 	enum dw_hdmi_devtype dev_type;
 	struct device *dev;
 	struct clk *isfr_clk;
@@ -1732,7 +1734,9 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 {
 	struct drm_device *drm = data;
 	struct device_node *np = dev->of_node;
+	struct platform_device_info pdevinfo;
 	struct device_node *ddc_node;
+	struct dw_hdmi_audio_data audio;
 	struct dw_hdmi *hdmi;
 	int ret;
 	u32 val = 1;
@@ -1860,6 +1864,23 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 	hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE),
 		    HDMI_IH_MUTE_PHY_STAT0);
 
+	memset(&pdevinfo, 0, sizeof(pdevinfo));
+	pdevinfo.parent = dev;
+	pdevinfo.id = PLATFORM_DEVID_AUTO;
+
+	if (hdmi_readb(hdmi, HDMI_CONFIG1_ID) & HDMI_CONFIG1_AHB) {
+		audio.phys = iores->start;
+		audio.base = hdmi->regs;
+		audio.irq = irq;
+		audio.hdmi = hdmi;
+
+		pdevinfo.name = "dw-hdmi-ahb-audio";
+		pdevinfo.data = &audio;
+		pdevinfo.size_data = sizeof(audio);
+		pdevinfo.dma_mask = DMA_BIT_MASK(32);
+		hdmi->audio = platform_device_register_full(&pdevinfo);
+	}
+
 	dev_set_drvdata(dev, hdmi);
 
 	return 0;
@@ -1877,6 +1898,9 @@ void dw_hdmi_unbind(struct device *dev, struct device *master, void *data)
 {
 	struct dw_hdmi *hdmi = dev_get_drvdata(dev);
 
+	if (hdmi->audio && !IS_ERR(hdmi->audio))
+		platform_device_unregister(hdmi->audio);
+
 	/* Disable all interrupts */
 	hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
 
diff --git a/drivers/gpu/drm/bridge/dw_hdmi.h b/drivers/gpu/drm/bridge/dw_hdmi.h
index 175dbc89a824..78e54e813212 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.h
+++ b/drivers/gpu/drm/bridge/dw_hdmi.h
@@ -545,6 +545,9 @@
 #define HDMI_I2CM_FS_SCL_LCNT_0_ADDR            0x7E12
 
 enum {
+/* CONFIG1_ID field values */
+	HDMI_CONFIG1_AHB = 0x01,
+
 /* IH_FC_INT2 field values */
 	HDMI_IH_FC_INT2_OVERFLOW_MASK = 0x03,
 	HDMI_IH_FC_INT2_LOW_PRIORITY_OVERFLOW = 0x02,
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH v2 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver David Airlie <airlied@linux.ie>, Sascha Hauer <s.hauer@pengutronix.de>, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, Jaroslav Kysela <perex@perex.cz>, linux-rockchip@lists.infradead.org, Mark Brown <broonie@kernel.org>, Philipp Zabel <p.zabel@pengutronix.de>, Yakir Yang <ykk@rock-chips.com>, Andy Yan <andy.yan@rock-chips.com>, Jon Nettleton <jon.nettleton@gmail.com>, linux-arm-kernel@lists.infradead.org
@ 2015-08-14 13:54         ` Russell King
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-14 13:54 UTC (permalink / raw)
  To: linux-arm-kernel

Add ALSA based HDMI AHB audio driver for dw_hdmi.  The only buffer
format supported by the hardware is its own special IEC958 based format,
which is not compatible with any ALSA format.  To avoid doing too much
data manipulation within the driver, we support only ALSAs IEC958 LE and
24-bit PCM formats for 2 to 6 channels, which we convert to its hardware
format.

A more desirable solution would be to have this conversion in userspace,
but ALSA does not appear to allow such transformations outside of
libasound itself.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
v2: updated with Takashi Iwai's comments.

 drivers/gpu/drm/bridge/Kconfig             |  10 +
 drivers/gpu/drm/bridge/Makefile            |   1 +
 drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c | 579 +++++++++++++++++++++++++++++
 drivers/gpu/drm/bridge/dw_hdmi-audio.h     |  13 +
 drivers/gpu/drm/bridge/dw_hdmi.c           |  24 ++
 drivers/gpu/drm/bridge/dw_hdmi.h           |   3 +
 6 files changed, 630 insertions(+)
 create mode 100644 drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
 create mode 100644 drivers/gpu/drm/bridge/dw_hdmi-audio.h

diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index acef3223772c..56ed35fe0734 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -3,6 +3,16 @@ config DRM_DW_HDMI
 	depends on DRM
 	select DRM_KMS_HELPER
 
+config DRM_DW_HDMI_AHB_AUDIO
+	tristate "Synopsis Designware AHB Audio interface"
+	depends on DRM_DW_HDMI && SND
+	select SND_PCM
+	select SND_PCM_IEC958
+	help
+	  Support the AHB Audio interface which is part of the Synopsis
+	  Designware HDMI block.  This is used in conjunction with
+	  the i.MX6 HDMI driver.
+
 config DRM_PTN3460
 	tristate "PTN3460 DP/LVDS bridge"
 	depends on DRM
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index 8dfebd984370..eb80dbbb8365 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -3,3 +3,4 @@ ccflags-y := -Iinclude/drm
 obj-$(CONFIG_DRM_PS8622) += ps8622.o
 obj-$(CONFIG_DRM_PTN3460) += ptn3460.o
 obj-$(CONFIG_DRM_DW_HDMI) += dw_hdmi.o
+obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw_hdmi-ahb-audio.o
diff --git a/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
new file mode 100644
index 000000000000..bf379310008a
--- /dev/null
+++ b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
@@ -0,0 +1,579 @@
+/*
+ * DesignWare HDMI audio driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Written and tested against the Designware HDMI Tx found in iMX6.
+ */
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <drm/bridge/dw_hdmi.h>
+
+#include <sound/asoundef.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_iec958.h>
+
+#include "dw_hdmi-audio.h"
+
+#define DRIVER_NAME "dw-hdmi-ahb-audio"
+
+/* Provide some bits rather than bit offsets */
+enum {
+	HDMI_AHB_DMA_CONF0_SW_FIFO_RST = BIT(7),
+	HDMI_AHB_DMA_CONF0_EN_HLOCK = BIT(3),
+	HDMI_AHB_DMA_START_START = BIT(0),
+	HDMI_AHB_DMA_STOP_STOP = BIT(0),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR = BIT(5),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST = BIT(4),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY = BIT(3),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE = BIT(2),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL = BIT(1),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL =
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY,
+	HDMI_IH_AHBDMAAUD_STAT0_ERROR = BIT(5),
+	HDMI_IH_AHBDMAAUD_STAT0_LOST = BIT(4),
+	HDMI_IH_AHBDMAAUD_STAT0_RETRY = BIT(3),
+	HDMI_IH_AHBDMAAUD_STAT0_DONE = BIT(2),
+	HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL = BIT(1),
+	HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0),
+	HDMI_IH_AHBDMAAUD_STAT0_ALL =
+		HDMI_IH_AHBDMAAUD_STAT0_ERROR |
+		HDMI_IH_AHBDMAAUD_STAT0_LOST |
+		HDMI_IH_AHBDMAAUD_STAT0_RETRY |
+		HDMI_IH_AHBDMAAUD_STAT0_DONE |
+		HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL |
+		HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY,
+	HDMI_AHB_DMA_CONF0_INCR16 = 2 << 1,
+	HDMI_AHB_DMA_CONF0_INCR8 = 1 << 1,
+	HDMI_AHB_DMA_CONF0_INCR4 = 0,
+	HDMI_AHB_DMA_CONF0_BURST_MODE = BIT(0),
+	HDMI_AHB_DMA_MASK_DONE = BIT(7),
+	HDMI_REVISION_ID = 0x0001,
+	HDMI_IH_AHBDMAAUD_STAT0 = 0x0109,
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0 = 0x0189,
+	HDMI_AHB_DMA_CONF0 = 0x3600,
+	HDMI_AHB_DMA_START = 0x3601,
+	HDMI_AHB_DMA_STOP = 0x3602,
+	HDMI_AHB_DMA_THRSLD = 0x3603,
+	HDMI_AHB_DMA_STRADDR0 = 0x3604,
+	HDMI_AHB_DMA_STPADDR0 = 0x3608,
+	HDMI_AHB_DMA_MASK = 0x3614,
+	HDMI_AHB_DMA_POL = 0x3615,
+	HDMI_AHB_DMA_CONF1 = 0x3616,
+	HDMI_AHB_DMA_BUFFPOL = 0x361a,
+};
+
+struct snd_dw_hdmi {
+	struct snd_card *card;
+	struct snd_pcm *pcm;
+	spinlock_t lock;
+	struct dw_hdmi_audio_data data;
+	struct snd_pcm_substream *substream;
+	void (*reformat)(struct snd_dw_hdmi *, size_t, size_t);
+	void *buf_src;
+	void *buf_dst;
+	dma_addr_t buf_addr;
+	unsigned buf_offset;
+	unsigned buf_period;
+	unsigned buf_size;
+	unsigned channels;
+	u8 revision;
+	u8 iec_offset;
+	u8 cs[192][8];
+};
+
+static void dw_hdmi_writel(u32 val, void __iomem *ptr)
+{
+	writeb_relaxed(val, ptr);
+	writeb_relaxed(val >> 8, ptr + 1);
+	writeb_relaxed(val >> 16, ptr + 2);
+	writeb_relaxed(val >> 24, ptr + 3);
+}
+
+/*
+ * Convert to hardware format: The userspace buffer contains IEC958 samples,
+ * with the PCUV bits in bits 31..28 and audio samples in bits 27..4.  We
+ * need these to be in bits 27..24, with the IEC B bit in bit 28, and audio
+ * samples in 23..0.
+ *
+ * Default preamble in bits 3..0: 8 = block start, 4 = even 2 = odd
+ *
+ * Ideally, we could do with having the data properly formatted in userspace.
+ */
+static void dw_hdmi_reformat_iec958(struct snd_dw_hdmi *dw,
+	size_t offset, size_t bytes)
+{
+	u32 *src = dw->buf_src + offset;
+	u32 *dst = dw->buf_dst + offset;
+	u32 *end = dw->buf_src + offset + bytes;
+
+	do {
+		u32 b, sample = *src++;
+
+		b = (sample & 8) << (28 - 3);
+
+		sample >>= 4;
+
+		*dst++ = sample | b;
+	} while (src < end);
+}
+
+static u32 parity(u32 sample)
+{
+	sample ^= sample >> 16;
+	sample ^= sample >> 8;
+	sample ^= sample >> 4;
+	sample ^= sample >> 2;
+	sample ^= sample >> 1;
+	return (sample & 1) << 27;
+}
+
+static void dw_hdmi_reformat_s24(struct snd_dw_hdmi *dw,
+	size_t offset, size_t bytes)
+{
+	u32 *src = dw->buf_src + offset;
+	u32 *dst = dw->buf_dst + offset;
+	u32 *end = dw->buf_src + offset + bytes;
+
+	do {
+		unsigned i;
+		u8 *cs;
+
+		cs = dw->cs[dw->iec_offset++];
+		if (dw->iec_offset >= 192)
+			dw->iec_offset = 0;
+
+		i = dw->channels;
+		do {
+			u32 sample = *src++;
+
+			sample &= ~0xff000000;
+			sample |= *cs++ << 24;
+			sample |= parity(sample & ~0xf8000000);
+
+			*dst++ = sample;
+		} while (--i);
+	} while (src < end);
+}
+
+static void dw_hdmi_create_cs(struct snd_dw_hdmi *dw,
+	struct snd_pcm_runtime *runtime)
+{
+	u8 cs[4];
+	unsigned ch, i, j;
+
+	snd_pcm_create_iec958_consumer(runtime, cs, sizeof(cs));
+
+	memset(dw->cs, 0, sizeof(dw->cs));
+
+	for (ch = 0; ch < 8; ch++) {
+		cs[2] &= ~IEC958_AES2_CON_CHANNEL;
+		cs[2] |= (ch + 1) << 4;
+
+		for (i = 0; i < ARRAY_SIZE(cs); i++) {
+			unsigned c = cs[i];
+
+			for (j = 0; j < 8; j++, c >>= 1)
+				dw->cs[i * 8 + j][ch] = (c & 1) << 2;
+		}
+	}
+	dw->cs[0][0] |= BIT(4);
+}
+
+static void dw_hdmi_start_dma(struct snd_dw_hdmi *dw)
+{
+	void __iomem *base = dw->data.base;
+	unsigned offset = dw->buf_offset;
+	unsigned period = dw->buf_period;
+	u32 start, stop;
+
+	dw->reformat(dw, offset, period);
+
+	/* Clear all irqs before enabling irqs and starting DMA */
+	writeb_relaxed(HDMI_IH_AHBDMAAUD_STAT0_ALL,
+		       base + HDMI_IH_AHBDMAAUD_STAT0);
+
+	start = dw->buf_addr + offset;
+	stop = start + period - 1;
+
+	/* Setup the hardware start/stop addresses */
+	dw_hdmi_writel(start, base + HDMI_AHB_DMA_STRADDR0);
+	dw_hdmi_writel(stop, base + HDMI_AHB_DMA_STPADDR0);
+
+	writeb_relaxed((u8)~HDMI_AHB_DMA_MASK_DONE, base + HDMI_AHB_DMA_MASK);
+	writeb(HDMI_AHB_DMA_START_START, base + HDMI_AHB_DMA_START);
+
+	offset += period;
+	if (offset >= dw->buf_size)
+		offset = 0;
+	dw->buf_offset = offset;
+}
+
+static void dw_hdmi_stop_dma(struct snd_dw_hdmi *dw)
+{
+	/* Disable interrupts before disabling DMA */
+	writeb_relaxed(~0, dw->data.base + HDMI_AHB_DMA_MASK);
+	writeb_relaxed(HDMI_AHB_DMA_STOP_STOP, dw->data.base + HDMI_AHB_DMA_STOP);
+}
+
+static irqreturn_t snd_dw_hdmi_irq(int irq, void *data)
+{
+	struct snd_dw_hdmi *dw = data;
+	struct snd_pcm_substream *substream;
+	unsigned stat;
+
+	stat = readb_relaxed(dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
+	if (!stat)
+		return IRQ_NONE;
+
+	writeb_relaxed(stat, dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
+
+	substream = dw->substream;
+	if (stat & HDMI_IH_AHBDMAAUD_STAT0_DONE && substream) {
+		snd_pcm_period_elapsed(substream);
+
+		spin_lock(&dw->lock);
+		if (dw->substream)
+			dw_hdmi_start_dma(dw);
+		spin_unlock(&dw->lock);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static struct snd_pcm_hardware dw_hdmi_hw = {
+	.info = SNDRV_PCM_INFO_INTERLEAVED |
+		SNDRV_PCM_INFO_BLOCK_TRANSFER |
+		SNDRV_PCM_INFO_MMAP |
+		SNDRV_PCM_INFO_MMAP_VALID,
+	.formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE |
+		   SNDRV_PCM_FMTBIT_S24_LE,
+	.rates = SNDRV_PCM_RATE_32000 |
+		 SNDRV_PCM_RATE_44100 |
+		 SNDRV_PCM_RATE_48000 |
+		 SNDRV_PCM_RATE_88200 |
+		 SNDRV_PCM_RATE_96000 |
+		 SNDRV_PCM_RATE_176400 |
+		 SNDRV_PCM_RATE_192000,
+	.channels_min = 2,
+	.channels_max = 8,
+	.buffer_bytes_max = 64 * 1024,
+	.period_bytes_min = 256,
+	.period_bytes_max = 8192,	/* ERR004323: must limit to 8k */
+	.periods_min = 2,
+	.periods_max = 16,
+	.fifo_size = 0,
+};
+
+static int dw_hdmi_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_dw_hdmi *dw = substream->private_data;
+	void __iomem *base = dw->data.base;
+	int ret;
+
+	runtime->hw = dw_hdmi_hw;
+
+	ret = snd_pcm_limit_hw_rates(runtime);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0)
+		return ret;
+
+	/* Clear FIFO */
+	writeb_relaxed(HDMI_AHB_DMA_CONF0_SW_FIFO_RST,
+		       base + HDMI_AHB_DMA_CONF0);
+
+	/* Configure interrupt polarities */
+	writeb_relaxed(~0, base + HDMI_AHB_DMA_POL);
+	writeb_relaxed(~0, base + HDMI_AHB_DMA_BUFFPOL);
+
+	/* Keep interrupts masked, and clear any pending */
+	writeb_relaxed(~0, base + HDMI_AHB_DMA_MASK);
+	writeb_relaxed(~0, base + HDMI_IH_AHBDMAAUD_STAT0);
+
+	ret = request_irq(dw->data.irq, snd_dw_hdmi_irq, IRQF_SHARED,
+			  "dw-hdmi-audio", dw);
+	if (ret)
+		return ret;
+
+	/* Un-mute done interrupt */
+	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL &
+		       ~HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE,
+		       base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
+
+	return 0;
+}
+
+static int dw_hdmi_close(struct snd_pcm_substream *substream)
+{
+	struct snd_dw_hdmi *dw = substream->private_data;
+
+	/* Mute all interrupts */
+	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
+		       dw->data.base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
+
+	free_irq(dw->data.irq, dw);
+
+	return 0;
+}
+
+static int dw_hdmi_hw_free(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int dw_hdmi_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	return snd_pcm_lib_alloc_vmalloc_buffer(substream,
+						params_buffer_bytes(params));
+}
+
+static int dw_hdmi_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_dw_hdmi *dw = substream->private_data;
+	u8 threshold, conf0, conf1;
+
+	/* Setup as per 3.0.5 FSL 4.1.0 BSP */
+	switch (dw->revision) {
+	case 0x0a:
+		conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
+			HDMI_AHB_DMA_CONF0_INCR4;
+		if (runtime->channels == 2)
+			threshold = 126;
+		else
+			threshold = 124;
+		break;
+	case 0x1a:
+		conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
+			HDMI_AHB_DMA_CONF0_INCR8;
+		threshold = 128;
+		break;
+	default:
+		/* NOTREACHED */
+		return -EINVAL;
+	}
+
+	dw_hdmi_set_sample_rate(dw->data.hdmi, runtime->rate);
+
+	/* Minimum number of bytes in the fifo. */
+	runtime->hw.fifo_size = threshold * 32;
+
+	conf0 |= HDMI_AHB_DMA_CONF0_EN_HLOCK;
+	conf1 = (1 << runtime->channels) - 1;
+
+	writeb_relaxed(threshold, dw->data.base + HDMI_AHB_DMA_THRSLD);
+	writeb_relaxed(conf0, dw->data.base + HDMI_AHB_DMA_CONF0);
+	writeb_relaxed(conf1, dw->data.base + HDMI_AHB_DMA_CONF1);
+
+	switch (runtime->format) {
+	case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
+		dw->reformat = dw_hdmi_reformat_iec958;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		dw_hdmi_create_cs(dw, runtime);
+		dw->reformat = dw_hdmi_reformat_s24;
+		break;
+	}
+	dw->iec_offset = 0;
+	dw->channels = runtime->channels;
+	dw->buf_src  = runtime->dma_area;
+	dw->buf_dst  = substream->dma_buffer.area;
+	dw->buf_addr = substream->dma_buffer.addr;
+	dw->buf_period = snd_pcm_lib_period_bytes(substream);
+	dw->buf_size = snd_pcm_lib_buffer_bytes(substream);
+
+	return 0;
+}
+
+static int dw_hdmi_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_dw_hdmi *dw = substream->private_data;
+	unsigned long flags;
+	int ret = 0;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		spin_lock_irqsave(&dw->lock, flags);
+		dw->buf_offset = 0;
+		dw->substream = substream;
+		dw_hdmi_start_dma(dw);
+		dw_hdmi_audio_enable(dw->data.hdmi);
+		spin_unlock_irqrestore(&dw->lock, flags);
+		substream->runtime->delay = substream->runtime->period_size;
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+		spin_lock_irqsave(&dw->lock, flags);
+		dw->substream = NULL;
+		dw_hdmi_stop_dma(dw);
+		dw_hdmi_audio_disable(dw->data.hdmi);
+		spin_unlock_irqrestore(&dw->lock, flags);
+		break;
+
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static snd_pcm_uframes_t dw_hdmi_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_dw_hdmi *dw = substream->private_data;
+
+	/*
+	 * We are unable to report the exact hardware position as
+	 * reading the 32-bit DMA position using 8-bit reads is racy.
+	 */
+	return bytes_to_frames(runtime, dw->buf_offset);
+}
+
+static struct snd_pcm_ops snd_dw_hdmi_ops = {
+	.open = dw_hdmi_open,
+	.close = dw_hdmi_close,
+	.ioctl = snd_pcm_lib_ioctl,
+	.hw_params = dw_hdmi_hw_params,
+	.hw_free = dw_hdmi_hw_free,
+	.prepare = dw_hdmi_prepare,
+	.trigger = dw_hdmi_trigger,
+	.pointer = dw_hdmi_pointer,
+	.page = snd_pcm_lib_get_vmalloc_page,
+};
+
+static int snd_dw_hdmi_probe(struct platform_device *pdev)
+{
+	const struct dw_hdmi_audio_data *data = pdev->dev.platform_data;
+	struct device *dev = pdev->dev.parent;
+	struct snd_dw_hdmi *dw;
+	struct snd_card *card;
+	struct snd_pcm *pcm;
+	unsigned revision;
+	int ret;
+
+	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
+		       data->base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
+	revision = readb_relaxed(data->base + HDMI_REVISION_ID);
+	if (revision != 0x0a && revision != 0x1a) {
+		dev_err(dev, "dw-hdmi-audio: unknown revision 0x%02x\n",
+			revision);
+		return -ENXIO;
+	}
+
+	ret = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
+			      THIS_MODULE, sizeof(struct snd_dw_hdmi), &card);
+	if (ret < 0)
+		return ret;
+
+	strlcpy(card->driver, DRIVER_NAME, sizeof(card->driver));
+	strlcpy(card->shortname, "DW-HDMI", sizeof(card->shortname));
+	snprintf(card->longname, sizeof(card->longname),
+		 "%s rev 0x%02x, irq %d", card->shortname, revision,
+		 data->irq);
+
+	dw = card->private_data;
+	dw->card = card;
+	dw->data = *data;
+	dw->revision = revision;
+
+	spin_lock_init(&dw->lock);
+
+	ret = snd_pcm_new(card, "DW HDMI", 0, 1, 0, &pcm);
+	if (ret < 0)
+		goto err;
+
+	dw->pcm = pcm;
+	pcm->private_data = dw;
+	strlcpy(pcm->name, DRIVER_NAME, sizeof(pcm->name));
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_dw_hdmi_ops);
+
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+			dev, 64 * 1024, 64 * 1024);
+
+	ret = snd_card_register(card);
+	if (ret < 0)
+		goto err;
+
+	platform_set_drvdata(pdev, dw);
+
+	return 0;
+
+err:
+	snd_card_free(card);
+	return ret;
+}
+
+static int snd_dw_hdmi_remove(struct platform_device *pdev)
+{
+	struct snd_dw_hdmi *dw = platform_get_drvdata(pdev);
+
+	snd_card_free(dw->card);
+
+	return 0;
+}
+
+#if defined(CONFIG_PM_SLEEP) && defined(IS_NOT_BROKEN)
+/*
+ * This code is fine, but requires implementation in the dw_hdmi_trigger()
+ * method which is currently missing as I have no way to test this.
+ */
+static int snd_dw_hdmi_suspend(struct device *dev)
+{
+	struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
+
+	snd_power_change_state(dw->card, SNDRV_CTL_POWER_D3cold);
+	snd_pcm_suspend_all(dw->pcm);
+
+	return 0;
+}
+
+static int snd_dw_hdmi_resume(struct device *dev)
+{
+	struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
+
+	snd_power_change_state(dw->card, SNDRV_CTL_POWER_D0);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(snd_dw_hdmi_pm, snd_dw_hdmi_suspend,
+			 snd_dw_hdmi_resume);
+#define PM_OPS &snd_dw_hdmi_pm
+#else
+#define PM_OPS NULL
+#endif
+
+static struct platform_driver snd_dw_hdmi_driver = {
+	.probe	= snd_dw_hdmi_probe,
+	.remove	= snd_dw_hdmi_remove,
+	.driver	= {
+		.name = DRIVER_NAME,
+		.owner = THIS_MODULE,
+		.pm = PM_OPS,
+	},
+};
+
+module_platform_driver(snd_dw_hdmi_driver);
+
+MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>");
+MODULE_DESCRIPTION("Synopsis Designware HDMI AHB ALSA interface");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/gpu/drm/bridge/dw_hdmi-audio.h b/drivers/gpu/drm/bridge/dw_hdmi-audio.h
new file mode 100644
index 000000000000..1e840118d90a
--- /dev/null
+++ b/drivers/gpu/drm/bridge/dw_hdmi-audio.h
@@ -0,0 +1,13 @@
+#ifndef DW_HDMI_AUDIO_H
+#define DW_HDMI_AUDIO_H
+
+struct dw_hdmi;
+
+struct dw_hdmi_audio_data {
+	phys_addr_t phys;
+	void __iomem *base;
+	int irq;
+	struct dw_hdmi *hdmi;
+};
+
+#endif
diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index fba25607ef88..b65464789fbd 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -28,6 +28,7 @@
 #include <drm/bridge/dw_hdmi.h>
 
 #include "dw_hdmi.h"
+#include "dw_hdmi-audio.h"
 
 #define HDMI_EDID_LEN		512
 
@@ -104,6 +105,7 @@ struct dw_hdmi {
 	struct drm_encoder *encoder;
 	struct drm_bridge *bridge;
 
+	struct platform_device *audio;
 	enum dw_hdmi_devtype dev_type;
 	struct device *dev;
 	struct clk *isfr_clk;
@@ -1732,7 +1734,9 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 {
 	struct drm_device *drm = data;
 	struct device_node *np = dev->of_node;
+	struct platform_device_info pdevinfo;
 	struct device_node *ddc_node;
+	struct dw_hdmi_audio_data audio;
 	struct dw_hdmi *hdmi;
 	int ret;
 	u32 val = 1;
@@ -1860,6 +1864,23 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 	hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE),
 		    HDMI_IH_MUTE_PHY_STAT0);
 
+	memset(&pdevinfo, 0, sizeof(pdevinfo));
+	pdevinfo.parent = dev;
+	pdevinfo.id = PLATFORM_DEVID_AUTO;
+
+	if (hdmi_readb(hdmi, HDMI_CONFIG1_ID) & HDMI_CONFIG1_AHB) {
+		audio.phys = iores->start;
+		audio.base = hdmi->regs;
+		audio.irq = irq;
+		audio.hdmi = hdmi;
+
+		pdevinfo.name = "dw-hdmi-ahb-audio";
+		pdevinfo.data = &audio;
+		pdevinfo.size_data = sizeof(audio);
+		pdevinfo.dma_mask = DMA_BIT_MASK(32);
+		hdmi->audio = platform_device_register_full(&pdevinfo);
+	}
+
 	dev_set_drvdata(dev, hdmi);
 
 	return 0;
@@ -1877,6 +1898,9 @@ void dw_hdmi_unbind(struct device *dev, struct device *master, void *data)
 {
 	struct dw_hdmi *hdmi = dev_get_drvdata(dev);
 
+	if (hdmi->audio && !IS_ERR(hdmi->audio))
+		platform_device_unregister(hdmi->audio);
+
 	/* Disable all interrupts */
 	hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
 
diff --git a/drivers/gpu/drm/bridge/dw_hdmi.h b/drivers/gpu/drm/bridge/dw_hdmi.h
index 175dbc89a824..78e54e813212 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.h
+++ b/drivers/gpu/drm/bridge/dw_hdmi.h
@@ -545,6 +545,9 @@
 #define HDMI_I2CM_FS_SCL_LCNT_0_ADDR            0x7E12
 
 enum {
+/* CONFIG1_ID field values */
+	HDMI_CONFIG1_AHB = 0x01,
+
 /* IH_FC_INT2 field values */
 	HDMI_IH_FC_INT2_OVERFLOW_MASK = 0x03,
 	HDMI_IH_FC_INT2_LOW_PRIORITY_OVERFLOW = 0x02,
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 226+ messages in thread

* Re: [PATCH v2 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver
  2015-08-10 10:05       ` Takashi Iwai
  (?)
@ 2015-08-14 14:04         ` Russell King
  -1 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-14 14:04 UTC (permalink / raw)
  To: Takashi Iwai
  Cc: Fabio Estevam, alsa-devel, David Airlie, Sascha Hauer,
	linux-kernel, dri-devel, Jaroslav Kysela, linux-rockchip,
	Mark Brown, Philipp Zabel, Yakir Yang, Andy Yan, Jon Nettleton,
	linux-arm-kernel, David Airlie, Sascha Hauer, linux-kernel,
	dri-devel, Jaroslav Kysela, linux-rockchip, Mark Brown,
	Philipp Zabel, Yakir Yang, Andy Yan, Jon Nettleton,
	linux-arm-kernel

Add ALSA based HDMI AHB audio driver for dw_hdmi.  The only buffer
format supported by the hardware is its own special IEC958 based format,
which is not compatible with any ALSA format.  To avoid doing too much
data manipulation within the driver, we support only ALSAs IEC958 LE and
24-bit PCM formats for 2 to 6 channels, which we convert to its hardware
format.

A more desirable solution would be to have this conversion in userspace,
but ALSA does not appear to allow such transformations outside of
libasound itself.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
v2: updated with Takashi Iwai's comments... and with a fixed Cc: line.

 drivers/gpu/drm/bridge/Kconfig             |  10 +
 drivers/gpu/drm/bridge/Makefile            |   1 +
 drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c | 579 +++++++++++++++++++++++++++++
 drivers/gpu/drm/bridge/dw_hdmi-audio.h     |  13 +
 drivers/gpu/drm/bridge/dw_hdmi.c           |  24 ++
 drivers/gpu/drm/bridge/dw_hdmi.h           |   3 +
 6 files changed, 630 insertions(+)
 create mode 100644 drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
 create mode 100644 drivers/gpu/drm/bridge/dw_hdmi-audio.h

diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index acef3223772c..56ed35fe0734 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -3,6 +3,16 @@ config DRM_DW_HDMI
 	depends on DRM
 	select DRM_KMS_HELPER
 
+config DRM_DW_HDMI_AHB_AUDIO
+	tristate "Synopsis Designware AHB Audio interface"
+	depends on DRM_DW_HDMI && SND
+	select SND_PCM
+	select SND_PCM_IEC958
+	help
+	  Support the AHB Audio interface which is part of the Synopsis
+	  Designware HDMI block.  This is used in conjunction with
+	  the i.MX6 HDMI driver.
+
 config DRM_PTN3460
 	tristate "PTN3460 DP/LVDS bridge"
 	depends on DRM
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index 8dfebd984370..eb80dbbb8365 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -3,3 +3,4 @@ ccflags-y := -Iinclude/drm
 obj-$(CONFIG_DRM_PS8622) += ps8622.o
 obj-$(CONFIG_DRM_PTN3460) += ptn3460.o
 obj-$(CONFIG_DRM_DW_HDMI) += dw_hdmi.o
+obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw_hdmi-ahb-audio.o
diff --git a/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
new file mode 100644
index 000000000000..bf379310008a
--- /dev/null
+++ b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
@@ -0,0 +1,579 @@
+/*
+ * DesignWare HDMI audio driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Written and tested against the Designware HDMI Tx found in iMX6.
+ */
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <drm/bridge/dw_hdmi.h>
+
+#include <sound/asoundef.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_iec958.h>
+
+#include "dw_hdmi-audio.h"
+
+#define DRIVER_NAME "dw-hdmi-ahb-audio"
+
+/* Provide some bits rather than bit offsets */
+enum {
+	HDMI_AHB_DMA_CONF0_SW_FIFO_RST = BIT(7),
+	HDMI_AHB_DMA_CONF0_EN_HLOCK = BIT(3),
+	HDMI_AHB_DMA_START_START = BIT(0),
+	HDMI_AHB_DMA_STOP_STOP = BIT(0),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR = BIT(5),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST = BIT(4),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY = BIT(3),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE = BIT(2),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL = BIT(1),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL =
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY,
+	HDMI_IH_AHBDMAAUD_STAT0_ERROR = BIT(5),
+	HDMI_IH_AHBDMAAUD_STAT0_LOST = BIT(4),
+	HDMI_IH_AHBDMAAUD_STAT0_RETRY = BIT(3),
+	HDMI_IH_AHBDMAAUD_STAT0_DONE = BIT(2),
+	HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL = BIT(1),
+	HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0),
+	HDMI_IH_AHBDMAAUD_STAT0_ALL =
+		HDMI_IH_AHBDMAAUD_STAT0_ERROR |
+		HDMI_IH_AHBDMAAUD_STAT0_LOST |
+		HDMI_IH_AHBDMAAUD_STAT0_RETRY |
+		HDMI_IH_AHBDMAAUD_STAT0_DONE |
+		HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL |
+		HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY,
+	HDMI_AHB_DMA_CONF0_INCR16 = 2 << 1,
+	HDMI_AHB_DMA_CONF0_INCR8 = 1 << 1,
+	HDMI_AHB_DMA_CONF0_INCR4 = 0,
+	HDMI_AHB_DMA_CONF0_BURST_MODE = BIT(0),
+	HDMI_AHB_DMA_MASK_DONE = BIT(7),
+	HDMI_REVISION_ID = 0x0001,
+	HDMI_IH_AHBDMAAUD_STAT0 = 0x0109,
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0 = 0x0189,
+	HDMI_AHB_DMA_CONF0 = 0x3600,
+	HDMI_AHB_DMA_START = 0x3601,
+	HDMI_AHB_DMA_STOP = 0x3602,
+	HDMI_AHB_DMA_THRSLD = 0x3603,
+	HDMI_AHB_DMA_STRADDR0 = 0x3604,
+	HDMI_AHB_DMA_STPADDR0 = 0x3608,
+	HDMI_AHB_DMA_MASK = 0x3614,
+	HDMI_AHB_DMA_POL = 0x3615,
+	HDMI_AHB_DMA_CONF1 = 0x3616,
+	HDMI_AHB_DMA_BUFFPOL = 0x361a,
+};
+
+struct snd_dw_hdmi {
+	struct snd_card *card;
+	struct snd_pcm *pcm;
+	spinlock_t lock;
+	struct dw_hdmi_audio_data data;
+	struct snd_pcm_substream *substream;
+	void (*reformat)(struct snd_dw_hdmi *, size_t, size_t);
+	void *buf_src;
+	void *buf_dst;
+	dma_addr_t buf_addr;
+	unsigned buf_offset;
+	unsigned buf_period;
+	unsigned buf_size;
+	unsigned channels;
+	u8 revision;
+	u8 iec_offset;
+	u8 cs[192][8];
+};
+
+static void dw_hdmi_writel(u32 val, void __iomem *ptr)
+{
+	writeb_relaxed(val, ptr);
+	writeb_relaxed(val >> 8, ptr + 1);
+	writeb_relaxed(val >> 16, ptr + 2);
+	writeb_relaxed(val >> 24, ptr + 3);
+}
+
+/*
+ * Convert to hardware format: The userspace buffer contains IEC958 samples,
+ * with the PCUV bits in bits 31..28 and audio samples in bits 27..4.  We
+ * need these to be in bits 27..24, with the IEC B bit in bit 28, and audio
+ * samples in 23..0.
+ *
+ * Default preamble in bits 3..0: 8 = block start, 4 = even 2 = odd
+ *
+ * Ideally, we could do with having the data properly formatted in userspace.
+ */
+static void dw_hdmi_reformat_iec958(struct snd_dw_hdmi *dw,
+	size_t offset, size_t bytes)
+{
+	u32 *src = dw->buf_src + offset;
+	u32 *dst = dw->buf_dst + offset;
+	u32 *end = dw->buf_src + offset + bytes;
+
+	do {
+		u32 b, sample = *src++;
+
+		b = (sample & 8) << (28 - 3);
+
+		sample >>= 4;
+
+		*dst++ = sample | b;
+	} while (src < end);
+}
+
+static u32 parity(u32 sample)
+{
+	sample ^= sample >> 16;
+	sample ^= sample >> 8;
+	sample ^= sample >> 4;
+	sample ^= sample >> 2;
+	sample ^= sample >> 1;
+	return (sample & 1) << 27;
+}
+
+static void dw_hdmi_reformat_s24(struct snd_dw_hdmi *dw,
+	size_t offset, size_t bytes)
+{
+	u32 *src = dw->buf_src + offset;
+	u32 *dst = dw->buf_dst + offset;
+	u32 *end = dw->buf_src + offset + bytes;
+
+	do {
+		unsigned i;
+		u8 *cs;
+
+		cs = dw->cs[dw->iec_offset++];
+		if (dw->iec_offset >= 192)
+			dw->iec_offset = 0;
+
+		i = dw->channels;
+		do {
+			u32 sample = *src++;
+
+			sample &= ~0xff000000;
+			sample |= *cs++ << 24;
+			sample |= parity(sample & ~0xf8000000);
+
+			*dst++ = sample;
+		} while (--i);
+	} while (src < end);
+}
+
+static void dw_hdmi_create_cs(struct snd_dw_hdmi *dw,
+	struct snd_pcm_runtime *runtime)
+{
+	u8 cs[4];
+	unsigned ch, i, j;
+
+	snd_pcm_create_iec958_consumer(runtime, cs, sizeof(cs));
+
+	memset(dw->cs, 0, sizeof(dw->cs));
+
+	for (ch = 0; ch < 8; ch++) {
+		cs[2] &= ~IEC958_AES2_CON_CHANNEL;
+		cs[2] |= (ch + 1) << 4;
+
+		for (i = 0; i < ARRAY_SIZE(cs); i++) {
+			unsigned c = cs[i];
+
+			for (j = 0; j < 8; j++, c >>= 1)
+				dw->cs[i * 8 + j][ch] = (c & 1) << 2;
+		}
+	}
+	dw->cs[0][0] |= BIT(4);
+}
+
+static void dw_hdmi_start_dma(struct snd_dw_hdmi *dw)
+{
+	void __iomem *base = dw->data.base;
+	unsigned offset = dw->buf_offset;
+	unsigned period = dw->buf_period;
+	u32 start, stop;
+
+	dw->reformat(dw, offset, period);
+
+	/* Clear all irqs before enabling irqs and starting DMA */
+	writeb_relaxed(HDMI_IH_AHBDMAAUD_STAT0_ALL,
+		       base + HDMI_IH_AHBDMAAUD_STAT0);
+
+	start = dw->buf_addr + offset;
+	stop = start + period - 1;
+
+	/* Setup the hardware start/stop addresses */
+	dw_hdmi_writel(start, base + HDMI_AHB_DMA_STRADDR0);
+	dw_hdmi_writel(stop, base + HDMI_AHB_DMA_STPADDR0);
+
+	writeb_relaxed((u8)~HDMI_AHB_DMA_MASK_DONE, base + HDMI_AHB_DMA_MASK);
+	writeb(HDMI_AHB_DMA_START_START, base + HDMI_AHB_DMA_START);
+
+	offset += period;
+	if (offset >= dw->buf_size)
+		offset = 0;
+	dw->buf_offset = offset;
+}
+
+static void dw_hdmi_stop_dma(struct snd_dw_hdmi *dw)
+{
+	/* Disable interrupts before disabling DMA */
+	writeb_relaxed(~0, dw->data.base + HDMI_AHB_DMA_MASK);
+	writeb_relaxed(HDMI_AHB_DMA_STOP_STOP, dw->data.base + HDMI_AHB_DMA_STOP);
+}
+
+static irqreturn_t snd_dw_hdmi_irq(int irq, void *data)
+{
+	struct snd_dw_hdmi *dw = data;
+	struct snd_pcm_substream *substream;
+	unsigned stat;
+
+	stat = readb_relaxed(dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
+	if (!stat)
+		return IRQ_NONE;
+
+	writeb_relaxed(stat, dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
+
+	substream = dw->substream;
+	if (stat & HDMI_IH_AHBDMAAUD_STAT0_DONE && substream) {
+		snd_pcm_period_elapsed(substream);
+
+		spin_lock(&dw->lock);
+		if (dw->substream)
+			dw_hdmi_start_dma(dw);
+		spin_unlock(&dw->lock);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static struct snd_pcm_hardware dw_hdmi_hw = {
+	.info = SNDRV_PCM_INFO_INTERLEAVED |
+		SNDRV_PCM_INFO_BLOCK_TRANSFER |
+		SNDRV_PCM_INFO_MMAP |
+		SNDRV_PCM_INFO_MMAP_VALID,
+	.formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE |
+		   SNDRV_PCM_FMTBIT_S24_LE,
+	.rates = SNDRV_PCM_RATE_32000 |
+		 SNDRV_PCM_RATE_44100 |
+		 SNDRV_PCM_RATE_48000 |
+		 SNDRV_PCM_RATE_88200 |
+		 SNDRV_PCM_RATE_96000 |
+		 SNDRV_PCM_RATE_176400 |
+		 SNDRV_PCM_RATE_192000,
+	.channels_min = 2,
+	.channels_max = 8,
+	.buffer_bytes_max = 64 * 1024,
+	.period_bytes_min = 256,
+	.period_bytes_max = 8192,	/* ERR004323: must limit to 8k */
+	.periods_min = 2,
+	.periods_max = 16,
+	.fifo_size = 0,
+};
+
+static int dw_hdmi_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_dw_hdmi *dw = substream->private_data;
+	void __iomem *base = dw->data.base;
+	int ret;
+
+	runtime->hw = dw_hdmi_hw;
+
+	ret = snd_pcm_limit_hw_rates(runtime);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0)
+		return ret;
+
+	/* Clear FIFO */
+	writeb_relaxed(HDMI_AHB_DMA_CONF0_SW_FIFO_RST,
+		       base + HDMI_AHB_DMA_CONF0);
+
+	/* Configure interrupt polarities */
+	writeb_relaxed(~0, base + HDMI_AHB_DMA_POL);
+	writeb_relaxed(~0, base + HDMI_AHB_DMA_BUFFPOL);
+
+	/* Keep interrupts masked, and clear any pending */
+	writeb_relaxed(~0, base + HDMI_AHB_DMA_MASK);
+	writeb_relaxed(~0, base + HDMI_IH_AHBDMAAUD_STAT0);
+
+	ret = request_irq(dw->data.irq, snd_dw_hdmi_irq, IRQF_SHARED,
+			  "dw-hdmi-audio", dw);
+	if (ret)
+		return ret;
+
+	/* Un-mute done interrupt */
+	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL &
+		       ~HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE,
+		       base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
+
+	return 0;
+}
+
+static int dw_hdmi_close(struct snd_pcm_substream *substream)
+{
+	struct snd_dw_hdmi *dw = substream->private_data;
+
+	/* Mute all interrupts */
+	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
+		       dw->data.base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
+
+	free_irq(dw->data.irq, dw);
+
+	return 0;
+}
+
+static int dw_hdmi_hw_free(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int dw_hdmi_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	return snd_pcm_lib_alloc_vmalloc_buffer(substream,
+						params_buffer_bytes(params));
+}
+
+static int dw_hdmi_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_dw_hdmi *dw = substream->private_data;
+	u8 threshold, conf0, conf1;
+
+	/* Setup as per 3.0.5 FSL 4.1.0 BSP */
+	switch (dw->revision) {
+	case 0x0a:
+		conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
+			HDMI_AHB_DMA_CONF0_INCR4;
+		if (runtime->channels == 2)
+			threshold = 126;
+		else
+			threshold = 124;
+		break;
+	case 0x1a:
+		conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
+			HDMI_AHB_DMA_CONF0_INCR8;
+		threshold = 128;
+		break;
+	default:
+		/* NOTREACHED */
+		return -EINVAL;
+	}
+
+	dw_hdmi_set_sample_rate(dw->data.hdmi, runtime->rate);
+
+	/* Minimum number of bytes in the fifo. */
+	runtime->hw.fifo_size = threshold * 32;
+
+	conf0 |= HDMI_AHB_DMA_CONF0_EN_HLOCK;
+	conf1 = (1 << runtime->channels) - 1;
+
+	writeb_relaxed(threshold, dw->data.base + HDMI_AHB_DMA_THRSLD);
+	writeb_relaxed(conf0, dw->data.base + HDMI_AHB_DMA_CONF0);
+	writeb_relaxed(conf1, dw->data.base + HDMI_AHB_DMA_CONF1);
+
+	switch (runtime->format) {
+	case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
+		dw->reformat = dw_hdmi_reformat_iec958;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		dw_hdmi_create_cs(dw, runtime);
+		dw->reformat = dw_hdmi_reformat_s24;
+		break;
+	}
+	dw->iec_offset = 0;
+	dw->channels = runtime->channels;
+	dw->buf_src  = runtime->dma_area;
+	dw->buf_dst  = substream->dma_buffer.area;
+	dw->buf_addr = substream->dma_buffer.addr;
+	dw->buf_period = snd_pcm_lib_period_bytes(substream);
+	dw->buf_size = snd_pcm_lib_buffer_bytes(substream);
+
+	return 0;
+}
+
+static int dw_hdmi_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_dw_hdmi *dw = substream->private_data;
+	unsigned long flags;
+	int ret = 0;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		spin_lock_irqsave(&dw->lock, flags);
+		dw->buf_offset = 0;
+		dw->substream = substream;
+		dw_hdmi_start_dma(dw);
+		dw_hdmi_audio_enable(dw->data.hdmi);
+		spin_unlock_irqrestore(&dw->lock, flags);
+		substream->runtime->delay = substream->runtime->period_size;
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+		spin_lock_irqsave(&dw->lock, flags);
+		dw->substream = NULL;
+		dw_hdmi_stop_dma(dw);
+		dw_hdmi_audio_disable(dw->data.hdmi);
+		spin_unlock_irqrestore(&dw->lock, flags);
+		break;
+
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static snd_pcm_uframes_t dw_hdmi_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_dw_hdmi *dw = substream->private_data;
+
+	/*
+	 * We are unable to report the exact hardware position as
+	 * reading the 32-bit DMA position using 8-bit reads is racy.
+	 */
+	return bytes_to_frames(runtime, dw->buf_offset);
+}
+
+static struct snd_pcm_ops snd_dw_hdmi_ops = {
+	.open = dw_hdmi_open,
+	.close = dw_hdmi_close,
+	.ioctl = snd_pcm_lib_ioctl,
+	.hw_params = dw_hdmi_hw_params,
+	.hw_free = dw_hdmi_hw_free,
+	.prepare = dw_hdmi_prepare,
+	.trigger = dw_hdmi_trigger,
+	.pointer = dw_hdmi_pointer,
+	.page = snd_pcm_lib_get_vmalloc_page,
+};
+
+static int snd_dw_hdmi_probe(struct platform_device *pdev)
+{
+	const struct dw_hdmi_audio_data *data = pdev->dev.platform_data;
+	struct device *dev = pdev->dev.parent;
+	struct snd_dw_hdmi *dw;
+	struct snd_card *card;
+	struct snd_pcm *pcm;
+	unsigned revision;
+	int ret;
+
+	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
+		       data->base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
+	revision = readb_relaxed(data->base + HDMI_REVISION_ID);
+	if (revision != 0x0a && revision != 0x1a) {
+		dev_err(dev, "dw-hdmi-audio: unknown revision 0x%02x\n",
+			revision);
+		return -ENXIO;
+	}
+
+	ret = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
+			      THIS_MODULE, sizeof(struct snd_dw_hdmi), &card);
+	if (ret < 0)
+		return ret;
+
+	strlcpy(card->driver, DRIVER_NAME, sizeof(card->driver));
+	strlcpy(card->shortname, "DW-HDMI", sizeof(card->shortname));
+	snprintf(card->longname, sizeof(card->longname),
+		 "%s rev 0x%02x, irq %d", card->shortname, revision,
+		 data->irq);
+
+	dw = card->private_data;
+	dw->card = card;
+	dw->data = *data;
+	dw->revision = revision;
+
+	spin_lock_init(&dw->lock);
+
+	ret = snd_pcm_new(card, "DW HDMI", 0, 1, 0, &pcm);
+	if (ret < 0)
+		goto err;
+
+	dw->pcm = pcm;
+	pcm->private_data = dw;
+	strlcpy(pcm->name, DRIVER_NAME, sizeof(pcm->name));
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_dw_hdmi_ops);
+
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+			dev, 64 * 1024, 64 * 1024);
+
+	ret = snd_card_register(card);
+	if (ret < 0)
+		goto err;
+
+	platform_set_drvdata(pdev, dw);
+
+	return 0;
+
+err:
+	snd_card_free(card);
+	return ret;
+}
+
+static int snd_dw_hdmi_remove(struct platform_device *pdev)
+{
+	struct snd_dw_hdmi *dw = platform_get_drvdata(pdev);
+
+	snd_card_free(dw->card);
+
+	return 0;
+}
+
+#if defined(CONFIG_PM_SLEEP) && defined(IS_NOT_BROKEN)
+/*
+ * This code is fine, but requires implementation in the dw_hdmi_trigger()
+ * method which is currently missing as I have no way to test this.
+ */
+static int snd_dw_hdmi_suspend(struct device *dev)
+{
+	struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
+
+	snd_power_change_state(dw->card, SNDRV_CTL_POWER_D3cold);
+	snd_pcm_suspend_all(dw->pcm);
+
+	return 0;
+}
+
+static int snd_dw_hdmi_resume(struct device *dev)
+{
+	struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
+
+	snd_power_change_state(dw->card, SNDRV_CTL_POWER_D0);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(snd_dw_hdmi_pm, snd_dw_hdmi_suspend,
+			 snd_dw_hdmi_resume);
+#define PM_OPS &snd_dw_hdmi_pm
+#else
+#define PM_OPS NULL
+#endif
+
+static struct platform_driver snd_dw_hdmi_driver = {
+	.probe	= snd_dw_hdmi_probe,
+	.remove	= snd_dw_hdmi_remove,
+	.driver	= {
+		.name = DRIVER_NAME,
+		.owner = THIS_MODULE,
+		.pm = PM_OPS,
+	},
+};
+
+module_platform_driver(snd_dw_hdmi_driver);
+
+MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>");
+MODULE_DESCRIPTION("Synopsis Designware HDMI AHB ALSA interface");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/gpu/drm/bridge/dw_hdmi-audio.h b/drivers/gpu/drm/bridge/dw_hdmi-audio.h
new file mode 100644
index 000000000000..1e840118d90a
--- /dev/null
+++ b/drivers/gpu/drm/bridge/dw_hdmi-audio.h
@@ -0,0 +1,13 @@
+#ifndef DW_HDMI_AUDIO_H
+#define DW_HDMI_AUDIO_H
+
+struct dw_hdmi;
+
+struct dw_hdmi_audio_data {
+	phys_addr_t phys;
+	void __iomem *base;
+	int irq;
+	struct dw_hdmi *hdmi;
+};
+
+#endif
diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index fba25607ef88..b65464789fbd 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -28,6 +28,7 @@
 #include <drm/bridge/dw_hdmi.h>
 
 #include "dw_hdmi.h"
+#include "dw_hdmi-audio.h"
 
 #define HDMI_EDID_LEN		512
 
@@ -104,6 +105,7 @@ struct dw_hdmi {
 	struct drm_encoder *encoder;
 	struct drm_bridge *bridge;
 
+	struct platform_device *audio;
 	enum dw_hdmi_devtype dev_type;
 	struct device *dev;
 	struct clk *isfr_clk;
@@ -1732,7 +1734,9 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 {
 	struct drm_device *drm = data;
 	struct device_node *np = dev->of_node;
+	struct platform_device_info pdevinfo;
 	struct device_node *ddc_node;
+	struct dw_hdmi_audio_data audio;
 	struct dw_hdmi *hdmi;
 	int ret;
 	u32 val = 1;
@@ -1860,6 +1864,23 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 	hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE),
 		    HDMI_IH_MUTE_PHY_STAT0);
 
+	memset(&pdevinfo, 0, sizeof(pdevinfo));
+	pdevinfo.parent = dev;
+	pdevinfo.id = PLATFORM_DEVID_AUTO;
+
+	if (hdmi_readb(hdmi, HDMI_CONFIG1_ID) & HDMI_CONFIG1_AHB) {
+		audio.phys = iores->start;
+		audio.base = hdmi->regs;
+		audio.irq = irq;
+		audio.hdmi = hdmi;
+
+		pdevinfo.name = "dw-hdmi-ahb-audio";
+		pdevinfo.data = &audio;
+		pdevinfo.size_data = sizeof(audio);
+		pdevinfo.dma_mask = DMA_BIT_MASK(32);
+		hdmi->audio = platform_device_register_full(&pdevinfo);
+	}
+
 	dev_set_drvdata(dev, hdmi);
 
 	return 0;
@@ -1877,6 +1898,9 @@ void dw_hdmi_unbind(struct device *dev, struct device *master, void *data)
 {
 	struct dw_hdmi *hdmi = dev_get_drvdata(dev);
 
+	if (hdmi->audio && !IS_ERR(hdmi->audio))
+		platform_device_unregister(hdmi->audio);
+
 	/* Disable all interrupts */
 	hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
 
diff --git a/drivers/gpu/drm/bridge/dw_hdmi.h b/drivers/gpu/drm/bridge/dw_hdmi.h
index 175dbc89a824..78e54e813212 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.h
+++ b/drivers/gpu/drm/bridge/dw_hdmi.h
@@ -545,6 +545,9 @@
 #define HDMI_I2CM_FS_SCL_LCNT_0_ADDR            0x7E12
 
 enum {
+/* CONFIG1_ID field values */
+	HDMI_CONFIG1_AHB = 0x01,
+
 /* IH_FC_INT2 field values */
 	HDMI_IH_FC_INT2_OVERFLOW_MASK = 0x03,
 	HDMI_IH_FC_INT2_LOW_PRIORITY_OVERFLOW = 0x02,
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 226+ messages in thread

* Re: [PATCH v2 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver
@ 2015-08-14 14:04         ` Russell King
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-14 14:04 UTC (permalink / raw)
  To: Takashi Iwai
  Cc: Fabio Estevam, alsa-devel, linux-kernel, dri-devel,
	Jaroslav Kysela, linux-rockchip, Mark Brown, Yakir Yang,
	Andy Yan, linux-arm-kernel

Add ALSA based HDMI AHB audio driver for dw_hdmi.  The only buffer
format supported by the hardware is its own special IEC958 based format,
which is not compatible with any ALSA format.  To avoid doing too much
data manipulation within the driver, we support only ALSAs IEC958 LE and
24-bit PCM formats for 2 to 6 channels, which we convert to its hardware
format.

A more desirable solution would be to have this conversion in userspace,
but ALSA does not appear to allow such transformations outside of
libasound itself.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
v2: updated with Takashi Iwai's comments... and with a fixed Cc: line.

 drivers/gpu/drm/bridge/Kconfig             |  10 +
 drivers/gpu/drm/bridge/Makefile            |   1 +
 drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c | 579 +++++++++++++++++++++++++++++
 drivers/gpu/drm/bridge/dw_hdmi-audio.h     |  13 +
 drivers/gpu/drm/bridge/dw_hdmi.c           |  24 ++
 drivers/gpu/drm/bridge/dw_hdmi.h           |   3 +
 6 files changed, 630 insertions(+)
 create mode 100644 drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
 create mode 100644 drivers/gpu/drm/bridge/dw_hdmi-audio.h

diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index acef3223772c..56ed35fe0734 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -3,6 +3,16 @@ config DRM_DW_HDMI
 	depends on DRM
 	select DRM_KMS_HELPER
 
+config DRM_DW_HDMI_AHB_AUDIO
+	tristate "Synopsis Designware AHB Audio interface"
+	depends on DRM_DW_HDMI && SND
+	select SND_PCM
+	select SND_PCM_IEC958
+	help
+	  Support the AHB Audio interface which is part of the Synopsis
+	  Designware HDMI block.  This is used in conjunction with
+	  the i.MX6 HDMI driver.
+
 config DRM_PTN3460
 	tristate "PTN3460 DP/LVDS bridge"
 	depends on DRM
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index 8dfebd984370..eb80dbbb8365 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -3,3 +3,4 @@ ccflags-y := -Iinclude/drm
 obj-$(CONFIG_DRM_PS8622) += ps8622.o
 obj-$(CONFIG_DRM_PTN3460) += ptn3460.o
 obj-$(CONFIG_DRM_DW_HDMI) += dw_hdmi.o
+obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw_hdmi-ahb-audio.o
diff --git a/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
new file mode 100644
index 000000000000..bf379310008a
--- /dev/null
+++ b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
@@ -0,0 +1,579 @@
+/*
+ * DesignWare HDMI audio driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Written and tested against the Designware HDMI Tx found in iMX6.
+ */
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <drm/bridge/dw_hdmi.h>
+
+#include <sound/asoundef.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_iec958.h>
+
+#include "dw_hdmi-audio.h"
+
+#define DRIVER_NAME "dw-hdmi-ahb-audio"
+
+/* Provide some bits rather than bit offsets */
+enum {
+	HDMI_AHB_DMA_CONF0_SW_FIFO_RST = BIT(7),
+	HDMI_AHB_DMA_CONF0_EN_HLOCK = BIT(3),
+	HDMI_AHB_DMA_START_START = BIT(0),
+	HDMI_AHB_DMA_STOP_STOP = BIT(0),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR = BIT(5),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST = BIT(4),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY = BIT(3),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE = BIT(2),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL = BIT(1),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL =
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY,
+	HDMI_IH_AHBDMAAUD_STAT0_ERROR = BIT(5),
+	HDMI_IH_AHBDMAAUD_STAT0_LOST = BIT(4),
+	HDMI_IH_AHBDMAAUD_STAT0_RETRY = BIT(3),
+	HDMI_IH_AHBDMAAUD_STAT0_DONE = BIT(2),
+	HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL = BIT(1),
+	HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0),
+	HDMI_IH_AHBDMAAUD_STAT0_ALL =
+		HDMI_IH_AHBDMAAUD_STAT0_ERROR |
+		HDMI_IH_AHBDMAAUD_STAT0_LOST |
+		HDMI_IH_AHBDMAAUD_STAT0_RETRY |
+		HDMI_IH_AHBDMAAUD_STAT0_DONE |
+		HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL |
+		HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY,
+	HDMI_AHB_DMA_CONF0_INCR16 = 2 << 1,
+	HDMI_AHB_DMA_CONF0_INCR8 = 1 << 1,
+	HDMI_AHB_DMA_CONF0_INCR4 = 0,
+	HDMI_AHB_DMA_CONF0_BURST_MODE = BIT(0),
+	HDMI_AHB_DMA_MASK_DONE = BIT(7),
+	HDMI_REVISION_ID = 0x0001,
+	HDMI_IH_AHBDMAAUD_STAT0 = 0x0109,
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0 = 0x0189,
+	HDMI_AHB_DMA_CONF0 = 0x3600,
+	HDMI_AHB_DMA_START = 0x3601,
+	HDMI_AHB_DMA_STOP = 0x3602,
+	HDMI_AHB_DMA_THRSLD = 0x3603,
+	HDMI_AHB_DMA_STRADDR0 = 0x3604,
+	HDMI_AHB_DMA_STPADDR0 = 0x3608,
+	HDMI_AHB_DMA_MASK = 0x3614,
+	HDMI_AHB_DMA_POL = 0x3615,
+	HDMI_AHB_DMA_CONF1 = 0x3616,
+	HDMI_AHB_DMA_BUFFPOL = 0x361a,
+};
+
+struct snd_dw_hdmi {
+	struct snd_card *card;
+	struct snd_pcm *pcm;
+	spinlock_t lock;
+	struct dw_hdmi_audio_data data;
+	struct snd_pcm_substream *substream;
+	void (*reformat)(struct snd_dw_hdmi *, size_t, size_t);
+	void *buf_src;
+	void *buf_dst;
+	dma_addr_t buf_addr;
+	unsigned buf_offset;
+	unsigned buf_period;
+	unsigned buf_size;
+	unsigned channels;
+	u8 revision;
+	u8 iec_offset;
+	u8 cs[192][8];
+};
+
+static void dw_hdmi_writel(u32 val, void __iomem *ptr)
+{
+	writeb_relaxed(val, ptr);
+	writeb_relaxed(val >> 8, ptr + 1);
+	writeb_relaxed(val >> 16, ptr + 2);
+	writeb_relaxed(val >> 24, ptr + 3);
+}
+
+/*
+ * Convert to hardware format: The userspace buffer contains IEC958 samples,
+ * with the PCUV bits in bits 31..28 and audio samples in bits 27..4.  We
+ * need these to be in bits 27..24, with the IEC B bit in bit 28, and audio
+ * samples in 23..0.
+ *
+ * Default preamble in bits 3..0: 8 = block start, 4 = even 2 = odd
+ *
+ * Ideally, we could do with having the data properly formatted in userspace.
+ */
+static void dw_hdmi_reformat_iec958(struct snd_dw_hdmi *dw,
+	size_t offset, size_t bytes)
+{
+	u32 *src = dw->buf_src + offset;
+	u32 *dst = dw->buf_dst + offset;
+	u32 *end = dw->buf_src + offset + bytes;
+
+	do {
+		u32 b, sample = *src++;
+
+		b = (sample & 8) << (28 - 3);
+
+		sample >>= 4;
+
+		*dst++ = sample | b;
+	} while (src < end);
+}
+
+static u32 parity(u32 sample)
+{
+	sample ^= sample >> 16;
+	sample ^= sample >> 8;
+	sample ^= sample >> 4;
+	sample ^= sample >> 2;
+	sample ^= sample >> 1;
+	return (sample & 1) << 27;
+}
+
+static void dw_hdmi_reformat_s24(struct snd_dw_hdmi *dw,
+	size_t offset, size_t bytes)
+{
+	u32 *src = dw->buf_src + offset;
+	u32 *dst = dw->buf_dst + offset;
+	u32 *end = dw->buf_src + offset + bytes;
+
+	do {
+		unsigned i;
+		u8 *cs;
+
+		cs = dw->cs[dw->iec_offset++];
+		if (dw->iec_offset >= 192)
+			dw->iec_offset = 0;
+
+		i = dw->channels;
+		do {
+			u32 sample = *src++;
+
+			sample &= ~0xff000000;
+			sample |= *cs++ << 24;
+			sample |= parity(sample & ~0xf8000000);
+
+			*dst++ = sample;
+		} while (--i);
+	} while (src < end);
+}
+
+static void dw_hdmi_create_cs(struct snd_dw_hdmi *dw,
+	struct snd_pcm_runtime *runtime)
+{
+	u8 cs[4];
+	unsigned ch, i, j;
+
+	snd_pcm_create_iec958_consumer(runtime, cs, sizeof(cs));
+
+	memset(dw->cs, 0, sizeof(dw->cs));
+
+	for (ch = 0; ch < 8; ch++) {
+		cs[2] &= ~IEC958_AES2_CON_CHANNEL;
+		cs[2] |= (ch + 1) << 4;
+
+		for (i = 0; i < ARRAY_SIZE(cs); i++) {
+			unsigned c = cs[i];
+
+			for (j = 0; j < 8; j++, c >>= 1)
+				dw->cs[i * 8 + j][ch] = (c & 1) << 2;
+		}
+	}
+	dw->cs[0][0] |= BIT(4);
+}
+
+static void dw_hdmi_start_dma(struct snd_dw_hdmi *dw)
+{
+	void __iomem *base = dw->data.base;
+	unsigned offset = dw->buf_offset;
+	unsigned period = dw->buf_period;
+	u32 start, stop;
+
+	dw->reformat(dw, offset, period);
+
+	/* Clear all irqs before enabling irqs and starting DMA */
+	writeb_relaxed(HDMI_IH_AHBDMAAUD_STAT0_ALL,
+		       base + HDMI_IH_AHBDMAAUD_STAT0);
+
+	start = dw->buf_addr + offset;
+	stop = start + period - 1;
+
+	/* Setup the hardware start/stop addresses */
+	dw_hdmi_writel(start, base + HDMI_AHB_DMA_STRADDR0);
+	dw_hdmi_writel(stop, base + HDMI_AHB_DMA_STPADDR0);
+
+	writeb_relaxed((u8)~HDMI_AHB_DMA_MASK_DONE, base + HDMI_AHB_DMA_MASK);
+	writeb(HDMI_AHB_DMA_START_START, base + HDMI_AHB_DMA_START);
+
+	offset += period;
+	if (offset >= dw->buf_size)
+		offset = 0;
+	dw->buf_offset = offset;
+}
+
+static void dw_hdmi_stop_dma(struct snd_dw_hdmi *dw)
+{
+	/* Disable interrupts before disabling DMA */
+	writeb_relaxed(~0, dw->data.base + HDMI_AHB_DMA_MASK);
+	writeb_relaxed(HDMI_AHB_DMA_STOP_STOP, dw->data.base + HDMI_AHB_DMA_STOP);
+}
+
+static irqreturn_t snd_dw_hdmi_irq(int irq, void *data)
+{
+	struct snd_dw_hdmi *dw = data;
+	struct snd_pcm_substream *substream;
+	unsigned stat;
+
+	stat = readb_relaxed(dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
+	if (!stat)
+		return IRQ_NONE;
+
+	writeb_relaxed(stat, dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
+
+	substream = dw->substream;
+	if (stat & HDMI_IH_AHBDMAAUD_STAT0_DONE && substream) {
+		snd_pcm_period_elapsed(substream);
+
+		spin_lock(&dw->lock);
+		if (dw->substream)
+			dw_hdmi_start_dma(dw);
+		spin_unlock(&dw->lock);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static struct snd_pcm_hardware dw_hdmi_hw = {
+	.info = SNDRV_PCM_INFO_INTERLEAVED |
+		SNDRV_PCM_INFO_BLOCK_TRANSFER |
+		SNDRV_PCM_INFO_MMAP |
+		SNDRV_PCM_INFO_MMAP_VALID,
+	.formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE |
+		   SNDRV_PCM_FMTBIT_S24_LE,
+	.rates = SNDRV_PCM_RATE_32000 |
+		 SNDRV_PCM_RATE_44100 |
+		 SNDRV_PCM_RATE_48000 |
+		 SNDRV_PCM_RATE_88200 |
+		 SNDRV_PCM_RATE_96000 |
+		 SNDRV_PCM_RATE_176400 |
+		 SNDRV_PCM_RATE_192000,
+	.channels_min = 2,
+	.channels_max = 8,
+	.buffer_bytes_max = 64 * 1024,
+	.period_bytes_min = 256,
+	.period_bytes_max = 8192,	/* ERR004323: must limit to 8k */
+	.periods_min = 2,
+	.periods_max = 16,
+	.fifo_size = 0,
+};
+
+static int dw_hdmi_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_dw_hdmi *dw = substream->private_data;
+	void __iomem *base = dw->data.base;
+	int ret;
+
+	runtime->hw = dw_hdmi_hw;
+
+	ret = snd_pcm_limit_hw_rates(runtime);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0)
+		return ret;
+
+	/* Clear FIFO */
+	writeb_relaxed(HDMI_AHB_DMA_CONF0_SW_FIFO_RST,
+		       base + HDMI_AHB_DMA_CONF0);
+
+	/* Configure interrupt polarities */
+	writeb_relaxed(~0, base + HDMI_AHB_DMA_POL);
+	writeb_relaxed(~0, base + HDMI_AHB_DMA_BUFFPOL);
+
+	/* Keep interrupts masked, and clear any pending */
+	writeb_relaxed(~0, base + HDMI_AHB_DMA_MASK);
+	writeb_relaxed(~0, base + HDMI_IH_AHBDMAAUD_STAT0);
+
+	ret = request_irq(dw->data.irq, snd_dw_hdmi_irq, IRQF_SHARED,
+			  "dw-hdmi-audio", dw);
+	if (ret)
+		return ret;
+
+	/* Un-mute done interrupt */
+	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL &
+		       ~HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE,
+		       base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
+
+	return 0;
+}
+
+static int dw_hdmi_close(struct snd_pcm_substream *substream)
+{
+	struct snd_dw_hdmi *dw = substream->private_data;
+
+	/* Mute all interrupts */
+	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
+		       dw->data.base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
+
+	free_irq(dw->data.irq, dw);
+
+	return 0;
+}
+
+static int dw_hdmi_hw_free(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int dw_hdmi_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	return snd_pcm_lib_alloc_vmalloc_buffer(substream,
+						params_buffer_bytes(params));
+}
+
+static int dw_hdmi_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_dw_hdmi *dw = substream->private_data;
+	u8 threshold, conf0, conf1;
+
+	/* Setup as per 3.0.5 FSL 4.1.0 BSP */
+	switch (dw->revision) {
+	case 0x0a:
+		conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
+			HDMI_AHB_DMA_CONF0_INCR4;
+		if (runtime->channels == 2)
+			threshold = 126;
+		else
+			threshold = 124;
+		break;
+	case 0x1a:
+		conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
+			HDMI_AHB_DMA_CONF0_INCR8;
+		threshold = 128;
+		break;
+	default:
+		/* NOTREACHED */
+		return -EINVAL;
+	}
+
+	dw_hdmi_set_sample_rate(dw->data.hdmi, runtime->rate);
+
+	/* Minimum number of bytes in the fifo. */
+	runtime->hw.fifo_size = threshold * 32;
+
+	conf0 |= HDMI_AHB_DMA_CONF0_EN_HLOCK;
+	conf1 = (1 << runtime->channels) - 1;
+
+	writeb_relaxed(threshold, dw->data.base + HDMI_AHB_DMA_THRSLD);
+	writeb_relaxed(conf0, dw->data.base + HDMI_AHB_DMA_CONF0);
+	writeb_relaxed(conf1, dw->data.base + HDMI_AHB_DMA_CONF1);
+
+	switch (runtime->format) {
+	case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
+		dw->reformat = dw_hdmi_reformat_iec958;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		dw_hdmi_create_cs(dw, runtime);
+		dw->reformat = dw_hdmi_reformat_s24;
+		break;
+	}
+	dw->iec_offset = 0;
+	dw->channels = runtime->channels;
+	dw->buf_src  = runtime->dma_area;
+	dw->buf_dst  = substream->dma_buffer.area;
+	dw->buf_addr = substream->dma_buffer.addr;
+	dw->buf_period = snd_pcm_lib_period_bytes(substream);
+	dw->buf_size = snd_pcm_lib_buffer_bytes(substream);
+
+	return 0;
+}
+
+static int dw_hdmi_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_dw_hdmi *dw = substream->private_data;
+	unsigned long flags;
+	int ret = 0;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		spin_lock_irqsave(&dw->lock, flags);
+		dw->buf_offset = 0;
+		dw->substream = substream;
+		dw_hdmi_start_dma(dw);
+		dw_hdmi_audio_enable(dw->data.hdmi);
+		spin_unlock_irqrestore(&dw->lock, flags);
+		substream->runtime->delay = substream->runtime->period_size;
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+		spin_lock_irqsave(&dw->lock, flags);
+		dw->substream = NULL;
+		dw_hdmi_stop_dma(dw);
+		dw_hdmi_audio_disable(dw->data.hdmi);
+		spin_unlock_irqrestore(&dw->lock, flags);
+		break;
+
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static snd_pcm_uframes_t dw_hdmi_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_dw_hdmi *dw = substream->private_data;
+
+	/*
+	 * We are unable to report the exact hardware position as
+	 * reading the 32-bit DMA position using 8-bit reads is racy.
+	 */
+	return bytes_to_frames(runtime, dw->buf_offset);
+}
+
+static struct snd_pcm_ops snd_dw_hdmi_ops = {
+	.open = dw_hdmi_open,
+	.close = dw_hdmi_close,
+	.ioctl = snd_pcm_lib_ioctl,
+	.hw_params = dw_hdmi_hw_params,
+	.hw_free = dw_hdmi_hw_free,
+	.prepare = dw_hdmi_prepare,
+	.trigger = dw_hdmi_trigger,
+	.pointer = dw_hdmi_pointer,
+	.page = snd_pcm_lib_get_vmalloc_page,
+};
+
+static int snd_dw_hdmi_probe(struct platform_device *pdev)
+{
+	const struct dw_hdmi_audio_data *data = pdev->dev.platform_data;
+	struct device *dev = pdev->dev.parent;
+	struct snd_dw_hdmi *dw;
+	struct snd_card *card;
+	struct snd_pcm *pcm;
+	unsigned revision;
+	int ret;
+
+	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
+		       data->base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
+	revision = readb_relaxed(data->base + HDMI_REVISION_ID);
+	if (revision != 0x0a && revision != 0x1a) {
+		dev_err(dev, "dw-hdmi-audio: unknown revision 0x%02x\n",
+			revision);
+		return -ENXIO;
+	}
+
+	ret = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
+			      THIS_MODULE, sizeof(struct snd_dw_hdmi), &card);
+	if (ret < 0)
+		return ret;
+
+	strlcpy(card->driver, DRIVER_NAME, sizeof(card->driver));
+	strlcpy(card->shortname, "DW-HDMI", sizeof(card->shortname));
+	snprintf(card->longname, sizeof(card->longname),
+		 "%s rev 0x%02x, irq %d", card->shortname, revision,
+		 data->irq);
+
+	dw = card->private_data;
+	dw->card = card;
+	dw->data = *data;
+	dw->revision = revision;
+
+	spin_lock_init(&dw->lock);
+
+	ret = snd_pcm_new(card, "DW HDMI", 0, 1, 0, &pcm);
+	if (ret < 0)
+		goto err;
+
+	dw->pcm = pcm;
+	pcm->private_data = dw;
+	strlcpy(pcm->name, DRIVER_NAME, sizeof(pcm->name));
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_dw_hdmi_ops);
+
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+			dev, 64 * 1024, 64 * 1024);
+
+	ret = snd_card_register(card);
+	if (ret < 0)
+		goto err;
+
+	platform_set_drvdata(pdev, dw);
+
+	return 0;
+
+err:
+	snd_card_free(card);
+	return ret;
+}
+
+static int snd_dw_hdmi_remove(struct platform_device *pdev)
+{
+	struct snd_dw_hdmi *dw = platform_get_drvdata(pdev);
+
+	snd_card_free(dw->card);
+
+	return 0;
+}
+
+#if defined(CONFIG_PM_SLEEP) && defined(IS_NOT_BROKEN)
+/*
+ * This code is fine, but requires implementation in the dw_hdmi_trigger()
+ * method which is currently missing as I have no way to test this.
+ */
+static int snd_dw_hdmi_suspend(struct device *dev)
+{
+	struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
+
+	snd_power_change_state(dw->card, SNDRV_CTL_POWER_D3cold);
+	snd_pcm_suspend_all(dw->pcm);
+
+	return 0;
+}
+
+static int snd_dw_hdmi_resume(struct device *dev)
+{
+	struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
+
+	snd_power_change_state(dw->card, SNDRV_CTL_POWER_D0);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(snd_dw_hdmi_pm, snd_dw_hdmi_suspend,
+			 snd_dw_hdmi_resume);
+#define PM_OPS &snd_dw_hdmi_pm
+#else
+#define PM_OPS NULL
+#endif
+
+static struct platform_driver snd_dw_hdmi_driver = {
+	.probe	= snd_dw_hdmi_probe,
+	.remove	= snd_dw_hdmi_remove,
+	.driver	= {
+		.name = DRIVER_NAME,
+		.owner = THIS_MODULE,
+		.pm = PM_OPS,
+	},
+};
+
+module_platform_driver(snd_dw_hdmi_driver);
+
+MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>");
+MODULE_DESCRIPTION("Synopsis Designware HDMI AHB ALSA interface");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/gpu/drm/bridge/dw_hdmi-audio.h b/drivers/gpu/drm/bridge/dw_hdmi-audio.h
new file mode 100644
index 000000000000..1e840118d90a
--- /dev/null
+++ b/drivers/gpu/drm/bridge/dw_hdmi-audio.h
@@ -0,0 +1,13 @@
+#ifndef DW_HDMI_AUDIO_H
+#define DW_HDMI_AUDIO_H
+
+struct dw_hdmi;
+
+struct dw_hdmi_audio_data {
+	phys_addr_t phys;
+	void __iomem *base;
+	int irq;
+	struct dw_hdmi *hdmi;
+};
+
+#endif
diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index fba25607ef88..b65464789fbd 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -28,6 +28,7 @@
 #include <drm/bridge/dw_hdmi.h>
 
 #include "dw_hdmi.h"
+#include "dw_hdmi-audio.h"
 
 #define HDMI_EDID_LEN		512
 
@@ -104,6 +105,7 @@ struct dw_hdmi {
 	struct drm_encoder *encoder;
 	struct drm_bridge *bridge;
 
+	struct platform_device *audio;
 	enum dw_hdmi_devtype dev_type;
 	struct device *dev;
 	struct clk *isfr_clk;
@@ -1732,7 +1734,9 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 {
 	struct drm_device *drm = data;
 	struct device_node *np = dev->of_node;
+	struct platform_device_info pdevinfo;
 	struct device_node *ddc_node;
+	struct dw_hdmi_audio_data audio;
 	struct dw_hdmi *hdmi;
 	int ret;
 	u32 val = 1;
@@ -1860,6 +1864,23 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 	hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE),
 		    HDMI_IH_MUTE_PHY_STAT0);
 
+	memset(&pdevinfo, 0, sizeof(pdevinfo));
+	pdevinfo.parent = dev;
+	pdevinfo.id = PLATFORM_DEVID_AUTO;
+
+	if (hdmi_readb(hdmi, HDMI_CONFIG1_ID) & HDMI_CONFIG1_AHB) {
+		audio.phys = iores->start;
+		audio.base = hdmi->regs;
+		audio.irq = irq;
+		audio.hdmi = hdmi;
+
+		pdevinfo.name = "dw-hdmi-ahb-audio";
+		pdevinfo.data = &audio;
+		pdevinfo.size_data = sizeof(audio);
+		pdevinfo.dma_mask = DMA_BIT_MASK(32);
+		hdmi->audio = platform_device_register_full(&pdevinfo);
+	}
+
 	dev_set_drvdata(dev, hdmi);
 
 	return 0;
@@ -1877,6 +1898,9 @@ void dw_hdmi_unbind(struct device *dev, struct device *master, void *data)
 {
 	struct dw_hdmi *hdmi = dev_get_drvdata(dev);
 
+	if (hdmi->audio && !IS_ERR(hdmi->audio))
+		platform_device_unregister(hdmi->audio);
+
 	/* Disable all interrupts */
 	hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
 
diff --git a/drivers/gpu/drm/bridge/dw_hdmi.h b/drivers/gpu/drm/bridge/dw_hdmi.h
index 175dbc89a824..78e54e813212 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.h
+++ b/drivers/gpu/drm/bridge/dw_hdmi.h
@@ -545,6 +545,9 @@
 #define HDMI_I2CM_FS_SCL_LCNT_0_ADDR            0x7E12
 
 enum {
+/* CONFIG1_ID field values */
+	HDMI_CONFIG1_AHB = 0x01,
+
 /* IH_FC_INT2 field values */
 	HDMI_IH_FC_INT2_OVERFLOW_MASK = 0x03,
 	HDMI_IH_FC_INT2_LOW_PRIORITY_OVERFLOW = 0x02,
-- 
2.1.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 226+ messages in thread

* [PATCH v2 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver
@ 2015-08-14 14:04         ` Russell King
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King @ 2015-08-14 14:04 UTC (permalink / raw)
  To: linux-arm-kernel

Add ALSA based HDMI AHB audio driver for dw_hdmi.  The only buffer
format supported by the hardware is its own special IEC958 based format,
which is not compatible with any ALSA format.  To avoid doing too much
data manipulation within the driver, we support only ALSAs IEC958 LE and
24-bit PCM formats for 2 to 6 channels, which we convert to its hardware
format.

A more desirable solution would be to have this conversion in userspace,
but ALSA does not appear to allow such transformations outside of
libasound itself.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
v2: updated with Takashi Iwai's comments... and with a fixed Cc: line.

 drivers/gpu/drm/bridge/Kconfig             |  10 +
 drivers/gpu/drm/bridge/Makefile            |   1 +
 drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c | 579 +++++++++++++++++++++++++++++
 drivers/gpu/drm/bridge/dw_hdmi-audio.h     |  13 +
 drivers/gpu/drm/bridge/dw_hdmi.c           |  24 ++
 drivers/gpu/drm/bridge/dw_hdmi.h           |   3 +
 6 files changed, 630 insertions(+)
 create mode 100644 drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
 create mode 100644 drivers/gpu/drm/bridge/dw_hdmi-audio.h

diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index acef3223772c..56ed35fe0734 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -3,6 +3,16 @@ config DRM_DW_HDMI
 	depends on DRM
 	select DRM_KMS_HELPER
 
+config DRM_DW_HDMI_AHB_AUDIO
+	tristate "Synopsis Designware AHB Audio interface"
+	depends on DRM_DW_HDMI && SND
+	select SND_PCM
+	select SND_PCM_IEC958
+	help
+	  Support the AHB Audio interface which is part of the Synopsis
+	  Designware HDMI block.  This is used in conjunction with
+	  the i.MX6 HDMI driver.
+
 config DRM_PTN3460
 	tristate "PTN3460 DP/LVDS bridge"
 	depends on DRM
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index 8dfebd984370..eb80dbbb8365 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -3,3 +3,4 @@ ccflags-y := -Iinclude/drm
 obj-$(CONFIG_DRM_PS8622) += ps8622.o
 obj-$(CONFIG_DRM_PTN3460) += ptn3460.o
 obj-$(CONFIG_DRM_DW_HDMI) += dw_hdmi.o
+obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw_hdmi-ahb-audio.o
diff --git a/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
new file mode 100644
index 000000000000..bf379310008a
--- /dev/null
+++ b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
@@ -0,0 +1,579 @@
+/*
+ * DesignWare HDMI audio driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Written and tested against the Designware HDMI Tx found in iMX6.
+ */
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <drm/bridge/dw_hdmi.h>
+
+#include <sound/asoundef.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_iec958.h>
+
+#include "dw_hdmi-audio.h"
+
+#define DRIVER_NAME "dw-hdmi-ahb-audio"
+
+/* Provide some bits rather than bit offsets */
+enum {
+	HDMI_AHB_DMA_CONF0_SW_FIFO_RST = BIT(7),
+	HDMI_AHB_DMA_CONF0_EN_HLOCK = BIT(3),
+	HDMI_AHB_DMA_START_START = BIT(0),
+	HDMI_AHB_DMA_STOP_STOP = BIT(0),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR = BIT(5),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST = BIT(4),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY = BIT(3),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE = BIT(2),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL = BIT(1),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL =
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY,
+	HDMI_IH_AHBDMAAUD_STAT0_ERROR = BIT(5),
+	HDMI_IH_AHBDMAAUD_STAT0_LOST = BIT(4),
+	HDMI_IH_AHBDMAAUD_STAT0_RETRY = BIT(3),
+	HDMI_IH_AHBDMAAUD_STAT0_DONE = BIT(2),
+	HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL = BIT(1),
+	HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0),
+	HDMI_IH_AHBDMAAUD_STAT0_ALL =
+		HDMI_IH_AHBDMAAUD_STAT0_ERROR |
+		HDMI_IH_AHBDMAAUD_STAT0_LOST |
+		HDMI_IH_AHBDMAAUD_STAT0_RETRY |
+		HDMI_IH_AHBDMAAUD_STAT0_DONE |
+		HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL |
+		HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY,
+	HDMI_AHB_DMA_CONF0_INCR16 = 2 << 1,
+	HDMI_AHB_DMA_CONF0_INCR8 = 1 << 1,
+	HDMI_AHB_DMA_CONF0_INCR4 = 0,
+	HDMI_AHB_DMA_CONF0_BURST_MODE = BIT(0),
+	HDMI_AHB_DMA_MASK_DONE = BIT(7),
+	HDMI_REVISION_ID = 0x0001,
+	HDMI_IH_AHBDMAAUD_STAT0 = 0x0109,
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0 = 0x0189,
+	HDMI_AHB_DMA_CONF0 = 0x3600,
+	HDMI_AHB_DMA_START = 0x3601,
+	HDMI_AHB_DMA_STOP = 0x3602,
+	HDMI_AHB_DMA_THRSLD = 0x3603,
+	HDMI_AHB_DMA_STRADDR0 = 0x3604,
+	HDMI_AHB_DMA_STPADDR0 = 0x3608,
+	HDMI_AHB_DMA_MASK = 0x3614,
+	HDMI_AHB_DMA_POL = 0x3615,
+	HDMI_AHB_DMA_CONF1 = 0x3616,
+	HDMI_AHB_DMA_BUFFPOL = 0x361a,
+};
+
+struct snd_dw_hdmi {
+	struct snd_card *card;
+	struct snd_pcm *pcm;
+	spinlock_t lock;
+	struct dw_hdmi_audio_data data;
+	struct snd_pcm_substream *substream;
+	void (*reformat)(struct snd_dw_hdmi *, size_t, size_t);
+	void *buf_src;
+	void *buf_dst;
+	dma_addr_t buf_addr;
+	unsigned buf_offset;
+	unsigned buf_period;
+	unsigned buf_size;
+	unsigned channels;
+	u8 revision;
+	u8 iec_offset;
+	u8 cs[192][8];
+};
+
+static void dw_hdmi_writel(u32 val, void __iomem *ptr)
+{
+	writeb_relaxed(val, ptr);
+	writeb_relaxed(val >> 8, ptr + 1);
+	writeb_relaxed(val >> 16, ptr + 2);
+	writeb_relaxed(val >> 24, ptr + 3);
+}
+
+/*
+ * Convert to hardware format: The userspace buffer contains IEC958 samples,
+ * with the PCUV bits in bits 31..28 and audio samples in bits 27..4.  We
+ * need these to be in bits 27..24, with the IEC B bit in bit 28, and audio
+ * samples in 23..0.
+ *
+ * Default preamble in bits 3..0: 8 = block start, 4 = even 2 = odd
+ *
+ * Ideally, we could do with having the data properly formatted in userspace.
+ */
+static void dw_hdmi_reformat_iec958(struct snd_dw_hdmi *dw,
+	size_t offset, size_t bytes)
+{
+	u32 *src = dw->buf_src + offset;
+	u32 *dst = dw->buf_dst + offset;
+	u32 *end = dw->buf_src + offset + bytes;
+
+	do {
+		u32 b, sample = *src++;
+
+		b = (sample & 8) << (28 - 3);
+
+		sample >>= 4;
+
+		*dst++ = sample | b;
+	} while (src < end);
+}
+
+static u32 parity(u32 sample)
+{
+	sample ^= sample >> 16;
+	sample ^= sample >> 8;
+	sample ^= sample >> 4;
+	sample ^= sample >> 2;
+	sample ^= sample >> 1;
+	return (sample & 1) << 27;
+}
+
+static void dw_hdmi_reformat_s24(struct snd_dw_hdmi *dw,
+	size_t offset, size_t bytes)
+{
+	u32 *src = dw->buf_src + offset;
+	u32 *dst = dw->buf_dst + offset;
+	u32 *end = dw->buf_src + offset + bytes;
+
+	do {
+		unsigned i;
+		u8 *cs;
+
+		cs = dw->cs[dw->iec_offset++];
+		if (dw->iec_offset >= 192)
+			dw->iec_offset = 0;
+
+		i = dw->channels;
+		do {
+			u32 sample = *src++;
+
+			sample &= ~0xff000000;
+			sample |= *cs++ << 24;
+			sample |= parity(sample & ~0xf8000000);
+
+			*dst++ = sample;
+		} while (--i);
+	} while (src < end);
+}
+
+static void dw_hdmi_create_cs(struct snd_dw_hdmi *dw,
+	struct snd_pcm_runtime *runtime)
+{
+	u8 cs[4];
+	unsigned ch, i, j;
+
+	snd_pcm_create_iec958_consumer(runtime, cs, sizeof(cs));
+
+	memset(dw->cs, 0, sizeof(dw->cs));
+
+	for (ch = 0; ch < 8; ch++) {
+		cs[2] &= ~IEC958_AES2_CON_CHANNEL;
+		cs[2] |= (ch + 1) << 4;
+
+		for (i = 0; i < ARRAY_SIZE(cs); i++) {
+			unsigned c = cs[i];
+
+			for (j = 0; j < 8; j++, c >>= 1)
+				dw->cs[i * 8 + j][ch] = (c & 1) << 2;
+		}
+	}
+	dw->cs[0][0] |= BIT(4);
+}
+
+static void dw_hdmi_start_dma(struct snd_dw_hdmi *dw)
+{
+	void __iomem *base = dw->data.base;
+	unsigned offset = dw->buf_offset;
+	unsigned period = dw->buf_period;
+	u32 start, stop;
+
+	dw->reformat(dw, offset, period);
+
+	/* Clear all irqs before enabling irqs and starting DMA */
+	writeb_relaxed(HDMI_IH_AHBDMAAUD_STAT0_ALL,
+		       base + HDMI_IH_AHBDMAAUD_STAT0);
+
+	start = dw->buf_addr + offset;
+	stop = start + period - 1;
+
+	/* Setup the hardware start/stop addresses */
+	dw_hdmi_writel(start, base + HDMI_AHB_DMA_STRADDR0);
+	dw_hdmi_writel(stop, base + HDMI_AHB_DMA_STPADDR0);
+
+	writeb_relaxed((u8)~HDMI_AHB_DMA_MASK_DONE, base + HDMI_AHB_DMA_MASK);
+	writeb(HDMI_AHB_DMA_START_START, base + HDMI_AHB_DMA_START);
+
+	offset += period;
+	if (offset >= dw->buf_size)
+		offset = 0;
+	dw->buf_offset = offset;
+}
+
+static void dw_hdmi_stop_dma(struct snd_dw_hdmi *dw)
+{
+	/* Disable interrupts before disabling DMA */
+	writeb_relaxed(~0, dw->data.base + HDMI_AHB_DMA_MASK);
+	writeb_relaxed(HDMI_AHB_DMA_STOP_STOP, dw->data.base + HDMI_AHB_DMA_STOP);
+}
+
+static irqreturn_t snd_dw_hdmi_irq(int irq, void *data)
+{
+	struct snd_dw_hdmi *dw = data;
+	struct snd_pcm_substream *substream;
+	unsigned stat;
+
+	stat = readb_relaxed(dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
+	if (!stat)
+		return IRQ_NONE;
+
+	writeb_relaxed(stat, dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
+
+	substream = dw->substream;
+	if (stat & HDMI_IH_AHBDMAAUD_STAT0_DONE && substream) {
+		snd_pcm_period_elapsed(substream);
+
+		spin_lock(&dw->lock);
+		if (dw->substream)
+			dw_hdmi_start_dma(dw);
+		spin_unlock(&dw->lock);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static struct snd_pcm_hardware dw_hdmi_hw = {
+	.info = SNDRV_PCM_INFO_INTERLEAVED |
+		SNDRV_PCM_INFO_BLOCK_TRANSFER |
+		SNDRV_PCM_INFO_MMAP |
+		SNDRV_PCM_INFO_MMAP_VALID,
+	.formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE |
+		   SNDRV_PCM_FMTBIT_S24_LE,
+	.rates = SNDRV_PCM_RATE_32000 |
+		 SNDRV_PCM_RATE_44100 |
+		 SNDRV_PCM_RATE_48000 |
+		 SNDRV_PCM_RATE_88200 |
+		 SNDRV_PCM_RATE_96000 |
+		 SNDRV_PCM_RATE_176400 |
+		 SNDRV_PCM_RATE_192000,
+	.channels_min = 2,
+	.channels_max = 8,
+	.buffer_bytes_max = 64 * 1024,
+	.period_bytes_min = 256,
+	.period_bytes_max = 8192,	/* ERR004323: must limit to 8k */
+	.periods_min = 2,
+	.periods_max = 16,
+	.fifo_size = 0,
+};
+
+static int dw_hdmi_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_dw_hdmi *dw = substream->private_data;
+	void __iomem *base = dw->data.base;
+	int ret;
+
+	runtime->hw = dw_hdmi_hw;
+
+	ret = snd_pcm_limit_hw_rates(runtime);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0)
+		return ret;
+
+	/* Clear FIFO */
+	writeb_relaxed(HDMI_AHB_DMA_CONF0_SW_FIFO_RST,
+		       base + HDMI_AHB_DMA_CONF0);
+
+	/* Configure interrupt polarities */
+	writeb_relaxed(~0, base + HDMI_AHB_DMA_POL);
+	writeb_relaxed(~0, base + HDMI_AHB_DMA_BUFFPOL);
+
+	/* Keep interrupts masked, and clear any pending */
+	writeb_relaxed(~0, base + HDMI_AHB_DMA_MASK);
+	writeb_relaxed(~0, base + HDMI_IH_AHBDMAAUD_STAT0);
+
+	ret = request_irq(dw->data.irq, snd_dw_hdmi_irq, IRQF_SHARED,
+			  "dw-hdmi-audio", dw);
+	if (ret)
+		return ret;
+
+	/* Un-mute done interrupt */
+	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL &
+		       ~HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE,
+		       base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
+
+	return 0;
+}
+
+static int dw_hdmi_close(struct snd_pcm_substream *substream)
+{
+	struct snd_dw_hdmi *dw = substream->private_data;
+
+	/* Mute all interrupts */
+	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
+		       dw->data.base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
+
+	free_irq(dw->data.irq, dw);
+
+	return 0;
+}
+
+static int dw_hdmi_hw_free(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int dw_hdmi_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	return snd_pcm_lib_alloc_vmalloc_buffer(substream,
+						params_buffer_bytes(params));
+}
+
+static int dw_hdmi_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_dw_hdmi *dw = substream->private_data;
+	u8 threshold, conf0, conf1;
+
+	/* Setup as per 3.0.5 FSL 4.1.0 BSP */
+	switch (dw->revision) {
+	case 0x0a:
+		conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
+			HDMI_AHB_DMA_CONF0_INCR4;
+		if (runtime->channels == 2)
+			threshold = 126;
+		else
+			threshold = 124;
+		break;
+	case 0x1a:
+		conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
+			HDMI_AHB_DMA_CONF0_INCR8;
+		threshold = 128;
+		break;
+	default:
+		/* NOTREACHED */
+		return -EINVAL;
+	}
+
+	dw_hdmi_set_sample_rate(dw->data.hdmi, runtime->rate);
+
+	/* Minimum number of bytes in the fifo. */
+	runtime->hw.fifo_size = threshold * 32;
+
+	conf0 |= HDMI_AHB_DMA_CONF0_EN_HLOCK;
+	conf1 = (1 << runtime->channels) - 1;
+
+	writeb_relaxed(threshold, dw->data.base + HDMI_AHB_DMA_THRSLD);
+	writeb_relaxed(conf0, dw->data.base + HDMI_AHB_DMA_CONF0);
+	writeb_relaxed(conf1, dw->data.base + HDMI_AHB_DMA_CONF1);
+
+	switch (runtime->format) {
+	case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
+		dw->reformat = dw_hdmi_reformat_iec958;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		dw_hdmi_create_cs(dw, runtime);
+		dw->reformat = dw_hdmi_reformat_s24;
+		break;
+	}
+	dw->iec_offset = 0;
+	dw->channels = runtime->channels;
+	dw->buf_src  = runtime->dma_area;
+	dw->buf_dst  = substream->dma_buffer.area;
+	dw->buf_addr = substream->dma_buffer.addr;
+	dw->buf_period = snd_pcm_lib_period_bytes(substream);
+	dw->buf_size = snd_pcm_lib_buffer_bytes(substream);
+
+	return 0;
+}
+
+static int dw_hdmi_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_dw_hdmi *dw = substream->private_data;
+	unsigned long flags;
+	int ret = 0;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		spin_lock_irqsave(&dw->lock, flags);
+		dw->buf_offset = 0;
+		dw->substream = substream;
+		dw_hdmi_start_dma(dw);
+		dw_hdmi_audio_enable(dw->data.hdmi);
+		spin_unlock_irqrestore(&dw->lock, flags);
+		substream->runtime->delay = substream->runtime->period_size;
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+		spin_lock_irqsave(&dw->lock, flags);
+		dw->substream = NULL;
+		dw_hdmi_stop_dma(dw);
+		dw_hdmi_audio_disable(dw->data.hdmi);
+		spin_unlock_irqrestore(&dw->lock, flags);
+		break;
+
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static snd_pcm_uframes_t dw_hdmi_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_dw_hdmi *dw = substream->private_data;
+
+	/*
+	 * We are unable to report the exact hardware position as
+	 * reading the 32-bit DMA position using 8-bit reads is racy.
+	 */
+	return bytes_to_frames(runtime, dw->buf_offset);
+}
+
+static struct snd_pcm_ops snd_dw_hdmi_ops = {
+	.open = dw_hdmi_open,
+	.close = dw_hdmi_close,
+	.ioctl = snd_pcm_lib_ioctl,
+	.hw_params = dw_hdmi_hw_params,
+	.hw_free = dw_hdmi_hw_free,
+	.prepare = dw_hdmi_prepare,
+	.trigger = dw_hdmi_trigger,
+	.pointer = dw_hdmi_pointer,
+	.page = snd_pcm_lib_get_vmalloc_page,
+};
+
+static int snd_dw_hdmi_probe(struct platform_device *pdev)
+{
+	const struct dw_hdmi_audio_data *data = pdev->dev.platform_data;
+	struct device *dev = pdev->dev.parent;
+	struct snd_dw_hdmi *dw;
+	struct snd_card *card;
+	struct snd_pcm *pcm;
+	unsigned revision;
+	int ret;
+
+	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
+		       data->base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
+	revision = readb_relaxed(data->base + HDMI_REVISION_ID);
+	if (revision != 0x0a && revision != 0x1a) {
+		dev_err(dev, "dw-hdmi-audio: unknown revision 0x%02x\n",
+			revision);
+		return -ENXIO;
+	}
+
+	ret = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
+			      THIS_MODULE, sizeof(struct snd_dw_hdmi), &card);
+	if (ret < 0)
+		return ret;
+
+	strlcpy(card->driver, DRIVER_NAME, sizeof(card->driver));
+	strlcpy(card->shortname, "DW-HDMI", sizeof(card->shortname));
+	snprintf(card->longname, sizeof(card->longname),
+		 "%s rev 0x%02x, irq %d", card->shortname, revision,
+		 data->irq);
+
+	dw = card->private_data;
+	dw->card = card;
+	dw->data = *data;
+	dw->revision = revision;
+
+	spin_lock_init(&dw->lock);
+
+	ret = snd_pcm_new(card, "DW HDMI", 0, 1, 0, &pcm);
+	if (ret < 0)
+		goto err;
+
+	dw->pcm = pcm;
+	pcm->private_data = dw;
+	strlcpy(pcm->name, DRIVER_NAME, sizeof(pcm->name));
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_dw_hdmi_ops);
+
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+			dev, 64 * 1024, 64 * 1024);
+
+	ret = snd_card_register(card);
+	if (ret < 0)
+		goto err;
+
+	platform_set_drvdata(pdev, dw);
+
+	return 0;
+
+err:
+	snd_card_free(card);
+	return ret;
+}
+
+static int snd_dw_hdmi_remove(struct platform_device *pdev)
+{
+	struct snd_dw_hdmi *dw = platform_get_drvdata(pdev);
+
+	snd_card_free(dw->card);
+
+	return 0;
+}
+
+#if defined(CONFIG_PM_SLEEP) && defined(IS_NOT_BROKEN)
+/*
+ * This code is fine, but requires implementation in the dw_hdmi_trigger()
+ * method which is currently missing as I have no way to test this.
+ */
+static int snd_dw_hdmi_suspend(struct device *dev)
+{
+	struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
+
+	snd_power_change_state(dw->card, SNDRV_CTL_POWER_D3cold);
+	snd_pcm_suspend_all(dw->pcm);
+
+	return 0;
+}
+
+static int snd_dw_hdmi_resume(struct device *dev)
+{
+	struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
+
+	snd_power_change_state(dw->card, SNDRV_CTL_POWER_D0);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(snd_dw_hdmi_pm, snd_dw_hdmi_suspend,
+			 snd_dw_hdmi_resume);
+#define PM_OPS &snd_dw_hdmi_pm
+#else
+#define PM_OPS NULL
+#endif
+
+static struct platform_driver snd_dw_hdmi_driver = {
+	.probe	= snd_dw_hdmi_probe,
+	.remove	= snd_dw_hdmi_remove,
+	.driver	= {
+		.name = DRIVER_NAME,
+		.owner = THIS_MODULE,
+		.pm = PM_OPS,
+	},
+};
+
+module_platform_driver(snd_dw_hdmi_driver);
+
+MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>");
+MODULE_DESCRIPTION("Synopsis Designware HDMI AHB ALSA interface");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/gpu/drm/bridge/dw_hdmi-audio.h b/drivers/gpu/drm/bridge/dw_hdmi-audio.h
new file mode 100644
index 000000000000..1e840118d90a
--- /dev/null
+++ b/drivers/gpu/drm/bridge/dw_hdmi-audio.h
@@ -0,0 +1,13 @@
+#ifndef DW_HDMI_AUDIO_H
+#define DW_HDMI_AUDIO_H
+
+struct dw_hdmi;
+
+struct dw_hdmi_audio_data {
+	phys_addr_t phys;
+	void __iomem *base;
+	int irq;
+	struct dw_hdmi *hdmi;
+};
+
+#endif
diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index fba25607ef88..b65464789fbd 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -28,6 +28,7 @@
 #include <drm/bridge/dw_hdmi.h>
 
 #include "dw_hdmi.h"
+#include "dw_hdmi-audio.h"
 
 #define HDMI_EDID_LEN		512
 
@@ -104,6 +105,7 @@ struct dw_hdmi {
 	struct drm_encoder *encoder;
 	struct drm_bridge *bridge;
 
+	struct platform_device *audio;
 	enum dw_hdmi_devtype dev_type;
 	struct device *dev;
 	struct clk *isfr_clk;
@@ -1732,7 +1734,9 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 {
 	struct drm_device *drm = data;
 	struct device_node *np = dev->of_node;
+	struct platform_device_info pdevinfo;
 	struct device_node *ddc_node;
+	struct dw_hdmi_audio_data audio;
 	struct dw_hdmi *hdmi;
 	int ret;
 	u32 val = 1;
@@ -1860,6 +1864,23 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 	hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE),
 		    HDMI_IH_MUTE_PHY_STAT0);
 
+	memset(&pdevinfo, 0, sizeof(pdevinfo));
+	pdevinfo.parent = dev;
+	pdevinfo.id = PLATFORM_DEVID_AUTO;
+
+	if (hdmi_readb(hdmi, HDMI_CONFIG1_ID) & HDMI_CONFIG1_AHB) {
+		audio.phys = iores->start;
+		audio.base = hdmi->regs;
+		audio.irq = irq;
+		audio.hdmi = hdmi;
+
+		pdevinfo.name = "dw-hdmi-ahb-audio";
+		pdevinfo.data = &audio;
+		pdevinfo.size_data = sizeof(audio);
+		pdevinfo.dma_mask = DMA_BIT_MASK(32);
+		hdmi->audio = platform_device_register_full(&pdevinfo);
+	}
+
 	dev_set_drvdata(dev, hdmi);
 
 	return 0;
@@ -1877,6 +1898,9 @@ void dw_hdmi_unbind(struct device *dev, struct device *master, void *data)
 {
 	struct dw_hdmi *hdmi = dev_get_drvdata(dev);
 
+	if (hdmi->audio && !IS_ERR(hdmi->audio))
+		platform_device_unregister(hdmi->audio);
+
 	/* Disable all interrupts */
 	hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
 
diff --git a/drivers/gpu/drm/bridge/dw_hdmi.h b/drivers/gpu/drm/bridge/dw_hdmi.h
index 175dbc89a824..78e54e813212 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.h
+++ b/drivers/gpu/drm/bridge/dw_hdmi.h
@@ -545,6 +545,9 @@
 #define HDMI_I2CM_FS_SCL_LCNT_0_ADDR            0x7E12
 
 enum {
+/* CONFIG1_ID field values */
+	HDMI_CONFIG1_AHB = 0x01,
+
 /* IH_FC_INT2 field values */
 	HDMI_IH_FC_INT2_OVERFLOW_MASK = 0x03,
 	HDMI_IH_FC_INT2_LOW_PRIORITY_OVERFLOW = 0x02,
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 226+ messages in thread

* Re: [alsa-devel] [PATCH v2 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver
  2015-08-14 14:04         ` Russell King
  (?)
@ 2015-08-14 14:34           ` Takashi Iwai
  -1 siblings, 0 replies; 226+ messages in thread
From: Takashi Iwai @ 2015-08-14 14:34 UTC (permalink / raw)
  To: Russell King
  Cc: Fabio Estevam, alsa-devel, David Airlie, Sascha Hauer,
	linux-kernel, dri-devel, linux-rockchip, Mark Brown,
	Philipp Zabel, Yakir Yang, Andy Yan, Jon Nettleton,
	linux-arm-kernel

On Fri, 14 Aug 2015 16:04:25 +0200,
Russell King wrote:
> 
> Add ALSA based HDMI AHB audio driver for dw_hdmi.  The only buffer
> format supported by the hardware is its own special IEC958 based format,
> which is not compatible with any ALSA format.  To avoid doing too much
> data manipulation within the driver, we support only ALSAs IEC958 LE and
> 24-bit PCM formats for 2 to 6 channels, which we convert to its hardware
> format.
> 
> A more desirable solution would be to have this conversion in userspace,
> but ALSA does not appear to allow such transformations outside of
> libasound itself.
> 
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> ---
> v2: updated with Takashi Iwai's comments... and with a fixed Cc: line.

Reviewed-by: Takashi Iwai <tiwai@suse.de>


thanks,

Takashi


>  drivers/gpu/drm/bridge/Kconfig             |  10 +
>  drivers/gpu/drm/bridge/Makefile            |   1 +
>  drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c | 579 +++++++++++++++++++++++++++++
>  drivers/gpu/drm/bridge/dw_hdmi-audio.h     |  13 +
>  drivers/gpu/drm/bridge/dw_hdmi.c           |  24 ++
>  drivers/gpu/drm/bridge/dw_hdmi.h           |   3 +
>  6 files changed, 630 insertions(+)
>  create mode 100644 drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
>  create mode 100644 drivers/gpu/drm/bridge/dw_hdmi-audio.h
> 
> diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
> index acef3223772c..56ed35fe0734 100644
> --- a/drivers/gpu/drm/bridge/Kconfig
> +++ b/drivers/gpu/drm/bridge/Kconfig
> @@ -3,6 +3,16 @@ config DRM_DW_HDMI
>  	depends on DRM
>  	select DRM_KMS_HELPER
>  
> +config DRM_DW_HDMI_AHB_AUDIO
> +	tristate "Synopsis Designware AHB Audio interface"
> +	depends on DRM_DW_HDMI && SND
> +	select SND_PCM
> +	select SND_PCM_IEC958
> +	help
> +	  Support the AHB Audio interface which is part of the Synopsis
> +	  Designware HDMI block.  This is used in conjunction with
> +	  the i.MX6 HDMI driver.
> +
>  config DRM_PTN3460
>  	tristate "PTN3460 DP/LVDS bridge"
>  	depends on DRM
> diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
> index 8dfebd984370..eb80dbbb8365 100644
> --- a/drivers/gpu/drm/bridge/Makefile
> +++ b/drivers/gpu/drm/bridge/Makefile
> @@ -3,3 +3,4 @@ ccflags-y := -Iinclude/drm
>  obj-$(CONFIG_DRM_PS8622) += ps8622.o
>  obj-$(CONFIG_DRM_PTN3460) += ptn3460.o
>  obj-$(CONFIG_DRM_DW_HDMI) += dw_hdmi.o
> +obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw_hdmi-ahb-audio.o
> diff --git a/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
> new file mode 100644
> index 000000000000..bf379310008a
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
> @@ -0,0 +1,579 @@
> +/*
> + * DesignWare HDMI audio driver
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * Written and tested against the Designware HDMI Tx found in iMX6.
> + */
> +#include <linux/io.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <drm/bridge/dw_hdmi.h>
> +
> +#include <sound/asoundef.h>
> +#include <sound/core.h>
> +#include <sound/initval.h>
> +#include <sound/pcm.h>
> +#include <sound/pcm_iec958.h>
> +
> +#include "dw_hdmi-audio.h"
> +
> +#define DRIVER_NAME "dw-hdmi-ahb-audio"
> +
> +/* Provide some bits rather than bit offsets */
> +enum {
> +	HDMI_AHB_DMA_CONF0_SW_FIFO_RST = BIT(7),
> +	HDMI_AHB_DMA_CONF0_EN_HLOCK = BIT(3),
> +	HDMI_AHB_DMA_START_START = BIT(0),
> +	HDMI_AHB_DMA_STOP_STOP = BIT(0),
> +	HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR = BIT(5),
> +	HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST = BIT(4),
> +	HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY = BIT(3),
> +	HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE = BIT(2),
> +	HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL = BIT(1),
> +	HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0),
> +	HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL =
> +		HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR |
> +		HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST |
> +		HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY |
> +		HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE |
> +		HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL |
> +		HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY,
> +	HDMI_IH_AHBDMAAUD_STAT0_ERROR = BIT(5),
> +	HDMI_IH_AHBDMAAUD_STAT0_LOST = BIT(4),
> +	HDMI_IH_AHBDMAAUD_STAT0_RETRY = BIT(3),
> +	HDMI_IH_AHBDMAAUD_STAT0_DONE = BIT(2),
> +	HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL = BIT(1),
> +	HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0),
> +	HDMI_IH_AHBDMAAUD_STAT0_ALL =
> +		HDMI_IH_AHBDMAAUD_STAT0_ERROR |
> +		HDMI_IH_AHBDMAAUD_STAT0_LOST |
> +		HDMI_IH_AHBDMAAUD_STAT0_RETRY |
> +		HDMI_IH_AHBDMAAUD_STAT0_DONE |
> +		HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL |
> +		HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY,
> +	HDMI_AHB_DMA_CONF0_INCR16 = 2 << 1,
> +	HDMI_AHB_DMA_CONF0_INCR8 = 1 << 1,
> +	HDMI_AHB_DMA_CONF0_INCR4 = 0,
> +	HDMI_AHB_DMA_CONF0_BURST_MODE = BIT(0),
> +	HDMI_AHB_DMA_MASK_DONE = BIT(7),
> +	HDMI_REVISION_ID = 0x0001,
> +	HDMI_IH_AHBDMAAUD_STAT0 = 0x0109,
> +	HDMI_IH_MUTE_AHBDMAAUD_STAT0 = 0x0189,
> +	HDMI_AHB_DMA_CONF0 = 0x3600,
> +	HDMI_AHB_DMA_START = 0x3601,
> +	HDMI_AHB_DMA_STOP = 0x3602,
> +	HDMI_AHB_DMA_THRSLD = 0x3603,
> +	HDMI_AHB_DMA_STRADDR0 = 0x3604,
> +	HDMI_AHB_DMA_STPADDR0 = 0x3608,
> +	HDMI_AHB_DMA_MASK = 0x3614,
> +	HDMI_AHB_DMA_POL = 0x3615,
> +	HDMI_AHB_DMA_CONF1 = 0x3616,
> +	HDMI_AHB_DMA_BUFFPOL = 0x361a,
> +};
> +
> +struct snd_dw_hdmi {
> +	struct snd_card *card;
> +	struct snd_pcm *pcm;
> +	spinlock_t lock;
> +	struct dw_hdmi_audio_data data;
> +	struct snd_pcm_substream *substream;
> +	void (*reformat)(struct snd_dw_hdmi *, size_t, size_t);
> +	void *buf_src;
> +	void *buf_dst;
> +	dma_addr_t buf_addr;
> +	unsigned buf_offset;
> +	unsigned buf_period;
> +	unsigned buf_size;
> +	unsigned channels;
> +	u8 revision;
> +	u8 iec_offset;
> +	u8 cs[192][8];
> +};
> +
> +static void dw_hdmi_writel(u32 val, void __iomem *ptr)
> +{
> +	writeb_relaxed(val, ptr);
> +	writeb_relaxed(val >> 8, ptr + 1);
> +	writeb_relaxed(val >> 16, ptr + 2);
> +	writeb_relaxed(val >> 24, ptr + 3);
> +}
> +
> +/*
> + * Convert to hardware format: The userspace buffer contains IEC958 samples,
> + * with the PCUV bits in bits 31..28 and audio samples in bits 27..4.  We
> + * need these to be in bits 27..24, with the IEC B bit in bit 28, and audio
> + * samples in 23..0.
> + *
> + * Default preamble in bits 3..0: 8 = block start, 4 = even 2 = odd
> + *
> + * Ideally, we could do with having the data properly formatted in userspace.
> + */
> +static void dw_hdmi_reformat_iec958(struct snd_dw_hdmi *dw,
> +	size_t offset, size_t bytes)
> +{
> +	u32 *src = dw->buf_src + offset;
> +	u32 *dst = dw->buf_dst + offset;
> +	u32 *end = dw->buf_src + offset + bytes;
> +
> +	do {
> +		u32 b, sample = *src++;
> +
> +		b = (sample & 8) << (28 - 3);
> +
> +		sample >>= 4;
> +
> +		*dst++ = sample | b;
> +	} while (src < end);
> +}
> +
> +static u32 parity(u32 sample)
> +{
> +	sample ^= sample >> 16;
> +	sample ^= sample >> 8;
> +	sample ^= sample >> 4;
> +	sample ^= sample >> 2;
> +	sample ^= sample >> 1;
> +	return (sample & 1) << 27;
> +}
> +
> +static void dw_hdmi_reformat_s24(struct snd_dw_hdmi *dw,
> +	size_t offset, size_t bytes)
> +{
> +	u32 *src = dw->buf_src + offset;
> +	u32 *dst = dw->buf_dst + offset;
> +	u32 *end = dw->buf_src + offset + bytes;
> +
> +	do {
> +		unsigned i;
> +		u8 *cs;
> +
> +		cs = dw->cs[dw->iec_offset++];
> +		if (dw->iec_offset >= 192)
> +			dw->iec_offset = 0;
> +
> +		i = dw->channels;
> +		do {
> +			u32 sample = *src++;
> +
> +			sample &= ~0xff000000;
> +			sample |= *cs++ << 24;
> +			sample |= parity(sample & ~0xf8000000);
> +
> +			*dst++ = sample;
> +		} while (--i);
> +	} while (src < end);
> +}
> +
> +static void dw_hdmi_create_cs(struct snd_dw_hdmi *dw,
> +	struct snd_pcm_runtime *runtime)
> +{
> +	u8 cs[4];
> +	unsigned ch, i, j;
> +
> +	snd_pcm_create_iec958_consumer(runtime, cs, sizeof(cs));
> +
> +	memset(dw->cs, 0, sizeof(dw->cs));
> +
> +	for (ch = 0; ch < 8; ch++) {
> +		cs[2] &= ~IEC958_AES2_CON_CHANNEL;
> +		cs[2] |= (ch + 1) << 4;
> +
> +		for (i = 0; i < ARRAY_SIZE(cs); i++) {
> +			unsigned c = cs[i];
> +
> +			for (j = 0; j < 8; j++, c >>= 1)
> +				dw->cs[i * 8 + j][ch] = (c & 1) << 2;
> +		}
> +	}
> +	dw->cs[0][0] |= BIT(4);
> +}
> +
> +static void dw_hdmi_start_dma(struct snd_dw_hdmi *dw)
> +{
> +	void __iomem *base = dw->data.base;
> +	unsigned offset = dw->buf_offset;
> +	unsigned period = dw->buf_period;
> +	u32 start, stop;
> +
> +	dw->reformat(dw, offset, period);
> +
> +	/* Clear all irqs before enabling irqs and starting DMA */
> +	writeb_relaxed(HDMI_IH_AHBDMAAUD_STAT0_ALL,
> +		       base + HDMI_IH_AHBDMAAUD_STAT0);
> +
> +	start = dw->buf_addr + offset;
> +	stop = start + period - 1;
> +
> +	/* Setup the hardware start/stop addresses */
> +	dw_hdmi_writel(start, base + HDMI_AHB_DMA_STRADDR0);
> +	dw_hdmi_writel(stop, base + HDMI_AHB_DMA_STPADDR0);
> +
> +	writeb_relaxed((u8)~HDMI_AHB_DMA_MASK_DONE, base + HDMI_AHB_DMA_MASK);
> +	writeb(HDMI_AHB_DMA_START_START, base + HDMI_AHB_DMA_START);
> +
> +	offset += period;
> +	if (offset >= dw->buf_size)
> +		offset = 0;
> +	dw->buf_offset = offset;
> +}
> +
> +static void dw_hdmi_stop_dma(struct snd_dw_hdmi *dw)
> +{
> +	/* Disable interrupts before disabling DMA */
> +	writeb_relaxed(~0, dw->data.base + HDMI_AHB_DMA_MASK);
> +	writeb_relaxed(HDMI_AHB_DMA_STOP_STOP, dw->data.base + HDMI_AHB_DMA_STOP);
> +}
> +
> +static irqreturn_t snd_dw_hdmi_irq(int irq, void *data)
> +{
> +	struct snd_dw_hdmi *dw = data;
> +	struct snd_pcm_substream *substream;
> +	unsigned stat;
> +
> +	stat = readb_relaxed(dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
> +	if (!stat)
> +		return IRQ_NONE;
> +
> +	writeb_relaxed(stat, dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
> +
> +	substream = dw->substream;
> +	if (stat & HDMI_IH_AHBDMAAUD_STAT0_DONE && substream) {
> +		snd_pcm_period_elapsed(substream);
> +
> +		spin_lock(&dw->lock);
> +		if (dw->substream)
> +			dw_hdmi_start_dma(dw);
> +		spin_unlock(&dw->lock);
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static struct snd_pcm_hardware dw_hdmi_hw = {
> +	.info = SNDRV_PCM_INFO_INTERLEAVED |
> +		SNDRV_PCM_INFO_BLOCK_TRANSFER |
> +		SNDRV_PCM_INFO_MMAP |
> +		SNDRV_PCM_INFO_MMAP_VALID,
> +	.formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE |
> +		   SNDRV_PCM_FMTBIT_S24_LE,
> +	.rates = SNDRV_PCM_RATE_32000 |
> +		 SNDRV_PCM_RATE_44100 |
> +		 SNDRV_PCM_RATE_48000 |
> +		 SNDRV_PCM_RATE_88200 |
> +		 SNDRV_PCM_RATE_96000 |
> +		 SNDRV_PCM_RATE_176400 |
> +		 SNDRV_PCM_RATE_192000,
> +	.channels_min = 2,
> +	.channels_max = 8,
> +	.buffer_bytes_max = 64 * 1024,
> +	.period_bytes_min = 256,
> +	.period_bytes_max = 8192,	/* ERR004323: must limit to 8k */
> +	.periods_min = 2,
> +	.periods_max = 16,
> +	.fifo_size = 0,
> +};
> +
> +static int dw_hdmi_open(struct snd_pcm_substream *substream)
> +{
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +	struct snd_dw_hdmi *dw = substream->private_data;
> +	void __iomem *base = dw->data.base;
> +	int ret;
> +
> +	runtime->hw = dw_hdmi_hw;
> +
> +	ret = snd_pcm_limit_hw_rates(runtime);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Clear FIFO */
> +	writeb_relaxed(HDMI_AHB_DMA_CONF0_SW_FIFO_RST,
> +		       base + HDMI_AHB_DMA_CONF0);
> +
> +	/* Configure interrupt polarities */
> +	writeb_relaxed(~0, base + HDMI_AHB_DMA_POL);
> +	writeb_relaxed(~0, base + HDMI_AHB_DMA_BUFFPOL);
> +
> +	/* Keep interrupts masked, and clear any pending */
> +	writeb_relaxed(~0, base + HDMI_AHB_DMA_MASK);
> +	writeb_relaxed(~0, base + HDMI_IH_AHBDMAAUD_STAT0);
> +
> +	ret = request_irq(dw->data.irq, snd_dw_hdmi_irq, IRQF_SHARED,
> +			  "dw-hdmi-audio", dw);
> +	if (ret)
> +		return ret;
> +
> +	/* Un-mute done interrupt */
> +	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL &
> +		       ~HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE,
> +		       base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
> +
> +	return 0;
> +}
> +
> +static int dw_hdmi_close(struct snd_pcm_substream *substream)
> +{
> +	struct snd_dw_hdmi *dw = substream->private_data;
> +
> +	/* Mute all interrupts */
> +	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
> +		       dw->data.base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
> +
> +	free_irq(dw->data.irq, dw);
> +
> +	return 0;
> +}
> +
> +static int dw_hdmi_hw_free(struct snd_pcm_substream *substream)
> +{
> +	return snd_pcm_lib_free_vmalloc_buffer(substream);
> +}
> +
> +static int dw_hdmi_hw_params(struct snd_pcm_substream *substream,
> +	struct snd_pcm_hw_params *params)
> +{
> +	return snd_pcm_lib_alloc_vmalloc_buffer(substream,
> +						params_buffer_bytes(params));
> +}
> +
> +static int dw_hdmi_prepare(struct snd_pcm_substream *substream)
> +{
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +	struct snd_dw_hdmi *dw = substream->private_data;
> +	u8 threshold, conf0, conf1;
> +
> +	/* Setup as per 3.0.5 FSL 4.1.0 BSP */
> +	switch (dw->revision) {
> +	case 0x0a:
> +		conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
> +			HDMI_AHB_DMA_CONF0_INCR4;
> +		if (runtime->channels == 2)
> +			threshold = 126;
> +		else
> +			threshold = 124;
> +		break;
> +	case 0x1a:
> +		conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
> +			HDMI_AHB_DMA_CONF0_INCR8;
> +		threshold = 128;
> +		break;
> +	default:
> +		/* NOTREACHED */
> +		return -EINVAL;
> +	}
> +
> +	dw_hdmi_set_sample_rate(dw->data.hdmi, runtime->rate);
> +
> +	/* Minimum number of bytes in the fifo. */
> +	runtime->hw.fifo_size = threshold * 32;
> +
> +	conf0 |= HDMI_AHB_DMA_CONF0_EN_HLOCK;
> +	conf1 = (1 << runtime->channels) - 1;
> +
> +	writeb_relaxed(threshold, dw->data.base + HDMI_AHB_DMA_THRSLD);
> +	writeb_relaxed(conf0, dw->data.base + HDMI_AHB_DMA_CONF0);
> +	writeb_relaxed(conf1, dw->data.base + HDMI_AHB_DMA_CONF1);
> +
> +	switch (runtime->format) {
> +	case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
> +		dw->reformat = dw_hdmi_reformat_iec958;
> +		break;
> +	case SNDRV_PCM_FORMAT_S24_LE:
> +		dw_hdmi_create_cs(dw, runtime);
> +		dw->reformat = dw_hdmi_reformat_s24;
> +		break;
> +	}
> +	dw->iec_offset = 0;
> +	dw->channels = runtime->channels;
> +	dw->buf_src  = runtime->dma_area;
> +	dw->buf_dst  = substream->dma_buffer.area;
> +	dw->buf_addr = substream->dma_buffer.addr;
> +	dw->buf_period = snd_pcm_lib_period_bytes(substream);
> +	dw->buf_size = snd_pcm_lib_buffer_bytes(substream);
> +
> +	return 0;
> +}
> +
> +static int dw_hdmi_trigger(struct snd_pcm_substream *substream, int cmd)
> +{
> +	struct snd_dw_hdmi *dw = substream->private_data;
> +	unsigned long flags;
> +	int ret = 0;
> +
> +	switch (cmd) {
> +	case SNDRV_PCM_TRIGGER_START:
> +		spin_lock_irqsave(&dw->lock, flags);
> +		dw->buf_offset = 0;
> +		dw->substream = substream;
> +		dw_hdmi_start_dma(dw);
> +		dw_hdmi_audio_enable(dw->data.hdmi);
> +		spin_unlock_irqrestore(&dw->lock, flags);
> +		substream->runtime->delay = substream->runtime->period_size;
> +		break;
> +
> +	case SNDRV_PCM_TRIGGER_STOP:
> +		spin_lock_irqsave(&dw->lock, flags);
> +		dw->substream = NULL;
> +		dw_hdmi_stop_dma(dw);
> +		dw_hdmi_audio_disable(dw->data.hdmi);
> +		spin_unlock_irqrestore(&dw->lock, flags);
> +		break;
> +
> +	default:
> +		ret = -EINVAL;
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +static snd_pcm_uframes_t dw_hdmi_pointer(struct snd_pcm_substream *substream)
> +{
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +	struct snd_dw_hdmi *dw = substream->private_data;
> +
> +	/*
> +	 * We are unable to report the exact hardware position as
> +	 * reading the 32-bit DMA position using 8-bit reads is racy.
> +	 */
> +	return bytes_to_frames(runtime, dw->buf_offset);
> +}
> +
> +static struct snd_pcm_ops snd_dw_hdmi_ops = {
> +	.open = dw_hdmi_open,
> +	.close = dw_hdmi_close,
> +	.ioctl = snd_pcm_lib_ioctl,
> +	.hw_params = dw_hdmi_hw_params,
> +	.hw_free = dw_hdmi_hw_free,
> +	.prepare = dw_hdmi_prepare,
> +	.trigger = dw_hdmi_trigger,
> +	.pointer = dw_hdmi_pointer,
> +	.page = snd_pcm_lib_get_vmalloc_page,
> +};
> +
> +static int snd_dw_hdmi_probe(struct platform_device *pdev)
> +{
> +	const struct dw_hdmi_audio_data *data = pdev->dev.platform_data;
> +	struct device *dev = pdev->dev.parent;
> +	struct snd_dw_hdmi *dw;
> +	struct snd_card *card;
> +	struct snd_pcm *pcm;
> +	unsigned revision;
> +	int ret;
> +
> +	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
> +		       data->base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
> +	revision = readb_relaxed(data->base + HDMI_REVISION_ID);
> +	if (revision != 0x0a && revision != 0x1a) {
> +		dev_err(dev, "dw-hdmi-audio: unknown revision 0x%02x\n",
> +			revision);
> +		return -ENXIO;
> +	}
> +
> +	ret = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
> +			      THIS_MODULE, sizeof(struct snd_dw_hdmi), &card);
> +	if (ret < 0)
> +		return ret;
> +
> +	strlcpy(card->driver, DRIVER_NAME, sizeof(card->driver));
> +	strlcpy(card->shortname, "DW-HDMI", sizeof(card->shortname));
> +	snprintf(card->longname, sizeof(card->longname),
> +		 "%s rev 0x%02x, irq %d", card->shortname, revision,
> +		 data->irq);
> +
> +	dw = card->private_data;
> +	dw->card = card;
> +	dw->data = *data;
> +	dw->revision = revision;
> +
> +	spin_lock_init(&dw->lock);
> +
> +	ret = snd_pcm_new(card, "DW HDMI", 0, 1, 0, &pcm);
> +	if (ret < 0)
> +		goto err;
> +
> +	dw->pcm = pcm;
> +	pcm->private_data = dw;
> +	strlcpy(pcm->name, DRIVER_NAME, sizeof(pcm->name));
> +	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_dw_hdmi_ops);
> +
> +	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
> +			dev, 64 * 1024, 64 * 1024);
> +
> +	ret = snd_card_register(card);
> +	if (ret < 0)
> +		goto err;
> +
> +	platform_set_drvdata(pdev, dw);
> +
> +	return 0;
> +
> +err:
> +	snd_card_free(card);
> +	return ret;
> +}
> +
> +static int snd_dw_hdmi_remove(struct platform_device *pdev)
> +{
> +	struct snd_dw_hdmi *dw = platform_get_drvdata(pdev);
> +
> +	snd_card_free(dw->card);
> +
> +	return 0;
> +}
> +
> +#if defined(CONFIG_PM_SLEEP) && defined(IS_NOT_BROKEN)
> +/*
> + * This code is fine, but requires implementation in the dw_hdmi_trigger()
> + * method which is currently missing as I have no way to test this.
> + */
> +static int snd_dw_hdmi_suspend(struct device *dev)
> +{
> +	struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
> +
> +	snd_power_change_state(dw->card, SNDRV_CTL_POWER_D3cold);
> +	snd_pcm_suspend_all(dw->pcm);
> +
> +	return 0;
> +}
> +
> +static int snd_dw_hdmi_resume(struct device *dev)
> +{
> +	struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
> +
> +	snd_power_change_state(dw->card, SNDRV_CTL_POWER_D0);
> +
> +	return 0;
> +}
> +
> +static SIMPLE_DEV_PM_OPS(snd_dw_hdmi_pm, snd_dw_hdmi_suspend,
> +			 snd_dw_hdmi_resume);
> +#define PM_OPS &snd_dw_hdmi_pm
> +#else
> +#define PM_OPS NULL
> +#endif
> +
> +static struct platform_driver snd_dw_hdmi_driver = {
> +	.probe	= snd_dw_hdmi_probe,
> +	.remove	= snd_dw_hdmi_remove,
> +	.driver	= {
> +		.name = DRIVER_NAME,
> +		.owner = THIS_MODULE,
> +		.pm = PM_OPS,
> +	},
> +};
> +
> +module_platform_driver(snd_dw_hdmi_driver);
> +
> +MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>");
> +MODULE_DESCRIPTION("Synopsis Designware HDMI AHB ALSA interface");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:" DRIVER_NAME);
> diff --git a/drivers/gpu/drm/bridge/dw_hdmi-audio.h b/drivers/gpu/drm/bridge/dw_hdmi-audio.h
> new file mode 100644
> index 000000000000..1e840118d90a
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/dw_hdmi-audio.h
> @@ -0,0 +1,13 @@
> +#ifndef DW_HDMI_AUDIO_H
> +#define DW_HDMI_AUDIO_H
> +
> +struct dw_hdmi;
> +
> +struct dw_hdmi_audio_data {
> +	phys_addr_t phys;
> +	void __iomem *base;
> +	int irq;
> +	struct dw_hdmi *hdmi;
> +};
> +
> +#endif
> diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
> index fba25607ef88..b65464789fbd 100644
> --- a/drivers/gpu/drm/bridge/dw_hdmi.c
> +++ b/drivers/gpu/drm/bridge/dw_hdmi.c
> @@ -28,6 +28,7 @@
>  #include <drm/bridge/dw_hdmi.h>
>  
>  #include "dw_hdmi.h"
> +#include "dw_hdmi-audio.h"
>  
>  #define HDMI_EDID_LEN		512
>  
> @@ -104,6 +105,7 @@ struct dw_hdmi {
>  	struct drm_encoder *encoder;
>  	struct drm_bridge *bridge;
>  
> +	struct platform_device *audio;
>  	enum dw_hdmi_devtype dev_type;
>  	struct device *dev;
>  	struct clk *isfr_clk;
> @@ -1732,7 +1734,9 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
>  {
>  	struct drm_device *drm = data;
>  	struct device_node *np = dev->of_node;
> +	struct platform_device_info pdevinfo;
>  	struct device_node *ddc_node;
> +	struct dw_hdmi_audio_data audio;
>  	struct dw_hdmi *hdmi;
>  	int ret;
>  	u32 val = 1;
> @@ -1860,6 +1864,23 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
>  	hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE),
>  		    HDMI_IH_MUTE_PHY_STAT0);
>  
> +	memset(&pdevinfo, 0, sizeof(pdevinfo));
> +	pdevinfo.parent = dev;
> +	pdevinfo.id = PLATFORM_DEVID_AUTO;
> +
> +	if (hdmi_readb(hdmi, HDMI_CONFIG1_ID) & HDMI_CONFIG1_AHB) {
> +		audio.phys = iores->start;
> +		audio.base = hdmi->regs;
> +		audio.irq = irq;
> +		audio.hdmi = hdmi;
> +
> +		pdevinfo.name = "dw-hdmi-ahb-audio";
> +		pdevinfo.data = &audio;
> +		pdevinfo.size_data = sizeof(audio);
> +		pdevinfo.dma_mask = DMA_BIT_MASK(32);
> +		hdmi->audio = platform_device_register_full(&pdevinfo);
> +	}
> +
>  	dev_set_drvdata(dev, hdmi);
>  
>  	return 0;
> @@ -1877,6 +1898,9 @@ void dw_hdmi_unbind(struct device *dev, struct device *master, void *data)
>  {
>  	struct dw_hdmi *hdmi = dev_get_drvdata(dev);
>  
> +	if (hdmi->audio && !IS_ERR(hdmi->audio))
> +		platform_device_unregister(hdmi->audio);
> +
>  	/* Disable all interrupts */
>  	hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
>  
> diff --git a/drivers/gpu/drm/bridge/dw_hdmi.h b/drivers/gpu/drm/bridge/dw_hdmi.h
> index 175dbc89a824..78e54e813212 100644
> --- a/drivers/gpu/drm/bridge/dw_hdmi.h
> +++ b/drivers/gpu/drm/bridge/dw_hdmi.h
> @@ -545,6 +545,9 @@
>  #define HDMI_I2CM_FS_SCL_LCNT_0_ADDR            0x7E12
>  
>  enum {
> +/* CONFIG1_ID field values */
> +	HDMI_CONFIG1_AHB = 0x01,
> +
>  /* IH_FC_INT2 field values */
>  	HDMI_IH_FC_INT2_OVERFLOW_MASK = 0x03,
>  	HDMI_IH_FC_INT2_LOW_PRIORITY_OVERFLOW = 0x02,
> -- 
> 2.1.0
> 
> _______________________________________________
> Alsa-devel mailing list
> Alsa-devel@alsa-project.org
> http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
> 

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [alsa-devel] [PATCH v2 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver
@ 2015-08-14 14:34           ` Takashi Iwai
  0 siblings, 0 replies; 226+ messages in thread
From: Takashi Iwai @ 2015-08-14 14:34 UTC (permalink / raw)
  To: Russell King
  Cc: Fabio Estevam, alsa-devel, linux-kernel, dri-devel,
	linux-rockchip, Mark Brown, Yakir Yang, Andy Yan,
	linux-arm-kernel

On Fri, 14 Aug 2015 16:04:25 +0200,
Russell King wrote:
> 
> Add ALSA based HDMI AHB audio driver for dw_hdmi.  The only buffer
> format supported by the hardware is its own special IEC958 based format,
> which is not compatible with any ALSA format.  To avoid doing too much
> data manipulation within the driver, we support only ALSAs IEC958 LE and
> 24-bit PCM formats for 2 to 6 channels, which we convert to its hardware
> format.
> 
> A more desirable solution would be to have this conversion in userspace,
> but ALSA does not appear to allow such transformations outside of
> libasound itself.
> 
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> ---
> v2: updated with Takashi Iwai's comments... and with a fixed Cc: line.

Reviewed-by: Takashi Iwai <tiwai@suse.de>


thanks,

Takashi


>  drivers/gpu/drm/bridge/Kconfig             |  10 +
>  drivers/gpu/drm/bridge/Makefile            |   1 +
>  drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c | 579 +++++++++++++++++++++++++++++
>  drivers/gpu/drm/bridge/dw_hdmi-audio.h     |  13 +
>  drivers/gpu/drm/bridge/dw_hdmi.c           |  24 ++
>  drivers/gpu/drm/bridge/dw_hdmi.h           |   3 +
>  6 files changed, 630 insertions(+)
>  create mode 100644 drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
>  create mode 100644 drivers/gpu/drm/bridge/dw_hdmi-audio.h
> 
> diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
> index acef3223772c..56ed35fe0734 100644
> --- a/drivers/gpu/drm/bridge/Kconfig
> +++ b/drivers/gpu/drm/bridge/Kconfig
> @@ -3,6 +3,16 @@ config DRM_DW_HDMI
>  	depends on DRM
>  	select DRM_KMS_HELPER
>  
> +config DRM_DW_HDMI_AHB_AUDIO
> +	tristate "Synopsis Designware AHB Audio interface"
> +	depends on DRM_DW_HDMI && SND
> +	select SND_PCM
> +	select SND_PCM_IEC958
> +	help
> +	  Support the AHB Audio interface which is part of the Synopsis
> +	  Designware HDMI block.  This is used in conjunction with
> +	  the i.MX6 HDMI driver.
> +
>  config DRM_PTN3460
>  	tristate "PTN3460 DP/LVDS bridge"
>  	depends on DRM
> diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
> index 8dfebd984370..eb80dbbb8365 100644
> --- a/drivers/gpu/drm/bridge/Makefile
> +++ b/drivers/gpu/drm/bridge/Makefile
> @@ -3,3 +3,4 @@ ccflags-y := -Iinclude/drm
>  obj-$(CONFIG_DRM_PS8622) += ps8622.o
>  obj-$(CONFIG_DRM_PTN3460) += ptn3460.o
>  obj-$(CONFIG_DRM_DW_HDMI) += dw_hdmi.o
> +obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw_hdmi-ahb-audio.o
> diff --git a/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
> new file mode 100644
> index 000000000000..bf379310008a
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
> @@ -0,0 +1,579 @@
> +/*
> + * DesignWare HDMI audio driver
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * Written and tested against the Designware HDMI Tx found in iMX6.
> + */
> +#include <linux/io.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <drm/bridge/dw_hdmi.h>
> +
> +#include <sound/asoundef.h>
> +#include <sound/core.h>
> +#include <sound/initval.h>
> +#include <sound/pcm.h>
> +#include <sound/pcm_iec958.h>
> +
> +#include "dw_hdmi-audio.h"
> +
> +#define DRIVER_NAME "dw-hdmi-ahb-audio"
> +
> +/* Provide some bits rather than bit offsets */
> +enum {
> +	HDMI_AHB_DMA_CONF0_SW_FIFO_RST = BIT(7),
> +	HDMI_AHB_DMA_CONF0_EN_HLOCK = BIT(3),
> +	HDMI_AHB_DMA_START_START = BIT(0),
> +	HDMI_AHB_DMA_STOP_STOP = BIT(0),
> +	HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR = BIT(5),
> +	HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST = BIT(4),
> +	HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY = BIT(3),
> +	HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE = BIT(2),
> +	HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL = BIT(1),
> +	HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0),
> +	HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL =
> +		HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR |
> +		HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST |
> +		HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY |
> +		HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE |
> +		HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL |
> +		HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY,
> +	HDMI_IH_AHBDMAAUD_STAT0_ERROR = BIT(5),
> +	HDMI_IH_AHBDMAAUD_STAT0_LOST = BIT(4),
> +	HDMI_IH_AHBDMAAUD_STAT0_RETRY = BIT(3),
> +	HDMI_IH_AHBDMAAUD_STAT0_DONE = BIT(2),
> +	HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL = BIT(1),
> +	HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0),
> +	HDMI_IH_AHBDMAAUD_STAT0_ALL =
> +		HDMI_IH_AHBDMAAUD_STAT0_ERROR |
> +		HDMI_IH_AHBDMAAUD_STAT0_LOST |
> +		HDMI_IH_AHBDMAAUD_STAT0_RETRY |
> +		HDMI_IH_AHBDMAAUD_STAT0_DONE |
> +		HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL |
> +		HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY,
> +	HDMI_AHB_DMA_CONF0_INCR16 = 2 << 1,
> +	HDMI_AHB_DMA_CONF0_INCR8 = 1 << 1,
> +	HDMI_AHB_DMA_CONF0_INCR4 = 0,
> +	HDMI_AHB_DMA_CONF0_BURST_MODE = BIT(0),
> +	HDMI_AHB_DMA_MASK_DONE = BIT(7),
> +	HDMI_REVISION_ID = 0x0001,
> +	HDMI_IH_AHBDMAAUD_STAT0 = 0x0109,
> +	HDMI_IH_MUTE_AHBDMAAUD_STAT0 = 0x0189,
> +	HDMI_AHB_DMA_CONF0 = 0x3600,
> +	HDMI_AHB_DMA_START = 0x3601,
> +	HDMI_AHB_DMA_STOP = 0x3602,
> +	HDMI_AHB_DMA_THRSLD = 0x3603,
> +	HDMI_AHB_DMA_STRADDR0 = 0x3604,
> +	HDMI_AHB_DMA_STPADDR0 = 0x3608,
> +	HDMI_AHB_DMA_MASK = 0x3614,
> +	HDMI_AHB_DMA_POL = 0x3615,
> +	HDMI_AHB_DMA_CONF1 = 0x3616,
> +	HDMI_AHB_DMA_BUFFPOL = 0x361a,
> +};
> +
> +struct snd_dw_hdmi {
> +	struct snd_card *card;
> +	struct snd_pcm *pcm;
> +	spinlock_t lock;
> +	struct dw_hdmi_audio_data data;
> +	struct snd_pcm_substream *substream;
> +	void (*reformat)(struct snd_dw_hdmi *, size_t, size_t);
> +	void *buf_src;
> +	void *buf_dst;
> +	dma_addr_t buf_addr;
> +	unsigned buf_offset;
> +	unsigned buf_period;
> +	unsigned buf_size;
> +	unsigned channels;
> +	u8 revision;
> +	u8 iec_offset;
> +	u8 cs[192][8];
> +};
> +
> +static void dw_hdmi_writel(u32 val, void __iomem *ptr)
> +{
> +	writeb_relaxed(val, ptr);
> +	writeb_relaxed(val >> 8, ptr + 1);
> +	writeb_relaxed(val >> 16, ptr + 2);
> +	writeb_relaxed(val >> 24, ptr + 3);
> +}
> +
> +/*
> + * Convert to hardware format: The userspace buffer contains IEC958 samples,
> + * with the PCUV bits in bits 31..28 and audio samples in bits 27..4.  We
> + * need these to be in bits 27..24, with the IEC B bit in bit 28, and audio
> + * samples in 23..0.
> + *
> + * Default preamble in bits 3..0: 8 = block start, 4 = even 2 = odd
> + *
> + * Ideally, we could do with having the data properly formatted in userspace.
> + */
> +static void dw_hdmi_reformat_iec958(struct snd_dw_hdmi *dw,
> +	size_t offset, size_t bytes)
> +{
> +	u32 *src = dw->buf_src + offset;
> +	u32 *dst = dw->buf_dst + offset;
> +	u32 *end = dw->buf_src + offset + bytes;
> +
> +	do {
> +		u32 b, sample = *src++;
> +
> +		b = (sample & 8) << (28 - 3);
> +
> +		sample >>= 4;
> +
> +		*dst++ = sample | b;
> +	} while (src < end);
> +}
> +
> +static u32 parity(u32 sample)
> +{
> +	sample ^= sample >> 16;
> +	sample ^= sample >> 8;
> +	sample ^= sample >> 4;
> +	sample ^= sample >> 2;
> +	sample ^= sample >> 1;
> +	return (sample & 1) << 27;
> +}
> +
> +static void dw_hdmi_reformat_s24(struct snd_dw_hdmi *dw,
> +	size_t offset, size_t bytes)
> +{
> +	u32 *src = dw->buf_src + offset;
> +	u32 *dst = dw->buf_dst + offset;
> +	u32 *end = dw->buf_src + offset + bytes;
> +
> +	do {
> +		unsigned i;
> +		u8 *cs;
> +
> +		cs = dw->cs[dw->iec_offset++];
> +		if (dw->iec_offset >= 192)
> +			dw->iec_offset = 0;
> +
> +		i = dw->channels;
> +		do {
> +			u32 sample = *src++;
> +
> +			sample &= ~0xff000000;
> +			sample |= *cs++ << 24;
> +			sample |= parity(sample & ~0xf8000000);
> +
> +			*dst++ = sample;
> +		} while (--i);
> +	} while (src < end);
> +}
> +
> +static void dw_hdmi_create_cs(struct snd_dw_hdmi *dw,
> +	struct snd_pcm_runtime *runtime)
> +{
> +	u8 cs[4];
> +	unsigned ch, i, j;
> +
> +	snd_pcm_create_iec958_consumer(runtime, cs, sizeof(cs));
> +
> +	memset(dw->cs, 0, sizeof(dw->cs));
> +
> +	for (ch = 0; ch < 8; ch++) {
> +		cs[2] &= ~IEC958_AES2_CON_CHANNEL;
> +		cs[2] |= (ch + 1) << 4;
> +
> +		for (i = 0; i < ARRAY_SIZE(cs); i++) {
> +			unsigned c = cs[i];
> +
> +			for (j = 0; j < 8; j++, c >>= 1)
> +				dw->cs[i * 8 + j][ch] = (c & 1) << 2;
> +		}
> +	}
> +	dw->cs[0][0] |= BIT(4);
> +}
> +
> +static void dw_hdmi_start_dma(struct snd_dw_hdmi *dw)
> +{
> +	void __iomem *base = dw->data.base;
> +	unsigned offset = dw->buf_offset;
> +	unsigned period = dw->buf_period;
> +	u32 start, stop;
> +
> +	dw->reformat(dw, offset, period);
> +
> +	/* Clear all irqs before enabling irqs and starting DMA */
> +	writeb_relaxed(HDMI_IH_AHBDMAAUD_STAT0_ALL,
> +		       base + HDMI_IH_AHBDMAAUD_STAT0);
> +
> +	start = dw->buf_addr + offset;
> +	stop = start + period - 1;
> +
> +	/* Setup the hardware start/stop addresses */
> +	dw_hdmi_writel(start, base + HDMI_AHB_DMA_STRADDR0);
> +	dw_hdmi_writel(stop, base + HDMI_AHB_DMA_STPADDR0);
> +
> +	writeb_relaxed((u8)~HDMI_AHB_DMA_MASK_DONE, base + HDMI_AHB_DMA_MASK);
> +	writeb(HDMI_AHB_DMA_START_START, base + HDMI_AHB_DMA_START);
> +
> +	offset += period;
> +	if (offset >= dw->buf_size)
> +		offset = 0;
> +	dw->buf_offset = offset;
> +}
> +
> +static void dw_hdmi_stop_dma(struct snd_dw_hdmi *dw)
> +{
> +	/* Disable interrupts before disabling DMA */
> +	writeb_relaxed(~0, dw->data.base + HDMI_AHB_DMA_MASK);
> +	writeb_relaxed(HDMI_AHB_DMA_STOP_STOP, dw->data.base + HDMI_AHB_DMA_STOP);
> +}
> +
> +static irqreturn_t snd_dw_hdmi_irq(int irq, void *data)
> +{
> +	struct snd_dw_hdmi *dw = data;
> +	struct snd_pcm_substream *substream;
> +	unsigned stat;
> +
> +	stat = readb_relaxed(dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
> +	if (!stat)
> +		return IRQ_NONE;
> +
> +	writeb_relaxed(stat, dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
> +
> +	substream = dw->substream;
> +	if (stat & HDMI_IH_AHBDMAAUD_STAT0_DONE && substream) {
> +		snd_pcm_period_elapsed(substream);
> +
> +		spin_lock(&dw->lock);
> +		if (dw->substream)
> +			dw_hdmi_start_dma(dw);
> +		spin_unlock(&dw->lock);
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static struct snd_pcm_hardware dw_hdmi_hw = {
> +	.info = SNDRV_PCM_INFO_INTERLEAVED |
> +		SNDRV_PCM_INFO_BLOCK_TRANSFER |
> +		SNDRV_PCM_INFO_MMAP |
> +		SNDRV_PCM_INFO_MMAP_VALID,
> +	.formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE |
> +		   SNDRV_PCM_FMTBIT_S24_LE,
> +	.rates = SNDRV_PCM_RATE_32000 |
> +		 SNDRV_PCM_RATE_44100 |
> +		 SNDRV_PCM_RATE_48000 |
> +		 SNDRV_PCM_RATE_88200 |
> +		 SNDRV_PCM_RATE_96000 |
> +		 SNDRV_PCM_RATE_176400 |
> +		 SNDRV_PCM_RATE_192000,
> +	.channels_min = 2,
> +	.channels_max = 8,
> +	.buffer_bytes_max = 64 * 1024,
> +	.period_bytes_min = 256,
> +	.period_bytes_max = 8192,	/* ERR004323: must limit to 8k */
> +	.periods_min = 2,
> +	.periods_max = 16,
> +	.fifo_size = 0,
> +};
> +
> +static int dw_hdmi_open(struct snd_pcm_substream *substream)
> +{
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +	struct snd_dw_hdmi *dw = substream->private_data;
> +	void __iomem *base = dw->data.base;
> +	int ret;
> +
> +	runtime->hw = dw_hdmi_hw;
> +
> +	ret = snd_pcm_limit_hw_rates(runtime);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Clear FIFO */
> +	writeb_relaxed(HDMI_AHB_DMA_CONF0_SW_FIFO_RST,
> +		       base + HDMI_AHB_DMA_CONF0);
> +
> +	/* Configure interrupt polarities */
> +	writeb_relaxed(~0, base + HDMI_AHB_DMA_POL);
> +	writeb_relaxed(~0, base + HDMI_AHB_DMA_BUFFPOL);
> +
> +	/* Keep interrupts masked, and clear any pending */
> +	writeb_relaxed(~0, base + HDMI_AHB_DMA_MASK);
> +	writeb_relaxed(~0, base + HDMI_IH_AHBDMAAUD_STAT0);
> +
> +	ret = request_irq(dw->data.irq, snd_dw_hdmi_irq, IRQF_SHARED,
> +			  "dw-hdmi-audio", dw);
> +	if (ret)
> +		return ret;
> +
> +	/* Un-mute done interrupt */
> +	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL &
> +		       ~HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE,
> +		       base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
> +
> +	return 0;
> +}
> +
> +static int dw_hdmi_close(struct snd_pcm_substream *substream)
> +{
> +	struct snd_dw_hdmi *dw = substream->private_data;
> +
> +	/* Mute all interrupts */
> +	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
> +		       dw->data.base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
> +
> +	free_irq(dw->data.irq, dw);
> +
> +	return 0;
> +}
> +
> +static int dw_hdmi_hw_free(struct snd_pcm_substream *substream)
> +{
> +	return snd_pcm_lib_free_vmalloc_buffer(substream);
> +}
> +
> +static int dw_hdmi_hw_params(struct snd_pcm_substream *substream,
> +	struct snd_pcm_hw_params *params)
> +{
> +	return snd_pcm_lib_alloc_vmalloc_buffer(substream,
> +						params_buffer_bytes(params));
> +}
> +
> +static int dw_hdmi_prepare(struct snd_pcm_substream *substream)
> +{
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +	struct snd_dw_hdmi *dw = substream->private_data;
> +	u8 threshold, conf0, conf1;
> +
> +	/* Setup as per 3.0.5 FSL 4.1.0 BSP */
> +	switch (dw->revision) {
> +	case 0x0a:
> +		conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
> +			HDMI_AHB_DMA_CONF0_INCR4;
> +		if (runtime->channels == 2)
> +			threshold = 126;
> +		else
> +			threshold = 124;
> +		break;
> +	case 0x1a:
> +		conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
> +			HDMI_AHB_DMA_CONF0_INCR8;
> +		threshold = 128;
> +		break;
> +	default:
> +		/* NOTREACHED */
> +		return -EINVAL;
> +	}
> +
> +	dw_hdmi_set_sample_rate(dw->data.hdmi, runtime->rate);
> +
> +	/* Minimum number of bytes in the fifo. */
> +	runtime->hw.fifo_size = threshold * 32;
> +
> +	conf0 |= HDMI_AHB_DMA_CONF0_EN_HLOCK;
> +	conf1 = (1 << runtime->channels) - 1;
> +
> +	writeb_relaxed(threshold, dw->data.base + HDMI_AHB_DMA_THRSLD);
> +	writeb_relaxed(conf0, dw->data.base + HDMI_AHB_DMA_CONF0);
> +	writeb_relaxed(conf1, dw->data.base + HDMI_AHB_DMA_CONF1);
> +
> +	switch (runtime->format) {
> +	case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
> +		dw->reformat = dw_hdmi_reformat_iec958;
> +		break;
> +	case SNDRV_PCM_FORMAT_S24_LE:
> +		dw_hdmi_create_cs(dw, runtime);
> +		dw->reformat = dw_hdmi_reformat_s24;
> +		break;
> +	}
> +	dw->iec_offset = 0;
> +	dw->channels = runtime->channels;
> +	dw->buf_src  = runtime->dma_area;
> +	dw->buf_dst  = substream->dma_buffer.area;
> +	dw->buf_addr = substream->dma_buffer.addr;
> +	dw->buf_period = snd_pcm_lib_period_bytes(substream);
> +	dw->buf_size = snd_pcm_lib_buffer_bytes(substream);
> +
> +	return 0;
> +}
> +
> +static int dw_hdmi_trigger(struct snd_pcm_substream *substream, int cmd)
> +{
> +	struct snd_dw_hdmi *dw = substream->private_data;
> +	unsigned long flags;
> +	int ret = 0;
> +
> +	switch (cmd) {
> +	case SNDRV_PCM_TRIGGER_START:
> +		spin_lock_irqsave(&dw->lock, flags);
> +		dw->buf_offset = 0;
> +		dw->substream = substream;
> +		dw_hdmi_start_dma(dw);
> +		dw_hdmi_audio_enable(dw->data.hdmi);
> +		spin_unlock_irqrestore(&dw->lock, flags);
> +		substream->runtime->delay = substream->runtime->period_size;
> +		break;
> +
> +	case SNDRV_PCM_TRIGGER_STOP:
> +		spin_lock_irqsave(&dw->lock, flags);
> +		dw->substream = NULL;
> +		dw_hdmi_stop_dma(dw);
> +		dw_hdmi_audio_disable(dw->data.hdmi);
> +		spin_unlock_irqrestore(&dw->lock, flags);
> +		break;
> +
> +	default:
> +		ret = -EINVAL;
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +static snd_pcm_uframes_t dw_hdmi_pointer(struct snd_pcm_substream *substream)
> +{
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +	struct snd_dw_hdmi *dw = substream->private_data;
> +
> +	/*
> +	 * We are unable to report the exact hardware position as
> +	 * reading the 32-bit DMA position using 8-bit reads is racy.
> +	 */
> +	return bytes_to_frames(runtime, dw->buf_offset);
> +}
> +
> +static struct snd_pcm_ops snd_dw_hdmi_ops = {
> +	.open = dw_hdmi_open,
> +	.close = dw_hdmi_close,
> +	.ioctl = snd_pcm_lib_ioctl,
> +	.hw_params = dw_hdmi_hw_params,
> +	.hw_free = dw_hdmi_hw_free,
> +	.prepare = dw_hdmi_prepare,
> +	.trigger = dw_hdmi_trigger,
> +	.pointer = dw_hdmi_pointer,
> +	.page = snd_pcm_lib_get_vmalloc_page,
> +};
> +
> +static int snd_dw_hdmi_probe(struct platform_device *pdev)
> +{
> +	const struct dw_hdmi_audio_data *data = pdev->dev.platform_data;
> +	struct device *dev = pdev->dev.parent;
> +	struct snd_dw_hdmi *dw;
> +	struct snd_card *card;
> +	struct snd_pcm *pcm;
> +	unsigned revision;
> +	int ret;
> +
> +	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
> +		       data->base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
> +	revision = readb_relaxed(data->base + HDMI_REVISION_ID);
> +	if (revision != 0x0a && revision != 0x1a) {
> +		dev_err(dev, "dw-hdmi-audio: unknown revision 0x%02x\n",
> +			revision);
> +		return -ENXIO;
> +	}
> +
> +	ret = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
> +			      THIS_MODULE, sizeof(struct snd_dw_hdmi), &card);
> +	if (ret < 0)
> +		return ret;
> +
> +	strlcpy(card->driver, DRIVER_NAME, sizeof(card->driver));
> +	strlcpy(card->shortname, "DW-HDMI", sizeof(card->shortname));
> +	snprintf(card->longname, sizeof(card->longname),
> +		 "%s rev 0x%02x, irq %d", card->shortname, revision,
> +		 data->irq);
> +
> +	dw = card->private_data;
> +	dw->card = card;
> +	dw->data = *data;
> +	dw->revision = revision;
> +
> +	spin_lock_init(&dw->lock);
> +
> +	ret = snd_pcm_new(card, "DW HDMI", 0, 1, 0, &pcm);
> +	if (ret < 0)
> +		goto err;
> +
> +	dw->pcm = pcm;
> +	pcm->private_data = dw;
> +	strlcpy(pcm->name, DRIVER_NAME, sizeof(pcm->name));
> +	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_dw_hdmi_ops);
> +
> +	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
> +			dev, 64 * 1024, 64 * 1024);
> +
> +	ret = snd_card_register(card);
> +	if (ret < 0)
> +		goto err;
> +
> +	platform_set_drvdata(pdev, dw);
> +
> +	return 0;
> +
> +err:
> +	snd_card_free(card);
> +	return ret;
> +}
> +
> +static int snd_dw_hdmi_remove(struct platform_device *pdev)
> +{
> +	struct snd_dw_hdmi *dw = platform_get_drvdata(pdev);
> +
> +	snd_card_free(dw->card);
> +
> +	return 0;
> +}
> +
> +#if defined(CONFIG_PM_SLEEP) && defined(IS_NOT_BROKEN)
> +/*
> + * This code is fine, but requires implementation in the dw_hdmi_trigger()
> + * method which is currently missing as I have no way to test this.
> + */
> +static int snd_dw_hdmi_suspend(struct device *dev)
> +{
> +	struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
> +
> +	snd_power_change_state(dw->card, SNDRV_CTL_POWER_D3cold);
> +	snd_pcm_suspend_all(dw->pcm);
> +
> +	return 0;
> +}
> +
> +static int snd_dw_hdmi_resume(struct device *dev)
> +{
> +	struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
> +
> +	snd_power_change_state(dw->card, SNDRV_CTL_POWER_D0);
> +
> +	return 0;
> +}
> +
> +static SIMPLE_DEV_PM_OPS(snd_dw_hdmi_pm, snd_dw_hdmi_suspend,
> +			 snd_dw_hdmi_resume);
> +#define PM_OPS &snd_dw_hdmi_pm
> +#else
> +#define PM_OPS NULL
> +#endif
> +
> +static struct platform_driver snd_dw_hdmi_driver = {
> +	.probe	= snd_dw_hdmi_probe,
> +	.remove	= snd_dw_hdmi_remove,
> +	.driver	= {
> +		.name = DRIVER_NAME,
> +		.owner = THIS_MODULE,
> +		.pm = PM_OPS,
> +	},
> +};
> +
> +module_platform_driver(snd_dw_hdmi_driver);
> +
> +MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>");
> +MODULE_DESCRIPTION("Synopsis Designware HDMI AHB ALSA interface");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:" DRIVER_NAME);
> diff --git a/drivers/gpu/drm/bridge/dw_hdmi-audio.h b/drivers/gpu/drm/bridge/dw_hdmi-audio.h
> new file mode 100644
> index 000000000000..1e840118d90a
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/dw_hdmi-audio.h
> @@ -0,0 +1,13 @@
> +#ifndef DW_HDMI_AUDIO_H
> +#define DW_HDMI_AUDIO_H
> +
> +struct dw_hdmi;
> +
> +struct dw_hdmi_audio_data {
> +	phys_addr_t phys;
> +	void __iomem *base;
> +	int irq;
> +	struct dw_hdmi *hdmi;
> +};
> +
> +#endif
> diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
> index fba25607ef88..b65464789fbd 100644
> --- a/drivers/gpu/drm/bridge/dw_hdmi.c
> +++ b/drivers/gpu/drm/bridge/dw_hdmi.c
> @@ -28,6 +28,7 @@
>  #include <drm/bridge/dw_hdmi.h>
>  
>  #include "dw_hdmi.h"
> +#include "dw_hdmi-audio.h"
>  
>  #define HDMI_EDID_LEN		512
>  
> @@ -104,6 +105,7 @@ struct dw_hdmi {
>  	struct drm_encoder *encoder;
>  	struct drm_bridge *bridge;
>  
> +	struct platform_device *audio;
>  	enum dw_hdmi_devtype dev_type;
>  	struct device *dev;
>  	struct clk *isfr_clk;
> @@ -1732,7 +1734,9 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
>  {
>  	struct drm_device *drm = data;
>  	struct device_node *np = dev->of_node;
> +	struct platform_device_info pdevinfo;
>  	struct device_node *ddc_node;
> +	struct dw_hdmi_audio_data audio;
>  	struct dw_hdmi *hdmi;
>  	int ret;
>  	u32 val = 1;
> @@ -1860,6 +1864,23 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
>  	hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE),
>  		    HDMI_IH_MUTE_PHY_STAT0);
>  
> +	memset(&pdevinfo, 0, sizeof(pdevinfo));
> +	pdevinfo.parent = dev;
> +	pdevinfo.id = PLATFORM_DEVID_AUTO;
> +
> +	if (hdmi_readb(hdmi, HDMI_CONFIG1_ID) & HDMI_CONFIG1_AHB) {
> +		audio.phys = iores->start;
> +		audio.base = hdmi->regs;
> +		audio.irq = irq;
> +		audio.hdmi = hdmi;
> +
> +		pdevinfo.name = "dw-hdmi-ahb-audio";
> +		pdevinfo.data = &audio;
> +		pdevinfo.size_data = sizeof(audio);
> +		pdevinfo.dma_mask = DMA_BIT_MASK(32);
> +		hdmi->audio = platform_device_register_full(&pdevinfo);
> +	}
> +
>  	dev_set_drvdata(dev, hdmi);
>  
>  	return 0;
> @@ -1877,6 +1898,9 @@ void dw_hdmi_unbind(struct device *dev, struct device *master, void *data)
>  {
>  	struct dw_hdmi *hdmi = dev_get_drvdata(dev);
>  
> +	if (hdmi->audio && !IS_ERR(hdmi->audio))
> +		platform_device_unregister(hdmi->audio);
> +
>  	/* Disable all interrupts */
>  	hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
>  
> diff --git a/drivers/gpu/drm/bridge/dw_hdmi.h b/drivers/gpu/drm/bridge/dw_hdmi.h
> index 175dbc89a824..78e54e813212 100644
> --- a/drivers/gpu/drm/bridge/dw_hdmi.h
> +++ b/drivers/gpu/drm/bridge/dw_hdmi.h
> @@ -545,6 +545,9 @@
>  #define HDMI_I2CM_FS_SCL_LCNT_0_ADDR            0x7E12
>  
>  enum {
> +/* CONFIG1_ID field values */
> +	HDMI_CONFIG1_AHB = 0x01,
> +
>  /* IH_FC_INT2 field values */
>  	HDMI_IH_FC_INT2_OVERFLOW_MASK = 0x03,
>  	HDMI_IH_FC_INT2_LOW_PRIORITY_OVERFLOW = 0x02,
> -- 
> 2.1.0
> 
> _______________________________________________
> Alsa-devel mailing list
> Alsa-devel@alsa-project.org
> http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
> 
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [alsa-devel] [PATCH v2 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver
@ 2015-08-14 14:34           ` Takashi Iwai
  0 siblings, 0 replies; 226+ messages in thread
From: Takashi Iwai @ 2015-08-14 14:34 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, 14 Aug 2015 16:04:25 +0200,
Russell King wrote:
> 
> Add ALSA based HDMI AHB audio driver for dw_hdmi.  The only buffer
> format supported by the hardware is its own special IEC958 based format,
> which is not compatible with any ALSA format.  To avoid doing too much
> data manipulation within the driver, we support only ALSAs IEC958 LE and
> 24-bit PCM formats for 2 to 6 channels, which we convert to its hardware
> format.
> 
> A more desirable solution would be to have this conversion in userspace,
> but ALSA does not appear to allow such transformations outside of
> libasound itself.
> 
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> ---
> v2: updated with Takashi Iwai's comments... and with a fixed Cc: line.

Reviewed-by: Takashi Iwai <tiwai@suse.de>


thanks,

Takashi


>  drivers/gpu/drm/bridge/Kconfig             |  10 +
>  drivers/gpu/drm/bridge/Makefile            |   1 +
>  drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c | 579 +++++++++++++++++++++++++++++
>  drivers/gpu/drm/bridge/dw_hdmi-audio.h     |  13 +
>  drivers/gpu/drm/bridge/dw_hdmi.c           |  24 ++
>  drivers/gpu/drm/bridge/dw_hdmi.h           |   3 +
>  6 files changed, 630 insertions(+)
>  create mode 100644 drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
>  create mode 100644 drivers/gpu/drm/bridge/dw_hdmi-audio.h
> 
> diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
> index acef3223772c..56ed35fe0734 100644
> --- a/drivers/gpu/drm/bridge/Kconfig
> +++ b/drivers/gpu/drm/bridge/Kconfig
> @@ -3,6 +3,16 @@ config DRM_DW_HDMI
>  	depends on DRM
>  	select DRM_KMS_HELPER
>  
> +config DRM_DW_HDMI_AHB_AUDIO
> +	tristate "Synopsis Designware AHB Audio interface"
> +	depends on DRM_DW_HDMI && SND
> +	select SND_PCM
> +	select SND_PCM_IEC958
> +	help
> +	  Support the AHB Audio interface which is part of the Synopsis
> +	  Designware HDMI block.  This is used in conjunction with
> +	  the i.MX6 HDMI driver.
> +
>  config DRM_PTN3460
>  	tristate "PTN3460 DP/LVDS bridge"
>  	depends on DRM
> diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
> index 8dfebd984370..eb80dbbb8365 100644
> --- a/drivers/gpu/drm/bridge/Makefile
> +++ b/drivers/gpu/drm/bridge/Makefile
> @@ -3,3 +3,4 @@ ccflags-y := -Iinclude/drm
>  obj-$(CONFIG_DRM_PS8622) += ps8622.o
>  obj-$(CONFIG_DRM_PTN3460) += ptn3460.o
>  obj-$(CONFIG_DRM_DW_HDMI) += dw_hdmi.o
> +obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw_hdmi-ahb-audio.o
> diff --git a/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
> new file mode 100644
> index 000000000000..bf379310008a
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
> @@ -0,0 +1,579 @@
> +/*
> + * DesignWare HDMI audio driver
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * Written and tested against the Designware HDMI Tx found in iMX6.
> + */
> +#include <linux/io.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <drm/bridge/dw_hdmi.h>
> +
> +#include <sound/asoundef.h>
> +#include <sound/core.h>
> +#include <sound/initval.h>
> +#include <sound/pcm.h>
> +#include <sound/pcm_iec958.h>
> +
> +#include "dw_hdmi-audio.h"
> +
> +#define DRIVER_NAME "dw-hdmi-ahb-audio"
> +
> +/* Provide some bits rather than bit offsets */
> +enum {
> +	HDMI_AHB_DMA_CONF0_SW_FIFO_RST = BIT(7),
> +	HDMI_AHB_DMA_CONF0_EN_HLOCK = BIT(3),
> +	HDMI_AHB_DMA_START_START = BIT(0),
> +	HDMI_AHB_DMA_STOP_STOP = BIT(0),
> +	HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR = BIT(5),
> +	HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST = BIT(4),
> +	HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY = BIT(3),
> +	HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE = BIT(2),
> +	HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL = BIT(1),
> +	HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0),
> +	HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL =
> +		HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR |
> +		HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST |
> +		HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY |
> +		HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE |
> +		HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL |
> +		HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY,
> +	HDMI_IH_AHBDMAAUD_STAT0_ERROR = BIT(5),
> +	HDMI_IH_AHBDMAAUD_STAT0_LOST = BIT(4),
> +	HDMI_IH_AHBDMAAUD_STAT0_RETRY = BIT(3),
> +	HDMI_IH_AHBDMAAUD_STAT0_DONE = BIT(2),
> +	HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL = BIT(1),
> +	HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0),
> +	HDMI_IH_AHBDMAAUD_STAT0_ALL =
> +		HDMI_IH_AHBDMAAUD_STAT0_ERROR |
> +		HDMI_IH_AHBDMAAUD_STAT0_LOST |
> +		HDMI_IH_AHBDMAAUD_STAT0_RETRY |
> +		HDMI_IH_AHBDMAAUD_STAT0_DONE |
> +		HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL |
> +		HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY,
> +	HDMI_AHB_DMA_CONF0_INCR16 = 2 << 1,
> +	HDMI_AHB_DMA_CONF0_INCR8 = 1 << 1,
> +	HDMI_AHB_DMA_CONF0_INCR4 = 0,
> +	HDMI_AHB_DMA_CONF0_BURST_MODE = BIT(0),
> +	HDMI_AHB_DMA_MASK_DONE = BIT(7),
> +	HDMI_REVISION_ID = 0x0001,
> +	HDMI_IH_AHBDMAAUD_STAT0 = 0x0109,
> +	HDMI_IH_MUTE_AHBDMAAUD_STAT0 = 0x0189,
> +	HDMI_AHB_DMA_CONF0 = 0x3600,
> +	HDMI_AHB_DMA_START = 0x3601,
> +	HDMI_AHB_DMA_STOP = 0x3602,
> +	HDMI_AHB_DMA_THRSLD = 0x3603,
> +	HDMI_AHB_DMA_STRADDR0 = 0x3604,
> +	HDMI_AHB_DMA_STPADDR0 = 0x3608,
> +	HDMI_AHB_DMA_MASK = 0x3614,
> +	HDMI_AHB_DMA_POL = 0x3615,
> +	HDMI_AHB_DMA_CONF1 = 0x3616,
> +	HDMI_AHB_DMA_BUFFPOL = 0x361a,
> +};
> +
> +struct snd_dw_hdmi {
> +	struct snd_card *card;
> +	struct snd_pcm *pcm;
> +	spinlock_t lock;
> +	struct dw_hdmi_audio_data data;
> +	struct snd_pcm_substream *substream;
> +	void (*reformat)(struct snd_dw_hdmi *, size_t, size_t);
> +	void *buf_src;
> +	void *buf_dst;
> +	dma_addr_t buf_addr;
> +	unsigned buf_offset;
> +	unsigned buf_period;
> +	unsigned buf_size;
> +	unsigned channels;
> +	u8 revision;
> +	u8 iec_offset;
> +	u8 cs[192][8];
> +};
> +
> +static void dw_hdmi_writel(u32 val, void __iomem *ptr)
> +{
> +	writeb_relaxed(val, ptr);
> +	writeb_relaxed(val >> 8, ptr + 1);
> +	writeb_relaxed(val >> 16, ptr + 2);
> +	writeb_relaxed(val >> 24, ptr + 3);
> +}
> +
> +/*
> + * Convert to hardware format: The userspace buffer contains IEC958 samples,
> + * with the PCUV bits in bits 31..28 and audio samples in bits 27..4.  We
> + * need these to be in bits 27..24, with the IEC B bit in bit 28, and audio
> + * samples in 23..0.
> + *
> + * Default preamble in bits 3..0: 8 = block start, 4 = even 2 = odd
> + *
> + * Ideally, we could do with having the data properly formatted in userspace.
> + */
> +static void dw_hdmi_reformat_iec958(struct snd_dw_hdmi *dw,
> +	size_t offset, size_t bytes)
> +{
> +	u32 *src = dw->buf_src + offset;
> +	u32 *dst = dw->buf_dst + offset;
> +	u32 *end = dw->buf_src + offset + bytes;
> +
> +	do {
> +		u32 b, sample = *src++;
> +
> +		b = (sample & 8) << (28 - 3);
> +
> +		sample >>= 4;
> +
> +		*dst++ = sample | b;
> +	} while (src < end);
> +}
> +
> +static u32 parity(u32 sample)
> +{
> +	sample ^= sample >> 16;
> +	sample ^= sample >> 8;
> +	sample ^= sample >> 4;
> +	sample ^= sample >> 2;
> +	sample ^= sample >> 1;
> +	return (sample & 1) << 27;
> +}
> +
> +static void dw_hdmi_reformat_s24(struct snd_dw_hdmi *dw,
> +	size_t offset, size_t bytes)
> +{
> +	u32 *src = dw->buf_src + offset;
> +	u32 *dst = dw->buf_dst + offset;
> +	u32 *end = dw->buf_src + offset + bytes;
> +
> +	do {
> +		unsigned i;
> +		u8 *cs;
> +
> +		cs = dw->cs[dw->iec_offset++];
> +		if (dw->iec_offset >= 192)
> +			dw->iec_offset = 0;
> +
> +		i = dw->channels;
> +		do {
> +			u32 sample = *src++;
> +
> +			sample &= ~0xff000000;
> +			sample |= *cs++ << 24;
> +			sample |= parity(sample & ~0xf8000000);
> +
> +			*dst++ = sample;
> +		} while (--i);
> +	} while (src < end);
> +}
> +
> +static void dw_hdmi_create_cs(struct snd_dw_hdmi *dw,
> +	struct snd_pcm_runtime *runtime)
> +{
> +	u8 cs[4];
> +	unsigned ch, i, j;
> +
> +	snd_pcm_create_iec958_consumer(runtime, cs, sizeof(cs));
> +
> +	memset(dw->cs, 0, sizeof(dw->cs));
> +
> +	for (ch = 0; ch < 8; ch++) {
> +		cs[2] &= ~IEC958_AES2_CON_CHANNEL;
> +		cs[2] |= (ch + 1) << 4;
> +
> +		for (i = 0; i < ARRAY_SIZE(cs); i++) {
> +			unsigned c = cs[i];
> +
> +			for (j = 0; j < 8; j++, c >>= 1)
> +				dw->cs[i * 8 + j][ch] = (c & 1) << 2;
> +		}
> +	}
> +	dw->cs[0][0] |= BIT(4);
> +}
> +
> +static void dw_hdmi_start_dma(struct snd_dw_hdmi *dw)
> +{
> +	void __iomem *base = dw->data.base;
> +	unsigned offset = dw->buf_offset;
> +	unsigned period = dw->buf_period;
> +	u32 start, stop;
> +
> +	dw->reformat(dw, offset, period);
> +
> +	/* Clear all irqs before enabling irqs and starting DMA */
> +	writeb_relaxed(HDMI_IH_AHBDMAAUD_STAT0_ALL,
> +		       base + HDMI_IH_AHBDMAAUD_STAT0);
> +
> +	start = dw->buf_addr + offset;
> +	stop = start + period - 1;
> +
> +	/* Setup the hardware start/stop addresses */
> +	dw_hdmi_writel(start, base + HDMI_AHB_DMA_STRADDR0);
> +	dw_hdmi_writel(stop, base + HDMI_AHB_DMA_STPADDR0);
> +
> +	writeb_relaxed((u8)~HDMI_AHB_DMA_MASK_DONE, base + HDMI_AHB_DMA_MASK);
> +	writeb(HDMI_AHB_DMA_START_START, base + HDMI_AHB_DMA_START);
> +
> +	offset += period;
> +	if (offset >= dw->buf_size)
> +		offset = 0;
> +	dw->buf_offset = offset;
> +}
> +
> +static void dw_hdmi_stop_dma(struct snd_dw_hdmi *dw)
> +{
> +	/* Disable interrupts before disabling DMA */
> +	writeb_relaxed(~0, dw->data.base + HDMI_AHB_DMA_MASK);
> +	writeb_relaxed(HDMI_AHB_DMA_STOP_STOP, dw->data.base + HDMI_AHB_DMA_STOP);
> +}
> +
> +static irqreturn_t snd_dw_hdmi_irq(int irq, void *data)
> +{
> +	struct snd_dw_hdmi *dw = data;
> +	struct snd_pcm_substream *substream;
> +	unsigned stat;
> +
> +	stat = readb_relaxed(dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
> +	if (!stat)
> +		return IRQ_NONE;
> +
> +	writeb_relaxed(stat, dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
> +
> +	substream = dw->substream;
> +	if (stat & HDMI_IH_AHBDMAAUD_STAT0_DONE && substream) {
> +		snd_pcm_period_elapsed(substream);
> +
> +		spin_lock(&dw->lock);
> +		if (dw->substream)
> +			dw_hdmi_start_dma(dw);
> +		spin_unlock(&dw->lock);
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static struct snd_pcm_hardware dw_hdmi_hw = {
> +	.info = SNDRV_PCM_INFO_INTERLEAVED |
> +		SNDRV_PCM_INFO_BLOCK_TRANSFER |
> +		SNDRV_PCM_INFO_MMAP |
> +		SNDRV_PCM_INFO_MMAP_VALID,
> +	.formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE |
> +		   SNDRV_PCM_FMTBIT_S24_LE,
> +	.rates = SNDRV_PCM_RATE_32000 |
> +		 SNDRV_PCM_RATE_44100 |
> +		 SNDRV_PCM_RATE_48000 |
> +		 SNDRV_PCM_RATE_88200 |
> +		 SNDRV_PCM_RATE_96000 |
> +		 SNDRV_PCM_RATE_176400 |
> +		 SNDRV_PCM_RATE_192000,
> +	.channels_min = 2,
> +	.channels_max = 8,
> +	.buffer_bytes_max = 64 * 1024,
> +	.period_bytes_min = 256,
> +	.period_bytes_max = 8192,	/* ERR004323: must limit to 8k */
> +	.periods_min = 2,
> +	.periods_max = 16,
> +	.fifo_size = 0,
> +};
> +
> +static int dw_hdmi_open(struct snd_pcm_substream *substream)
> +{
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +	struct snd_dw_hdmi *dw = substream->private_data;
> +	void __iomem *base = dw->data.base;
> +	int ret;
> +
> +	runtime->hw = dw_hdmi_hw;
> +
> +	ret = snd_pcm_limit_hw_rates(runtime);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Clear FIFO */
> +	writeb_relaxed(HDMI_AHB_DMA_CONF0_SW_FIFO_RST,
> +		       base + HDMI_AHB_DMA_CONF0);
> +
> +	/* Configure interrupt polarities */
> +	writeb_relaxed(~0, base + HDMI_AHB_DMA_POL);
> +	writeb_relaxed(~0, base + HDMI_AHB_DMA_BUFFPOL);
> +
> +	/* Keep interrupts masked, and clear any pending */
> +	writeb_relaxed(~0, base + HDMI_AHB_DMA_MASK);
> +	writeb_relaxed(~0, base + HDMI_IH_AHBDMAAUD_STAT0);
> +
> +	ret = request_irq(dw->data.irq, snd_dw_hdmi_irq, IRQF_SHARED,
> +			  "dw-hdmi-audio", dw);
> +	if (ret)
> +		return ret;
> +
> +	/* Un-mute done interrupt */
> +	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL &
> +		       ~HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE,
> +		       base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
> +
> +	return 0;
> +}
> +
> +static int dw_hdmi_close(struct snd_pcm_substream *substream)
> +{
> +	struct snd_dw_hdmi *dw = substream->private_data;
> +
> +	/* Mute all interrupts */
> +	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
> +		       dw->data.base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
> +
> +	free_irq(dw->data.irq, dw);
> +
> +	return 0;
> +}
> +
> +static int dw_hdmi_hw_free(struct snd_pcm_substream *substream)
> +{
> +	return snd_pcm_lib_free_vmalloc_buffer(substream);
> +}
> +
> +static int dw_hdmi_hw_params(struct snd_pcm_substream *substream,
> +	struct snd_pcm_hw_params *params)
> +{
> +	return snd_pcm_lib_alloc_vmalloc_buffer(substream,
> +						params_buffer_bytes(params));
> +}
> +
> +static int dw_hdmi_prepare(struct snd_pcm_substream *substream)
> +{
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +	struct snd_dw_hdmi *dw = substream->private_data;
> +	u8 threshold, conf0, conf1;
> +
> +	/* Setup as per 3.0.5 FSL 4.1.0 BSP */
> +	switch (dw->revision) {
> +	case 0x0a:
> +		conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
> +			HDMI_AHB_DMA_CONF0_INCR4;
> +		if (runtime->channels == 2)
> +			threshold = 126;
> +		else
> +			threshold = 124;
> +		break;
> +	case 0x1a:
> +		conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
> +			HDMI_AHB_DMA_CONF0_INCR8;
> +		threshold = 128;
> +		break;
> +	default:
> +		/* NOTREACHED */
> +		return -EINVAL;
> +	}
> +
> +	dw_hdmi_set_sample_rate(dw->data.hdmi, runtime->rate);
> +
> +	/* Minimum number of bytes in the fifo. */
> +	runtime->hw.fifo_size = threshold * 32;
> +
> +	conf0 |= HDMI_AHB_DMA_CONF0_EN_HLOCK;
> +	conf1 = (1 << runtime->channels) - 1;
> +
> +	writeb_relaxed(threshold, dw->data.base + HDMI_AHB_DMA_THRSLD);
> +	writeb_relaxed(conf0, dw->data.base + HDMI_AHB_DMA_CONF0);
> +	writeb_relaxed(conf1, dw->data.base + HDMI_AHB_DMA_CONF1);
> +
> +	switch (runtime->format) {
> +	case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
> +		dw->reformat = dw_hdmi_reformat_iec958;
> +		break;
> +	case SNDRV_PCM_FORMAT_S24_LE:
> +		dw_hdmi_create_cs(dw, runtime);
> +		dw->reformat = dw_hdmi_reformat_s24;
> +		break;
> +	}
> +	dw->iec_offset = 0;
> +	dw->channels = runtime->channels;
> +	dw->buf_src  = runtime->dma_area;
> +	dw->buf_dst  = substream->dma_buffer.area;
> +	dw->buf_addr = substream->dma_buffer.addr;
> +	dw->buf_period = snd_pcm_lib_period_bytes(substream);
> +	dw->buf_size = snd_pcm_lib_buffer_bytes(substream);
> +
> +	return 0;
> +}
> +
> +static int dw_hdmi_trigger(struct snd_pcm_substream *substream, int cmd)
> +{
> +	struct snd_dw_hdmi *dw = substream->private_data;
> +	unsigned long flags;
> +	int ret = 0;
> +
> +	switch (cmd) {
> +	case SNDRV_PCM_TRIGGER_START:
> +		spin_lock_irqsave(&dw->lock, flags);
> +		dw->buf_offset = 0;
> +		dw->substream = substream;
> +		dw_hdmi_start_dma(dw);
> +		dw_hdmi_audio_enable(dw->data.hdmi);
> +		spin_unlock_irqrestore(&dw->lock, flags);
> +		substream->runtime->delay = substream->runtime->period_size;
> +		break;
> +
> +	case SNDRV_PCM_TRIGGER_STOP:
> +		spin_lock_irqsave(&dw->lock, flags);
> +		dw->substream = NULL;
> +		dw_hdmi_stop_dma(dw);
> +		dw_hdmi_audio_disable(dw->data.hdmi);
> +		spin_unlock_irqrestore(&dw->lock, flags);
> +		break;
> +
> +	default:
> +		ret = -EINVAL;
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +static snd_pcm_uframes_t dw_hdmi_pointer(struct snd_pcm_substream *substream)
> +{
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +	struct snd_dw_hdmi *dw = substream->private_data;
> +
> +	/*
> +	 * We are unable to report the exact hardware position as
> +	 * reading the 32-bit DMA position using 8-bit reads is racy.
> +	 */
> +	return bytes_to_frames(runtime, dw->buf_offset);
> +}
> +
> +static struct snd_pcm_ops snd_dw_hdmi_ops = {
> +	.open = dw_hdmi_open,
> +	.close = dw_hdmi_close,
> +	.ioctl = snd_pcm_lib_ioctl,
> +	.hw_params = dw_hdmi_hw_params,
> +	.hw_free = dw_hdmi_hw_free,
> +	.prepare = dw_hdmi_prepare,
> +	.trigger = dw_hdmi_trigger,
> +	.pointer = dw_hdmi_pointer,
> +	.page = snd_pcm_lib_get_vmalloc_page,
> +};
> +
> +static int snd_dw_hdmi_probe(struct platform_device *pdev)
> +{
> +	const struct dw_hdmi_audio_data *data = pdev->dev.platform_data;
> +	struct device *dev = pdev->dev.parent;
> +	struct snd_dw_hdmi *dw;
> +	struct snd_card *card;
> +	struct snd_pcm *pcm;
> +	unsigned revision;
> +	int ret;
> +
> +	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
> +		       data->base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
> +	revision = readb_relaxed(data->base + HDMI_REVISION_ID);
> +	if (revision != 0x0a && revision != 0x1a) {
> +		dev_err(dev, "dw-hdmi-audio: unknown revision 0x%02x\n",
> +			revision);
> +		return -ENXIO;
> +	}
> +
> +	ret = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
> +			      THIS_MODULE, sizeof(struct snd_dw_hdmi), &card);
> +	if (ret < 0)
> +		return ret;
> +
> +	strlcpy(card->driver, DRIVER_NAME, sizeof(card->driver));
> +	strlcpy(card->shortname, "DW-HDMI", sizeof(card->shortname));
> +	snprintf(card->longname, sizeof(card->longname),
> +		 "%s rev 0x%02x, irq %d", card->shortname, revision,
> +		 data->irq);
> +
> +	dw = card->private_data;
> +	dw->card = card;
> +	dw->data = *data;
> +	dw->revision = revision;
> +
> +	spin_lock_init(&dw->lock);
> +
> +	ret = snd_pcm_new(card, "DW HDMI", 0, 1, 0, &pcm);
> +	if (ret < 0)
> +		goto err;
> +
> +	dw->pcm = pcm;
> +	pcm->private_data = dw;
> +	strlcpy(pcm->name, DRIVER_NAME, sizeof(pcm->name));
> +	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_dw_hdmi_ops);
> +
> +	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
> +			dev, 64 * 1024, 64 * 1024);
> +
> +	ret = snd_card_register(card);
> +	if (ret < 0)
> +		goto err;
> +
> +	platform_set_drvdata(pdev, dw);
> +
> +	return 0;
> +
> +err:
> +	snd_card_free(card);
> +	return ret;
> +}
> +
> +static int snd_dw_hdmi_remove(struct platform_device *pdev)
> +{
> +	struct snd_dw_hdmi *dw = platform_get_drvdata(pdev);
> +
> +	snd_card_free(dw->card);
> +
> +	return 0;
> +}
> +
> +#if defined(CONFIG_PM_SLEEP) && defined(IS_NOT_BROKEN)
> +/*
> + * This code is fine, but requires implementation in the dw_hdmi_trigger()
> + * method which is currently missing as I have no way to test this.
> + */
> +static int snd_dw_hdmi_suspend(struct device *dev)
> +{
> +	struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
> +
> +	snd_power_change_state(dw->card, SNDRV_CTL_POWER_D3cold);
> +	snd_pcm_suspend_all(dw->pcm);
> +
> +	return 0;
> +}
> +
> +static int snd_dw_hdmi_resume(struct device *dev)
> +{
> +	struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
> +
> +	snd_power_change_state(dw->card, SNDRV_CTL_POWER_D0);
> +
> +	return 0;
> +}
> +
> +static SIMPLE_DEV_PM_OPS(snd_dw_hdmi_pm, snd_dw_hdmi_suspend,
> +			 snd_dw_hdmi_resume);
> +#define PM_OPS &snd_dw_hdmi_pm
> +#else
> +#define PM_OPS NULL
> +#endif
> +
> +static struct platform_driver snd_dw_hdmi_driver = {
> +	.probe	= snd_dw_hdmi_probe,
> +	.remove	= snd_dw_hdmi_remove,
> +	.driver	= {
> +		.name = DRIVER_NAME,
> +		.owner = THIS_MODULE,
> +		.pm = PM_OPS,
> +	},
> +};
> +
> +module_platform_driver(snd_dw_hdmi_driver);
> +
> +MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>");
> +MODULE_DESCRIPTION("Synopsis Designware HDMI AHB ALSA interface");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:" DRIVER_NAME);
> diff --git a/drivers/gpu/drm/bridge/dw_hdmi-audio.h b/drivers/gpu/drm/bridge/dw_hdmi-audio.h
> new file mode 100644
> index 000000000000..1e840118d90a
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/dw_hdmi-audio.h
> @@ -0,0 +1,13 @@
> +#ifndef DW_HDMI_AUDIO_H
> +#define DW_HDMI_AUDIO_H
> +
> +struct dw_hdmi;
> +
> +struct dw_hdmi_audio_data {
> +	phys_addr_t phys;
> +	void __iomem *base;
> +	int irq;
> +	struct dw_hdmi *hdmi;
> +};
> +
> +#endif
> diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
> index fba25607ef88..b65464789fbd 100644
> --- a/drivers/gpu/drm/bridge/dw_hdmi.c
> +++ b/drivers/gpu/drm/bridge/dw_hdmi.c
> @@ -28,6 +28,7 @@
>  #include <drm/bridge/dw_hdmi.h>
>  
>  #include "dw_hdmi.h"
> +#include "dw_hdmi-audio.h"
>  
>  #define HDMI_EDID_LEN		512
>  
> @@ -104,6 +105,7 @@ struct dw_hdmi {
>  	struct drm_encoder *encoder;
>  	struct drm_bridge *bridge;
>  
> +	struct platform_device *audio;
>  	enum dw_hdmi_devtype dev_type;
>  	struct device *dev;
>  	struct clk *isfr_clk;
> @@ -1732,7 +1734,9 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
>  {
>  	struct drm_device *drm = data;
>  	struct device_node *np = dev->of_node;
> +	struct platform_device_info pdevinfo;
>  	struct device_node *ddc_node;
> +	struct dw_hdmi_audio_data audio;
>  	struct dw_hdmi *hdmi;
>  	int ret;
>  	u32 val = 1;
> @@ -1860,6 +1864,23 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
>  	hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE),
>  		    HDMI_IH_MUTE_PHY_STAT0);
>  
> +	memset(&pdevinfo, 0, sizeof(pdevinfo));
> +	pdevinfo.parent = dev;
> +	pdevinfo.id = PLATFORM_DEVID_AUTO;
> +
> +	if (hdmi_readb(hdmi, HDMI_CONFIG1_ID) & HDMI_CONFIG1_AHB) {
> +		audio.phys = iores->start;
> +		audio.base = hdmi->regs;
> +		audio.irq = irq;
> +		audio.hdmi = hdmi;
> +
> +		pdevinfo.name = "dw-hdmi-ahb-audio";
> +		pdevinfo.data = &audio;
> +		pdevinfo.size_data = sizeof(audio);
> +		pdevinfo.dma_mask = DMA_BIT_MASK(32);
> +		hdmi->audio = platform_device_register_full(&pdevinfo);
> +	}
> +
>  	dev_set_drvdata(dev, hdmi);
>  
>  	return 0;
> @@ -1877,6 +1898,9 @@ void dw_hdmi_unbind(struct device *dev, struct device *master, void *data)
>  {
>  	struct dw_hdmi *hdmi = dev_get_drvdata(dev);
>  
> +	if (hdmi->audio && !IS_ERR(hdmi->audio))
> +		platform_device_unregister(hdmi->audio);
> +
>  	/* Disable all interrupts */
>  	hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
>  
> diff --git a/drivers/gpu/drm/bridge/dw_hdmi.h b/drivers/gpu/drm/bridge/dw_hdmi.h
> index 175dbc89a824..78e54e813212 100644
> --- a/drivers/gpu/drm/bridge/dw_hdmi.h
> +++ b/drivers/gpu/drm/bridge/dw_hdmi.h
> @@ -545,6 +545,9 @@
>  #define HDMI_I2CM_FS_SCL_LCNT_0_ADDR            0x7E12
>  
>  enum {
> +/* CONFIG1_ID field values */
> +	HDMI_CONFIG1_AHB = 0x01,
> +
>  /* IH_FC_INT2 field values */
>  	HDMI_IH_FC_INT2_OVERFLOW_MASK = 0x03,
>  	HDMI_IH_FC_INT2_LOW_PRIORITY_OVERFLOW = 0x02,
> -- 
> 2.1.0
> 
> _______________________________________________
> Alsa-devel mailing list
> Alsa-devel at alsa-project.org
> http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
> 

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 00/12] dw-hdmi development
  2015-08-10 12:21   ` Thierry Reding
  (?)
@ 2015-08-18 10:37     ` Russell King - ARM Linux
  -1 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-08-18 10:37 UTC (permalink / raw)
  To: Thierry Reding
  Cc: linux-rockchip, alsa-devel, dri-devel, linux-kernel,
	linux-arm-kernel, Fabio Estevam, Yakir Yang, Andy Yan

On Mon, Aug 10, 2015 at 02:21:36PM +0200, Thierry Reding wrote:
> On Sat, Aug 08, 2015 at 05:02:51PM +0100, Russell King - ARM Linux wrote:
> > This sub-series is a mixture of development:
> > 
> > * Removing the incorrect pixel repetition configuration code
> > * Preventing pixel-doubled modes from being used
> > * Adding interlaced video support
> > * Implementing the sink_is_hdmi/sink_has_audio flags I suggested a few
> >   months ago
> > * Only enabling audio support if the sink indicates it has audio
> > * Avoiding double-enabling the HDMI interface
> > * Fixing the mis-leading name of "dw_hdmi_phy_enable_power"
> > * Adding connector mode forcing (important if your monitor bounces
> >   RXSENSE and HPD signals while in low-power mode.)
> > * Improving the HDMI enable/disabling on sink status
> > 
> > For review (and testing if people feel like it).  Acks/tested-bys etc
> > welcome.  It applies on top of my drm-dwhdmi-devel branch, which is
> > waiting for David Airlie to pull (see pull request on dri-devel, 15th
> > July.)
> 
> Hi Russell,
> 
> I have in the past merged patches for the bridge subdirectory via the
> drm/panel tree, though lately much of the dw-hdmi patches have gone in
> via Philipp or you directly. This seems to have worked fine so far, but
> this time around I carry a patch to clean up Kconfig and Makefile a
> little and bring more consistency to the subdirectory and I think it's
> going to conflict with your series here (and potentially any ongoing
> work you have).
> 
> Would you be open to me picking up these patches into the drm/panel
> tree? It feeds into linux-next, so the code would get some exposure
> before Dave's return.

I haven't seen any acks or comments on this set of 12 patches yet which
is rather disappointing.

David has now returned, and as David hasn't pulled stuff from the 15th,
my intention is to re-send that pull request, but with certain patches
from this set included in that - patches 1, 2, 6, 7, 8, 9 and 10.  I'll
also include Vladimir Zapolskiy's "fix register I2CM_ADDRESS register
name" patch in the set too.

None of that touches the Makefile or Kconfig, so there shouldn't be any
conflicts with your work.

-- 
FTTC broadband for 0.8mile line: currently at 10.5Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 00/12] dw-hdmi development
@ 2015-08-18 10:37     ` Russell King - ARM Linux
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-08-18 10:37 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Fabio Estevam, alsa-devel, linux-kernel, dri-devel,
	linux-rockchip, Yakir Yang, Andy Yan, linux-arm-kernel

On Mon, Aug 10, 2015 at 02:21:36PM +0200, Thierry Reding wrote:
> On Sat, Aug 08, 2015 at 05:02:51PM +0100, Russell King - ARM Linux wrote:
> > This sub-series is a mixture of development:
> > 
> > * Removing the incorrect pixel repetition configuration code
> > * Preventing pixel-doubled modes from being used
> > * Adding interlaced video support
> > * Implementing the sink_is_hdmi/sink_has_audio flags I suggested a few
> >   months ago
> > * Only enabling audio support if the sink indicates it has audio
> > * Avoiding double-enabling the HDMI interface
> > * Fixing the mis-leading name of "dw_hdmi_phy_enable_power"
> > * Adding connector mode forcing (important if your monitor bounces
> >   RXSENSE and HPD signals while in low-power mode.)
> > * Improving the HDMI enable/disabling on sink status
> > 
> > For review (and testing if people feel like it).  Acks/tested-bys etc
> > welcome.  It applies on top of my drm-dwhdmi-devel branch, which is
> > waiting for David Airlie to pull (see pull request on dri-devel, 15th
> > July.)
> 
> Hi Russell,
> 
> I have in the past merged patches for the bridge subdirectory via the
> drm/panel tree, though lately much of the dw-hdmi patches have gone in
> via Philipp or you directly. This seems to have worked fine so far, but
> this time around I carry a patch to clean up Kconfig and Makefile a
> little and bring more consistency to the subdirectory and I think it's
> going to conflict with your series here (and potentially any ongoing
> work you have).
> 
> Would you be open to me picking up these patches into the drm/panel
> tree? It feeds into linux-next, so the code would get some exposure
> before Dave's return.

I haven't seen any acks or comments on this set of 12 patches yet which
is rather disappointing.

David has now returned, and as David hasn't pulled stuff from the 15th,
my intention is to re-send that pull request, but with certain patches
from this set included in that - patches 1, 2, 6, 7, 8, 9 and 10.  I'll
also include Vladimir Zapolskiy's "fix register I2CM_ADDRESS register
name" patch in the set too.

None of that touches the Makefile or Kconfig, so there shouldn't be any
conflicts with your work.

-- 
FTTC broadband for 0.8mile line: currently at 10.5Mbps down 400kbps up
according to speedtest.net.
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 00/12] dw-hdmi development
@ 2015-08-18 10:37     ` Russell King - ARM Linux
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-08-18 10:37 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Aug 10, 2015 at 02:21:36PM +0200, Thierry Reding wrote:
> On Sat, Aug 08, 2015 at 05:02:51PM +0100, Russell King - ARM Linux wrote:
> > This sub-series is a mixture of development:
> > 
> > * Removing the incorrect pixel repetition configuration code
> > * Preventing pixel-doubled modes from being used
> > * Adding interlaced video support
> > * Implementing the sink_is_hdmi/sink_has_audio flags I suggested a few
> >   months ago
> > * Only enabling audio support if the sink indicates it has audio
> > * Avoiding double-enabling the HDMI interface
> > * Fixing the mis-leading name of "dw_hdmi_phy_enable_power"
> > * Adding connector mode forcing (important if your monitor bounces
> >   RXSENSE and HPD signals while in low-power mode.)
> > * Improving the HDMI enable/disabling on sink status
> > 
> > For review (and testing if people feel like it).  Acks/tested-bys etc
> > welcome.  It applies on top of my drm-dwhdmi-devel branch, which is
> > waiting for David Airlie to pull (see pull request on dri-devel, 15th
> > July.)
> 
> Hi Russell,
> 
> I have in the past merged patches for the bridge subdirectory via the
> drm/panel tree, though lately much of the dw-hdmi patches have gone in
> via Philipp or you directly. This seems to have worked fine so far, but
> this time around I carry a patch to clean up Kconfig and Makefile a
> little and bring more consistency to the subdirectory and I think it's
> going to conflict with your series here (and potentially any ongoing
> work you have).
> 
> Would you be open to me picking up these patches into the drm/panel
> tree? It feeds into linux-next, so the code would get some exposure
> before Dave's return.

I haven't seen any acks or comments on this set of 12 patches yet which
is rather disappointing.

David has now returned, and as David hasn't pulled stuff from the 15th,
my intention is to re-send that pull request, but with certain patches
from this set included in that - patches 1, 2, 6, 7, 8, 9 and 10.  I'll
also include Vladimir Zapolskiy's "fix register I2CM_ADDRESS register
name" patch in the set too.

None of that touches the Makefile or Kconfig, so there shouldn't be any
conflicts with your work.

-- 
FTTC broadband for 0.8mile line: currently at 10.5Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 04/12] gpu: imx: fix support for interlaced modes
@ 2015-08-27  8:39     ` Philipp Zabel
  0 siblings, 0 replies; 226+ messages in thread
From: Philipp Zabel @ 2015-08-27  8:39 UTC (permalink / raw)
  To: Russell King
  Cc: linux-rockchip, dri-devel, linux-kernel, linux-arm-kernel,
	Andy Yan, Yakir Yang, Fabio Estevam, Sascha Hauer, Jon Nettleton

Hi Russell,

Am Samstag, den 08.08.2015, 17:03 +0100 schrieb Russell King:
> The support for interlaced video modes seems to be broken; we don't use
> anything other than the vtotal/htotal from the timing information to
> define the various sync counters.

I finally made time to test this series:

Tested-by: Philipp Zabel <p.zabel@pengutronix.de>
on i.MX6 GK802 via HDMI connected to a TV (1080p60, 1080i60).

Unfortunately these timings are completely different from what Freescale
came up with for the TV Encoder on i.MX5, but the code we have currently
in mainline doesn't work for that either. I suppose I'll follow up with
a patch that adds yet another sync counter setup for the i.MX5/TVE case.

I'd like to take the two ipu-v3 patches, making a few small changes on
this one:

> Freescale patches for interlaced video support contain an alternative
> sync counter setup, which we include here.  This setup produces the
> hsync and vsync via the normal counter 2 and 3, but moves the display
> enable signal from counter 5 to counter 6.  Therefore, we need to
> change the display controller setup as well.
> 
> The corresponding Freescale patches for this change are:
>   iMX6-HDMI-support-interlaced-display-mode.patch
>   IPU-fine-tuning-the-interlace-display-timing-for-CEA.patch
> 
> This produces a working interlace format output from the IPU.

... on i.MX6 via HDMI.

> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> ---
>  drivers/gpu/ipu-v3/ipu-dc.c | 18 ++++++++---
>  drivers/gpu/ipu-v3/ipu-di.c | 79 +++++++++++++++++++++------------------------
>  2 files changed, 51 insertions(+), 46 deletions(-)
> 
> diff --git a/drivers/gpu/ipu-v3/ipu-dc.c b/drivers/gpu/ipu-v3/ipu-dc.c
> index 9ef2e1f54ca4..aa560855c1dc 100644
> --- a/drivers/gpu/ipu-v3/ipu-dc.c
> +++ b/drivers/gpu/ipu-v3/ipu-dc.c
> @@ -183,12 +183,22 @@ int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced,
>  	}
>  
>  	if (interlaced) {
> -		dc_link_event(dc, DC_EVT_NL, 0, 3);
> -		dc_link_event(dc, DC_EVT_EOL, 0, 2);
> -		dc_link_event(dc, DC_EVT_NEW_DATA, 0, 1);
> +		int word, addr;
> +
> +		if (dc->di) {
> +			addr = 1;
> +			word = 1;

These two are really one and the same. The address written to the link
register for the given event has to point to the address of the
microcode instruction written to the template memory that should be
executed on this event.

I'd like to drop the word variable and use addr for both.

> +		} else {
> +			addr = 0;
> +			word = 0;
> +		}
> +
> +		dc_link_event(dc, DC_EVT_NL, addr, 3);
> +		dc_link_event(dc, DC_EVT_EOL, addr, 2);
> +		dc_link_event(dc, DC_EVT_NEW_DATA, addr, 1);
>  
>  		/* Init template microcode */
> -		dc_write_tmpl(dc, 0, WROD(0), 0, map, SYNC_WAVE, 0, 8, 1);
> +		dc_write_tmpl(dc, word, WROD(0), 0, map, SYNC_WAVE, 0, 6, 1);
>  	} else {
>  		if (dc->di) {
>  			dc_link_event(dc, DC_EVT_NL, 2, 3);
> diff --git a/drivers/gpu/ipu-v3/ipu-di.c b/drivers/gpu/ipu-v3/ipu-di.c
> index a96991c5c15f..359268e3a166 100644
> --- a/drivers/gpu/ipu-v3/ipu-di.c
> +++ b/drivers/gpu/ipu-v3/ipu-di.c
> @@ -71,6 +71,10 @@ enum di_sync_wave {
>  	DI_SYNC_HSYNC = 3,
>  	DI_SYNC_VSYNC = 4,
>  	DI_SYNC_DE = 6,
> +
> +	DI_SYNC_CNT1 = 2,	/* counter >= 2 only */
> +	DI_SYNC_CNT4 = 5,	/* counter >= 5 only */
> +	DI_SYNC_CNT5 = 6,	/* counter >= 6 only */
>  };
>  
>  #define SYNC_WAVE 0
> @@ -211,66 +215,59 @@ static void ipu_di_sync_config_interlaced(struct ipu_di *di,
>  		sig->mode.hback_porch + sig->mode.hfront_porch;
>  	u32 v_total = sig->mode.vactive + sig->mode.vsync_len +
>  		sig->mode.vback_porch + sig->mode.vfront_porch;
> -	u32 reg;
>  	struct di_sync_config cfg[] = {
>  		{
> -			.run_count = h_total / 2 - 1,
> -			.run_src = DI_SYNC_CLK,
> +			/* 1: internal VSYNC for each frame */
> +			.run_count = v_total * 2 - 1,
> +			.run_src = 3,			/* == counter 7 */

Do you know why this works? The Reference Manual v2 lists that value as
"NA" in the DI counter #1 Run Resolution field.

>  		}, {
> -			.run_count = h_total - 11,
> +			/* PIN2: HSYNC waveform */
> +			.run_count = h_total - 1,
>  			.run_src = DI_SYNC_CLK,
> -			.cnt_down = 4,
> +			.cnt_polarity_gen_en = 1,
> +			.cnt_polarity_trigger_src = DI_SYNC_CLK,
> +			.cnt_down = sig->mode.hsync_len * 2,
>  		}, {
> -			.run_count = v_total * 2 - 1,
> -			.run_src = DI_SYNC_INT_HSYNC,
> -			.offset_count = 1,
> -			.offset_src = DI_SYNC_INT_HSYNC,
> -			.cnt_down = 4,
> +			/* PIN3: VSYNC waveform */
> +			.run_count = v_total - 1,
> +			.run_src = 4,			/* == counter 7 */

Same here, ...

> +			.cnt_polarity_gen_en = 1,
> +			.cnt_polarity_trigger_src = 4,	/* == counter 7 */

... and same here, the DI counter #3 polarity Clear select field lists
the value 4 as "Reserved".

> +			.cnt_down = sig->mode.vsync_len * 2,
> +			.cnt_clr_src = DI_SYNC_CNT1,
>  		}, {
[...]
>  		}
>  	};
>  
>  	ipu_di_sync_config(di, cfg, 0, ARRAY_SIZE(cfg));
>  
> -	/* set gentime select and tag sel */
> -	reg = ipu_di_read(di, DI_SW_GEN1(9));
> -	reg &= 0x1FFFFFFF;
> -	reg |= (3 - 1) << 29 | 0x00008000;
> -	ipu_di_write(di, reg, DI_SW_GEN1(9));

As far as I understood, attaching counter #9 to counter #3 is needed to
generate the second vsync on i.MX5. Since this doesn't currently work,
I'm fine with removing it for now.

>  	ipu_di_write(di, v_total / 2 - 1, DI_SCR_CONF);
>  }
>  
> @@ -605,10 +602,8 @@ int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig)
>  
>  		/* set y_sel = 1 */
>  		di_gen |= 0x10000000;
> -		di_gen |= DI_GEN_POLARITY_5;
> -		di_gen |= DI_GEN_POLARITY_8;
>  
> -		vsync_cnt = 7;
> +		vsync_cnt = 3;
>  	} else {
>  		ipu_di_sync_config_noninterlaced(di, sig, div);

regards
Philipp



^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 04/12] gpu: imx: fix support for interlaced modes
@ 2015-08-27  8:39     ` Philipp Zabel
  0 siblings, 0 replies; 226+ messages in thread
From: Philipp Zabel @ 2015-08-27  8:39 UTC (permalink / raw)
  To: Russell King
  Cc: Fabio Estevam, Sascha Hauer, linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Yakir Yang,
	Andy Yan, Jon Nettleton,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

Hi Russell,

Am Samstag, den 08.08.2015, 17:03 +0100 schrieb Russell King:
> The support for interlaced video modes seems to be broken; we don't use
> anything other than the vtotal/htotal from the timing information to
> define the various sync counters.

I finally made time to test this series:

Tested-by: Philipp Zabel <p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
on i.MX6 GK802 via HDMI connected to a TV (1080p60, 1080i60).

Unfortunately these timings are completely different from what Freescale
came up with for the TV Encoder on i.MX5, but the code we have currently
in mainline doesn't work for that either. I suppose I'll follow up with
a patch that adds yet another sync counter setup for the i.MX5/TVE case.

I'd like to take the two ipu-v3 patches, making a few small changes on
this one:

> Freescale patches for interlaced video support contain an alternative
> sync counter setup, which we include here.  This setup produces the
> hsync and vsync via the normal counter 2 and 3, but moves the display
> enable signal from counter 5 to counter 6.  Therefore, we need to
> change the display controller setup as well.
> 
> The corresponding Freescale patches for this change are:
>   iMX6-HDMI-support-interlaced-display-mode.patch
>   IPU-fine-tuning-the-interlace-display-timing-for-CEA.patch
> 
> This produces a working interlace format output from the IPU.

... on i.MX6 via HDMI.

> Signed-off-by: Russell King <rmk+kernel-lFZ/pmaqli7XmaaqVzeoHQ@public.gmane.org>
> ---
>  drivers/gpu/ipu-v3/ipu-dc.c | 18 ++++++++---
>  drivers/gpu/ipu-v3/ipu-di.c | 79 +++++++++++++++++++++------------------------
>  2 files changed, 51 insertions(+), 46 deletions(-)
> 
> diff --git a/drivers/gpu/ipu-v3/ipu-dc.c b/drivers/gpu/ipu-v3/ipu-dc.c
> index 9ef2e1f54ca4..aa560855c1dc 100644
> --- a/drivers/gpu/ipu-v3/ipu-dc.c
> +++ b/drivers/gpu/ipu-v3/ipu-dc.c
> @@ -183,12 +183,22 @@ int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced,
>  	}
>  
>  	if (interlaced) {
> -		dc_link_event(dc, DC_EVT_NL, 0, 3);
> -		dc_link_event(dc, DC_EVT_EOL, 0, 2);
> -		dc_link_event(dc, DC_EVT_NEW_DATA, 0, 1);
> +		int word, addr;
> +
> +		if (dc->di) {
> +			addr = 1;
> +			word = 1;

These two are really one and the same. The address written to the link
register for the given event has to point to the address of the
microcode instruction written to the template memory that should be
executed on this event.

I'd like to drop the word variable and use addr for both.

> +		} else {
> +			addr = 0;
> +			word = 0;
> +		}
> +
> +		dc_link_event(dc, DC_EVT_NL, addr, 3);
> +		dc_link_event(dc, DC_EVT_EOL, addr, 2);
> +		dc_link_event(dc, DC_EVT_NEW_DATA, addr, 1);
>  
>  		/* Init template microcode */
> -		dc_write_tmpl(dc, 0, WROD(0), 0, map, SYNC_WAVE, 0, 8, 1);
> +		dc_write_tmpl(dc, word, WROD(0), 0, map, SYNC_WAVE, 0, 6, 1);
>  	} else {
>  		if (dc->di) {
>  			dc_link_event(dc, DC_EVT_NL, 2, 3);
> diff --git a/drivers/gpu/ipu-v3/ipu-di.c b/drivers/gpu/ipu-v3/ipu-di.c
> index a96991c5c15f..359268e3a166 100644
> --- a/drivers/gpu/ipu-v3/ipu-di.c
> +++ b/drivers/gpu/ipu-v3/ipu-di.c
> @@ -71,6 +71,10 @@ enum di_sync_wave {
>  	DI_SYNC_HSYNC = 3,
>  	DI_SYNC_VSYNC = 4,
>  	DI_SYNC_DE = 6,
> +
> +	DI_SYNC_CNT1 = 2,	/* counter >= 2 only */
> +	DI_SYNC_CNT4 = 5,	/* counter >= 5 only */
> +	DI_SYNC_CNT5 = 6,	/* counter >= 6 only */
>  };
>  
>  #define SYNC_WAVE 0
> @@ -211,66 +215,59 @@ static void ipu_di_sync_config_interlaced(struct ipu_di *di,
>  		sig->mode.hback_porch + sig->mode.hfront_porch;
>  	u32 v_total = sig->mode.vactive + sig->mode.vsync_len +
>  		sig->mode.vback_porch + sig->mode.vfront_porch;
> -	u32 reg;
>  	struct di_sync_config cfg[] = {
>  		{
> -			.run_count = h_total / 2 - 1,
> -			.run_src = DI_SYNC_CLK,
> +			/* 1: internal VSYNC for each frame */
> +			.run_count = v_total * 2 - 1,
> +			.run_src = 3,			/* == counter 7 */

Do you know why this works? The Reference Manual v2 lists that value as
"NA" in the DI counter #1 Run Resolution field.

>  		}, {
> -			.run_count = h_total - 11,
> +			/* PIN2: HSYNC waveform */
> +			.run_count = h_total - 1,
>  			.run_src = DI_SYNC_CLK,
> -			.cnt_down = 4,
> +			.cnt_polarity_gen_en = 1,
> +			.cnt_polarity_trigger_src = DI_SYNC_CLK,
> +			.cnt_down = sig->mode.hsync_len * 2,
>  		}, {
> -			.run_count = v_total * 2 - 1,
> -			.run_src = DI_SYNC_INT_HSYNC,
> -			.offset_count = 1,
> -			.offset_src = DI_SYNC_INT_HSYNC,
> -			.cnt_down = 4,
> +			/* PIN3: VSYNC waveform */
> +			.run_count = v_total - 1,
> +			.run_src = 4,			/* == counter 7 */

Same here, ...

> +			.cnt_polarity_gen_en = 1,
> +			.cnt_polarity_trigger_src = 4,	/* == counter 7 */

... and same here, the DI counter #3 polarity Clear select field lists
the value 4 as "Reserved".

> +			.cnt_down = sig->mode.vsync_len * 2,
> +			.cnt_clr_src = DI_SYNC_CNT1,
>  		}, {
[...]
>  		}
>  	};
>  
>  	ipu_di_sync_config(di, cfg, 0, ARRAY_SIZE(cfg));
>  
> -	/* set gentime select and tag sel */
> -	reg = ipu_di_read(di, DI_SW_GEN1(9));
> -	reg &= 0x1FFFFFFF;
> -	reg |= (3 - 1) << 29 | 0x00008000;
> -	ipu_di_write(di, reg, DI_SW_GEN1(9));

As far as I understood, attaching counter #9 to counter #3 is needed to
generate the second vsync on i.MX5. Since this doesn't currently work,
I'm fine with removing it for now.

>  	ipu_di_write(di, v_total / 2 - 1, DI_SCR_CONF);
>  }
>  
> @@ -605,10 +602,8 @@ int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig)
>  
>  		/* set y_sel = 1 */
>  		di_gen |= 0x10000000;
> -		di_gen |= DI_GEN_POLARITY_5;
> -		di_gen |= DI_GEN_POLARITY_8;
>  
> -		vsync_cnt = 7;
> +		vsync_cnt = 3;
>  	} else {
>  		ipu_di_sync_config_noninterlaced(di, sig, div);

regards
Philipp

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 04/12] gpu: imx: fix support for interlaced modes
@ 2015-08-27  8:39     ` Philipp Zabel
  0 siblings, 0 replies; 226+ messages in thread
From: Philipp Zabel @ 2015-08-27  8:39 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Russell,

Am Samstag, den 08.08.2015, 17:03 +0100 schrieb Russell King:
> The support for interlaced video modes seems to be broken; we don't use
> anything other than the vtotal/htotal from the timing information to
> define the various sync counters.

I finally made time to test this series:

Tested-by: Philipp Zabel <p.zabel@pengutronix.de>
on i.MX6 GK802 via HDMI connected to a TV (1080p60, 1080i60).

Unfortunately these timings are completely different from what Freescale
came up with for the TV Encoder on i.MX5, but the code we have currently
in mainline doesn't work for that either. I suppose I'll follow up with
a patch that adds yet another sync counter setup for the i.MX5/TVE case.

I'd like to take the two ipu-v3 patches, making a few small changes on
this one:

> Freescale patches for interlaced video support contain an alternative
> sync counter setup, which we include here.  This setup produces the
> hsync and vsync via the normal counter 2 and 3, but moves the display
> enable signal from counter 5 to counter 6.  Therefore, we need to
> change the display controller setup as well.
> 
> The corresponding Freescale patches for this change are:
>   iMX6-HDMI-support-interlaced-display-mode.patch
>   IPU-fine-tuning-the-interlace-display-timing-for-CEA.patch
> 
> This produces a working interlace format output from the IPU.

... on i.MX6 via HDMI.

> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> ---
>  drivers/gpu/ipu-v3/ipu-dc.c | 18 ++++++++---
>  drivers/gpu/ipu-v3/ipu-di.c | 79 +++++++++++++++++++++------------------------
>  2 files changed, 51 insertions(+), 46 deletions(-)
> 
> diff --git a/drivers/gpu/ipu-v3/ipu-dc.c b/drivers/gpu/ipu-v3/ipu-dc.c
> index 9ef2e1f54ca4..aa560855c1dc 100644
> --- a/drivers/gpu/ipu-v3/ipu-dc.c
> +++ b/drivers/gpu/ipu-v3/ipu-dc.c
> @@ -183,12 +183,22 @@ int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced,
>  	}
>  
>  	if (interlaced) {
> -		dc_link_event(dc, DC_EVT_NL, 0, 3);
> -		dc_link_event(dc, DC_EVT_EOL, 0, 2);
> -		dc_link_event(dc, DC_EVT_NEW_DATA, 0, 1);
> +		int word, addr;
> +
> +		if (dc->di) {
> +			addr = 1;
> +			word = 1;

These two are really one and the same. The address written to the link
register for the given event has to point to the address of the
microcode instruction written to the template memory that should be
executed on this event.

I'd like to drop the word variable and use addr for both.

> +		} else {
> +			addr = 0;
> +			word = 0;
> +		}
> +
> +		dc_link_event(dc, DC_EVT_NL, addr, 3);
> +		dc_link_event(dc, DC_EVT_EOL, addr, 2);
> +		dc_link_event(dc, DC_EVT_NEW_DATA, addr, 1);
>  
>  		/* Init template microcode */
> -		dc_write_tmpl(dc, 0, WROD(0), 0, map, SYNC_WAVE, 0, 8, 1);
> +		dc_write_tmpl(dc, word, WROD(0), 0, map, SYNC_WAVE, 0, 6, 1);
>  	} else {
>  		if (dc->di) {
>  			dc_link_event(dc, DC_EVT_NL, 2, 3);
> diff --git a/drivers/gpu/ipu-v3/ipu-di.c b/drivers/gpu/ipu-v3/ipu-di.c
> index a96991c5c15f..359268e3a166 100644
> --- a/drivers/gpu/ipu-v3/ipu-di.c
> +++ b/drivers/gpu/ipu-v3/ipu-di.c
> @@ -71,6 +71,10 @@ enum di_sync_wave {
>  	DI_SYNC_HSYNC = 3,
>  	DI_SYNC_VSYNC = 4,
>  	DI_SYNC_DE = 6,
> +
> +	DI_SYNC_CNT1 = 2,	/* counter >= 2 only */
> +	DI_SYNC_CNT4 = 5,	/* counter >= 5 only */
> +	DI_SYNC_CNT5 = 6,	/* counter >= 6 only */
>  };
>  
>  #define SYNC_WAVE 0
> @@ -211,66 +215,59 @@ static void ipu_di_sync_config_interlaced(struct ipu_di *di,
>  		sig->mode.hback_porch + sig->mode.hfront_porch;
>  	u32 v_total = sig->mode.vactive + sig->mode.vsync_len +
>  		sig->mode.vback_porch + sig->mode.vfront_porch;
> -	u32 reg;
>  	struct di_sync_config cfg[] = {
>  		{
> -			.run_count = h_total / 2 - 1,
> -			.run_src = DI_SYNC_CLK,
> +			/* 1: internal VSYNC for each frame */
> +			.run_count = v_total * 2 - 1,
> +			.run_src = 3,			/* == counter 7 */

Do you know why this works? The Reference Manual v2 lists that value as
"NA" in the DI counter #1 Run Resolution field.

>  		}, {
> -			.run_count = h_total - 11,
> +			/* PIN2: HSYNC waveform */
> +			.run_count = h_total - 1,
>  			.run_src = DI_SYNC_CLK,
> -			.cnt_down = 4,
> +			.cnt_polarity_gen_en = 1,
> +			.cnt_polarity_trigger_src = DI_SYNC_CLK,
> +			.cnt_down = sig->mode.hsync_len * 2,
>  		}, {
> -			.run_count = v_total * 2 - 1,
> -			.run_src = DI_SYNC_INT_HSYNC,
> -			.offset_count = 1,
> -			.offset_src = DI_SYNC_INT_HSYNC,
> -			.cnt_down = 4,
> +			/* PIN3: VSYNC waveform */
> +			.run_count = v_total - 1,
> +			.run_src = 4,			/* == counter 7 */

Same here, ...

> +			.cnt_polarity_gen_en = 1,
> +			.cnt_polarity_trigger_src = 4,	/* == counter 7 */

... and same here, the DI counter #3 polarity Clear select field lists
the value 4 as "Reserved".

> +			.cnt_down = sig->mode.vsync_len * 2,
> +			.cnt_clr_src = DI_SYNC_CNT1,
>  		}, {
[...]
>  		}
>  	};
>  
>  	ipu_di_sync_config(di, cfg, 0, ARRAY_SIZE(cfg));
>  
> -	/* set gentime select and tag sel */
> -	reg = ipu_di_read(di, DI_SW_GEN1(9));
> -	reg &= 0x1FFFFFFF;
> -	reg |= (3 - 1) << 29 | 0x00008000;
> -	ipu_di_write(di, reg, DI_SW_GEN1(9));

As far as I understood, attaching counter #9 to counter #3 is needed to
generate the second vsync on i.MX5. Since this doesn't currently work,
I'm fine with removing it for now.

>  	ipu_di_write(di, v_total / 2 - 1, DI_SCR_CONF);
>  }
>  
> @@ -605,10 +602,8 @@ int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig)
>  
>  		/* set y_sel = 1 */
>  		di_gen |= 0x10000000;
> -		di_gen |= DI_GEN_POLARITY_5;
> -		di_gen |= DI_GEN_POLARITY_8;
>  
> -		vsync_cnt = 7;
> +		vsync_cnt = 3;
>  	} else {
>  		ipu_di_sync_config_noninterlaced(di, sig, div);

regards
Philipp

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 0/9] dw-hdmi audio support
  2015-08-08 16:09   ` Russell King - ARM Linux
  (?)
@ 2015-08-27  8:42     ` Philipp Zabel
  -1 siblings, 0 replies; 226+ messages in thread
From: Philipp Zabel @ 2015-08-27  8:42 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: linux-rockchip, alsa-devel, dri-devel, linux-kernel,
	linux-arm-kernel, Fabio Estevam, David Airlie, Jon Nettleton,
	Yakir Yang, Andy Yan, Sascha Hauer

Am Samstag, den 08.08.2015, 17:09 +0100 schrieb Russell King - ARM
Linux:
> Following on from the previous sub-series, this sub-series adds audio
> support to dw-hdmi.
> 
> The two different variants are now in this patch: AHB audio support
> found on iMX6 platforms, and I2S support found on Rockchip patches.
> Thanks to Yakir Yang for contributing the I2S support.
> 
> I suspect that there is still some discussion to be had on this
> series, though I would like to see it moving forward so that we can
> get something merged.

Tested-by: Philipp Zabel <p.zabel@pengutronix.de>
on i.MX6 GK802 via HDMI connected to a TV (stereo only).

except for the i2s patch, which is broken in this series.

regards
Philipp


^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 0/9] dw-hdmi audio support
@ 2015-08-27  8:42     ` Philipp Zabel
  0 siblings, 0 replies; 226+ messages in thread
From: Philipp Zabel @ 2015-08-27  8:42 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Fabio Estevam, alsa-devel, linux-kernel, dri-devel,
	linux-rockchip, Yakir Yang, Andy Yan, linux-arm-kernel

Am Samstag, den 08.08.2015, 17:09 +0100 schrieb Russell King - ARM
Linux:
> Following on from the previous sub-series, this sub-series adds audio
> support to dw-hdmi.
> 
> The two different variants are now in this patch: AHB audio support
> found on iMX6 platforms, and I2S support found on Rockchip patches.
> Thanks to Yakir Yang for contributing the I2S support.
> 
> I suspect that there is still some discussion to be had on this
> series, though I would like to see it moving forward so that we can
> get something merged.

Tested-by: Philipp Zabel <p.zabel@pengutronix.de>
on i.MX6 GK802 via HDMI connected to a TV (stereo only).

except for the i2s patch, which is broken in this series.

regards
Philipp

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 0/9] dw-hdmi audio support
@ 2015-08-27  8:42     ` Philipp Zabel
  0 siblings, 0 replies; 226+ messages in thread
From: Philipp Zabel @ 2015-08-27  8:42 UTC (permalink / raw)
  To: linux-arm-kernel

Am Samstag, den 08.08.2015, 17:09 +0100 schrieb Russell King - ARM
Linux:
> Following on from the previous sub-series, this sub-series adds audio
> support to dw-hdmi.
> 
> The two different variants are now in this patch: AHB audio support
> found on iMX6 platforms, and I2S support found on Rockchip patches.
> Thanks to Yakir Yang for contributing the I2S support.
> 
> I suspect that there is still some discussion to be had on this
> series, though I would like to see it moving forward so that we can
> get something merged.

Tested-by: Philipp Zabel <p.zabel@pengutronix.de>
on i.MX6 GK802 via HDMI connected to a TV (stereo only).

except for the i2s patch, which is broken in this series.

regards
Philipp

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 04/12] gpu: imx: fix support for interlaced modes
  2015-08-27  8:39     ` Philipp Zabel
  (?)
@ 2015-08-27  8:54       ` Russell King - ARM Linux
  -1 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-08-27  8:54 UTC (permalink / raw)
  To: Philipp Zabel
  Cc: linux-rockchip, dri-devel, linux-kernel, linux-arm-kernel,
	Andy Yan, Yakir Yang, Fabio Estevam, Sascha Hauer, Jon Nettleton

On Thu, Aug 27, 2015 at 10:39:12AM +0200, Philipp Zabel wrote:
> Hi Russell,
> 
> Am Samstag, den 08.08.2015, 17:03 +0100 schrieb Russell King:
> > The support for interlaced video modes seems to be broken; we don't use
> > anything other than the vtotal/htotal from the timing information to
> > define the various sync counters.
> 
> I finally made time to test this series:
> 
> Tested-by: Philipp Zabel <p.zabel@pengutronix.de>
> on i.MX6 GK802 via HDMI connected to a TV (1080p60, 1080i60).
> 
> Unfortunately these timings are completely different from what Freescale
> came up with for the TV Encoder on i.MX5, but the code we have currently
> in mainline doesn't work for that either. I suppose I'll follow up with
> a patch that adds yet another sync counter setup for the i.MX5/TVE case.
> 
> I'd like to take the two ipu-v3 patches, making a few small changes on
> this one:

Please don't split the series up.  The reason it's a series is because
there's interdependencies between the patches.

> > Freescale patches for interlaced video support contain an alternative
> > sync counter setup, which we include here.  This setup produces the
> > hsync and vsync via the normal counter 2 and 3, but moves the display
> > enable signal from counter 5 to counter 6.  Therefore, we need to
> > change the display controller setup as well.
> > 
> > The corresponding Freescale patches for this change are:
> >   iMX6-HDMI-support-interlaced-display-mode.patch
> >   IPU-fine-tuning-the-interlace-display-timing-for-CEA.patch
> > 
> > This produces a working interlace format output from the IPU.
> 
> ... on i.MX6 via HDMI.

It should also be correct for any other source which wants interlaced
output.

> > Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> > ---
> >  drivers/gpu/ipu-v3/ipu-dc.c | 18 ++++++++---
> >  drivers/gpu/ipu-v3/ipu-di.c | 79 +++++++++++++++++++++------------------------
> >  2 files changed, 51 insertions(+), 46 deletions(-)
> > 
> > diff --git a/drivers/gpu/ipu-v3/ipu-dc.c b/drivers/gpu/ipu-v3/ipu-dc.c
> > index 9ef2e1f54ca4..aa560855c1dc 100644
> > --- a/drivers/gpu/ipu-v3/ipu-dc.c
> > +++ b/drivers/gpu/ipu-v3/ipu-dc.c
> > @@ -183,12 +183,22 @@ int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced,
> >  	}
> >  
> >  	if (interlaced) {
> > -		dc_link_event(dc, DC_EVT_NL, 0, 3);
> > -		dc_link_event(dc, DC_EVT_EOL, 0, 2);
> > -		dc_link_event(dc, DC_EVT_NEW_DATA, 0, 1);
> > +		int word, addr;
> > +
> > +		if (dc->di) {
> > +			addr = 1;
> > +			word = 1;
> 
> These two are really one and the same. The address written to the link
> register for the given event has to point to the address of the
> microcode instruction written to the template memory that should be
> executed on this event.
> 
> I'd like to drop the word variable and use addr for both.

Ok.  As I said in the commit message, this code comes from Freescale's
patches which I pointed to above.

> > +		} else {
> > +			addr = 0;
> > +			word = 0;
> > +		}
> > +
> > +		dc_link_event(dc, DC_EVT_NL, addr, 3);
> > +		dc_link_event(dc, DC_EVT_EOL, addr, 2);
> > +		dc_link_event(dc, DC_EVT_NEW_DATA, addr, 1);
> >  
> >  		/* Init template microcode */
> > -		dc_write_tmpl(dc, 0, WROD(0), 0, map, SYNC_WAVE, 0, 8, 1);
> > +		dc_write_tmpl(dc, word, WROD(0), 0, map, SYNC_WAVE, 0, 6, 1);
> >  	} else {
> >  		if (dc->di) {
> >  			dc_link_event(dc, DC_EVT_NL, 2, 3);
> > diff --git a/drivers/gpu/ipu-v3/ipu-di.c b/drivers/gpu/ipu-v3/ipu-di.c
> > index a96991c5c15f..359268e3a166 100644
> > --- a/drivers/gpu/ipu-v3/ipu-di.c
> > +++ b/drivers/gpu/ipu-v3/ipu-di.c
> > @@ -71,6 +71,10 @@ enum di_sync_wave {
> >  	DI_SYNC_HSYNC = 3,
> >  	DI_SYNC_VSYNC = 4,
> >  	DI_SYNC_DE = 6,
> > +
> > +	DI_SYNC_CNT1 = 2,	/* counter >= 2 only */
> > +	DI_SYNC_CNT4 = 5,	/* counter >= 5 only */
> > +	DI_SYNC_CNT5 = 6,	/* counter >= 6 only */
> >  };
> >  
> >  #define SYNC_WAVE 0
> > @@ -211,66 +215,59 @@ static void ipu_di_sync_config_interlaced(struct ipu_di *di,
> >  		sig->mode.hback_porch + sig->mode.hfront_porch;
> >  	u32 v_total = sig->mode.vactive + sig->mode.vsync_len +
> >  		sig->mode.vback_porch + sig->mode.vfront_porch;
> > -	u32 reg;
> >  	struct di_sync_config cfg[] = {
> >  		{
> > -			.run_count = h_total / 2 - 1,
> > -			.run_src = DI_SYNC_CLK,
> > +			/* 1: internal VSYNC for each frame */
> > +			.run_count = v_total * 2 - 1,
> > +			.run_src = 3,			/* == counter 7 */
> 
> Do you know why this works? The Reference Manual v2 lists that value as
> "NA" in the DI counter #1 Run Resolution field.

Yes, I noticed that Freescale were using values for the source fields
which were marked as NA in the manual.  As it works, I can only assume
that the engineer who came up with this setup has more knowledge than
is public.

> >  		}, {
> > -			.run_count = h_total - 11,
> > +			/* PIN2: HSYNC waveform */
> > +			.run_count = h_total - 1,
> >  			.run_src = DI_SYNC_CLK,
> > -			.cnt_down = 4,
> > +			.cnt_polarity_gen_en = 1,
> > +			.cnt_polarity_trigger_src = DI_SYNC_CLK,
> > +			.cnt_down = sig->mode.hsync_len * 2,
> >  		}, {
> > -			.run_count = v_total * 2 - 1,
> > -			.run_src = DI_SYNC_INT_HSYNC,
> > -			.offset_count = 1,
> > -			.offset_src = DI_SYNC_INT_HSYNC,
> > -			.cnt_down = 4,
> > +			/* PIN3: VSYNC waveform */
> > +			.run_count = v_total - 1,
> > +			.run_src = 4,			/* == counter 7 */
> 
> Same here, ...
> 
> > +			.cnt_polarity_gen_en = 1,
> > +			.cnt_polarity_trigger_src = 4,	/* == counter 7 */
> 
> ... and same here, the DI counter #3 polarity Clear select field lists
> the value 4 as "Reserved".
> 
> > +			.cnt_down = sig->mode.vsync_len * 2,
> > +			.cnt_clr_src = DI_SYNC_CNT1,
> >  		}, {
> [...]
> >  		}
> >  	};
> >  
> >  	ipu_di_sync_config(di, cfg, 0, ARRAY_SIZE(cfg));
> >  
> > -	/* set gentime select and tag sel */
> > -	reg = ipu_di_read(di, DI_SW_GEN1(9));
> > -	reg &= 0x1FFFFFFF;
> > -	reg |= (3 - 1) << 29 | 0x00008000;
> > -	ipu_di_write(di, reg, DI_SW_GEN1(9));
> 
> As far as I understood, attaching counter #9 to counter #3 is needed to
> generate the second vsync on i.MX5. Since this doesn't currently work,
> I'm fine with removing it for now.

I went through the counter setup to understand how it works, and it
seems correct provided you accept that the various source values do
work as the code claims, which includes creating the vsync at the
appropriate half-scanline position without needing this hack.

It's not easy to work back from the counter setup to get a mental
picture of what's going on, but it is possible to do so.

-- 
FTTC broadband for 0.8mile line: currently at 10.5Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 04/12] gpu: imx: fix support for interlaced modes
@ 2015-08-27  8:54       ` Russell King - ARM Linux
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-08-27  8:54 UTC (permalink / raw)
  To: Philipp Zabel
  Cc: Fabio Estevam, linux-kernel, dri-devel, linux-rockchip,
	Yakir Yang, Andy Yan, linux-arm-kernel

On Thu, Aug 27, 2015 at 10:39:12AM +0200, Philipp Zabel wrote:
> Hi Russell,
> 
> Am Samstag, den 08.08.2015, 17:03 +0100 schrieb Russell King:
> > The support for interlaced video modes seems to be broken; we don't use
> > anything other than the vtotal/htotal from the timing information to
> > define the various sync counters.
> 
> I finally made time to test this series:
> 
> Tested-by: Philipp Zabel <p.zabel@pengutronix.de>
> on i.MX6 GK802 via HDMI connected to a TV (1080p60, 1080i60).
> 
> Unfortunately these timings are completely different from what Freescale
> came up with for the TV Encoder on i.MX5, but the code we have currently
> in mainline doesn't work for that either. I suppose I'll follow up with
> a patch that adds yet another sync counter setup for the i.MX5/TVE case.
> 
> I'd like to take the two ipu-v3 patches, making a few small changes on
> this one:

Please don't split the series up.  The reason it's a series is because
there's interdependencies between the patches.

> > Freescale patches for interlaced video support contain an alternative
> > sync counter setup, which we include here.  This setup produces the
> > hsync and vsync via the normal counter 2 and 3, but moves the display
> > enable signal from counter 5 to counter 6.  Therefore, we need to
> > change the display controller setup as well.
> > 
> > The corresponding Freescale patches for this change are:
> >   iMX6-HDMI-support-interlaced-display-mode.patch
> >   IPU-fine-tuning-the-interlace-display-timing-for-CEA.patch
> > 
> > This produces a working interlace format output from the IPU.
> 
> ... on i.MX6 via HDMI.

It should also be correct for any other source which wants interlaced
output.

> > Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> > ---
> >  drivers/gpu/ipu-v3/ipu-dc.c | 18 ++++++++---
> >  drivers/gpu/ipu-v3/ipu-di.c | 79 +++++++++++++++++++++------------------------
> >  2 files changed, 51 insertions(+), 46 deletions(-)
> > 
> > diff --git a/drivers/gpu/ipu-v3/ipu-dc.c b/drivers/gpu/ipu-v3/ipu-dc.c
> > index 9ef2e1f54ca4..aa560855c1dc 100644
> > --- a/drivers/gpu/ipu-v3/ipu-dc.c
> > +++ b/drivers/gpu/ipu-v3/ipu-dc.c
> > @@ -183,12 +183,22 @@ int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced,
> >  	}
> >  
> >  	if (interlaced) {
> > -		dc_link_event(dc, DC_EVT_NL, 0, 3);
> > -		dc_link_event(dc, DC_EVT_EOL, 0, 2);
> > -		dc_link_event(dc, DC_EVT_NEW_DATA, 0, 1);
> > +		int word, addr;
> > +
> > +		if (dc->di) {
> > +			addr = 1;
> > +			word = 1;
> 
> These two are really one and the same. The address written to the link
> register for the given event has to point to the address of the
> microcode instruction written to the template memory that should be
> executed on this event.
> 
> I'd like to drop the word variable and use addr for both.

Ok.  As I said in the commit message, this code comes from Freescale's
patches which I pointed to above.

> > +		} else {
> > +			addr = 0;
> > +			word = 0;
> > +		}
> > +
> > +		dc_link_event(dc, DC_EVT_NL, addr, 3);
> > +		dc_link_event(dc, DC_EVT_EOL, addr, 2);
> > +		dc_link_event(dc, DC_EVT_NEW_DATA, addr, 1);
> >  
> >  		/* Init template microcode */
> > -		dc_write_tmpl(dc, 0, WROD(0), 0, map, SYNC_WAVE, 0, 8, 1);
> > +		dc_write_tmpl(dc, word, WROD(0), 0, map, SYNC_WAVE, 0, 6, 1);
> >  	} else {
> >  		if (dc->di) {
> >  			dc_link_event(dc, DC_EVT_NL, 2, 3);
> > diff --git a/drivers/gpu/ipu-v3/ipu-di.c b/drivers/gpu/ipu-v3/ipu-di.c
> > index a96991c5c15f..359268e3a166 100644
> > --- a/drivers/gpu/ipu-v3/ipu-di.c
> > +++ b/drivers/gpu/ipu-v3/ipu-di.c
> > @@ -71,6 +71,10 @@ enum di_sync_wave {
> >  	DI_SYNC_HSYNC = 3,
> >  	DI_SYNC_VSYNC = 4,
> >  	DI_SYNC_DE = 6,
> > +
> > +	DI_SYNC_CNT1 = 2,	/* counter >= 2 only */
> > +	DI_SYNC_CNT4 = 5,	/* counter >= 5 only */
> > +	DI_SYNC_CNT5 = 6,	/* counter >= 6 only */
> >  };
> >  
> >  #define SYNC_WAVE 0
> > @@ -211,66 +215,59 @@ static void ipu_di_sync_config_interlaced(struct ipu_di *di,
> >  		sig->mode.hback_porch + sig->mode.hfront_porch;
> >  	u32 v_total = sig->mode.vactive + sig->mode.vsync_len +
> >  		sig->mode.vback_porch + sig->mode.vfront_porch;
> > -	u32 reg;
> >  	struct di_sync_config cfg[] = {
> >  		{
> > -			.run_count = h_total / 2 - 1,
> > -			.run_src = DI_SYNC_CLK,
> > +			/* 1: internal VSYNC for each frame */
> > +			.run_count = v_total * 2 - 1,
> > +			.run_src = 3,			/* == counter 7 */
> 
> Do you know why this works? The Reference Manual v2 lists that value as
> "NA" in the DI counter #1 Run Resolution field.

Yes, I noticed that Freescale were using values for the source fields
which were marked as NA in the manual.  As it works, I can only assume
that the engineer who came up with this setup has more knowledge than
is public.

> >  		}, {
> > -			.run_count = h_total - 11,
> > +			/* PIN2: HSYNC waveform */
> > +			.run_count = h_total - 1,
> >  			.run_src = DI_SYNC_CLK,
> > -			.cnt_down = 4,
> > +			.cnt_polarity_gen_en = 1,
> > +			.cnt_polarity_trigger_src = DI_SYNC_CLK,
> > +			.cnt_down = sig->mode.hsync_len * 2,
> >  		}, {
> > -			.run_count = v_total * 2 - 1,
> > -			.run_src = DI_SYNC_INT_HSYNC,
> > -			.offset_count = 1,
> > -			.offset_src = DI_SYNC_INT_HSYNC,
> > -			.cnt_down = 4,
> > +			/* PIN3: VSYNC waveform */
> > +			.run_count = v_total - 1,
> > +			.run_src = 4,			/* == counter 7 */
> 
> Same here, ...
> 
> > +			.cnt_polarity_gen_en = 1,
> > +			.cnt_polarity_trigger_src = 4,	/* == counter 7 */
> 
> ... and same here, the DI counter #3 polarity Clear select field lists
> the value 4 as "Reserved".
> 
> > +			.cnt_down = sig->mode.vsync_len * 2,
> > +			.cnt_clr_src = DI_SYNC_CNT1,
> >  		}, {
> [...]
> >  		}
> >  	};
> >  
> >  	ipu_di_sync_config(di, cfg, 0, ARRAY_SIZE(cfg));
> >  
> > -	/* set gentime select and tag sel */
> > -	reg = ipu_di_read(di, DI_SW_GEN1(9));
> > -	reg &= 0x1FFFFFFF;
> > -	reg |= (3 - 1) << 29 | 0x00008000;
> > -	ipu_di_write(di, reg, DI_SW_GEN1(9));
> 
> As far as I understood, attaching counter #9 to counter #3 is needed to
> generate the second vsync on i.MX5. Since this doesn't currently work,
> I'm fine with removing it for now.

I went through the counter setup to understand how it works, and it
seems correct provided you accept that the various source values do
work as the code claims, which includes creating the vsync at the
appropriate half-scanline position without needing this hack.

It's not easy to work back from the counter setup to get a mental
picture of what's going on, but it is possible to do so.

-- 
FTTC broadband for 0.8mile line: currently at 10.5Mbps down 400kbps up
according to speedtest.net.
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 04/12] gpu: imx: fix support for interlaced modes
@ 2015-08-27  8:54       ` Russell King - ARM Linux
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-08-27  8:54 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Aug 27, 2015 at 10:39:12AM +0200, Philipp Zabel wrote:
> Hi Russell,
> 
> Am Samstag, den 08.08.2015, 17:03 +0100 schrieb Russell King:
> > The support for interlaced video modes seems to be broken; we don't use
> > anything other than the vtotal/htotal from the timing information to
> > define the various sync counters.
> 
> I finally made time to test this series:
> 
> Tested-by: Philipp Zabel <p.zabel@pengutronix.de>
> on i.MX6 GK802 via HDMI connected to a TV (1080p60, 1080i60).
> 
> Unfortunately these timings are completely different from what Freescale
> came up with for the TV Encoder on i.MX5, but the code we have currently
> in mainline doesn't work for that either. I suppose I'll follow up with
> a patch that adds yet another sync counter setup for the i.MX5/TVE case.
> 
> I'd like to take the two ipu-v3 patches, making a few small changes on
> this one:

Please don't split the series up.  The reason it's a series is because
there's interdependencies between the patches.

> > Freescale patches for interlaced video support contain an alternative
> > sync counter setup, which we include here.  This setup produces the
> > hsync and vsync via the normal counter 2 and 3, but moves the display
> > enable signal from counter 5 to counter 6.  Therefore, we need to
> > change the display controller setup as well.
> > 
> > The corresponding Freescale patches for this change are:
> >   iMX6-HDMI-support-interlaced-display-mode.patch
> >   IPU-fine-tuning-the-interlace-display-timing-for-CEA.patch
> > 
> > This produces a working interlace format output from the IPU.
> 
> ... on i.MX6 via HDMI.

It should also be correct for any other source which wants interlaced
output.

> > Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> > ---
> >  drivers/gpu/ipu-v3/ipu-dc.c | 18 ++++++++---
> >  drivers/gpu/ipu-v3/ipu-di.c | 79 +++++++++++++++++++++------------------------
> >  2 files changed, 51 insertions(+), 46 deletions(-)
> > 
> > diff --git a/drivers/gpu/ipu-v3/ipu-dc.c b/drivers/gpu/ipu-v3/ipu-dc.c
> > index 9ef2e1f54ca4..aa560855c1dc 100644
> > --- a/drivers/gpu/ipu-v3/ipu-dc.c
> > +++ b/drivers/gpu/ipu-v3/ipu-dc.c
> > @@ -183,12 +183,22 @@ int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced,
> >  	}
> >  
> >  	if (interlaced) {
> > -		dc_link_event(dc, DC_EVT_NL, 0, 3);
> > -		dc_link_event(dc, DC_EVT_EOL, 0, 2);
> > -		dc_link_event(dc, DC_EVT_NEW_DATA, 0, 1);
> > +		int word, addr;
> > +
> > +		if (dc->di) {
> > +			addr = 1;
> > +			word = 1;
> 
> These two are really one and the same. The address written to the link
> register for the given event has to point to the address of the
> microcode instruction written to the template memory that should be
> executed on this event.
> 
> I'd like to drop the word variable and use addr for both.

Ok.  As I said in the commit message, this code comes from Freescale's
patches which I pointed to above.

> > +		} else {
> > +			addr = 0;
> > +			word = 0;
> > +		}
> > +
> > +		dc_link_event(dc, DC_EVT_NL, addr, 3);
> > +		dc_link_event(dc, DC_EVT_EOL, addr, 2);
> > +		dc_link_event(dc, DC_EVT_NEW_DATA, addr, 1);
> >  
> >  		/* Init template microcode */
> > -		dc_write_tmpl(dc, 0, WROD(0), 0, map, SYNC_WAVE, 0, 8, 1);
> > +		dc_write_tmpl(dc, word, WROD(0), 0, map, SYNC_WAVE, 0, 6, 1);
> >  	} else {
> >  		if (dc->di) {
> >  			dc_link_event(dc, DC_EVT_NL, 2, 3);
> > diff --git a/drivers/gpu/ipu-v3/ipu-di.c b/drivers/gpu/ipu-v3/ipu-di.c
> > index a96991c5c15f..359268e3a166 100644
> > --- a/drivers/gpu/ipu-v3/ipu-di.c
> > +++ b/drivers/gpu/ipu-v3/ipu-di.c
> > @@ -71,6 +71,10 @@ enum di_sync_wave {
> >  	DI_SYNC_HSYNC = 3,
> >  	DI_SYNC_VSYNC = 4,
> >  	DI_SYNC_DE = 6,
> > +
> > +	DI_SYNC_CNT1 = 2,	/* counter >= 2 only */
> > +	DI_SYNC_CNT4 = 5,	/* counter >= 5 only */
> > +	DI_SYNC_CNT5 = 6,	/* counter >= 6 only */
> >  };
> >  
> >  #define SYNC_WAVE 0
> > @@ -211,66 +215,59 @@ static void ipu_di_sync_config_interlaced(struct ipu_di *di,
> >  		sig->mode.hback_porch + sig->mode.hfront_porch;
> >  	u32 v_total = sig->mode.vactive + sig->mode.vsync_len +
> >  		sig->mode.vback_porch + sig->mode.vfront_porch;
> > -	u32 reg;
> >  	struct di_sync_config cfg[] = {
> >  		{
> > -			.run_count = h_total / 2 - 1,
> > -			.run_src = DI_SYNC_CLK,
> > +			/* 1: internal VSYNC for each frame */
> > +			.run_count = v_total * 2 - 1,
> > +			.run_src = 3,			/* == counter 7 */
> 
> Do you know why this works? The Reference Manual v2 lists that value as
> "NA" in the DI counter #1 Run Resolution field.

Yes, I noticed that Freescale were using values for the source fields
which were marked as NA in the manual.  As it works, I can only assume
that the engineer who came up with this setup has more knowledge than
is public.

> >  		}, {
> > -			.run_count = h_total - 11,
> > +			/* PIN2: HSYNC waveform */
> > +			.run_count = h_total - 1,
> >  			.run_src = DI_SYNC_CLK,
> > -			.cnt_down = 4,
> > +			.cnt_polarity_gen_en = 1,
> > +			.cnt_polarity_trigger_src = DI_SYNC_CLK,
> > +			.cnt_down = sig->mode.hsync_len * 2,
> >  		}, {
> > -			.run_count = v_total * 2 - 1,
> > -			.run_src = DI_SYNC_INT_HSYNC,
> > -			.offset_count = 1,
> > -			.offset_src = DI_SYNC_INT_HSYNC,
> > -			.cnt_down = 4,
> > +			/* PIN3: VSYNC waveform */
> > +			.run_count = v_total - 1,
> > +			.run_src = 4,			/* == counter 7 */
> 
> Same here, ...
> 
> > +			.cnt_polarity_gen_en = 1,
> > +			.cnt_polarity_trigger_src = 4,	/* == counter 7 */
> 
> ... and same here, the DI counter #3 polarity Clear select field lists
> the value 4 as "Reserved".
> 
> > +			.cnt_down = sig->mode.vsync_len * 2,
> > +			.cnt_clr_src = DI_SYNC_CNT1,
> >  		}, {
> [...]
> >  		}
> >  	};
> >  
> >  	ipu_di_sync_config(di, cfg, 0, ARRAY_SIZE(cfg));
> >  
> > -	/* set gentime select and tag sel */
> > -	reg = ipu_di_read(di, DI_SW_GEN1(9));
> > -	reg &= 0x1FFFFFFF;
> > -	reg |= (3 - 1) << 29 | 0x00008000;
> > -	ipu_di_write(di, reg, DI_SW_GEN1(9));
> 
> As far as I understood, attaching counter #9 to counter #3 is needed to
> generate the second vsync on i.MX5. Since this doesn't currently work,
> I'm fine with removing it for now.

I went through the counter setup to understand how it works, and it
seems correct provided you accept that the various source values do
work as the code claims, which includes creating the vsync at the
appropriate half-scanline position without needing this hack.

It's not easy to work back from the counter setup to get a mental
picture of what's going on, but it is possible to do so.

-- 
FTTC broadband for 0.8mile line: currently at 10.5Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 04/12] gpu: imx: fix support for interlaced modes
  2015-08-27  8:54       ` Russell King - ARM Linux
  (?)
@ 2015-08-27  9:40         ` Philipp Zabel
  -1 siblings, 0 replies; 226+ messages in thread
From: Philipp Zabel @ 2015-08-27  9:40 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: linux-rockchip, dri-devel, linux-kernel, linux-arm-kernel,
	Andy Yan, Yakir Yang, Fabio Estevam, Sascha Hauer, Jon Nettleton

Am Donnerstag, den 27.08.2015, 09:54 +0100 schrieb Russell King - ARM
Linux:
> On Thu, Aug 27, 2015 at 10:39:12AM +0200, Philipp Zabel wrote:
> > Hi Russell,
> > 
> > Am Samstag, den 08.08.2015, 17:03 +0100 schrieb Russell King:
> > > The support for interlaced video modes seems to be broken; we don't use
> > > anything other than the vtotal/htotal from the timing information to
> > > define the various sync counters.
> > 
> > I finally made time to test this series:
> > 
> > Tested-by: Philipp Zabel <p.zabel@pengutronix.de>
> > on i.MX6 GK802 via HDMI connected to a TV (1080p60, 1080i60).
> > 
> > Unfortunately these timings are completely different from what Freescale
> > came up with for the TV Encoder on i.MX5, but the code we have currently
> > in mainline doesn't work for that either. I suppose I'll follow up with
> > a patch that adds yet another sync counter setup for the i.MX5/TVE case.
> > 
> > I'd like to take the two ipu-v3 patches, making a few small changes on
> > this one:
> 
> Please don't split the series up.  The reason it's a series is because
> there's interdependencies between the patches.

In that case, can I take the whole series? Or would you like to respin
and have my Acked-by: Philipp Zabel <p.zabel@pengutronix.de> with the
changes below:

> > > Freescale patches for interlaced video support contain an alternative
> > > sync counter setup, which we include here.  This setup produces the
> > > hsync and vsync via the normal counter 2 and 3, but moves the display
> > > enable signal from counter 5 to counter 6.  Therefore, we need to
> > > change the display controller setup as well.
> > > 
> > > The corresponding Freescale patches for this change are:
> > >   iMX6-HDMI-support-interlaced-display-mode.patch
> > >   IPU-fine-tuning-the-interlace-display-timing-for-CEA.patch
> > > 
> > > This produces a working interlace format output from the IPU.
> > 
> > ... on i.MX6 via HDMI.
> 
> It should also be correct for any other source which wants interlaced
> output.

... on i.MX6, then. Right now I don't know what is the effect of the
undocumented settings on the i.MX5's IPUv3M.

> > > Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> > > ---
> > >  drivers/gpu/ipu-v3/ipu-dc.c | 18 ++++++++---
> > >  drivers/gpu/ipu-v3/ipu-di.c | 79 +++++++++++++++++++++------------------------
> > >  2 files changed, 51 insertions(+), 46 deletions(-)
> > > 
> > > diff --git a/drivers/gpu/ipu-v3/ipu-dc.c b/drivers/gpu/ipu-v3/ipu-dc.c
> > > index 9ef2e1f54ca4..aa560855c1dc 100644
> > > --- a/drivers/gpu/ipu-v3/ipu-dc.c
> > > +++ b/drivers/gpu/ipu-v3/ipu-dc.c
> > > @@ -183,12 +183,22 @@ int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced,
> > >  	}
> > >  
> > >  	if (interlaced) {
> > > -		dc_link_event(dc, DC_EVT_NL, 0, 3);
> > > -		dc_link_event(dc, DC_EVT_EOL, 0, 2);
> > > -		dc_link_event(dc, DC_EVT_NEW_DATA, 0, 1);
> > > +		int word, addr;
> > > +
> > > +		if (dc->di) {
> > > +			addr = 1;
> > > +			word = 1;
> > 
> > These two are really one and the same. The address written to the link
> > register for the given event has to point to the address of the
> > microcode instruction written to the template memory that should be
> > executed on this event.
> > 
> > I'd like to drop the word variable and use addr for both.
> 
> Ok.  As I said in the commit message, this code comes from Freescale's
> patches which I pointed to above.
[...]
> > > @@ -211,66 +215,59 @@ static void ipu_di_sync_config_interlaced(struct ipu_di *di,
> > >  		sig->mode.hback_porch + sig->mode.hfront_porch;
> > >  	u32 v_total = sig->mode.vactive + sig->mode.vsync_len +
> > >  		sig->mode.vback_porch + sig->mode.vfront_porch;
> > > -	u32 reg;
> > >  	struct di_sync_config cfg[] = {
> > >  		{
> > > -			.run_count = h_total / 2 - 1,
> > > -			.run_src = DI_SYNC_CLK,
> > > +			/* 1: internal VSYNC for each frame */
> > > +			.run_count = v_total * 2 - 1,
> > > +			.run_src = 3,			/* == counter 7 */
> > 
> > Do you know why this works? The Reference Manual v2 lists that value as
> > "NA" in the DI counter #1 Run Resolution field.
> 
> Yes, I noticed that Freescale were using values for the source fields
> which were marked as NA in the manual.  As it works, I can only assume
> that the engineer who came up with this setup has more knowledge than
> is public.

I'd like small a comment here that yes, we know this is marked as
NA/Reserved in the manuals.

[...]
> I went through the counter setup to understand how it works, and it
> seems correct provided you accept that the various source values do
> work as the code claims, which includes creating the vsync at the
> appropriate half-scanline position without needing this hack.
> 
> It's not easy to work back from the counter setup to get a mental
> picture of what's going on, but it is possible to do so.

Yes, being able to actually reference counters with higher numbers makes
things a lot easier to follow.

regards
Philipp


^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 04/12] gpu: imx: fix support for interlaced modes
@ 2015-08-27  9:40         ` Philipp Zabel
  0 siblings, 0 replies; 226+ messages in thread
From: Philipp Zabel @ 2015-08-27  9:40 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Fabio Estevam, linux-kernel, dri-devel, linux-rockchip,
	Yakir Yang, Andy Yan, linux-arm-kernel

Am Donnerstag, den 27.08.2015, 09:54 +0100 schrieb Russell King - ARM
Linux:
> On Thu, Aug 27, 2015 at 10:39:12AM +0200, Philipp Zabel wrote:
> > Hi Russell,
> > 
> > Am Samstag, den 08.08.2015, 17:03 +0100 schrieb Russell King:
> > > The support for interlaced video modes seems to be broken; we don't use
> > > anything other than the vtotal/htotal from the timing information to
> > > define the various sync counters.
> > 
> > I finally made time to test this series:
> > 
> > Tested-by: Philipp Zabel <p.zabel@pengutronix.de>
> > on i.MX6 GK802 via HDMI connected to a TV (1080p60, 1080i60).
> > 
> > Unfortunately these timings are completely different from what Freescale
> > came up with for the TV Encoder on i.MX5, but the code we have currently
> > in mainline doesn't work for that either. I suppose I'll follow up with
> > a patch that adds yet another sync counter setup for the i.MX5/TVE case.
> > 
> > I'd like to take the two ipu-v3 patches, making a few small changes on
> > this one:
> 
> Please don't split the series up.  The reason it's a series is because
> there's interdependencies between the patches.

In that case, can I take the whole series? Or would you like to respin
and have my Acked-by: Philipp Zabel <p.zabel@pengutronix.de> with the
changes below:

> > > Freescale patches for interlaced video support contain an alternative
> > > sync counter setup, which we include here.  This setup produces the
> > > hsync and vsync via the normal counter 2 and 3, but moves the display
> > > enable signal from counter 5 to counter 6.  Therefore, we need to
> > > change the display controller setup as well.
> > > 
> > > The corresponding Freescale patches for this change are:
> > >   iMX6-HDMI-support-interlaced-display-mode.patch
> > >   IPU-fine-tuning-the-interlace-display-timing-for-CEA.patch
> > > 
> > > This produces a working interlace format output from the IPU.
> > 
> > ... on i.MX6 via HDMI.
> 
> It should also be correct for any other source which wants interlaced
> output.

... on i.MX6, then. Right now I don't know what is the effect of the
undocumented settings on the i.MX5's IPUv3M.

> > > Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> > > ---
> > >  drivers/gpu/ipu-v3/ipu-dc.c | 18 ++++++++---
> > >  drivers/gpu/ipu-v3/ipu-di.c | 79 +++++++++++++++++++++------------------------
> > >  2 files changed, 51 insertions(+), 46 deletions(-)
> > > 
> > > diff --git a/drivers/gpu/ipu-v3/ipu-dc.c b/drivers/gpu/ipu-v3/ipu-dc.c
> > > index 9ef2e1f54ca4..aa560855c1dc 100644
> > > --- a/drivers/gpu/ipu-v3/ipu-dc.c
> > > +++ b/drivers/gpu/ipu-v3/ipu-dc.c
> > > @@ -183,12 +183,22 @@ int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced,
> > >  	}
> > >  
> > >  	if (interlaced) {
> > > -		dc_link_event(dc, DC_EVT_NL, 0, 3);
> > > -		dc_link_event(dc, DC_EVT_EOL, 0, 2);
> > > -		dc_link_event(dc, DC_EVT_NEW_DATA, 0, 1);
> > > +		int word, addr;
> > > +
> > > +		if (dc->di) {
> > > +			addr = 1;
> > > +			word = 1;
> > 
> > These two are really one and the same. The address written to the link
> > register for the given event has to point to the address of the
> > microcode instruction written to the template memory that should be
> > executed on this event.
> > 
> > I'd like to drop the word variable and use addr for both.
> 
> Ok.  As I said in the commit message, this code comes from Freescale's
> patches which I pointed to above.
[...]
> > > @@ -211,66 +215,59 @@ static void ipu_di_sync_config_interlaced(struct ipu_di *di,
> > >  		sig->mode.hback_porch + sig->mode.hfront_porch;
> > >  	u32 v_total = sig->mode.vactive + sig->mode.vsync_len +
> > >  		sig->mode.vback_porch + sig->mode.vfront_porch;
> > > -	u32 reg;
> > >  	struct di_sync_config cfg[] = {
> > >  		{
> > > -			.run_count = h_total / 2 - 1,
> > > -			.run_src = DI_SYNC_CLK,
> > > +			/* 1: internal VSYNC for each frame */
> > > +			.run_count = v_total * 2 - 1,
> > > +			.run_src = 3,			/* == counter 7 */
> > 
> > Do you know why this works? The Reference Manual v2 lists that value as
> > "NA" in the DI counter #1 Run Resolution field.
> 
> Yes, I noticed that Freescale were using values for the source fields
> which were marked as NA in the manual.  As it works, I can only assume
> that the engineer who came up with this setup has more knowledge than
> is public.

I'd like small a comment here that yes, we know this is marked as
NA/Reserved in the manuals.

[...]
> I went through the counter setup to understand how it works, and it
> seems correct provided you accept that the various source values do
> work as the code claims, which includes creating the vsync at the
> appropriate half-scanline position without needing this hack.
> 
> It's not easy to work back from the counter setup to get a mental
> picture of what's going on, but it is possible to do so.

Yes, being able to actually reference counters with higher numbers makes
things a lot easier to follow.

regards
Philipp

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 04/12] gpu: imx: fix support for interlaced modes
@ 2015-08-27  9:40         ` Philipp Zabel
  0 siblings, 0 replies; 226+ messages in thread
From: Philipp Zabel @ 2015-08-27  9:40 UTC (permalink / raw)
  To: linux-arm-kernel

Am Donnerstag, den 27.08.2015, 09:54 +0100 schrieb Russell King - ARM
Linux:
> On Thu, Aug 27, 2015 at 10:39:12AM +0200, Philipp Zabel wrote:
> > Hi Russell,
> > 
> > Am Samstag, den 08.08.2015, 17:03 +0100 schrieb Russell King:
> > > The support for interlaced video modes seems to be broken; we don't use
> > > anything other than the vtotal/htotal from the timing information to
> > > define the various sync counters.
> > 
> > I finally made time to test this series:
> > 
> > Tested-by: Philipp Zabel <p.zabel@pengutronix.de>
> > on i.MX6 GK802 via HDMI connected to a TV (1080p60, 1080i60).
> > 
> > Unfortunately these timings are completely different from what Freescale
> > came up with for the TV Encoder on i.MX5, but the code we have currently
> > in mainline doesn't work for that either. I suppose I'll follow up with
> > a patch that adds yet another sync counter setup for the i.MX5/TVE case.
> > 
> > I'd like to take the two ipu-v3 patches, making a few small changes on
> > this one:
> 
> Please don't split the series up.  The reason it's a series is because
> there's interdependencies between the patches.

In that case, can I take the whole series? Or would you like to respin
and have my Acked-by: Philipp Zabel <p.zabel@pengutronix.de> with the
changes below:

> > > Freescale patches for interlaced video support contain an alternative
> > > sync counter setup, which we include here.  This setup produces the
> > > hsync and vsync via the normal counter 2 and 3, but moves the display
> > > enable signal from counter 5 to counter 6.  Therefore, we need to
> > > change the display controller setup as well.
> > > 
> > > The corresponding Freescale patches for this change are:
> > >   iMX6-HDMI-support-interlaced-display-mode.patch
> > >   IPU-fine-tuning-the-interlace-display-timing-for-CEA.patch
> > > 
> > > This produces a working interlace format output from the IPU.
> > 
> > ... on i.MX6 via HDMI.
> 
> It should also be correct for any other source which wants interlaced
> output.

... on i.MX6, then. Right now I don't know what is the effect of the
undocumented settings on the i.MX5's IPUv3M.

> > > Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> > > ---
> > >  drivers/gpu/ipu-v3/ipu-dc.c | 18 ++++++++---
> > >  drivers/gpu/ipu-v3/ipu-di.c | 79 +++++++++++++++++++++------------------------
> > >  2 files changed, 51 insertions(+), 46 deletions(-)
> > > 
> > > diff --git a/drivers/gpu/ipu-v3/ipu-dc.c b/drivers/gpu/ipu-v3/ipu-dc.c
> > > index 9ef2e1f54ca4..aa560855c1dc 100644
> > > --- a/drivers/gpu/ipu-v3/ipu-dc.c
> > > +++ b/drivers/gpu/ipu-v3/ipu-dc.c
> > > @@ -183,12 +183,22 @@ int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced,
> > >  	}
> > >  
> > >  	if (interlaced) {
> > > -		dc_link_event(dc, DC_EVT_NL, 0, 3);
> > > -		dc_link_event(dc, DC_EVT_EOL, 0, 2);
> > > -		dc_link_event(dc, DC_EVT_NEW_DATA, 0, 1);
> > > +		int word, addr;
> > > +
> > > +		if (dc->di) {
> > > +			addr = 1;
> > > +			word = 1;
> > 
> > These two are really one and the same. The address written to the link
> > register for the given event has to point to the address of the
> > microcode instruction written to the template memory that should be
> > executed on this event.
> > 
> > I'd like to drop the word variable and use addr for both.
> 
> Ok.  As I said in the commit message, this code comes from Freescale's
> patches which I pointed to above.
[...]
> > > @@ -211,66 +215,59 @@ static void ipu_di_sync_config_interlaced(struct ipu_di *di,
> > >  		sig->mode.hback_porch + sig->mode.hfront_porch;
> > >  	u32 v_total = sig->mode.vactive + sig->mode.vsync_len +
> > >  		sig->mode.vback_porch + sig->mode.vfront_porch;
> > > -	u32 reg;
> > >  	struct di_sync_config cfg[] = {
> > >  		{
> > > -			.run_count = h_total / 2 - 1,
> > > -			.run_src = DI_SYNC_CLK,
> > > +			/* 1: internal VSYNC for each frame */
> > > +			.run_count = v_total * 2 - 1,
> > > +			.run_src = 3,			/* == counter 7 */
> > 
> > Do you know why this works? The Reference Manual v2 lists that value as
> > "NA" in the DI counter #1 Run Resolution field.
> 
> Yes, I noticed that Freescale were using values for the source fields
> which were marked as NA in the manual.  As it works, I can only assume
> that the engineer who came up with this setup has more knowledge than
> is public.

I'd like small a comment here that yes, we know this is marked as
NA/Reserved in the manuals.

[...]
> I went through the counter setup to understand how it works, and it
> seems correct provided you accept that the various source values do
> work as the code claims, which includes creating the vsync at the
> appropriate half-scanline position without needing this hack.
> 
> It's not easy to work back from the counter setup to get a mental
> picture of what's going on, but it is possible to do so.

Yes, being able to actually reference counters with higher numbers makes
things a lot easier to follow.

regards
Philipp

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 5/9] drm: bridge/dw_hdmi: avoid being recursive in N calculation
  2015-08-08 16:10     ` Russell King
  (?)
@ 2015-09-04 17:50       ` Doug Anderson
  -1 siblings, 0 replies; 226+ messages in thread
From: Doug Anderson @ 2015-09-04 17:50 UTC (permalink / raw)
  To: Russell King
  Cc: open list:ARM/Rockchip SoC...,
	alsa-devel, dri-devel, linux-kernel, linux-arm-kernel,
	Philipp Zabel, Andy Yan, Yakir Yang, Fabio Estevam, Mark Brown,
	Takashi Iwai, Jaroslav Kysela, Sascha Hauer, Jon Nettleton,
	David Airlie

Russell,

On Sat, Aug 8, 2015 at 9:10 AM, Russell King
<rmk+kernel@arm.linux.org.uk> wrote:
> There's no need to be recursive when computing the N value for the ACR
> packet - we can instead calculate the multiplier prior to our switch()
> based lookup, and multiply the N value appropriately afterwards.
>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> ---
>  drivers/gpu/drm/bridge/dw_hdmi.c | 25 +++++++++----------------
>  1 file changed, 9 insertions(+), 16 deletions(-)

For what it's worth, I backported this change into my local 3.14-based
tree and it worked for me.  It also looks good to me.

Reviewed-by: Douglas Anderson <dianders@chromium.org>
Tested-by: Douglas Anderson <dianders@chromium.org>

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 5/9] drm: bridge/dw_hdmi: avoid being recursive in N calculation
@ 2015-09-04 17:50       ` Doug Anderson
  0 siblings, 0 replies; 226+ messages in thread
From: Doug Anderson @ 2015-09-04 17:50 UTC (permalink / raw)
  To: Russell King
  Cc: open list:ARM/Rockchip SoC...,
	alsa-devel, dri-devel, linux-kernel, linux-arm-kernel,
	Philipp Zabel, Andy Yan, Yakir Yang, Fabio Estevam, Mark Brown,
	Takashi Iwai, Jaroslav Kysela, Sascha Hauer, Jon Nettleton,
	David Airlie

Russell,

On Sat, Aug 8, 2015 at 9:10 AM, Russell King
<rmk+kernel@arm.linux.org.uk> wrote:
> There's no need to be recursive when computing the N value for the ACR
> packet - we can instead calculate the multiplier prior to our switch()
> based lookup, and multiply the N value appropriately afterwards.
>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> ---
>  drivers/gpu/drm/bridge/dw_hdmi.c | 25 +++++++++----------------
>  1 file changed, 9 insertions(+), 16 deletions(-)

For what it's worth, I backported this change into my local 3.14-based
tree and it worked for me.  It also looks good to me.

Reviewed-by: Douglas Anderson <dianders@chromium.org>
Tested-by: Douglas Anderson <dianders@chromium.org>

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 5/9] drm: bridge/dw_hdmi: avoid being recursive in N calculation
@ 2015-09-04 17:50       ` Doug Anderson
  0 siblings, 0 replies; 226+ messages in thread
From: Doug Anderson @ 2015-09-04 17:50 UTC (permalink / raw)
  To: linux-arm-kernel

Russell,

On Sat, Aug 8, 2015 at 9:10 AM, Russell King
<rmk+kernel@arm.linux.org.uk> wrote:
> There's no need to be recursive when computing the N value for the ACR
> packet - we can instead calculate the multiplier prior to our switch()
> based lookup, and multiply the N value appropriately afterwards.
>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> ---
>  drivers/gpu/drm/bridge/dw_hdmi.c | 25 +++++++++----------------
>  1 file changed, 9 insertions(+), 16 deletions(-)

For what it's worth, I backported this change into my local 3.14-based
tree and it worked for me.  It also looks good to me.

Reviewed-by: Douglas Anderson <dianders@chromium.org>
Tested-by: Douglas Anderson <dianders@chromium.org>

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 6/9] drm: bridge/dw_hdmi: adjust pixel clock values in N calculation
  2015-08-08 16:10     ` Russell King
  (?)
@ 2015-09-04 18:21       ` Doug Anderson
  -1 siblings, 0 replies; 226+ messages in thread
From: Doug Anderson @ 2015-09-04 18:21 UTC (permalink / raw)
  To: Russell King
  Cc: open list:ARM/Rockchip SoC...,
	alsa-devel, dri-devel, linux-kernel, linux-arm-kernel,
	Philipp Zabel, Andy Yan, Yakir Yang, Fabio Estevam, Mark Brown,
	Takashi Iwai, Jaroslav Kysela, Sascha Hauer, Jon Nettleton,
	David Airlie

Russell,

On Sat, Aug 8, 2015 at 9:10 AM, Russell King
<rmk+kernel@arm.linux.org.uk> wrote:
> Adjust the pixel clock values in the N calculation to match the more
> accurate clock values we're given by the DRM subsystem, which are the
> kHz pixel rate, with any fractional kHz rounded down in the case of
> the non-240, non-480 line modes, or rounded up for the others.  So,
>
>          25.20 / 1.001 =>  25175
>          27.00 * 1.001 =>  27027
>          74.25 / 1.001 =>  74176
>         148.50 / 1.001 => 148352
>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> ---
>  drivers/gpu/drm/bridge/dw_hdmi.c | 20 ++++++++++----------
>  1 file changed, 10 insertions(+), 10 deletions(-)

For what it's worth, I backported this change into my local 3.14-based
tree and it doesn't cause any problems, though it looks like the code
isn't being run in my case...

I can confirm that the rates you list match the rates I actually see
requested by DRM, but in my current tree I've actually got something
that allows a little bit of "slop" in HDMI rates because my system
can't actually always make exactly the modes requested, but it appears
that getting "close enough" works, especially if your clock jitter is
low enough (because the sink needs to have a little bit of wiggle room
for jitter anyway).  For instance, when 25.175 is requested we
actually end up making 25.170732.

In my tree this adjustment happens in mode_fixup by changing the
adj_mode.  In one particular case, some debug prints show:
  640x480, mode=25200000, adj=25171000, actual=25170732
  freq=48000, pixel_clk=25171000, n=6144

I'm not enough of an HDMI expert to say whether it's better to be
using n=6144 or n=6864 in this case, but audio does play with either
on the TV I tested.

In any case, I'd say that your change at least makes things better
than they were, so I'd be in favor of taking it.  If someone later
decides that we should add a little margin to these numbers, then a
patch to add that could go atop yours.

Reviewed-by: Douglas Anderson <dianders@chromium.org>

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 6/9] drm: bridge/dw_hdmi: adjust pixel clock values in N calculation
@ 2015-09-04 18:21       ` Doug Anderson
  0 siblings, 0 replies; 226+ messages in thread
From: Doug Anderson @ 2015-09-04 18:21 UTC (permalink / raw)
  To: Russell King
  Cc: Fabio Estevam, alsa-devel, linux-kernel, dri-devel,
	Jaroslav Kysela, open list:ARM/Rockchip SoC...,
	Mark Brown, Yakir Yang, Andy Yan, linux-arm-kernel

Russell,

On Sat, Aug 8, 2015 at 9:10 AM, Russell King
<rmk+kernel@arm.linux.org.uk> wrote:
> Adjust the pixel clock values in the N calculation to match the more
> accurate clock values we're given by the DRM subsystem, which are the
> kHz pixel rate, with any fractional kHz rounded down in the case of
> the non-240, non-480 line modes, or rounded up for the others.  So,
>
>          25.20 / 1.001 =>  25175
>          27.00 * 1.001 =>  27027
>          74.25 / 1.001 =>  74176
>         148.50 / 1.001 => 148352
>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> ---
>  drivers/gpu/drm/bridge/dw_hdmi.c | 20 ++++++++++----------
>  1 file changed, 10 insertions(+), 10 deletions(-)

For what it's worth, I backported this change into my local 3.14-based
tree and it doesn't cause any problems, though it looks like the code
isn't being run in my case...

I can confirm that the rates you list match the rates I actually see
requested by DRM, but in my current tree I've actually got something
that allows a little bit of "slop" in HDMI rates because my system
can't actually always make exactly the modes requested, but it appears
that getting "close enough" works, especially if your clock jitter is
low enough (because the sink needs to have a little bit of wiggle room
for jitter anyway).  For instance, when 25.175 is requested we
actually end up making 25.170732.

In my tree this adjustment happens in mode_fixup by changing the
adj_mode.  In one particular case, some debug prints show:
  640x480, mode=25200000, adj=25171000, actual=25170732
  freq=48000, pixel_clk=25171000, n=6144

I'm not enough of an HDMI expert to say whether it's better to be
using n=6144 or n=6864 in this case, but audio does play with either
on the TV I tested.

In any case, I'd say that your change at least makes things better
than they were, so I'd be in favor of taking it.  If someone later
decides that we should add a little margin to these numbers, then a
patch to add that could go atop yours.

Reviewed-by: Douglas Anderson <dianders@chromium.org>
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 6/9] drm: bridge/dw_hdmi: adjust pixel clock values in N calculation
@ 2015-09-04 18:21       ` Doug Anderson
  0 siblings, 0 replies; 226+ messages in thread
From: Doug Anderson @ 2015-09-04 18:21 UTC (permalink / raw)
  To: linux-arm-kernel

Russell,

On Sat, Aug 8, 2015 at 9:10 AM, Russell King
<rmk+kernel@arm.linux.org.uk> wrote:
> Adjust the pixel clock values in the N calculation to match the more
> accurate clock values we're given by the DRM subsystem, which are the
> kHz pixel rate, with any fractional kHz rounded down in the case of
> the non-240, non-480 line modes, or rounded up for the others.  So,
>
>          25.20 / 1.001 =>  25175
>          27.00 * 1.001 =>  27027
>          74.25 / 1.001 =>  74176
>         148.50 / 1.001 => 148352
>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> ---
>  drivers/gpu/drm/bridge/dw_hdmi.c | 20 ++++++++++----------
>  1 file changed, 10 insertions(+), 10 deletions(-)

For what it's worth, I backported this change into my local 3.14-based
tree and it doesn't cause any problems, though it looks like the code
isn't being run in my case...

I can confirm that the rates you list match the rates I actually see
requested by DRM, but in my current tree I've actually got something
that allows a little bit of "slop" in HDMI rates because my system
can't actually always make exactly the modes requested, but it appears
that getting "close enough" works, especially if your clock jitter is
low enough (because the sink needs to have a little bit of wiggle room
for jitter anyway).  For instance, when 25.175 is requested we
actually end up making 25.170732.

In my tree this adjustment happens in mode_fixup by changing the
adj_mode.  In one particular case, some debug prints show:
  640x480, mode=25200000, adj=25171000, actual=25170732
  freq=48000, pixel_clk=25171000, n=6144

I'm not enough of an HDMI expert to say whether it's better to be
using n=6144 or n=6864 in this case, but audio does play with either
on the TV I tested.

In any case, I'd say that your change at least makes things better
than they were, so I'd be in favor of taking it.  If someone later
decides that we should add a little margin to these numbers, then a
patch to add that could go atop yours.

Reviewed-by: Douglas Anderson <dianders@chromium.org>

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 7/9] drm: bridge/dw_hdmi: remove ratio support from ACR code
  2015-08-08 16:10     ` Russell King
  (?)
@ 2015-09-04 18:24       ` Doug Anderson
  -1 siblings, 0 replies; 226+ messages in thread
From: Doug Anderson @ 2015-09-04 18:24 UTC (permalink / raw)
  To: Russell King
  Cc: open list:ARM/Rockchip SoC...,
	alsa-devel, dri-devel, linux-kernel, linux-arm-kernel,
	Philipp Zabel, Andy Yan, Yakir Yang, Fabio Estevam, Mark Brown,
	Takashi Iwai, Jaroslav Kysela, Sascha Hauer, Jon Nettleton,
	David Airlie

Russell,

On Sat, Aug 8, 2015 at 9:10 AM, Russell King
<rmk+kernel@arm.linux.org.uk> wrote:
> We never set the ratio for CTS/N calculation for the audio clock
> regenerator (ACR) to anything but 100, so this adds pointless
> complexity.  Should we support pixel repetition, we should update the
> CTS/N calculation code to use those parameters or the actual TMDS clock
> rate instead of a ratio.
>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> ---
>  drivers/gpu/drm/bridge/dw_hdmi.c | 44 ++++++++++++++++------------------------
>  1 file changed, 18 insertions(+), 26 deletions(-)

It looks like I've got a slightly older version of the driver in my
tree (based on some earlier mailing list postings), but backporting
this was pretty trivial and I've tried it in my 3.14 kernel.  It
doesn't cause any problems for me to remove the "radio" and I agree
that we should add it if/when pixel repetition is added.

Reviewed-by: Douglas Anderson <dianders@chromium.org>
Tested-by: Douglas Anderson <dianders@chromium.org>

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 7/9] drm: bridge/dw_hdmi: remove ratio support from ACR code
@ 2015-09-04 18:24       ` Doug Anderson
  0 siblings, 0 replies; 226+ messages in thread
From: Doug Anderson @ 2015-09-04 18:24 UTC (permalink / raw)
  To: Russell King
  Cc: Fabio Estevam, alsa-devel, linux-kernel, dri-devel,
	Jaroslav Kysela, open list:ARM/Rockchip SoC...,
	Mark Brown, Yakir Yang, Andy Yan, linux-arm-kernel

Russell,

On Sat, Aug 8, 2015 at 9:10 AM, Russell King
<rmk+kernel@arm.linux.org.uk> wrote:
> We never set the ratio for CTS/N calculation for the audio clock
> regenerator (ACR) to anything but 100, so this adds pointless
> complexity.  Should we support pixel repetition, we should update the
> CTS/N calculation code to use those parameters or the actual TMDS clock
> rate instead of a ratio.
>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> ---
>  drivers/gpu/drm/bridge/dw_hdmi.c | 44 ++++++++++++++++------------------------
>  1 file changed, 18 insertions(+), 26 deletions(-)

It looks like I've got a slightly older version of the driver in my
tree (based on some earlier mailing list postings), but backporting
this was pretty trivial and I've tried it in my 3.14 kernel.  It
doesn't cause any problems for me to remove the "radio" and I agree
that we should add it if/when pixel repetition is added.

Reviewed-by: Douglas Anderson <dianders@chromium.org>
Tested-by: Douglas Anderson <dianders@chromium.org>
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 7/9] drm: bridge/dw_hdmi: remove ratio support from ACR code
@ 2015-09-04 18:24       ` Doug Anderson
  0 siblings, 0 replies; 226+ messages in thread
From: Doug Anderson @ 2015-09-04 18:24 UTC (permalink / raw)
  To: linux-arm-kernel

Russell,

On Sat, Aug 8, 2015 at 9:10 AM, Russell King
<rmk+kernel@arm.linux.org.uk> wrote:
> We never set the ratio for CTS/N calculation for the audio clock
> regenerator (ACR) to anything but 100, so this adds pointless
> complexity.  Should we support pixel repetition, we should update the
> CTS/N calculation code to use those parameters or the actual TMDS clock
> rate instead of a ratio.
>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> ---
>  drivers/gpu/drm/bridge/dw_hdmi.c | 44 ++++++++++++++++------------------------
>  1 file changed, 18 insertions(+), 26 deletions(-)

It looks like I've got a slightly older version of the driver in my
tree (based on some earlier mailing list postings), but backporting
this was pretty trivial and I've tried it in my 3.14 kernel.  It
doesn't cause any problems for me to remove the "radio" and I agree
that we should add it if/when pixel repetition is added.

Reviewed-by: Douglas Anderson <dianders@chromium.org>
Tested-by: Douglas Anderson <dianders@chromium.org>

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 6/9] drm: bridge/dw_hdmi: adjust pixel clock values in N calculation
  2015-09-04 18:21       ` Doug Anderson
  (?)
@ 2015-09-04 19:48         ` Doug Anderson
  -1 siblings, 0 replies; 226+ messages in thread
From: Doug Anderson @ 2015-09-04 19:48 UTC (permalink / raw)
  To: Russell King
  Cc: open list:ARM/Rockchip SoC...,
	alsa-devel, dri-devel, linux-kernel, linux-arm-kernel,
	Philipp Zabel, Andy Yan, Yakir Yang, Fabio Estevam, Mark Brown,
	Takashi Iwai, Jaroslav Kysela, Sascha Hauer, Jon Nettleton,
	David Airlie

Hi,

On Fri, Sep 4, 2015 at 11:21 AM, Doug Anderson <dianders@chromium.org> wrote:
> Russell,
>
> On Sat, Aug 8, 2015 at 9:10 AM, Russell King
> <rmk+kernel@arm.linux.org.uk> wrote:
>> Adjust the pixel clock values in the N calculation to match the more
>> accurate clock values we're given by the DRM subsystem, which are the
>> kHz pixel rate, with any fractional kHz rounded down in the case of
>> the non-240, non-480 line modes, or rounded up for the others.  So,
>>
>>          25.20 / 1.001 =>  25175
>>          27.00 * 1.001 =>  27027
>>          74.25 / 1.001 =>  74176
>>         148.50 / 1.001 => 148352
>>
>> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
>> ---
>>  drivers/gpu/drm/bridge/dw_hdmi.c | 20 ++++++++++----------
>>  1 file changed, 10 insertions(+), 10 deletions(-)
>
> For what it's worth, I backported this change into my local 3.14-based
> tree and it doesn't cause any problems, though it looks like the code
> isn't being run in my case...
>
> I can confirm that the rates you list match the rates I actually see
> requested by DRM, but in my current tree I've actually got something
> that allows a little bit of "slop" in HDMI rates because my system
> can't actually always make exactly the modes requested, but it appears
> that getting "close enough" works, especially if your clock jitter is
> low enough (because the sink needs to have a little bit of wiggle room
> for jitter anyway).  For instance, when 25.175 is requested we
> actually end up making 25.170732.
>
> In my tree this adjustment happens in mode_fixup by changing the
> adj_mode.  In one particular case, some debug prints show:
>   640x480, mode=25200000, adj=25171000, actual=25170732
>   freq=48000, pixel_clk=25171000, n=6144
>
> I'm not enough of an HDMI expert to say whether it's better to be
> using n=6144 or n=6864 in this case, but audio does play with either
> on the TV I tested.
>
> In any case, I'd say that your change at least makes things better
> than they were, so I'd be in favor of taking it.  If someone later
> decides that we should add a little margin to these numbers, then a
> patch to add that could go atop yours.

Oh!  I just figured this out!  :)

Basically the spec is saying that you want both N and CTS to be
integral.  ...as you say you really want:
  CTS = (TMDS * N) / (128 * audio_freq)

...CTS has no other restrictions (other than being integral) and
you're allowed a bit of slop for N (you aim for 128 * audio_freq but
can go up or down a bit).
...and the TMDS frequency has no such restrictions for being integral
in their calculations.

Apparently it's more important to optimize for the CTS formula working
out then it is for getting close to "128 * audio freq".  ...and that's
the reason for these special case N values...


So to put some numbers:

We're perfect when we have exactly 25.2:
  25200 * 4096 / (128 * 32)
  => 25200, so CTS for 25.2 MHz is 25200.  Perfect

...but when we have 25.2 / 1.001 we get a non-integral CTS:
  (25200 / 1.001) * 4096 / (128 * 32)
  => 25174.82517482518

...we can get an integral CTS and still remain in range if:
  (25200 / 1.001) * 4576 / (128 * 32)
  => 28125

In the case of Linux, I'm afraid we just don't have this type of
accuracy in our APIs.  The spec is talking about making
25.17482517482518 MHz.  As I said, in my case I'm actually making
25170732.  In your case you're probably making the value that Linux
asked you to make, AKA 25.175000 MHz.  Unsurprisingly, if you do the
calculations with 25.175 MHz (or any integral kHz value) you don't
have to do any special optimization to stay integral:

  25175 * 4096 / (128 * 32)
  => 25175


So unless you have some way to know that the underlying clock is
actually (25.2 / 1.001) MHz and not just 25.175 MHz then your patch
looks wrong.


As a first step I'd suggest just removing all the special cases and
add a comment.  From real world testing it doesn't seem terribly
critical to be slightly off on CTS.  ...and in any case for any clock
rates except the small handful in the HDMI spec we'll be slightly off
on CTS anyway...

As a second step you could actually use the rate from "clk_get_rate()"
to see what clock rate was actually made.  You'll at least get Hz
here.  If you've somehow structured your machine to give you 25174825
Hz when DRM asked for 25175000 Hz (or if you redo the calculations and
ignore what DRM told you), then that would give you this slightly more
optimal rate.

As a third step you could somehow add the more detailed Hz information
to DRM (sounds like a big task, but I'm nowhere near a DRM expert).

As a fourth step you could try to write the code in a generic way to
figure out the best N / CTS to minimize error in the formula while
still staying within the required ranges.  If you did that, it
probably would belong in some generic helper and not in dw_hdmi...


...anyway, I'm not suggestion that you do everything above since I
think just removing the special cases is probably good enough.  ...but
if you wanted everything to be perfect it seems like the way to go.



So I guess remove my Reviewed-by for this patch?


-Doug

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 6/9] drm: bridge/dw_hdmi: adjust pixel clock values in N calculation
@ 2015-09-04 19:48         ` Doug Anderson
  0 siblings, 0 replies; 226+ messages in thread
From: Doug Anderson @ 2015-09-04 19:48 UTC (permalink / raw)
  To: Russell King
  Cc: Fabio Estevam, alsa-devel, linux-kernel, dri-devel,
	Jaroslav Kysela, open list:ARM/Rockchip SoC...,
	Mark Brown, Yakir Yang, Andy Yan, linux-arm-kernel

Hi,

On Fri, Sep 4, 2015 at 11:21 AM, Doug Anderson <dianders@chromium.org> wrote:
> Russell,
>
> On Sat, Aug 8, 2015 at 9:10 AM, Russell King
> <rmk+kernel@arm.linux.org.uk> wrote:
>> Adjust the pixel clock values in the N calculation to match the more
>> accurate clock values we're given by the DRM subsystem, which are the
>> kHz pixel rate, with any fractional kHz rounded down in the case of
>> the non-240, non-480 line modes, or rounded up for the others.  So,
>>
>>          25.20 / 1.001 =>  25175
>>          27.00 * 1.001 =>  27027
>>          74.25 / 1.001 =>  74176
>>         148.50 / 1.001 => 148352
>>
>> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
>> ---
>>  drivers/gpu/drm/bridge/dw_hdmi.c | 20 ++++++++++----------
>>  1 file changed, 10 insertions(+), 10 deletions(-)
>
> For what it's worth, I backported this change into my local 3.14-based
> tree and it doesn't cause any problems, though it looks like the code
> isn't being run in my case...
>
> I can confirm that the rates you list match the rates I actually see
> requested by DRM, but in my current tree I've actually got something
> that allows a little bit of "slop" in HDMI rates because my system
> can't actually always make exactly the modes requested, but it appears
> that getting "close enough" works, especially if your clock jitter is
> low enough (because the sink needs to have a little bit of wiggle room
> for jitter anyway).  For instance, when 25.175 is requested we
> actually end up making 25.170732.
>
> In my tree this adjustment happens in mode_fixup by changing the
> adj_mode.  In one particular case, some debug prints show:
>   640x480, mode=25200000, adj=25171000, actual=25170732
>   freq=48000, pixel_clk=25171000, n=6144
>
> I'm not enough of an HDMI expert to say whether it's better to be
> using n=6144 or n=6864 in this case, but audio does play with either
> on the TV I tested.
>
> In any case, I'd say that your change at least makes things better
> than they were, so I'd be in favor of taking it.  If someone later
> decides that we should add a little margin to these numbers, then a
> patch to add that could go atop yours.

Oh!  I just figured this out!  :)

Basically the spec is saying that you want both N and CTS to be
integral.  ...as you say you really want:
  CTS = (TMDS * N) / (128 * audio_freq)

...CTS has no other restrictions (other than being integral) and
you're allowed a bit of slop for N (you aim for 128 * audio_freq but
can go up or down a bit).
...and the TMDS frequency has no such restrictions for being integral
in their calculations.

Apparently it's more important to optimize for the CTS formula working
out then it is for getting close to "128 * audio freq".  ...and that's
the reason for these special case N values...


So to put some numbers:

We're perfect when we have exactly 25.2:
  25200 * 4096 / (128 * 32)
  => 25200, so CTS for 25.2 MHz is 25200.  Perfect

...but when we have 25.2 / 1.001 we get a non-integral CTS:
  (25200 / 1.001) * 4096 / (128 * 32)
  => 25174.82517482518

...we can get an integral CTS and still remain in range if:
  (25200 / 1.001) * 4576 / (128 * 32)
  => 28125

In the case of Linux, I'm afraid we just don't have this type of
accuracy in our APIs.  The spec is talking about making
25.17482517482518 MHz.  As I said, in my case I'm actually making
25170732.  In your case you're probably making the value that Linux
asked you to make, AKA 25.175000 MHz.  Unsurprisingly, if you do the
calculations with 25.175 MHz (or any integral kHz value) you don't
have to do any special optimization to stay integral:

  25175 * 4096 / (128 * 32)
  => 25175


So unless you have some way to know that the underlying clock is
actually (25.2 / 1.001) MHz and not just 25.175 MHz then your patch
looks wrong.


As a first step I'd suggest just removing all the special cases and
add a comment.  From real world testing it doesn't seem terribly
critical to be slightly off on CTS.  ...and in any case for any clock
rates except the small handful in the HDMI spec we'll be slightly off
on CTS anyway...

As a second step you could actually use the rate from "clk_get_rate()"
to see what clock rate was actually made.  You'll at least get Hz
here.  If you've somehow structured your machine to give you 25174825
Hz when DRM asked for 25175000 Hz (or if you redo the calculations and
ignore what DRM told you), then that would give you this slightly more
optimal rate.

As a third step you could somehow add the more detailed Hz information
to DRM (sounds like a big task, but I'm nowhere near a DRM expert).

As a fourth step you could try to write the code in a generic way to
figure out the best N / CTS to minimize error in the formula while
still staying within the required ranges.  If you did that, it
probably would belong in some generic helper and not in dw_hdmi...


...anyway, I'm not suggestion that you do everything above since I
think just removing the special cases is probably good enough.  ...but
if you wanted everything to be perfect it seems like the way to go.



So I guess remove my Reviewed-by for this patch?


-Doug
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 6/9] drm: bridge/dw_hdmi: adjust pixel clock values in N calculation
@ 2015-09-04 19:48         ` Doug Anderson
  0 siblings, 0 replies; 226+ messages in thread
From: Doug Anderson @ 2015-09-04 19:48 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

On Fri, Sep 4, 2015 at 11:21 AM, Doug Anderson <dianders@chromium.org> wrote:
> Russell,
>
> On Sat, Aug 8, 2015 at 9:10 AM, Russell King
> <rmk+kernel@arm.linux.org.uk> wrote:
>> Adjust the pixel clock values in the N calculation to match the more
>> accurate clock values we're given by the DRM subsystem, which are the
>> kHz pixel rate, with any fractional kHz rounded down in the case of
>> the non-240, non-480 line modes, or rounded up for the others.  So,
>>
>>          25.20 / 1.001 =>  25175
>>          27.00 * 1.001 =>  27027
>>          74.25 / 1.001 =>  74176
>>         148.50 / 1.001 => 148352
>>
>> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
>> ---
>>  drivers/gpu/drm/bridge/dw_hdmi.c | 20 ++++++++++----------
>>  1 file changed, 10 insertions(+), 10 deletions(-)
>
> For what it's worth, I backported this change into my local 3.14-based
> tree and it doesn't cause any problems, though it looks like the code
> isn't being run in my case...
>
> I can confirm that the rates you list match the rates I actually see
> requested by DRM, but in my current tree I've actually got something
> that allows a little bit of "slop" in HDMI rates because my system
> can't actually always make exactly the modes requested, but it appears
> that getting "close enough" works, especially if your clock jitter is
> low enough (because the sink needs to have a little bit of wiggle room
> for jitter anyway).  For instance, when 25.175 is requested we
> actually end up making 25.170732.
>
> In my tree this adjustment happens in mode_fixup by changing the
> adj_mode.  In one particular case, some debug prints show:
>   640x480, mode=25200000, adj=25171000, actual=25170732
>   freq=48000, pixel_clk=25171000, n=6144
>
> I'm not enough of an HDMI expert to say whether it's better to be
> using n=6144 or n=6864 in this case, but audio does play with either
> on the TV I tested.
>
> In any case, I'd say that your change at least makes things better
> than they were, so I'd be in favor of taking it.  If someone later
> decides that we should add a little margin to these numbers, then a
> patch to add that could go atop yours.

Oh!  I just figured this out!  :)

Basically the spec is saying that you want both N and CTS to be
integral.  ...as you say you really want:
  CTS = (TMDS * N) / (128 * audio_freq)

...CTS has no other restrictions (other than being integral) and
you're allowed a bit of slop for N (you aim for 128 * audio_freq but
can go up or down a bit).
...and the TMDS frequency has no such restrictions for being integral
in their calculations.

Apparently it's more important to optimize for the CTS formula working
out then it is for getting close to "128 * audio freq".  ...and that's
the reason for these special case N values...


So to put some numbers:

We're perfect when we have exactly 25.2:
  25200 * 4096 / (128 * 32)
  => 25200, so CTS for 25.2 MHz is 25200.  Perfect

...but when we have 25.2 / 1.001 we get a non-integral CTS:
  (25200 / 1.001) * 4096 / (128 * 32)
  => 25174.82517482518

...we can get an integral CTS and still remain in range if:
  (25200 / 1.001) * 4576 / (128 * 32)
  => 28125

In the case of Linux, I'm afraid we just don't have this type of
accuracy in our APIs.  The spec is talking about making
25.17482517482518 MHz.  As I said, in my case I'm actually making
25170732.  In your case you're probably making the value that Linux
asked you to make, AKA 25.175000 MHz.  Unsurprisingly, if you do the
calculations with 25.175 MHz (or any integral kHz value) you don't
have to do any special optimization to stay integral:

  25175 * 4096 / (128 * 32)
  => 25175


So unless you have some way to know that the underlying clock is
actually (25.2 / 1.001) MHz and not just 25.175 MHz then your patch
looks wrong.


As a first step I'd suggest just removing all the special cases and
add a comment.  From real world testing it doesn't seem terribly
critical to be slightly off on CTS.  ...and in any case for any clock
rates except the small handful in the HDMI spec we'll be slightly off
on CTS anyway...

As a second step you could actually use the rate from "clk_get_rate()"
to see what clock rate was actually made.  You'll at least get Hz
here.  If you've somehow structured your machine to give you 25174825
Hz when DRM asked for 25175000 Hz (or if you redo the calculations and
ignore what DRM told you), then that would give you this slightly more
optimal rate.

As a third step you could somehow add the more detailed Hz information
to DRM (sounds like a big task, but I'm nowhere near a DRM expert).

As a fourth step you could try to write the code in a generic way to
figure out the best N / CTS to minimize error in the formula while
still staying within the required ranges.  If you did that, it
probably would belong in some generic helper and not in dw_hdmi...


...anyway, I'm not suggestion that you do everything above since I
think just removing the special cases is probably good enough.  ...but
if you wanted everything to be perfect it seems like the way to go.



So I guess remove my Reviewed-by for this patch?


-Doug

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 8/9] drm: bridge/dw_hdmi: replace CTS calculation for the ACR
  2015-08-08 16:10     ` Russell King
  (?)
@ 2015-09-04 20:00       ` Doug Anderson
  -1 siblings, 0 replies; 226+ messages in thread
From: Doug Anderson @ 2015-09-04 20:00 UTC (permalink / raw)
  To: Russell King
  Cc: open list:ARM/Rockchip SoC...,
	alsa-devel, dri-devel, linux-kernel, linux-arm-kernel,
	Philipp Zabel, Andy Yan, Yakir Yang, Fabio Estevam, Mark Brown,
	Takashi Iwai, Jaroslav Kysela, Sascha Hauer, Jon Nettleton,
	David Airlie

Russell,

On Sat, Aug 8, 2015 at 9:10 AM, Russell King
<rmk+kernel@arm.linux.org.uk> wrote:
> Given the TDMS clock, audio sample rate, and the N parameter, we can
> calculate the CTS value for the audio clock regenerator (ACR) using the
> following calculation given in the HDMI specification:
>
>         CTS = ftdms * N / (128 * fs)
>
> The specification says that the CTS value is an average value, which is
> true if the source hardware measures it.  Where source hardware needs it
> to be programmed, it is particularly difficult to alternate it between
> two values correctly to ensure that we achieve a correct "average"
> fractional value at the sink.
>
> Also, there's the problem that our "ftdms" is not a fully accurate
> value; it is rounded to a kHz value.  This introduces an unnecessary
> (and harmless) fractional value into the above equation for combinations
> like 148.5MHz/1.001 for 44100Hz - we still calculate the correct CTS
> value.
>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> ---
>  drivers/gpu/drm/bridge/dw_hdmi.c | 92 +++++++---------------------------------
>  1 file changed, 16 insertions(+), 76 deletions(-)

If you take my feedback about your "drm: bridge/dw_hdmi: adjust pixel
clock values in N calculation" patch [1], all this math just works out
to "cts = pixel_clk / 1000".  ...but doing the math does future proof
us a bit, so it seems like a good idea.

Reviewed-by: Douglas Anderson <dianders@chromium.org>
Tested-by: Douglas Anderson <dianders@chromium.org>


[1] https://patchwork.kernel.org/patch/6975221/

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 8/9] drm: bridge/dw_hdmi: replace CTS calculation for the ACR
@ 2015-09-04 20:00       ` Doug Anderson
  0 siblings, 0 replies; 226+ messages in thread
From: Doug Anderson @ 2015-09-04 20:00 UTC (permalink / raw)
  To: Russell King
  Cc: Fabio Estevam, alsa-devel, linux-kernel, dri-devel,
	Jaroslav Kysela, open list:ARM/Rockchip SoC...,
	Mark Brown, Yakir Yang, Andy Yan, linux-arm-kernel

Russell,

On Sat, Aug 8, 2015 at 9:10 AM, Russell King
<rmk+kernel@arm.linux.org.uk> wrote:
> Given the TDMS clock, audio sample rate, and the N parameter, we can
> calculate the CTS value for the audio clock regenerator (ACR) using the
> following calculation given in the HDMI specification:
>
>         CTS = ftdms * N / (128 * fs)
>
> The specification says that the CTS value is an average value, which is
> true if the source hardware measures it.  Where source hardware needs it
> to be programmed, it is particularly difficult to alternate it between
> two values correctly to ensure that we achieve a correct "average"
> fractional value at the sink.
>
> Also, there's the problem that our "ftdms" is not a fully accurate
> value; it is rounded to a kHz value.  This introduces an unnecessary
> (and harmless) fractional value into the above equation for combinations
> like 148.5MHz/1.001 for 44100Hz - we still calculate the correct CTS
> value.
>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> ---
>  drivers/gpu/drm/bridge/dw_hdmi.c | 92 +++++++---------------------------------
>  1 file changed, 16 insertions(+), 76 deletions(-)

If you take my feedback about your "drm: bridge/dw_hdmi: adjust pixel
clock values in N calculation" patch [1], all this math just works out
to "cts = pixel_clk / 1000".  ...but doing the math does future proof
us a bit, so it seems like a good idea.

Reviewed-by: Douglas Anderson <dianders@chromium.org>
Tested-by: Douglas Anderson <dianders@chromium.org>


[1] https://patchwork.kernel.org/patch/6975221/
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 8/9] drm: bridge/dw_hdmi: replace CTS calculation for the ACR
@ 2015-09-04 20:00       ` Doug Anderson
  0 siblings, 0 replies; 226+ messages in thread
From: Doug Anderson @ 2015-09-04 20:00 UTC (permalink / raw)
  To: linux-arm-kernel

Russell,

On Sat, Aug 8, 2015 at 9:10 AM, Russell King
<rmk+kernel@arm.linux.org.uk> wrote:
> Given the TDMS clock, audio sample rate, and the N parameter, we can
> calculate the CTS value for the audio clock regenerator (ACR) using the
> following calculation given in the HDMI specification:
>
>         CTS = ftdms * N / (128 * fs)
>
> The specification says that the CTS value is an average value, which is
> true if the source hardware measures it.  Where source hardware needs it
> to be programmed, it is particularly difficult to alternate it between
> two values correctly to ensure that we achieve a correct "average"
> fractional value at the sink.
>
> Also, there's the problem that our "ftdms" is not a fully accurate
> value; it is rounded to a kHz value.  This introduces an unnecessary
> (and harmless) fractional value into the above equation for combinations
> like 148.5MHz/1.001 for 44100Hz - we still calculate the correct CTS
> value.
>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> ---
>  drivers/gpu/drm/bridge/dw_hdmi.c | 92 +++++++---------------------------------
>  1 file changed, 16 insertions(+), 76 deletions(-)

If you take my feedback about your "drm: bridge/dw_hdmi: adjust pixel
clock values in N calculation" patch [1], all this math just works out
to "cts = pixel_clk / 1000".  ...but doing the math does future proof
us a bit, so it seems like a good idea.

Reviewed-by: Douglas Anderson <dianders@chromium.org>
Tested-by: Douglas Anderson <dianders@chromium.org>


[1] https://patchwork.kernel.org/patch/6975221/

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 6/9] drm: bridge/dw_hdmi: adjust pixel clock values in N calculation
  2015-09-04 19:48         ` Doug Anderson
  (?)
@ 2015-09-04 21:24           ` Russell King - ARM Linux
  -1 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-09-04 21:24 UTC (permalink / raw)
  To: Doug Anderson
  Cc: open list:ARM/Rockchip SoC...,
	alsa-devel, dri-devel, linux-kernel, linux-arm-kernel,
	Philipp Zabel, Andy Yan, Yakir Yang, Fabio Estevam, Mark Brown,
	Takashi Iwai, Jaroslav Kysela, Sascha Hauer, Jon Nettleton,
	David Airlie

On Fri, Sep 04, 2015 at 12:48:02PM -0700, Doug Anderson wrote:
> Hi,
> 
> On Fri, Sep 4, 2015 at 11:21 AM, Doug Anderson <dianders@chromium.org> wrote:
> > Russell,
> >
> > On Sat, Aug 8, 2015 at 9:10 AM, Russell King
> > <rmk+kernel@arm.linux.org.uk> wrote:
> >> Adjust the pixel clock values in the N calculation to match the more
> >> accurate clock values we're given by the DRM subsystem, which are the
> >> kHz pixel rate, with any fractional kHz rounded down in the case of
> >> the non-240, non-480 line modes, or rounded up for the others.  So,
> >>
> >>          25.20 / 1.001 =>  25175
> >>          27.00 * 1.001 =>  27027
> >>          74.25 / 1.001 =>  74176
> >>         148.50 / 1.001 => 148352
> >>
> >> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> >> ---
> >>  drivers/gpu/drm/bridge/dw_hdmi.c | 20 ++++++++++----------
> >>  1 file changed, 10 insertions(+), 10 deletions(-)
> >
> > For what it's worth, I backported this change into my local 3.14-based
> > tree and it doesn't cause any problems, though it looks like the code
> > isn't being run in my case...
> >
> > I can confirm that the rates you list match the rates I actually see
> > requested by DRM, but in my current tree I've actually got something
> > that allows a little bit of "slop" in HDMI rates because my system
> > can't actually always make exactly the modes requested, but it appears
> > that getting "close enough" works, especially if your clock jitter is
> > low enough (because the sink needs to have a little bit of wiggle room
> > for jitter anyway).  For instance, when 25.175 is requested we
> > actually end up making 25.170732.
> >
> > In my tree this adjustment happens in mode_fixup by changing the
> > adj_mode.  In one particular case, some debug prints show:
> >   640x480, mode=25200000, adj=25171000, actual=25170732
> >   freq=48000, pixel_clk=25171000, n=6144
> >
> > I'm not enough of an HDMI expert to say whether it's better to be
> > using n=6144 or n=6864 in this case, but audio does play with either
> > on the TV I tested.
> >
> > In any case, I'd say that your change at least makes things better
> > than they were, so I'd be in favor of taking it.  If someone later
> > decides that we should add a little margin to these numbers, then a
> > patch to add that could go atop yours.
> 
> Oh!  I just figured this out!  :)
> 
> Basically the spec is saying that you want both N and CTS to be
> integral.  ...as you say you really want:
>   CTS = (TMDS * N) / (128 * audio_freq)

In the case of software-programmed CTS and N values, they have to be
integral because there's no such thing as fractional division here.
The CTS and N values get sent across the HDMI link to the sink, and
they use those in a PLL like arrangement to derive the audio clock.

More "inteligent" hardware automatically measures the CTS number and
continually updates the sink, which allows the sink to remain in
sync with the audio at non-coherent rates.

> ...CTS has no other restrictions (other than being integral) and
> you're allowed a bit of slop for N (you aim for 128 * audio_freq but
> can go up or down a bit).

No.  Both CTS and N have to be accurate to generate the correct
sample rate from the TDMS clock.

> Apparently it's more important to optimize for the CTS formula working
> out then it is for getting close to "128 * audio freq".  ...and that's
> the reason for these special case N values...

The "128 * audio freq" is just a recommendation.  Going through the HDMI
spec's recommended values for various clock rates and sample rates
reveals that quite a number of them are far from this "recommendation".

So I wouldn't read too much into the "128 * audio freq" thing.

> So to put some numbers:
> 
> We're perfect when we have exactly 25.2:
>   25200 * 4096 / (128 * 32)
>   => 25200, so CTS for 25.2 MHz is 25200.  Perfect
> 
> ...but when we have 25.2 / 1.001 we get a non-integral CTS:
>   (25200 / 1.001) * 4096 / (128 * 32)
>   => 25174.82517482518
> 
> ...we can get an integral CTS and still remain in range if:
>   (25200 / 1.001) * 4576 / (128 * 32)
>   => 28125

Correct.  These are the values given in the HDMI specification for each
of your clock rates you mention above.

You can even use 4096 for N _provided_ the source measures and sends
the CTS value (that's basically what happens in the case of
"non-coherent" clocks.)

> In the case of Linux, I'm afraid we just don't have this type of
> accuracy in our APIs.

We don't have that kind of precision in the DRM API, but we do have the
precision in the clock API.

> The spec is talking about making 25.17482517482518 MHz.

+/- 0.5%, according to CEA-861-B.

> As I said, in my case I'm actually making 25170732.

... which is within 0.02%, so is within spec.

> In your case you're probably making the value that Linux
> asked you to make, AKA 25.175000 MHz.

... which is the spec value.

> Unsurprisingly, if you do the
> calculations with 25.175 MHz (or any integral kHz value) you don't
> have to do any special optimization to stay integral:
> 
>   25175 * 4096 / (128 * 32)
>   => 25175
> 
> 
> So unless you have some way to know that the underlying clock is
> actually (25.2 / 1.001) MHz and not just 25.175 MHz then your patch
> looks wrong.

I don't believe you can make that statement.  If you wish to take the
lack of precision up with the authors of the CEA-861 and HDMI
specifications, since they "approximate" to the values I have in this
patch, and are what userspace passes in the mode structures to kernel
space.

> As a first step I'd suggest just removing all the special cases and
> add a comment.  From real world testing it doesn't seem terribly
> critical to be slightly off on CTS.  ...and in any case for any clock
> rates except the small handful in the HDMI spec we'll be slightly off
> on CTS anyway...

They're not "special cases" made up to fit something - they're from the
tables in the HDMI specification.

[everything else cut I'm getting tired...]

At the end of the day, when it comes to video playback, what matters
more is that your video and audio rates are related.  If the stream
audio is 48kHz and your video is expected to be 60fps, then the decoder
is going to want to see audio being consumed at 48kHz and video at
60fps.  If your actual video output is slightly slow due to a crap
hardware implementation, then having the audio clock slow by the same
proportion means that the video decoder doesn't have to stretch or
squeeze the audio to try and make things fit, or worse, skip frames.

That assumes that the audio and video clocks are coherent.  On iMX6
hardware using this, the audio is clocked at the rate defined by the
TDMS clock and the CTS/N values.

Other hardware, where the audio clock is derived differently (and
therefore, noncoherently), won't be using the CTS value software
supplies, because that's meaningless - it's got to measure the audio
clock rate, and pass that over to the sink using CTS - so called
auto-CTS mode.  That allows the sink to track the audio clock rate
irrespective of the actual TDMS clock rate.

-- 
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 6/9] drm: bridge/dw_hdmi: adjust pixel clock values in N calculation
@ 2015-09-04 21:24           ` Russell King - ARM Linux
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-09-04 21:24 UTC (permalink / raw)
  To: Doug Anderson
  Cc: Fabio Estevam, alsa-devel, linux-kernel, dri-devel,
	Jaroslav Kysela, open list:ARM/Rockchip SoC...,
	Mark Brown, Yakir Yang, Andy Yan, linux-arm-kernel

On Fri, Sep 04, 2015 at 12:48:02PM -0700, Doug Anderson wrote:
> Hi,
> 
> On Fri, Sep 4, 2015 at 11:21 AM, Doug Anderson <dianders@chromium.org> wrote:
> > Russell,
> >
> > On Sat, Aug 8, 2015 at 9:10 AM, Russell King
> > <rmk+kernel@arm.linux.org.uk> wrote:
> >> Adjust the pixel clock values in the N calculation to match the more
> >> accurate clock values we're given by the DRM subsystem, which are the
> >> kHz pixel rate, with any fractional kHz rounded down in the case of
> >> the non-240, non-480 line modes, or rounded up for the others.  So,
> >>
> >>          25.20 / 1.001 =>  25175
> >>          27.00 * 1.001 =>  27027
> >>          74.25 / 1.001 =>  74176
> >>         148.50 / 1.001 => 148352
> >>
> >> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> >> ---
> >>  drivers/gpu/drm/bridge/dw_hdmi.c | 20 ++++++++++----------
> >>  1 file changed, 10 insertions(+), 10 deletions(-)
> >
> > For what it's worth, I backported this change into my local 3.14-based
> > tree and it doesn't cause any problems, though it looks like the code
> > isn't being run in my case...
> >
> > I can confirm that the rates you list match the rates I actually see
> > requested by DRM, but in my current tree I've actually got something
> > that allows a little bit of "slop" in HDMI rates because my system
> > can't actually always make exactly the modes requested, but it appears
> > that getting "close enough" works, especially if your clock jitter is
> > low enough (because the sink needs to have a little bit of wiggle room
> > for jitter anyway).  For instance, when 25.175 is requested we
> > actually end up making 25.170732.
> >
> > In my tree this adjustment happens in mode_fixup by changing the
> > adj_mode.  In one particular case, some debug prints show:
> >   640x480, mode=25200000, adj=25171000, actual=25170732
> >   freq=48000, pixel_clk=25171000, n=6144
> >
> > I'm not enough of an HDMI expert to say whether it's better to be
> > using n=6144 or n=6864 in this case, but audio does play with either
> > on the TV I tested.
> >
> > In any case, I'd say that your change at least makes things better
> > than they were, so I'd be in favor of taking it.  If someone later
> > decides that we should add a little margin to these numbers, then a
> > patch to add that could go atop yours.
> 
> Oh!  I just figured this out!  :)
> 
> Basically the spec is saying that you want both N and CTS to be
> integral.  ...as you say you really want:
>   CTS = (TMDS * N) / (128 * audio_freq)

In the case of software-programmed CTS and N values, they have to be
integral because there's no such thing as fractional division here.
The CTS and N values get sent across the HDMI link to the sink, and
they use those in a PLL like arrangement to derive the audio clock.

More "inteligent" hardware automatically measures the CTS number and
continually updates the sink, which allows the sink to remain in
sync with the audio at non-coherent rates.

> ...CTS has no other restrictions (other than being integral) and
> you're allowed a bit of slop for N (you aim for 128 * audio_freq but
> can go up or down a bit).

No.  Both CTS and N have to be accurate to generate the correct
sample rate from the TDMS clock.

> Apparently it's more important to optimize for the CTS formula working
> out then it is for getting close to "128 * audio freq".  ...and that's
> the reason for these special case N values...

The "128 * audio freq" is just a recommendation.  Going through the HDMI
spec's recommended values for various clock rates and sample rates
reveals that quite a number of them are far from this "recommendation".

So I wouldn't read too much into the "128 * audio freq" thing.

> So to put some numbers:
> 
> We're perfect when we have exactly 25.2:
>   25200 * 4096 / (128 * 32)
>   => 25200, so CTS for 25.2 MHz is 25200.  Perfect
> 
> ...but when we have 25.2 / 1.001 we get a non-integral CTS:
>   (25200 / 1.001) * 4096 / (128 * 32)
>   => 25174.82517482518
> 
> ...we can get an integral CTS and still remain in range if:
>   (25200 / 1.001) * 4576 / (128 * 32)
>   => 28125

Correct.  These are the values given in the HDMI specification for each
of your clock rates you mention above.

You can even use 4096 for N _provided_ the source measures and sends
the CTS value (that's basically what happens in the case of
"non-coherent" clocks.)

> In the case of Linux, I'm afraid we just don't have this type of
> accuracy in our APIs.

We don't have that kind of precision in the DRM API, but we do have the
precision in the clock API.

> The spec is talking about making 25.17482517482518 MHz.

+/- 0.5%, according to CEA-861-B.

> As I said, in my case I'm actually making 25170732.

... which is within 0.02%, so is within spec.

> In your case you're probably making the value that Linux
> asked you to make, AKA 25.175000 MHz.

... which is the spec value.

> Unsurprisingly, if you do the
> calculations with 25.175 MHz (or any integral kHz value) you don't
> have to do any special optimization to stay integral:
> 
>   25175 * 4096 / (128 * 32)
>   => 25175
> 
> 
> So unless you have some way to know that the underlying clock is
> actually (25.2 / 1.001) MHz and not just 25.175 MHz then your patch
> looks wrong.

I don't believe you can make that statement.  If you wish to take the
lack of precision up with the authors of the CEA-861 and HDMI
specifications, since they "approximate" to the values I have in this
patch, and are what userspace passes in the mode structures to kernel
space.

> As a first step I'd suggest just removing all the special cases and
> add a comment.  From real world testing it doesn't seem terribly
> critical to be slightly off on CTS.  ...and in any case for any clock
> rates except the small handful in the HDMI spec we'll be slightly off
> on CTS anyway...

They're not "special cases" made up to fit something - they're from the
tables in the HDMI specification.

[everything else cut I'm getting tired...]

At the end of the day, when it comes to video playback, what matters
more is that your video and audio rates are related.  If the stream
audio is 48kHz and your video is expected to be 60fps, then the decoder
is going to want to see audio being consumed at 48kHz and video at
60fps.  If your actual video output is slightly slow due to a crap
hardware implementation, then having the audio clock slow by the same
proportion means that the video decoder doesn't have to stretch or
squeeze the audio to try and make things fit, or worse, skip frames.

That assumes that the audio and video clocks are coherent.  On iMX6
hardware using this, the audio is clocked at the rate defined by the
TDMS clock and the CTS/N values.

Other hardware, where the audio clock is derived differently (and
therefore, noncoherently), won't be using the CTS value software
supplies, because that's meaningless - it's got to measure the audio
clock rate, and pass that over to the sink using CTS - so called
auto-CTS mode.  That allows the sink to track the audio clock rate
irrespective of the actual TDMS clock rate.

-- 
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 6/9] drm: bridge/dw_hdmi: adjust pixel clock values in N calculation
@ 2015-09-04 21:24           ` Russell King - ARM Linux
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-09-04 21:24 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Sep 04, 2015 at 12:48:02PM -0700, Doug Anderson wrote:
> Hi,
> 
> On Fri, Sep 4, 2015 at 11:21 AM, Doug Anderson <dianders@chromium.org> wrote:
> > Russell,
> >
> > On Sat, Aug 8, 2015 at 9:10 AM, Russell King
> > <rmk+kernel@arm.linux.org.uk> wrote:
> >> Adjust the pixel clock values in the N calculation to match the more
> >> accurate clock values we're given by the DRM subsystem, which are the
> >> kHz pixel rate, with any fractional kHz rounded down in the case of
> >> the non-240, non-480 line modes, or rounded up for the others.  So,
> >>
> >>          25.20 / 1.001 =>  25175
> >>          27.00 * 1.001 =>  27027
> >>          74.25 / 1.001 =>  74176
> >>         148.50 / 1.001 => 148352
> >>
> >> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> >> ---
> >>  drivers/gpu/drm/bridge/dw_hdmi.c | 20 ++++++++++----------
> >>  1 file changed, 10 insertions(+), 10 deletions(-)
> >
> > For what it's worth, I backported this change into my local 3.14-based
> > tree and it doesn't cause any problems, though it looks like the code
> > isn't being run in my case...
> >
> > I can confirm that the rates you list match the rates I actually see
> > requested by DRM, but in my current tree I've actually got something
> > that allows a little bit of "slop" in HDMI rates because my system
> > can't actually always make exactly the modes requested, but it appears
> > that getting "close enough" works, especially if your clock jitter is
> > low enough (because the sink needs to have a little bit of wiggle room
> > for jitter anyway).  For instance, when 25.175 is requested we
> > actually end up making 25.170732.
> >
> > In my tree this adjustment happens in mode_fixup by changing the
> > adj_mode.  In one particular case, some debug prints show:
> >   640x480, mode=25200000, adj=25171000, actual=25170732
> >   freq=48000, pixel_clk=25171000, n=6144
> >
> > I'm not enough of an HDMI expert to say whether it's better to be
> > using n=6144 or n=6864 in this case, but audio does play with either
> > on the TV I tested.
> >
> > In any case, I'd say that your change at least makes things better
> > than they were, so I'd be in favor of taking it.  If someone later
> > decides that we should add a little margin to these numbers, then a
> > patch to add that could go atop yours.
> 
> Oh!  I just figured this out!  :)
> 
> Basically the spec is saying that you want both N and CTS to be
> integral.  ...as you say you really want:
>   CTS = (TMDS * N) / (128 * audio_freq)

In the case of software-programmed CTS and N values, they have to be
integral because there's no such thing as fractional division here.
The CTS and N values get sent across the HDMI link to the sink, and
they use those in a PLL like arrangement to derive the audio clock.

More "inteligent" hardware automatically measures the CTS number and
continually updates the sink, which allows the sink to remain in
sync with the audio at non-coherent rates.

> ...CTS has no other restrictions (other than being integral) and
> you're allowed a bit of slop for N (you aim for 128 * audio_freq but
> can go up or down a bit).

No.  Both CTS and N have to be accurate to generate the correct
sample rate from the TDMS clock.

> Apparently it's more important to optimize for the CTS formula working
> out then it is for getting close to "128 * audio freq".  ...and that's
> the reason for these special case N values...

The "128 * audio freq" is just a recommendation.  Going through the HDMI
spec's recommended values for various clock rates and sample rates
reveals that quite a number of them are far from this "recommendation".

So I wouldn't read too much into the "128 * audio freq" thing.

> So to put some numbers:
> 
> We're perfect when we have exactly 25.2:
>   25200 * 4096 / (128 * 32)
>   => 25200, so CTS for 25.2 MHz is 25200.  Perfect
> 
> ...but when we have 25.2 / 1.001 we get a non-integral CTS:
>   (25200 / 1.001) * 4096 / (128 * 32)
>   => 25174.82517482518
> 
> ...we can get an integral CTS and still remain in range if:
>   (25200 / 1.001) * 4576 / (128 * 32)
>   => 28125

Correct.  These are the values given in the HDMI specification for each
of your clock rates you mention above.

You can even use 4096 for N _provided_ the source measures and sends
the CTS value (that's basically what happens in the case of
"non-coherent" clocks.)

> In the case of Linux, I'm afraid we just don't have this type of
> accuracy in our APIs.

We don't have that kind of precision in the DRM API, but we do have the
precision in the clock API.

> The spec is talking about making 25.17482517482518 MHz.

+/- 0.5%, according to CEA-861-B.

> As I said, in my case I'm actually making 25170732.

... which is within 0.02%, so is within spec.

> In your case you're probably making the value that Linux
> asked you to make, AKA 25.175000 MHz.

... which is the spec value.

> Unsurprisingly, if you do the
> calculations with 25.175 MHz (or any integral kHz value) you don't
> have to do any special optimization to stay integral:
> 
>   25175 * 4096 / (128 * 32)
>   => 25175
> 
> 
> So unless you have some way to know that the underlying clock is
> actually (25.2 / 1.001) MHz and not just 25.175 MHz then your patch
> looks wrong.

I don't believe you can make that statement.  If you wish to take the
lack of precision up with the authors of the CEA-861 and HDMI
specifications, since they "approximate" to the values I have in this
patch, and are what userspace passes in the mode structures to kernel
space.

> As a first step I'd suggest just removing all the special cases and
> add a comment.  From real world testing it doesn't seem terribly
> critical to be slightly off on CTS.  ...and in any case for any clock
> rates except the small handful in the HDMI spec we'll be slightly off
> on CTS anyway...

They're not "special cases" made up to fit something - they're from the
tables in the HDMI specification.

[everything else cut I'm getting tired...]

At the end of the day, when it comes to video playback, what matters
more is that your video and audio rates are related.  If the stream
audio is 48kHz and your video is expected to be 60fps, then the decoder
is going to want to see audio being consumed at 48kHz and video at
60fps.  If your actual video output is slightly slow due to a crap
hardware implementation, then having the audio clock slow by the same
proportion means that the video decoder doesn't have to stretch or
squeeze the audio to try and make things fit, or worse, skip frames.

That assumes that the audio and video clocks are coherent.  On iMX6
hardware using this, the audio is clocked at the rate defined by the
TDMS clock and the CTS/N values.

Other hardware, where the audio clock is derived differently (and
therefore, noncoherently), won't be using the CTS value software
supplies, because that's meaningless - it's got to measure the audio
clock rate, and pass that over to the sink using CTS - so called
auto-CTS mode.  That allows the sink to track the audio clock rate
irrespective of the actual TDMS clock rate.

-- 
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 6/9] drm: bridge/dw_hdmi: adjust pixel clock values in N calculation
  2015-09-04 21:24           ` Russell King - ARM Linux
  (?)
@ 2015-09-04 23:50             ` Doug Anderson
  -1 siblings, 0 replies; 226+ messages in thread
From: Doug Anderson @ 2015-09-04 23:50 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: open list:ARM/Rockchip SoC...,
	alsa-devel, dri-devel, linux-kernel, linux-arm-kernel,
	Philipp Zabel, Andy Yan, Yakir Yang, Fabio Estevam, Mark Brown,
	Takashi Iwai, Jaroslav Kysela, Sascha Hauer, Jon Nettleton,
	David Airlie

Russell,

On Fri, Sep 4, 2015 at 2:24 PM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
>> Basically the spec is saying that you want both N and CTS to be
>> integral.  ...as you say you really want:
>>   CTS = (TMDS * N) / (128 * audio_freq)
>
> In the case of software-programmed CTS and N values, they have to be
> integral because there's no such thing as fractional division here.
> The CTS and N values get sent across the HDMI link to the sink, and
> they use those in a PLL like arrangement to derive the audio clock.
>
> More "inteligent" hardware automatically measures the CTS number and
> continually updates the sink, which allows the sink to remain in
> sync with the audio at non-coherent rates.
>
>> ...CTS has no other restrictions (other than being integral) and
>> you're allowed a bit of slop for N (you aim for 128 * audio_freq but
>> can go up or down a bit).
>
> No.  Both CTS and N have to be accurate to generate the correct
> sample rate from the TDMS clock.

I guess by "other" I meant no restrictions other than that, which is
listed above that "CTS = (TMDS * N) / (128 * audio_freq)".  Anyway,
sounds like we're on the same page here...


>> Apparently it's more important to optimize for the CTS formula working
>> out then it is for getting close to "128 * audio freq".  ...and that's
>> the reason for these special case N values...
>
> The "128 * audio freq" is just a recommendation.  Going through the HDMI
> spec's recommended values for various clock rates and sample rates
> reveals that quite a number of them are far from this "recommendation".
>
> So I wouldn't read too much into the "128 * audio freq" thing.

Again, same page.


>> So to put some numbers:
>>
>> We're perfect when we have exactly 25.2:
>>   25200 * 4096 / (128 * 32)
>>   => 25200, so CTS for 25.2 MHz is 25200.  Perfect
>>
>> ...but when we have 25.2 / 1.001 we get a non-integral CTS:
>>   (25200 / 1.001) * 4096 / (128 * 32)
>>   => 25174.82517482518
>>
>> ...we can get an integral CTS and still remain in range if:
>>   (25200 / 1.001) * 4576 / (128 * 32)
>>   => 28125
>
> Correct.  These are the values given in the HDMI specification for each
> of your clock rates you mention above.
>
> You can even use 4096 for N _provided_ the source measures and sends
> the CTS value (that's basically what happens in the case of
> "non-coherent" clocks.)
>
>> In the case of Linux, I'm afraid we just don't have this type of
>> accuracy in our APIs.
>
> We don't have that kind of precision in the DRM API, but we do have the
> precision in the clock API.

Yup.  On the same page.  See my suggestions of using the common clock framework.


>> The spec is talking about making 25.17482517482518 MHz.
>
> +/- 0.5%, according to CEA-861-B.
>
>> As I said, in my case I'm actually making 25170732.
>
> ... which is within 0.02%, so is within spec.

Yup, that's why we're doing it.  Note that total jitter has to be
under +/- 0.5% right?  ...so if you've got error here you've got to
make sure your clock is extra clean I think.


>> In your case you're probably making the value that Linux
>> asked you to make, AKA 25.175000 MHz.
>
> ... which is the spec value.

This is where we're not on the same page.  Where in the spec does it
say 25.17500 MHz?  I see in the spec:
 25.2 / 1.001

...and this is a crucial difference here.  Please double-check my math, but:

(25175000 * 4576) / (128 * 32000.)
=> 28125.1953125

(25174825 * 4576) / (128 * 32000.)
=> 28125.0

This calculation is what led to my belief that the goal here is to
make an integral CTS.  If you have 25.175 MHZ clock and N of 4576 you
_will not_ have an integral CTS.  If you instead have 25.174825 MHz
clock and N of 4576 you _will_ have an integral CTS.

Said another way:

1. The reason 25174825 Hz has a different N is to make an integral CTS.

2. If you are indeed making 25175000 then there is no need for a
different N to make an integral CTS

3. If you use 4576 for N but you're making 25175000 Hz, you end up in
a _worse_ position than if you use the standard 4096 for N.


>> Unsurprisingly, if you do the
>> calculations with 25.175 MHz (or any integral kHz value) you don't
>> have to do any special optimization to stay integral:
>>
>>   25175 * 4096 / (128 * 32)
>>   => 25175
>>
>>
>> So unless you have some way to know that the underlying clock is
>> actually (25.2 / 1.001) MHz and not just 25.175 MHz then your patch
>> looks wrong.
>
> I don't believe you can make that statement.  If you wish to take the
> lack of precision up with the authors of the CEA-861 and HDMI
> specifications, since they "approximate" to the values I have in this
> patch, and are what userspace passes in the mode structures to kernel
> space.

I will repeat my mantra: I'm a visitor here and decidedly not an
expert.  However, from my reading of the HDMI spec shows that the spec
itself is fine.  They are just assuming that you're providing a
25.174825 MHz clock and giving you optimized values for said clock.

If the current driver says that it's providing 25.175000 MHz then you
shouldn't assume that it's actually making 25.174825 MHz


>> As a first step I'd suggest just removing all the special cases and
>> add a comment.  From real world testing it doesn't seem terribly
>> critical to be slightly off on CTS.  ...and in any case for any clock
>> rates except the small handful in the HDMI spec we'll be slightly off
>> on CTS anyway...
>
> They're not "special cases" made up to fit something - they're from the
> tables in the HDMI specification.

They are definitely "special cases".  There is a general rule in the
code you posted (aim for 128 * freq) and these are cases for certain
clocks that are an exception to the general rule.  AKA they are
special cases.

I'm not arguing that there's not a valid reason for these special
cases.  I'm simply arguing that the special cases are likely for a
different situation than the one we're in.

The HDMI spec itself (loosely interpreted) pretty much says: if
there's any doubt, just use the equations--don't use the tables.


> That assumes that the audio and video clocks are coherent.  On iMX6
> hardware using this, the audio is clocked at the rate defined by the
> TDMS clock and the CTS/N values.

I'll admit I haven't looked at the audio section of dw_hdmi much, but
I'd imagine that for all users of this controller / PHY the audio and
video clocks are coherent.

I think in the perfect world we'd be able to generate exactly
25174825.174825177 Hz and we'd use all the rates from the HDMI spec.
and we'd get spot on 32 kHz audio.  ...but I'm simply saying that
we're not in that perfect world yet.

Also note that there are many many rates not in the HDMI spec that
could benefit from similar optimization of trying to adjust N to make
an integral CTS.

---

As a side note: I realized one part of the HDMI spec that isn't trying
to make an integral value but still uses a different value for N: 297
MHz.  From the DesignWare spec I have it appears that 594 MHz is
similar.  For those cases it looks like we have:

if (pixel_clk == 297000000) {
  switch (freq) {
  case 32000:
    return (128 * freq) / 1333;
  case 44100:
  case 48000:
  case 88200:
  case 96000:
  case 176400:
    return (128 * freq) / 1200;
  }
} else if (pixel_clk == 594000000) {
  switch (freq) {
  case 32000:
    return (128 * freq) / 1333;
  case 44100:
  case 88200:
  case 176400:
    return (128 * freq) / 600;
  }
}

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 6/9] drm: bridge/dw_hdmi: adjust pixel clock values in N calculation
@ 2015-09-04 23:50             ` Doug Anderson
  0 siblings, 0 replies; 226+ messages in thread
From: Doug Anderson @ 2015-09-04 23:50 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Fabio Estevam, alsa-devel, linux-kernel, dri-devel,
	Jaroslav Kysela, open list:ARM/Rockchip SoC...,
	Mark Brown, Yakir Yang, Andy Yan, linux-arm-kernel

Russell,

On Fri, Sep 4, 2015 at 2:24 PM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
>> Basically the spec is saying that you want both N and CTS to be
>> integral.  ...as you say you really want:
>>   CTS = (TMDS * N) / (128 * audio_freq)
>
> In the case of software-programmed CTS and N values, they have to be
> integral because there's no such thing as fractional division here.
> The CTS and N values get sent across the HDMI link to the sink, and
> they use those in a PLL like arrangement to derive the audio clock.
>
> More "inteligent" hardware automatically measures the CTS number and
> continually updates the sink, which allows the sink to remain in
> sync with the audio at non-coherent rates.
>
>> ...CTS has no other restrictions (other than being integral) and
>> you're allowed a bit of slop for N (you aim for 128 * audio_freq but
>> can go up or down a bit).
>
> No.  Both CTS and N have to be accurate to generate the correct
> sample rate from the TDMS clock.

I guess by "other" I meant no restrictions other than that, which is
listed above that "CTS = (TMDS * N) / (128 * audio_freq)".  Anyway,
sounds like we're on the same page here...


>> Apparently it's more important to optimize for the CTS formula working
>> out then it is for getting close to "128 * audio freq".  ...and that's
>> the reason for these special case N values...
>
> The "128 * audio freq" is just a recommendation.  Going through the HDMI
> spec's recommended values for various clock rates and sample rates
> reveals that quite a number of them are far from this "recommendation".
>
> So I wouldn't read too much into the "128 * audio freq" thing.

Again, same page.


>> So to put some numbers:
>>
>> We're perfect when we have exactly 25.2:
>>   25200 * 4096 / (128 * 32)
>>   => 25200, so CTS for 25.2 MHz is 25200.  Perfect
>>
>> ...but when we have 25.2 / 1.001 we get a non-integral CTS:
>>   (25200 / 1.001) * 4096 / (128 * 32)
>>   => 25174.82517482518
>>
>> ...we can get an integral CTS and still remain in range if:
>>   (25200 / 1.001) * 4576 / (128 * 32)
>>   => 28125
>
> Correct.  These are the values given in the HDMI specification for each
> of your clock rates you mention above.
>
> You can even use 4096 for N _provided_ the source measures and sends
> the CTS value (that's basically what happens in the case of
> "non-coherent" clocks.)
>
>> In the case of Linux, I'm afraid we just don't have this type of
>> accuracy in our APIs.
>
> We don't have that kind of precision in the DRM API, but we do have the
> precision in the clock API.

Yup.  On the same page.  See my suggestions of using the common clock framework.


>> The spec is talking about making 25.17482517482518 MHz.
>
> +/- 0.5%, according to CEA-861-B.
>
>> As I said, in my case I'm actually making 25170732.
>
> ... which is within 0.02%, so is within spec.

Yup, that's why we're doing it.  Note that total jitter has to be
under +/- 0.5% right?  ...so if you've got error here you've got to
make sure your clock is extra clean I think.


>> In your case you're probably making the value that Linux
>> asked you to make, AKA 25.175000 MHz.
>
> ... which is the spec value.

This is where we're not on the same page.  Where in the spec does it
say 25.17500 MHz?  I see in the spec:
 25.2 / 1.001

...and this is a crucial difference here.  Please double-check my math, but:

(25175000 * 4576) / (128 * 32000.)
=> 28125.1953125

(25174825 * 4576) / (128 * 32000.)
=> 28125.0

This calculation is what led to my belief that the goal here is to
make an integral CTS.  If you have 25.175 MHZ clock and N of 4576 you
_will not_ have an integral CTS.  If you instead have 25.174825 MHz
clock and N of 4576 you _will_ have an integral CTS.

Said another way:

1. The reason 25174825 Hz has a different N is to make an integral CTS.

2. If you are indeed making 25175000 then there is no need for a
different N to make an integral CTS

3. If you use 4576 for N but you're making 25175000 Hz, you end up in
a _worse_ position than if you use the standard 4096 for N.


>> Unsurprisingly, if you do the
>> calculations with 25.175 MHz (or any integral kHz value) you don't
>> have to do any special optimization to stay integral:
>>
>>   25175 * 4096 / (128 * 32)
>>   => 25175
>>
>>
>> So unless you have some way to know that the underlying clock is
>> actually (25.2 / 1.001) MHz and not just 25.175 MHz then your patch
>> looks wrong.
>
> I don't believe you can make that statement.  If you wish to take the
> lack of precision up with the authors of the CEA-861 and HDMI
> specifications, since they "approximate" to the values I have in this
> patch, and are what userspace passes in the mode structures to kernel
> space.

I will repeat my mantra: I'm a visitor here and decidedly not an
expert.  However, from my reading of the HDMI spec shows that the spec
itself is fine.  They are just assuming that you're providing a
25.174825 MHz clock and giving you optimized values for said clock.

If the current driver says that it's providing 25.175000 MHz then you
shouldn't assume that it's actually making 25.174825 MHz


>> As a first step I'd suggest just removing all the special cases and
>> add a comment.  From real world testing it doesn't seem terribly
>> critical to be slightly off on CTS.  ...and in any case for any clock
>> rates except the small handful in the HDMI spec we'll be slightly off
>> on CTS anyway...
>
> They're not "special cases" made up to fit something - they're from the
> tables in the HDMI specification.

They are definitely "special cases".  There is a general rule in the
code you posted (aim for 128 * freq) and these are cases for certain
clocks that are an exception to the general rule.  AKA they are
special cases.

I'm not arguing that there's not a valid reason for these special
cases.  I'm simply arguing that the special cases are likely for a
different situation than the one we're in.

The HDMI spec itself (loosely interpreted) pretty much says: if
there's any doubt, just use the equations--don't use the tables.


> That assumes that the audio and video clocks are coherent.  On iMX6
> hardware using this, the audio is clocked at the rate defined by the
> TDMS clock and the CTS/N values.

I'll admit I haven't looked at the audio section of dw_hdmi much, but
I'd imagine that for all users of this controller / PHY the audio and
video clocks are coherent.

I think in the perfect world we'd be able to generate exactly
25174825.174825177 Hz and we'd use all the rates from the HDMI spec.
and we'd get spot on 32 kHz audio.  ...but I'm simply saying that
we're not in that perfect world yet.

Also note that there are many many rates not in the HDMI spec that
could benefit from similar optimization of trying to adjust N to make
an integral CTS.

---

As a side note: I realized one part of the HDMI spec that isn't trying
to make an integral value but still uses a different value for N: 297
MHz.  From the DesignWare spec I have it appears that 594 MHz is
similar.  For those cases it looks like we have:

if (pixel_clk == 297000000) {
  switch (freq) {
  case 32000:
    return (128 * freq) / 1333;
  case 44100:
  case 48000:
  case 88200:
  case 96000:
  case 176400:
    return (128 * freq) / 1200;
  }
} else if (pixel_clk == 594000000) {
  switch (freq) {
  case 32000:
    return (128 * freq) / 1333;
  case 44100:
  case 88200:
  case 176400:
    return (128 * freq) / 600;
  }
}
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 6/9] drm: bridge/dw_hdmi: adjust pixel clock values in N calculation
@ 2015-09-04 23:50             ` Doug Anderson
  0 siblings, 0 replies; 226+ messages in thread
From: Doug Anderson @ 2015-09-04 23:50 UTC (permalink / raw)
  To: linux-arm-kernel

Russell,

On Fri, Sep 4, 2015 at 2:24 PM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
>> Basically the spec is saying that you want both N and CTS to be
>> integral.  ...as you say you really want:
>>   CTS = (TMDS * N) / (128 * audio_freq)
>
> In the case of software-programmed CTS and N values, they have to be
> integral because there's no such thing as fractional division here.
> The CTS and N values get sent across the HDMI link to the sink, and
> they use those in a PLL like arrangement to derive the audio clock.
>
> More "inteligent" hardware automatically measures the CTS number and
> continually updates the sink, which allows the sink to remain in
> sync with the audio at non-coherent rates.
>
>> ...CTS has no other restrictions (other than being integral) and
>> you're allowed a bit of slop for N (you aim for 128 * audio_freq but
>> can go up or down a bit).
>
> No.  Both CTS and N have to be accurate to generate the correct
> sample rate from the TDMS clock.

I guess by "other" I meant no restrictions other than that, which is
listed above that "CTS = (TMDS * N) / (128 * audio_freq)".  Anyway,
sounds like we're on the same page here...


>> Apparently it's more important to optimize for the CTS formula working
>> out then it is for getting close to "128 * audio freq".  ...and that's
>> the reason for these special case N values...
>
> The "128 * audio freq" is just a recommendation.  Going through the HDMI
> spec's recommended values for various clock rates and sample rates
> reveals that quite a number of them are far from this "recommendation".
>
> So I wouldn't read too much into the "128 * audio freq" thing.

Again, same page.


>> So to put some numbers:
>>
>> We're perfect when we have exactly 25.2:
>>   25200 * 4096 / (128 * 32)
>>   => 25200, so CTS for 25.2 MHz is 25200.  Perfect
>>
>> ...but when we have 25.2 / 1.001 we get a non-integral CTS:
>>   (25200 / 1.001) * 4096 / (128 * 32)
>>   => 25174.82517482518
>>
>> ...we can get an integral CTS and still remain in range if:
>>   (25200 / 1.001) * 4576 / (128 * 32)
>>   => 28125
>
> Correct.  These are the values given in the HDMI specification for each
> of your clock rates you mention above.
>
> You can even use 4096 for N _provided_ the source measures and sends
> the CTS value (that's basically what happens in the case of
> "non-coherent" clocks.)
>
>> In the case of Linux, I'm afraid we just don't have this type of
>> accuracy in our APIs.
>
> We don't have that kind of precision in the DRM API, but we do have the
> precision in the clock API.

Yup.  On the same page.  See my suggestions of using the common clock framework.


>> The spec is talking about making 25.17482517482518 MHz.
>
> +/- 0.5%, according to CEA-861-B.
>
>> As I said, in my case I'm actually making 25170732.
>
> ... which is within 0.02%, so is within spec.

Yup, that's why we're doing it.  Note that total jitter has to be
under +/- 0.5% right?  ...so if you've got error here you've got to
make sure your clock is extra clean I think.


>> In your case you're probably making the value that Linux
>> asked you to make, AKA 25.175000 MHz.
>
> ... which is the spec value.

This is where we're not on the same page.  Where in the spec does it
say 25.17500 MHz?  I see in the spec:
 25.2 / 1.001

...and this is a crucial difference here.  Please double-check my math, but:

(25175000 * 4576) / (128 * 32000.)
=> 28125.1953125

(25174825 * 4576) / (128 * 32000.)
=> 28125.0

This calculation is what led to my belief that the goal here is to
make an integral CTS.  If you have 25.175 MHZ clock and N of 4576 you
_will not_ have an integral CTS.  If you instead have 25.174825 MHz
clock and N of 4576 you _will_ have an integral CTS.

Said another way:

1. The reason 25174825 Hz has a different N is to make an integral CTS.

2. If you are indeed making 25175000 then there is no need for a
different N to make an integral CTS

3. If you use 4576 for N but you're making 25175000 Hz, you end up in
a _worse_ position than if you use the standard 4096 for N.


>> Unsurprisingly, if you do the
>> calculations with 25.175 MHz (or any integral kHz value) you don't
>> have to do any special optimization to stay integral:
>>
>>   25175 * 4096 / (128 * 32)
>>   => 25175
>>
>>
>> So unless you have some way to know that the underlying clock is
>> actually (25.2 / 1.001) MHz and not just 25.175 MHz then your patch
>> looks wrong.
>
> I don't believe you can make that statement.  If you wish to take the
> lack of precision up with the authors of the CEA-861 and HDMI
> specifications, since they "approximate" to the values I have in this
> patch, and are what userspace passes in the mode structures to kernel
> space.

I will repeat my mantra: I'm a visitor here and decidedly not an
expert.  However, from my reading of the HDMI spec shows that the spec
itself is fine.  They are just assuming that you're providing a
25.174825 MHz clock and giving you optimized values for said clock.

If the current driver says that it's providing 25.175000 MHz then you
shouldn't assume that it's actually making 25.174825 MHz


>> As a first step I'd suggest just removing all the special cases and
>> add a comment.  From real world testing it doesn't seem terribly
>> critical to be slightly off on CTS.  ...and in any case for any clock
>> rates except the small handful in the HDMI spec we'll be slightly off
>> on CTS anyway...
>
> They're not "special cases" made up to fit something - they're from the
> tables in the HDMI specification.

They are definitely "special cases".  There is a general rule in the
code you posted (aim for 128 * freq) and these are cases for certain
clocks that are an exception to the general rule.  AKA they are
special cases.

I'm not arguing that there's not a valid reason for these special
cases.  I'm simply arguing that the special cases are likely for a
different situation than the one we're in.

The HDMI spec itself (loosely interpreted) pretty much says: if
there's any doubt, just use the equations--don't use the tables.


> That assumes that the audio and video clocks are coherent.  On iMX6
> hardware using this, the audio is clocked at the rate defined by the
> TDMS clock and the CTS/N values.

I'll admit I haven't looked at the audio section of dw_hdmi much, but
I'd imagine that for all users of this controller / PHY the audio and
video clocks are coherent.

I think in the perfect world we'd be able to generate exactly
25174825.174825177 Hz and we'd use all the rates from the HDMI spec.
and we'd get spot on 32 kHz audio.  ...but I'm simply saying that
we're not in that perfect world yet.

Also note that there are many many rates not in the HDMI spec that
could benefit from similar optimization of trying to adjust N to make
an integral CTS.

---

As a side note: I realized one part of the HDMI spec that isn't trying
to make an integral value but still uses a different value for N: 297
MHz.  From the DesignWare spec I have it appears that 594 MHz is
similar.  For those cases it looks like we have:

if (pixel_clk == 297000000) {
  switch (freq) {
  case 32000:
    return (128 * freq) / 1333;
  case 44100:
  case 48000:
  case 88200:
  case 96000:
  case 176400:
    return (128 * freq) / 1200;
  }
} else if (pixel_clk == 594000000) {
  switch (freq) {
  case 32000:
    return (128 * freq) / 1333;
  case 44100:
  case 88200:
  case 176400:
    return (128 * freq) / 600;
  }
}

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 6/9] drm: bridge/dw_hdmi: adjust pixel clock values in N calculation
  2015-09-04 23:50             ` Doug Anderson
  (?)
@ 2015-09-05  0:27               ` Russell King - ARM Linux
  -1 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-09-05  0:27 UTC (permalink / raw)
  To: Doug Anderson
  Cc: open list:ARM/Rockchip SoC...,
	alsa-devel, dri-devel, linux-kernel, linux-arm-kernel,
	Philipp Zabel, Andy Yan, Yakir Yang, Fabio Estevam, Mark Brown,
	Takashi Iwai, Jaroslav Kysela, Sascha Hauer, Jon Nettleton,
	David Airlie

On Fri, Sep 04, 2015 at 04:50:03PM -0700, Doug Anderson wrote:
> Russell,
> 
> On Fri, Sep 4, 2015 at 2:24 PM, Russell King - ARM Linux
> <linux@arm.linux.org.uk> wrote:
> >> In your case you're probably making the value that Linux
> >> asked you to make, AKA 25.175000 MHz.
> >
> > ... which is the spec value.
> 
> This is where we're not on the same page.  Where in the spec does it
> say 25.17500 MHz?  I see in the spec:
>  25.2 / 1.001

Section 4 of CEA-861-B, which defines the video clock rates and their
accuracy of 0.5%.

> ...and this is a crucial difference here.  Please double-check my math, but:
> 
> (25175000 * 4576) / (128 * 32000.)
> => 28125.1953125
> 
> (25174825 * 4576) / (128 * 32000.)
> => 28125.0
> 
> This calculation is what led to my belief that the goal here is to
> make an integral CTS.  If you have 25.175 MHZ clock and N of 4576 you
> _will not_ have an integral CTS.  If you instead have 25.174825 MHz
> clock and N of 4576 you _will_ have an integral CTS.

Right, but 25.175 is close enough to 25.174825.  Do this calculation:

25175000 * 4576 / 28125 / 128

That'll give you the resulting audio sample rate, which is 32000.222Hz.
That's an error of... 0.00069%, which is probably around the typical
error of your average crystal oscillator.  Really not worth bothering
with.

> Said another way:
> 
> 1. The reason 25174825 Hz has a different N is to make an integral CTS.
> 
> 2. If you are indeed making 25175000 then there is no need for a
> different N to make an integral CTS
> 
> 3. If you use 4576 for N but you're making 25175000 Hz, you end up in
> a _worse_ position than if you use the standard 4096 for N.

Total rubbish.  Sorry, but it is.

Follow the code.  Pixel clock is 25175000.  For 32kHz, N will be 4576.
25175000 * 4576 = 1.152008e11.  Divide that by the audio clock rate
(128 * 32000) gives 28125.19531.  Since we're using integer division,
that gets rounded down to 28125.

DRM uses a clock rate of "25175" to represent 25.2/1.001 modes.  So,
if your hardware sets a video clock rate of 25.2MHz/1.001, then you
end up with a sample rate of exactly 32kHz.  If you set exactly
25.175MHz, you end up with an approximate 32kHz sample rate - one
which is 0.00069% in error, which is (excluse the language) fuck all
different from exactly 32kHz.

Are you _really_ going to continue arguing over a 0.00069% error?
If you are, I'm not going to listen anymore - it's soo damned small
that it's not worth bothering with.  At all.

The only time that you'd need to worry about it is if you wanted a
super-accurate system, and for that you'd need an atomic clock to
source your system clocks to reduce aging effects, temperature
induced drift, etc, maybe locking the atomic clock to a national
frequency standard like the Anthorn MSF 60kHz transmitter signal
broadcast by the UK National Physics Laboratory.

> >> As a first step I'd suggest just removing all the special cases and
> >> add a comment.  From real world testing it doesn't seem terribly
> >> critical to be slightly off on CTS.  ...and in any case for any clock
> >> rates except the small handful in the HDMI spec we'll be slightly off
> >> on CTS anyway...
> >
> > They're not "special cases" made up to fit something - they're from the
> > tables in the HDMI specification.
> 
> They are definitely "special cases".  There is a general rule in the
> code you posted (aim for 128 * freq) and these are cases for certain
> clocks that are an exception to the general rule.  AKA they are
> special cases.

Sorry, I disagree with you.

> > That assumes that the audio and video clocks are coherent.  On iMX6
> > hardware using this, the audio is clocked at the rate defined by the
> > TDMS clock and the CTS/N values.
> 
> I'll admit I haven't looked at the audio section of dw_hdmi much, but
> I'd imagine that for all users of this controller / PHY the audio and
> video clocks are coherent.

Not if the audio clock comes from an I2S master rather than being
sourced from the HDMI block.

> I think in the perfect world we'd be able to generate exactly
> 25174825.174825177 Hz and we'd use all the rates from the HDMI spec.

To generate something of that accuracy, you'd need something like a
caesium fountain atomic clock.

> and we'd get spot on 32 kHz audio.  ...but I'm simply saying that
> we're not in that perfect world yet.
> 
> Also note that there are many many rates not in the HDMI spec that
> could benefit from similar optimization of trying to adjust N to make
> an integral CTS.

Now go and look at the HDMI spec, where it gives the CTS value for
74.25/1.001 for 32kHz.  That can't be represented by an integer CTS
value, so using this hardware, we can't generate that sample rate
without an error.  We'd use a fixed CTS value of 210937 instead, which
works out at a 0.00024% error.  Again, not worth worrying about.


> 
> ---
> 
> As a side note: I realized one part of the HDMI spec that isn't trying
> to make an integral value but still uses a different value for N: 297
> MHz.  From the DesignWare spec I have it appears that 594 MHz is
> similar.  For those cases it looks like we have:

297MHz _does_ work.

297000000 * 3072 / 222750 = 128 * 32000 exactly.

> 
> if (pixel_clk == 297000000) {
>   switch (freq) {
>   case 32000:
>     return (128 * freq) / 1333;

Plug the numbers in.  128 * 32000 / 1333 = 3072.96 but because we're using
integer math, that's 3072.  Which just happens to be the value in the HDMI
spec.

>   case 44100:
>   case 48000:
>   case 88200:
>   case 96000:
>   case 176400:
>     return (128 * freq) / 1200;

Do the math again.  You get the spec figures for N.

-- 
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 6/9] drm: bridge/dw_hdmi: adjust pixel clock values in N calculation
@ 2015-09-05  0:27               ` Russell King - ARM Linux
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-09-05  0:27 UTC (permalink / raw)
  To: Doug Anderson
  Cc: Fabio Estevam, alsa-devel, linux-kernel, dri-devel,
	Jaroslav Kysela, open list:ARM/Rockchip SoC...,
	Mark Brown, Yakir Yang, Andy Yan, linux-arm-kernel

On Fri, Sep 04, 2015 at 04:50:03PM -0700, Doug Anderson wrote:
> Russell,
> 
> On Fri, Sep 4, 2015 at 2:24 PM, Russell King - ARM Linux
> <linux@arm.linux.org.uk> wrote:
> >> In your case you're probably making the value that Linux
> >> asked you to make, AKA 25.175000 MHz.
> >
> > ... which is the spec value.
> 
> This is where we're not on the same page.  Where in the spec does it
> say 25.17500 MHz?  I see in the spec:
>  25.2 / 1.001

Section 4 of CEA-861-B, which defines the video clock rates and their
accuracy of 0.5%.

> ...and this is a crucial difference here.  Please double-check my math, but:
> 
> (25175000 * 4576) / (128 * 32000.)
> => 28125.1953125
> 
> (25174825 * 4576) / (128 * 32000.)
> => 28125.0
> 
> This calculation is what led to my belief that the goal here is to
> make an integral CTS.  If you have 25.175 MHZ clock and N of 4576 you
> _will not_ have an integral CTS.  If you instead have 25.174825 MHz
> clock and N of 4576 you _will_ have an integral CTS.

Right, but 25.175 is close enough to 25.174825.  Do this calculation:

25175000 * 4576 / 28125 / 128

That'll give you the resulting audio sample rate, which is 32000.222Hz.
That's an error of... 0.00069%, which is probably around the typical
error of your average crystal oscillator.  Really not worth bothering
with.

> Said another way:
> 
> 1. The reason 25174825 Hz has a different N is to make an integral CTS.
> 
> 2. If you are indeed making 25175000 then there is no need for a
> different N to make an integral CTS
> 
> 3. If you use 4576 for N but you're making 25175000 Hz, you end up in
> a _worse_ position than if you use the standard 4096 for N.

Total rubbish.  Sorry, but it is.

Follow the code.  Pixel clock is 25175000.  For 32kHz, N will be 4576.
25175000 * 4576 = 1.152008e11.  Divide that by the audio clock rate
(128 * 32000) gives 28125.19531.  Since we're using integer division,
that gets rounded down to 28125.

DRM uses a clock rate of "25175" to represent 25.2/1.001 modes.  So,
if your hardware sets a video clock rate of 25.2MHz/1.001, then you
end up with a sample rate of exactly 32kHz.  If you set exactly
25.175MHz, you end up with an approximate 32kHz sample rate - one
which is 0.00069% in error, which is (excluse the language) fuck all
different from exactly 32kHz.

Are you _really_ going to continue arguing over a 0.00069% error?
If you are, I'm not going to listen anymore - it's soo damned small
that it's not worth bothering with.  At all.

The only time that you'd need to worry about it is if you wanted a
super-accurate system, and for that you'd need an atomic clock to
source your system clocks to reduce aging effects, temperature
induced drift, etc, maybe locking the atomic clock to a national
frequency standard like the Anthorn MSF 60kHz transmitter signal
broadcast by the UK National Physics Laboratory.

> >> As a first step I'd suggest just removing all the special cases and
> >> add a comment.  From real world testing it doesn't seem terribly
> >> critical to be slightly off on CTS.  ...and in any case for any clock
> >> rates except the small handful in the HDMI spec we'll be slightly off
> >> on CTS anyway...
> >
> > They're not "special cases" made up to fit something - they're from the
> > tables in the HDMI specification.
> 
> They are definitely "special cases".  There is a general rule in the
> code you posted (aim for 128 * freq) and these are cases for certain
> clocks that are an exception to the general rule.  AKA they are
> special cases.

Sorry, I disagree with you.

> > That assumes that the audio and video clocks are coherent.  On iMX6
> > hardware using this, the audio is clocked at the rate defined by the
> > TDMS clock and the CTS/N values.
> 
> I'll admit I haven't looked at the audio section of dw_hdmi much, but
> I'd imagine that for all users of this controller / PHY the audio and
> video clocks are coherent.

Not if the audio clock comes from an I2S master rather than being
sourced from the HDMI block.

> I think in the perfect world we'd be able to generate exactly
> 25174825.174825177 Hz and we'd use all the rates from the HDMI spec.

To generate something of that accuracy, you'd need something like a
caesium fountain atomic clock.

> and we'd get spot on 32 kHz audio.  ...but I'm simply saying that
> we're not in that perfect world yet.
> 
> Also note that there are many many rates not in the HDMI spec that
> could benefit from similar optimization of trying to adjust N to make
> an integral CTS.

Now go and look at the HDMI spec, where it gives the CTS value for
74.25/1.001 for 32kHz.  That can't be represented by an integer CTS
value, so using this hardware, we can't generate that sample rate
without an error.  We'd use a fixed CTS value of 210937 instead, which
works out at a 0.00024% error.  Again, not worth worrying about.


> 
> ---
> 
> As a side note: I realized one part of the HDMI spec that isn't trying
> to make an integral value but still uses a different value for N: 297
> MHz.  From the DesignWare spec I have it appears that 594 MHz is
> similar.  For those cases it looks like we have:

297MHz _does_ work.

297000000 * 3072 / 222750 = 128 * 32000 exactly.

> 
> if (pixel_clk == 297000000) {
>   switch (freq) {
>   case 32000:
>     return (128 * freq) / 1333;

Plug the numbers in.  128 * 32000 / 1333 = 3072.96 but because we're using
integer math, that's 3072.  Which just happens to be the value in the HDMI
spec.

>   case 44100:
>   case 48000:
>   case 88200:
>   case 96000:
>   case 176400:
>     return (128 * freq) / 1200;

Do the math again.  You get the spec figures for N.

-- 
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 6/9] drm: bridge/dw_hdmi: adjust pixel clock values in N calculation
@ 2015-09-05  0:27               ` Russell King - ARM Linux
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-09-05  0:27 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Sep 04, 2015 at 04:50:03PM -0700, Doug Anderson wrote:
> Russell,
> 
> On Fri, Sep 4, 2015 at 2:24 PM, Russell King - ARM Linux
> <linux@arm.linux.org.uk> wrote:
> >> In your case you're probably making the value that Linux
> >> asked you to make, AKA 25.175000 MHz.
> >
> > ... which is the spec value.
> 
> This is where we're not on the same page.  Where in the spec does it
> say 25.17500 MHz?  I see in the spec:
>  25.2 / 1.001

Section 4 of CEA-861-B, which defines the video clock rates and their
accuracy of 0.5%.

> ...and this is a crucial difference here.  Please double-check my math, but:
> 
> (25175000 * 4576) / (128 * 32000.)
> => 28125.1953125
> 
> (25174825 * 4576) / (128 * 32000.)
> => 28125.0
> 
> This calculation is what led to my belief that the goal here is to
> make an integral CTS.  If you have 25.175 MHZ clock and N of 4576 you
> _will not_ have an integral CTS.  If you instead have 25.174825 MHz
> clock and N of 4576 you _will_ have an integral CTS.

Right, but 25.175 is close enough to 25.174825.  Do this calculation:

25175000 * 4576 / 28125 / 128

That'll give you the resulting audio sample rate, which is 32000.222Hz.
That's an error of... 0.00069%, which is probably around the typical
error of your average crystal oscillator.  Really not worth bothering
with.

> Said another way:
> 
> 1. The reason 25174825 Hz has a different N is to make an integral CTS.
> 
> 2. If you are indeed making 25175000 then there is no need for a
> different N to make an integral CTS
> 
> 3. If you use 4576 for N but you're making 25175000 Hz, you end up in
> a _worse_ position than if you use the standard 4096 for N.

Total rubbish.  Sorry, but it is.

Follow the code.  Pixel clock is 25175000.  For 32kHz, N will be 4576.
25175000 * 4576 = 1.152008e11.  Divide that by the audio clock rate
(128 * 32000) gives 28125.19531.  Since we're using integer division,
that gets rounded down to 28125.

DRM uses a clock rate of "25175" to represent 25.2/1.001 modes.  So,
if your hardware sets a video clock rate of 25.2MHz/1.001, then you
end up with a sample rate of exactly 32kHz.  If you set exactly
25.175MHz, you end up with an approximate 32kHz sample rate - one
which is 0.00069% in error, which is (excluse the language) fuck all
different from exactly 32kHz.

Are you _really_ going to continue arguing over a 0.00069% error?
If you are, I'm not going to listen anymore - it's soo damned small
that it's not worth bothering with.  At all.

The only time that you'd need to worry about it is if you wanted a
super-accurate system, and for that you'd need an atomic clock to
source your system clocks to reduce aging effects, temperature
induced drift, etc, maybe locking the atomic clock to a national
frequency standard like the Anthorn MSF 60kHz transmitter signal
broadcast by the UK National Physics Laboratory.

> >> As a first step I'd suggest just removing all the special cases and
> >> add a comment.  From real world testing it doesn't seem terribly
> >> critical to be slightly off on CTS.  ...and in any case for any clock
> >> rates except the small handful in the HDMI spec we'll be slightly off
> >> on CTS anyway...
> >
> > They're not "special cases" made up to fit something - they're from the
> > tables in the HDMI specification.
> 
> They are definitely "special cases".  There is a general rule in the
> code you posted (aim for 128 * freq) and these are cases for certain
> clocks that are an exception to the general rule.  AKA they are
> special cases.

Sorry, I disagree with you.

> > That assumes that the audio and video clocks are coherent.  On iMX6
> > hardware using this, the audio is clocked at the rate defined by the
> > TDMS clock and the CTS/N values.
> 
> I'll admit I haven't looked at the audio section of dw_hdmi much, but
> I'd imagine that for all users of this controller / PHY the audio and
> video clocks are coherent.

Not if the audio clock comes from an I2S master rather than being
sourced from the HDMI block.

> I think in the perfect world we'd be able to generate exactly
> 25174825.174825177 Hz and we'd use all the rates from the HDMI spec.

To generate something of that accuracy, you'd need something like a
caesium fountain atomic clock.

> and we'd get spot on 32 kHz audio.  ...but I'm simply saying that
> we're not in that perfect world yet.
> 
> Also note that there are many many rates not in the HDMI spec that
> could benefit from similar optimization of trying to adjust N to make
> an integral CTS.

Now go and look at the HDMI spec, where it gives the CTS value for
74.25/1.001 for 32kHz.  That can't be represented by an integer CTS
value, so using this hardware, we can't generate that sample rate
without an error.  We'd use a fixed CTS value of 210937 instead, which
works out at a 0.00024% error.  Again, not worth worrying about.


> 
> ---
> 
> As a side note: I realized one part of the HDMI spec that isn't trying
> to make an integral value but still uses a different value for N: 297
> MHz.  From the DesignWare spec I have it appears that 594 MHz is
> similar.  For those cases it looks like we have:

297MHz _does_ work.

297000000 * 3072 / 222750 = 128 * 32000 exactly.

> 
> if (pixel_clk == 297000000) {
>   switch (freq) {
>   case 32000:
>     return (128 * freq) / 1333;

Plug the numbers in.  128 * 32000 / 1333 = 3072.96 but because we're using
integer math, that's 3072.  Which just happens to be the value in the HDMI
spec.

>   case 44100:
>   case 48000:
>   case 88200:
>   case 96000:
>   case 176400:
>     return (128 * freq) / 1200;

Do the math again.  You get the spec figures for N.

-- 
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 6/9] drm: bridge/dw_hdmi: adjust pixel clock values in N calculation
  2015-09-05  0:27               ` Russell King - ARM Linux
  (?)
@ 2015-09-05  2:03                 ` Doug Anderson
  -1 siblings, 0 replies; 226+ messages in thread
From: Doug Anderson @ 2015-09-05  2:03 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: open list:ARM/Rockchip SoC...,
	alsa-devel, dri-devel, linux-kernel, linux-arm-kernel,
	Philipp Zabel, Andy Yan, Yakir Yang, Fabio Estevam, Mark Brown,
	Takashi Iwai, Jaroslav Kysela, Sascha Hauer, Jon Nettleton,
	David Airlie

Russell,

On Fri, Sep 4, 2015 at 5:27 PM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> On Fri, Sep 04, 2015 at 04:50:03PM -0700, Doug Anderson wrote:
>> Russell,
>>
>> On Fri, Sep 4, 2015 at 2:24 PM, Russell King - ARM Linux
>> <linux@arm.linux.org.uk> wrote:
>> >> In your case you're probably making the value that Linux
>> >> asked you to make, AKA 25.175000 MHz.
>> >
>> > ... which is the spec value.
>>
>> This is where we're not on the same page.  Where in the spec does it
>> say 25.17500 MHz?  I see in the spec:
>>  25.2 / 1.001
>
> Section 4 of CEA-861-B, which defines the video clock rates and their
> accuracy of 0.5%.

Then perhaps you shouldn't be using a switch statement.  You won't
catch all values that are within .05% of (25.2 / 1.001).


>> ...and this is a crucial difference here.  Please double-check my math, but:
>>
>> (25175000 * 4576) / (128 * 32000.)
>> => 28125.1953125
>>
>> (25174825 * 4576) / (128 * 32000.)
>> => 28125.0
>>
>> This calculation is what led to my belief that the goal here is to
>> make an integral CTS.  If you have 25.175 MHZ clock and N of 4576 you
>> _will not_ have an integral CTS.  If you instead have 25.174825 MHz
>> clock and N of 4576 you _will_ have an integral CTS.
>
> Right, but 25.175 is close enough to 25.174825.  Do this calculation:
>
> 25175000 * 4576 / 28125 / 128
>
> That'll give you the resulting audio sample rate, which is 32000.222Hz.
> That's an error of... 0.00069%, which is probably around the typical
> error of your average crystal oscillator.  Really not worth bothering
> with.

OK, so do this calculation:

25175000 * 4096 / 25175 / 128

You get 32000.000000000000000000

I'm not saying there's anything terribly wrong with 32000.222 Hz and
I'm sure it will work just dandy for you.  I'm saying that you're
adding complexity _and_ ending up with a slightly worse rate.

AKA: just replace your entire "compute_n" function with:

return (128 * freq) / 1000;

...and it's 100% simpler _and_ gets you a (marginally) better rate
(assuming you really have 22.175000).  If it was just about a
32000.222 vs 32000 I'd not be saying anything right now.  It's about
adding complexity.


>> Said another way:
>>
>> 1. The reason 25174825 Hz has a different N is to make an integral CTS.
>>
>> 2. If you are indeed making 25175000 then there is no need for a
>> different N to make an integral CTS
>>
>> 3. If you use 4576 for N but you're making 25175000 Hz, you end up in
>> a _worse_ position than if you use the standard 4096 for N.
>
> Total rubbish.  Sorry, but it is.
>
> Follow the code.  Pixel clock is 25175000.  For 32kHz, N will be 4576.
> 25175000 * 4576 = 1.152008e11.  Divide that by the audio clock rate
> (128 * 32000) gives 28125.19531.  Since we're using integer division,
> that gets rounded down to 28125.
>
> DRM uses a clock rate of "25175" to represent 25.2/1.001 modes.  So,
> if your hardware sets a video clock rate of 25.2MHz/1.001, then you
> end up with a sample rate of exactly 32kHz.  If you set exactly
> 25.175MHz, you end up with an approximate 32kHz sample rate - one
> which is 0.00069% in error, which is (excluse the language) fuck all
> different from exactly 32kHz.

Agree that the difference is negligible.

I will say that IMHO the kind folks who wrote the HDMI spec were still
trying their best to make that error 0.00%.  That's entirely the
reason that they have that table and they don't just use "(128 * freq)
/ 1000" for everything.

AKA, I can imagine something like:

Person 1: Is there any reason to pick a N value that's exactly (128 *
freq) / 1000?

Person 2: Not really

Person 1: Hrm, but I notice that I can get a tiny bit more accurate
audio clock when I have a pixel clock of (25.2 / 1.001) if I use a N
that's not (128 * freq) / 1000.  Is that OK?

Person 2: Yeah, go ahead.  Add it to the spec.

Person 1: OK.  I've got some nifty tables I can add.  Cool!  Now we
get exactly the right audio clock.

Person 2: Nice job!

...but I have no idea if that's really true.


> Are you _really_ going to continue arguing over a 0.00069% error?
> If you are, I'm not going to listen anymore - it's soo damned small
> that it's not worth bothering with.  At all.

Well, I think I've adequately expressed my opinion.  If you want to
land your patch, I certainly won't yell.  I think it adds extra
complexity and produces a (marginally) inferior audio rate, but that's
up to the folks who maintain the code to deal with.


>> As a side note: I realized one part of the HDMI spec that isn't trying
>> to make an integral value but still uses a different value for N: 297
>> MHz.  From the DesignWare spec I have it appears that 594 MHz is
>> similar.  For those cases it looks like we have:
>
> 297MHz _does_ work.
>
> 297000000 * 3072 / 222750 = 128 * 32000 exactly.

I guess I didn't express myself clearly enough.  I'm saying that:

* The only reason I can discern for using non "(128 * freq) / 1000" N
values for rates < 297 Mhz is to try to make an integral CTS.

* For rates >= 297 MHz you could make CTS integral and still keep
"(128 * freq) / 1000", but the spec still says to use something
different.  I don't know why.  My formula accurately makes values in
the spec for 297 MHz.


Anyway, I'm about done commenting on this thread.  Feel free to land
this if folks are happy with it, but I'd prefer not to have my
Reviewed-by on it given all that I've discovered.

-Doug

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 6/9] drm: bridge/dw_hdmi: adjust pixel clock values in N calculation
@ 2015-09-05  2:03                 ` Doug Anderson
  0 siblings, 0 replies; 226+ messages in thread
From: Doug Anderson @ 2015-09-05  2:03 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Fabio Estevam, alsa-devel, linux-kernel, dri-devel,
	Jaroslav Kysela, open list:ARM/Rockchip SoC...,
	Mark Brown, Yakir Yang, Andy Yan, linux-arm-kernel

Russell,

On Fri, Sep 4, 2015 at 5:27 PM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> On Fri, Sep 04, 2015 at 04:50:03PM -0700, Doug Anderson wrote:
>> Russell,
>>
>> On Fri, Sep 4, 2015 at 2:24 PM, Russell King - ARM Linux
>> <linux@arm.linux.org.uk> wrote:
>> >> In your case you're probably making the value that Linux
>> >> asked you to make, AKA 25.175000 MHz.
>> >
>> > ... which is the spec value.
>>
>> This is where we're not on the same page.  Where in the spec does it
>> say 25.17500 MHz?  I see in the spec:
>>  25.2 / 1.001
>
> Section 4 of CEA-861-B, which defines the video clock rates and their
> accuracy of 0.5%.

Then perhaps you shouldn't be using a switch statement.  You won't
catch all values that are within .05% of (25.2 / 1.001).


>> ...and this is a crucial difference here.  Please double-check my math, but:
>>
>> (25175000 * 4576) / (128 * 32000.)
>> => 28125.1953125
>>
>> (25174825 * 4576) / (128 * 32000.)
>> => 28125.0
>>
>> This calculation is what led to my belief that the goal here is to
>> make an integral CTS.  If you have 25.175 MHZ clock and N of 4576 you
>> _will not_ have an integral CTS.  If you instead have 25.174825 MHz
>> clock and N of 4576 you _will_ have an integral CTS.
>
> Right, but 25.175 is close enough to 25.174825.  Do this calculation:
>
> 25175000 * 4576 / 28125 / 128
>
> That'll give you the resulting audio sample rate, which is 32000.222Hz.
> That's an error of... 0.00069%, which is probably around the typical
> error of your average crystal oscillator.  Really not worth bothering
> with.

OK, so do this calculation:

25175000 * 4096 / 25175 / 128

You get 32000.000000000000000000

I'm not saying there's anything terribly wrong with 32000.222 Hz and
I'm sure it will work just dandy for you.  I'm saying that you're
adding complexity _and_ ending up with a slightly worse rate.

AKA: just replace your entire "compute_n" function with:

return (128 * freq) / 1000;

...and it's 100% simpler _and_ gets you a (marginally) better rate
(assuming you really have 22.175000).  If it was just about a
32000.222 vs 32000 I'd not be saying anything right now.  It's about
adding complexity.


>> Said another way:
>>
>> 1. The reason 25174825 Hz has a different N is to make an integral CTS.
>>
>> 2. If you are indeed making 25175000 then there is no need for a
>> different N to make an integral CTS
>>
>> 3. If you use 4576 for N but you're making 25175000 Hz, you end up in
>> a _worse_ position than if you use the standard 4096 for N.
>
> Total rubbish.  Sorry, but it is.
>
> Follow the code.  Pixel clock is 25175000.  For 32kHz, N will be 4576.
> 25175000 * 4576 = 1.152008e11.  Divide that by the audio clock rate
> (128 * 32000) gives 28125.19531.  Since we're using integer division,
> that gets rounded down to 28125.
>
> DRM uses a clock rate of "25175" to represent 25.2/1.001 modes.  So,
> if your hardware sets a video clock rate of 25.2MHz/1.001, then you
> end up with a sample rate of exactly 32kHz.  If you set exactly
> 25.175MHz, you end up with an approximate 32kHz sample rate - one
> which is 0.00069% in error, which is (excluse the language) fuck all
> different from exactly 32kHz.

Agree that the difference is negligible.

I will say that IMHO the kind folks who wrote the HDMI spec were still
trying their best to make that error 0.00%.  That's entirely the
reason that they have that table and they don't just use "(128 * freq)
/ 1000" for everything.

AKA, I can imagine something like:

Person 1: Is there any reason to pick a N value that's exactly (128 *
freq) / 1000?

Person 2: Not really

Person 1: Hrm, but I notice that I can get a tiny bit more accurate
audio clock when I have a pixel clock of (25.2 / 1.001) if I use a N
that's not (128 * freq) / 1000.  Is that OK?

Person 2: Yeah, go ahead.  Add it to the spec.

Person 1: OK.  I've got some nifty tables I can add.  Cool!  Now we
get exactly the right audio clock.

Person 2: Nice job!

...but I have no idea if that's really true.


> Are you _really_ going to continue arguing over a 0.00069% error?
> If you are, I'm not going to listen anymore - it's soo damned small
> that it's not worth bothering with.  At all.

Well, I think I've adequately expressed my opinion.  If you want to
land your patch, I certainly won't yell.  I think it adds extra
complexity and produces a (marginally) inferior audio rate, but that's
up to the folks who maintain the code to deal with.


>> As a side note: I realized one part of the HDMI spec that isn't trying
>> to make an integral value but still uses a different value for N: 297
>> MHz.  From the DesignWare spec I have it appears that 594 MHz is
>> similar.  For those cases it looks like we have:
>
> 297MHz _does_ work.
>
> 297000000 * 3072 / 222750 = 128 * 32000 exactly.

I guess I didn't express myself clearly enough.  I'm saying that:

* The only reason I can discern for using non "(128 * freq) / 1000" N
values for rates < 297 Mhz is to try to make an integral CTS.

* For rates >= 297 MHz you could make CTS integral and still keep
"(128 * freq) / 1000", but the spec still says to use something
different.  I don't know why.  My formula accurately makes values in
the spec for 297 MHz.


Anyway, I'm about done commenting on this thread.  Feel free to land
this if folks are happy with it, but I'd prefer not to have my
Reviewed-by on it given all that I've discovered.

-Doug
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 6/9] drm: bridge/dw_hdmi: adjust pixel clock values in N calculation
@ 2015-09-05  2:03                 ` Doug Anderson
  0 siblings, 0 replies; 226+ messages in thread
From: Doug Anderson @ 2015-09-05  2:03 UTC (permalink / raw)
  To: linux-arm-kernel

Russell,

On Fri, Sep 4, 2015 at 5:27 PM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> On Fri, Sep 04, 2015 at 04:50:03PM -0700, Doug Anderson wrote:
>> Russell,
>>
>> On Fri, Sep 4, 2015 at 2:24 PM, Russell King - ARM Linux
>> <linux@arm.linux.org.uk> wrote:
>> >> In your case you're probably making the value that Linux
>> >> asked you to make, AKA 25.175000 MHz.
>> >
>> > ... which is the spec value.
>>
>> This is where we're not on the same page.  Where in the spec does it
>> say 25.17500 MHz?  I see in the spec:
>>  25.2 / 1.001
>
> Section 4 of CEA-861-B, which defines the video clock rates and their
> accuracy of 0.5%.

Then perhaps you shouldn't be using a switch statement.  You won't
catch all values that are within .05% of (25.2 / 1.001).


>> ...and this is a crucial difference here.  Please double-check my math, but:
>>
>> (25175000 * 4576) / (128 * 32000.)
>> => 28125.1953125
>>
>> (25174825 * 4576) / (128 * 32000.)
>> => 28125.0
>>
>> This calculation is what led to my belief that the goal here is to
>> make an integral CTS.  If you have 25.175 MHZ clock and N of 4576 you
>> _will not_ have an integral CTS.  If you instead have 25.174825 MHz
>> clock and N of 4576 you _will_ have an integral CTS.
>
> Right, but 25.175 is close enough to 25.174825.  Do this calculation:
>
> 25175000 * 4576 / 28125 / 128
>
> That'll give you the resulting audio sample rate, which is 32000.222Hz.
> That's an error of... 0.00069%, which is probably around the typical
> error of your average crystal oscillator.  Really not worth bothering
> with.

OK, so do this calculation:

25175000 * 4096 / 25175 / 128

You get 32000.000000000000000000

I'm not saying there's anything terribly wrong with 32000.222 Hz and
I'm sure it will work just dandy for you.  I'm saying that you're
adding complexity _and_ ending up with a slightly worse rate.

AKA: just replace your entire "compute_n" function with:

return (128 * freq) / 1000;

...and it's 100% simpler _and_ gets you a (marginally) better rate
(assuming you really have 22.175000).  If it was just about a
32000.222 vs 32000 I'd not be saying anything right now.  It's about
adding complexity.


>> Said another way:
>>
>> 1. The reason 25174825 Hz has a different N is to make an integral CTS.
>>
>> 2. If you are indeed making 25175000 then there is no need for a
>> different N to make an integral CTS
>>
>> 3. If you use 4576 for N but you're making 25175000 Hz, you end up in
>> a _worse_ position than if you use the standard 4096 for N.
>
> Total rubbish.  Sorry, but it is.
>
> Follow the code.  Pixel clock is 25175000.  For 32kHz, N will be 4576.
> 25175000 * 4576 = 1.152008e11.  Divide that by the audio clock rate
> (128 * 32000) gives 28125.19531.  Since we're using integer division,
> that gets rounded down to 28125.
>
> DRM uses a clock rate of "25175" to represent 25.2/1.001 modes.  So,
> if your hardware sets a video clock rate of 25.2MHz/1.001, then you
> end up with a sample rate of exactly 32kHz.  If you set exactly
> 25.175MHz, you end up with an approximate 32kHz sample rate - one
> which is 0.00069% in error, which is (excluse the language) fuck all
> different from exactly 32kHz.

Agree that the difference is negligible.

I will say that IMHO the kind folks who wrote the HDMI spec were still
trying their best to make that error 0.00%.  That's entirely the
reason that they have that table and they don't just use "(128 * freq)
/ 1000" for everything.

AKA, I can imagine something like:

Person 1: Is there any reason to pick a N value that's exactly (128 *
freq) / 1000?

Person 2: Not really

Person 1: Hrm, but I notice that I can get a tiny bit more accurate
audio clock when I have a pixel clock of (25.2 / 1.001) if I use a N
that's not (128 * freq) / 1000.  Is that OK?

Person 2: Yeah, go ahead.  Add it to the spec.

Person 1: OK.  I've got some nifty tables I can add.  Cool!  Now we
get exactly the right audio clock.

Person 2: Nice job!

...but I have no idea if that's really true.


> Are you _really_ going to continue arguing over a 0.00069% error?
> If you are, I'm not going to listen anymore - it's soo damned small
> that it's not worth bothering with.  At all.

Well, I think I've adequately expressed my opinion.  If you want to
land your patch, I certainly won't yell.  I think it adds extra
complexity and produces a (marginally) inferior audio rate, but that's
up to the folks who maintain the code to deal with.


>> As a side note: I realized one part of the HDMI spec that isn't trying
>> to make an integral value but still uses a different value for N: 297
>> MHz.  From the DesignWare spec I have it appears that 594 MHz is
>> similar.  For those cases it looks like we have:
>
> 297MHz _does_ work.
>
> 297000000 * 3072 / 222750 = 128 * 32000 exactly.

I guess I didn't express myself clearly enough.  I'm saying that:

* The only reason I can discern for using non "(128 * freq) / 1000" N
values for rates < 297 Mhz is to try to make an integral CTS.

* For rates >= 297 MHz you could make CTS integral and still keep
"(128 * freq) / 1000", but the spec still says to use something
different.  I don't know why.  My formula accurately makes values in
the spec for 297 MHz.


Anyway, I'm about done commenting on this thread.  Feel free to land
this if folks are happy with it, but I'd prefer not to have my
Reviewed-by on it given all that I've discovered.

-Doug

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 6/9] drm: bridge/dw_hdmi: adjust pixel clock values in N calculation
  2015-09-05  2:03                 ` Doug Anderson
  (?)
@ 2015-09-05  8:31                   ` Russell King - ARM Linux
  -1 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-09-05  8:31 UTC (permalink / raw)
  To: Doug Anderson
  Cc: open list:ARM/Rockchip SoC...,
	alsa-devel, dri-devel, linux-kernel, linux-arm-kernel,
	Philipp Zabel, Andy Yan, Yakir Yang, Fabio Estevam, Mark Brown,
	Takashi Iwai, Jaroslav Kysela, Sascha Hauer, Jon Nettleton,
	David Airlie

On Fri, Sep 04, 2015 at 07:03:11PM -0700, Doug Anderson wrote:
> AKA: just replace your entire "compute_n" function with:
> 
> return (128 * freq) / 1000;
> 
> ...and it's 100% simpler _and_ gets you a (marginally) better rate
> (assuming you really have 22.175000).  If it was just about a
> 32000.222 vs 32000 I'd not be saying anything right now.  It's about
> adding complexity.

No.  It doesn't work for all cases.  Do the calculations for every
sample rate in those tables in the HDMI spec, and you'll find out
why.

-- 
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 6/9] drm: bridge/dw_hdmi: adjust pixel clock values in N calculation
@ 2015-09-05  8:31                   ` Russell King - ARM Linux
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-09-05  8:31 UTC (permalink / raw)
  To: Doug Anderson
  Cc: Fabio Estevam, alsa-devel, linux-kernel, dri-devel,
	Jaroslav Kysela, open list:ARM/Rockchip SoC...,
	Mark Brown, Yakir Yang, Andy Yan, linux-arm-kernel

On Fri, Sep 04, 2015 at 07:03:11PM -0700, Doug Anderson wrote:
> AKA: just replace your entire "compute_n" function with:
> 
> return (128 * freq) / 1000;
> 
> ...and it's 100% simpler _and_ gets you a (marginally) better rate
> (assuming you really have 22.175000).  If it was just about a
> 32000.222 vs 32000 I'd not be saying anything right now.  It's about
> adding complexity.

No.  It doesn't work for all cases.  Do the calculations for every
sample rate in those tables in the HDMI spec, and you'll find out
why.

-- 
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 6/9] drm: bridge/dw_hdmi: adjust pixel clock values in N calculation
@ 2015-09-05  8:31                   ` Russell King - ARM Linux
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-09-05  8:31 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Sep 04, 2015 at 07:03:11PM -0700, Doug Anderson wrote:
> AKA: just replace your entire "compute_n" function with:
> 
> return (128 * freq) / 1000;
> 
> ...and it's 100% simpler _and_ gets you a (marginally) better rate
> (assuming you really have 22.175000).  If it was just about a
> 32000.222 vs 32000 I'd not be saying anything right now.  It's about
> adding complexity.

No.  It doesn't work for all cases.  Do the calculations for every
sample rate in those tables in the HDMI spec, and you'll find out
why.

-- 
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 6/9] drm: bridge/dw_hdmi: adjust pixel clock values in N calculation
  2015-09-05  2:03                 ` Doug Anderson
  (?)
@ 2015-09-05  8:34                   ` Russell King - ARM Linux
  -1 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-09-05  8:34 UTC (permalink / raw)
  To: Doug Anderson
  Cc: open list:ARM/Rockchip SoC...,
	alsa-devel, dri-devel, linux-kernel, linux-arm-kernel,
	Philipp Zabel, Andy Yan, Yakir Yang, Fabio Estevam, Mark Brown,
	Takashi Iwai, Jaroslav Kysela, Sascha Hauer, Jon Nettleton,
	David Airlie

On Fri, Sep 04, 2015 at 07:03:11PM -0700, Doug Anderson wrote:
> Then perhaps you shouldn't be using a switch statement.  You won't
> catch all values that are within .05% of (25.2 / 1.001).

No.

The clock rates you get ultimately come from the EDID via either the
detailed timing modes or from the CEA mode IDs, which are then looked
up in tables in the DRM EDID parsing code.

Either way, you will end up with 25175 and not 25170 or something
strange based on what the platform does.

-- 
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 6/9] drm: bridge/dw_hdmi: adjust pixel clock values in N calculation
@ 2015-09-05  8:34                   ` Russell King - ARM Linux
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-09-05  8:34 UTC (permalink / raw)
  To: Doug Anderson
  Cc: Fabio Estevam, alsa-devel, linux-kernel, dri-devel,
	Jaroslav Kysela, open list:ARM/Rockchip SoC...,
	Mark Brown, Yakir Yang, Andy Yan, linux-arm-kernel

On Fri, Sep 04, 2015 at 07:03:11PM -0700, Doug Anderson wrote:
> Then perhaps you shouldn't be using a switch statement.  You won't
> catch all values that are within .05% of (25.2 / 1.001).

No.

The clock rates you get ultimately come from the EDID via either the
detailed timing modes or from the CEA mode IDs, which are then looked
up in tables in the DRM EDID parsing code.

Either way, you will end up with 25175 and not 25170 or something
strange based on what the platform does.

-- 
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 6/9] drm: bridge/dw_hdmi: adjust pixel clock values in N calculation
@ 2015-09-05  8:34                   ` Russell King - ARM Linux
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-09-05  8:34 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Sep 04, 2015 at 07:03:11PM -0700, Doug Anderson wrote:
> Then perhaps you shouldn't be using a switch statement.  You won't
> catch all values that are within .05% of (25.2 / 1.001).

No.

The clock rates you get ultimately come from the EDID via either the
detailed timing modes or from the CEA mode IDs, which are then looked
up in tables in the DRM EDID parsing code.

Either way, you will end up with 25175 and not 25170 or something
strange based on what the platform does.

-- 
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 6/9] drm: bridge/dw_hdmi: adjust pixel clock values in N calculation
  2015-09-05  8:31                   ` Russell King - ARM Linux
  (?)
@ 2015-09-05 13:46                     ` Doug Anderson
  -1 siblings, 0 replies; 226+ messages in thread
From: Doug Anderson @ 2015-09-05 13:46 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: open list:ARM/Rockchip SoC...,
	alsa-devel, dri-devel, linux-kernel, linux-arm-kernel,
	Philipp Zabel, Andy Yan, Yakir Yang, Fabio Estevam, Mark Brown,
	Takashi Iwai, Jaroslav Kysela, Sascha Hauer, Jon Nettleton,
	David Airlie

Russell,

On Sat, Sep 5, 2015 at 1:31 AM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> On Fri, Sep 04, 2015 at 07:03:11PM -0700, Doug Anderson wrote:
>> AKA: just replace your entire "compute_n" function with:
>>
>> return (128 * freq) / 1000;
>>
>> ...and it's 100% simpler _and_ gets you a (marginally) better rate
>> (assuming you really have 22.175000).  If it was just about a
>> 32000.222 vs 32000 I'd not be saying anything right now.  It's about
>> adding complexity.
>
> No.  It doesn't work for all cases.  Do the calculations for every
> sample rate in those tables in the HDMI spec, and you'll find out
> why.

If you know the answer, just tell me.  If you're talking about 74.25
vs. 32 kHz it is further evidence of what I'm saying.  Note that
picking only one of the two listed CTS values again puts you in a
worse position for regenerating the proper audio clock then just using
the default N=4096.

-Doug

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 6/9] drm: bridge/dw_hdmi: adjust pixel clock values in N calculation
@ 2015-09-05 13:46                     ` Doug Anderson
  0 siblings, 0 replies; 226+ messages in thread
From: Doug Anderson @ 2015-09-05 13:46 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Fabio Estevam, alsa-devel, linux-kernel, dri-devel,
	Jaroslav Kysela, open list:ARM/Rockchip SoC...,
	Mark Brown, Yakir Yang, Andy Yan, linux-arm-kernel

Russell,

On Sat, Sep 5, 2015 at 1:31 AM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> On Fri, Sep 04, 2015 at 07:03:11PM -0700, Doug Anderson wrote:
>> AKA: just replace your entire "compute_n" function with:
>>
>> return (128 * freq) / 1000;
>>
>> ...and it's 100% simpler _and_ gets you a (marginally) better rate
>> (assuming you really have 22.175000).  If it was just about a
>> 32000.222 vs 32000 I'd not be saying anything right now.  It's about
>> adding complexity.
>
> No.  It doesn't work for all cases.  Do the calculations for every
> sample rate in those tables in the HDMI spec, and you'll find out
> why.

If you know the answer, just tell me.  If you're talking about 74.25
vs. 32 kHz it is further evidence of what I'm saying.  Note that
picking only one of the two listed CTS values again puts you in a
worse position for regenerating the proper audio clock then just using
the default N=4096.

-Doug
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 6/9] drm: bridge/dw_hdmi: adjust pixel clock values in N calculation
@ 2015-09-05 13:46                     ` Doug Anderson
  0 siblings, 0 replies; 226+ messages in thread
From: Doug Anderson @ 2015-09-05 13:46 UTC (permalink / raw)
  To: linux-arm-kernel

Russell,

On Sat, Sep 5, 2015 at 1:31 AM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> On Fri, Sep 04, 2015 at 07:03:11PM -0700, Doug Anderson wrote:
>> AKA: just replace your entire "compute_n" function with:
>>
>> return (128 * freq) / 1000;
>>
>> ...and it's 100% simpler _and_ gets you a (marginally) better rate
>> (assuming you really have 22.175000).  If it was just about a
>> 32000.222 vs 32000 I'd not be saying anything right now.  It's about
>> adding complexity.
>
> No.  It doesn't work for all cases.  Do the calculations for every
> sample rate in those tables in the HDMI spec, and you'll find out
> why.

If you know the answer, just tell me.  If you're talking about 74.25
vs. 32 kHz it is further evidence of what I'm saying.  Note that
picking only one of the two listed CTS values again puts you in a
worse position for regenerating the proper audio clock then just using
the default N=4096.

-Doug

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 6/9] drm: bridge/dw_hdmi: adjust pixel clock values in N calculation
  2015-09-05  8:34                   ` Russell King - ARM Linux
  (?)
@ 2015-09-05 13:50                     ` Doug Anderson
  -1 siblings, 0 replies; 226+ messages in thread
From: Doug Anderson @ 2015-09-05 13:50 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: open list:ARM/Rockchip SoC...,
	alsa-devel, dri-devel, linux-kernel, linux-arm-kernel,
	Philipp Zabel, Andy Yan, Yakir Yang, Fabio Estevam, Mark Brown,
	Takashi Iwai, Jaroslav Kysela, Sascha Hauer, Jon Nettleton,
	David Airlie

Russell,

On Sat, Sep 5, 2015 at 1:34 AM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> On Fri, Sep 04, 2015 at 07:03:11PM -0700, Doug Anderson wrote:
>> Then perhaps you shouldn't be using a switch statement.  You won't
>> catch all values that are within .05% of (25.2 / 1.001).
>
> No.
>
> The clock rates you get ultimately come from the EDID via either the
> detailed timing modes or from the CEA mode IDs, which are then looked
> up in tables in the DRM EDID parsing code.

I guess in my case the (non-upsteram) code is adjusting the clock in
fixup_mode.  It's no longer something based on the EDID.  Perhaps the
fault if there, but...


> Either way, you will end up with 25175 and not 25170 or something
> strange based on what the platform does.

I was talking to someone else about this and I guess the question is
whether you should be sending a N/CTS for audio based on the
theoretical or the actual clock.

If you are supposed to do calculations based on the theoretical clock
then you're right.  If you are supposed to do calculations based on
the actual clock then I'm not so sure.

Note that:
* I believe that you'll get better audio if you use the actual clock.

* If your actual clock is an integral number of kHz, the calculations
are simpler by using the actual clock.


-Doug

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 6/9] drm: bridge/dw_hdmi: adjust pixel clock values in N calculation
@ 2015-09-05 13:50                     ` Doug Anderson
  0 siblings, 0 replies; 226+ messages in thread
From: Doug Anderson @ 2015-09-05 13:50 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Fabio Estevam, alsa-devel, linux-kernel, dri-devel,
	Jaroslav Kysela, open list:ARM/Rockchip SoC...,
	Mark Brown, Yakir Yang, Andy Yan, linux-arm-kernel

Russell,

On Sat, Sep 5, 2015 at 1:34 AM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> On Fri, Sep 04, 2015 at 07:03:11PM -0700, Doug Anderson wrote:
>> Then perhaps you shouldn't be using a switch statement.  You won't
>> catch all values that are within .05% of (25.2 / 1.001).
>
> No.
>
> The clock rates you get ultimately come from the EDID via either the
> detailed timing modes or from the CEA mode IDs, which are then looked
> up in tables in the DRM EDID parsing code.

I guess in my case the (non-upsteram) code is adjusting the clock in
fixup_mode.  It's no longer something based on the EDID.  Perhaps the
fault if there, but...


> Either way, you will end up with 25175 and not 25170 or something
> strange based on what the platform does.

I was talking to someone else about this and I guess the question is
whether you should be sending a N/CTS for audio based on the
theoretical or the actual clock.

If you are supposed to do calculations based on the theoretical clock
then you're right.  If you are supposed to do calculations based on
the actual clock then I'm not so sure.

Note that:
* I believe that you'll get better audio if you use the actual clock.

* If your actual clock is an integral number of kHz, the calculations
are simpler by using the actual clock.


-Doug
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 6/9] drm: bridge/dw_hdmi: adjust pixel clock values in N calculation
@ 2015-09-05 13:50                     ` Doug Anderson
  0 siblings, 0 replies; 226+ messages in thread
From: Doug Anderson @ 2015-09-05 13:50 UTC (permalink / raw)
  To: linux-arm-kernel

Russell,

On Sat, Sep 5, 2015 at 1:34 AM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> On Fri, Sep 04, 2015 at 07:03:11PM -0700, Doug Anderson wrote:
>> Then perhaps you shouldn't be using a switch statement.  You won't
>> catch all values that are within .05% of (25.2 / 1.001).
>
> No.
>
> The clock rates you get ultimately come from the EDID via either the
> detailed timing modes or from the CEA mode IDs, which are then looked
> up in tables in the DRM EDID parsing code.

I guess in my case the (non-upsteram) code is adjusting the clock in
fixup_mode.  It's no longer something based on the EDID.  Perhaps the
fault if there, but...


> Either way, you will end up with 25175 and not 25170 or something
> strange based on what the platform does.

I was talking to someone else about this and I guess the question is
whether you should be sending a N/CTS for audio based on the
theoretical or the actual clock.

If you are supposed to do calculations based on the theoretical clock
then you're right.  If you are supposed to do calculations based on
the actual clock then I'm not so sure.

Note that:
* I believe that you'll get better audio if you use the actual clock.

* If your actual clock is an integral number of kHz, the calculations
are simpler by using the actual clock.


-Doug

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 6/9] drm: bridge/dw_hdmi: adjust pixel clock values in N calculation
  2015-09-05 13:46                     ` Doug Anderson
  (?)
@ 2015-09-05 14:01                       ` Russell King - ARM Linux
  -1 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-09-05 14:01 UTC (permalink / raw)
  To: Doug Anderson
  Cc: open list:ARM/Rockchip SoC...,
	alsa-devel, dri-devel, linux-kernel, linux-arm-kernel,
	Philipp Zabel, Andy Yan, Yakir Yang, Fabio Estevam, Mark Brown,
	Takashi Iwai, Jaroslav Kysela, Sascha Hauer, Jon Nettleton,
	David Airlie

On Sat, Sep 05, 2015 at 06:46:04AM -0700, Doug Anderson wrote:
> Russell,
> 
> On Sat, Sep 5, 2015 at 1:31 AM, Russell King - ARM Linux
> <linux@arm.linux.org.uk> wrote:
> > On Fri, Sep 04, 2015 at 07:03:11PM -0700, Doug Anderson wrote:
> >> AKA: just replace your entire "compute_n" function with:
> >>
> >> return (128 * freq) / 1000;
> >>
> >> ...and it's 100% simpler _and_ gets you a (marginally) better rate
> >> (assuming you really have 22.175000).  If it was just about a
> >> 32000.222 vs 32000 I'd not be saying anything right now.  It's about
> >> adding complexity.
> >
> > No.  It doesn't work for all cases.  Do the calculations for every
> > sample rate in those tables in the HDMI spec, and you'll find out
> > why.
> 
> If you know the answer, just tell me.  If you're talking about 74.25
> vs. 32 kHz it is further evidence of what I'm saying.  Note that
> picking only one of the two listed CTS values again puts you in a
> worse position for regenerating the proper audio clock then just using
> the default N=4096.

No it doesn't.

74.25MHz/1.001 * 4096 / (128 * 32000) = 74175 (rounded down)

Now do the calcuation.

(74.25MHz/1.001) / 74175 * 4096 = 4096045.511 => 32000.35556Hz
	=> error of 0.001111%

Now for the calcuation using the proscribed figures.

(74.25MHz/1.001) / 210937 * 11648 = 4096009.709 => 32000.07585Hz
	=> error of 0.000237%

That's significantly less error using that than your "better" idea.
Now, if we take the pixel clock rate as 74.175MHz, which is just a
representation of 74.25MHz/1.001:

74.175MHz / 210937 * 11648 = 4095964.198 => 31999.72029Hz
	=> error of 0.0008741%

That's still lower than your "better" idea.

And as I've already said, the pixel clock rate given to us here will
be the _specified_ clock rate of 74.175MHz, *not* some cocked up
platform screwed crap that you think we will.  It _will_ be 74175
not 74170 or some other shite like that.

Right, I've had enough.  I'm going to be ignoring this thread from now
on, this is a waste of my time - you clearly have no understanding of
what's going on here.

-- 
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 6/9] drm: bridge/dw_hdmi: adjust pixel clock values in N calculation
@ 2015-09-05 14:01                       ` Russell King - ARM Linux
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-09-05 14:01 UTC (permalink / raw)
  To: Doug Anderson
  Cc: Fabio Estevam, alsa-devel, linux-kernel, dri-devel,
	Jaroslav Kysela, open list:ARM/Rockchip SoC...,
	Mark Brown, Yakir Yang, Andy Yan, linux-arm-kernel

On Sat, Sep 05, 2015 at 06:46:04AM -0700, Doug Anderson wrote:
> Russell,
> 
> On Sat, Sep 5, 2015 at 1:31 AM, Russell King - ARM Linux
> <linux@arm.linux.org.uk> wrote:
> > On Fri, Sep 04, 2015 at 07:03:11PM -0700, Doug Anderson wrote:
> >> AKA: just replace your entire "compute_n" function with:
> >>
> >> return (128 * freq) / 1000;
> >>
> >> ...and it's 100% simpler _and_ gets you a (marginally) better rate
> >> (assuming you really have 22.175000).  If it was just about a
> >> 32000.222 vs 32000 I'd not be saying anything right now.  It's about
> >> adding complexity.
> >
> > No.  It doesn't work for all cases.  Do the calculations for every
> > sample rate in those tables in the HDMI spec, and you'll find out
> > why.
> 
> If you know the answer, just tell me.  If you're talking about 74.25
> vs. 32 kHz it is further evidence of what I'm saying.  Note that
> picking only one of the two listed CTS values again puts you in a
> worse position for regenerating the proper audio clock then just using
> the default N=4096.

No it doesn't.

74.25MHz/1.001 * 4096 / (128 * 32000) = 74175 (rounded down)

Now do the calcuation.

(74.25MHz/1.001) / 74175 * 4096 = 4096045.511 => 32000.35556Hz
	=> error of 0.001111%

Now for the calcuation using the proscribed figures.

(74.25MHz/1.001) / 210937 * 11648 = 4096009.709 => 32000.07585Hz
	=> error of 0.000237%

That's significantly less error using that than your "better" idea.
Now, if we take the pixel clock rate as 74.175MHz, which is just a
representation of 74.25MHz/1.001:

74.175MHz / 210937 * 11648 = 4095964.198 => 31999.72029Hz
	=> error of 0.0008741%

That's still lower than your "better" idea.

And as I've already said, the pixel clock rate given to us here will
be the _specified_ clock rate of 74.175MHz, *not* some cocked up
platform screwed crap that you think we will.  It _will_ be 74175
not 74170 or some other shite like that.

Right, I've had enough.  I'm going to be ignoring this thread from now
on, this is a waste of my time - you clearly have no understanding of
what's going on here.

-- 
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 6/9] drm: bridge/dw_hdmi: adjust pixel clock values in N calculation
@ 2015-09-05 14:01                       ` Russell King - ARM Linux
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-09-05 14:01 UTC (permalink / raw)
  To: linux-arm-kernel

On Sat, Sep 05, 2015 at 06:46:04AM -0700, Doug Anderson wrote:
> Russell,
> 
> On Sat, Sep 5, 2015 at 1:31 AM, Russell King - ARM Linux
> <linux@arm.linux.org.uk> wrote:
> > On Fri, Sep 04, 2015 at 07:03:11PM -0700, Doug Anderson wrote:
> >> AKA: just replace your entire "compute_n" function with:
> >>
> >> return (128 * freq) / 1000;
> >>
> >> ...and it's 100% simpler _and_ gets you a (marginally) better rate
> >> (assuming you really have 22.175000).  If it was just about a
> >> 32000.222 vs 32000 I'd not be saying anything right now.  It's about
> >> adding complexity.
> >
> > No.  It doesn't work for all cases.  Do the calculations for every
> > sample rate in those tables in the HDMI spec, and you'll find out
> > why.
> 
> If you know the answer, just tell me.  If you're talking about 74.25
> vs. 32 kHz it is further evidence of what I'm saying.  Note that
> picking only one of the two listed CTS values again puts you in a
> worse position for regenerating the proper audio clock then just using
> the default N=4096.

No it doesn't.

74.25MHz/1.001 * 4096 / (128 * 32000) = 74175 (rounded down)

Now do the calcuation.

(74.25MHz/1.001) / 74175 * 4096 = 4096045.511 => 32000.35556Hz
	=> error of 0.001111%

Now for the calcuation using the proscribed figures.

(74.25MHz/1.001) / 210937 * 11648 = 4096009.709 => 32000.07585Hz
	=> error of 0.000237%

That's significantly less error using that than your "better" idea.
Now, if we take the pixel clock rate as 74.175MHz, which is just a
representation of 74.25MHz/1.001:

74.175MHz / 210937 * 11648 = 4095964.198 => 31999.72029Hz
	=> error of 0.0008741%

That's still lower than your "better" idea.

And as I've already said, the pixel clock rate given to us here will
be the _specified_ clock rate of 74.175MHz, *not* some cocked up
platform screwed crap that you think we will.  It _will_ be 74175
not 74170 or some other shite like that.

Right, I've had enough.  I'm going to be ignoring this thread from now
on, this is a waste of my time - you clearly have no understanding of
what's going on here.

-- 
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 6/9] drm: bridge/dw_hdmi: adjust pixel clock values in N calculation
  2015-09-05 14:01                       ` Russell King - ARM Linux
  (?)
@ 2015-09-05 19:44                         ` Doug Anderson
  -1 siblings, 0 replies; 226+ messages in thread
From: Doug Anderson @ 2015-09-05 19:44 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: open list:ARM/Rockchip SoC...,
	alsa-devel, dri-devel, linux-kernel, linux-arm-kernel,
	Philipp Zabel, Andy Yan, Yakir Yang, Fabio Estevam, Mark Brown,
	Takashi Iwai, Jaroslav Kysela, Sascha Hauer, Jon Nettleton,
	David Airlie

Hi,

On Sat, Sep 5, 2015 at 7:01 AM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
>> If you know the answer, just tell me.  If you're talking about 74.25
>> vs. 32 kHz it is further evidence of what I'm saying.  Note that
>> picking only one of the two listed CTS values again puts you in a
>> worse position for regenerating the proper audio clock then just using
>> the default N=4096.
>
> No it doesn't.
>
> 74.25MHz/1.001 * 4096 / (128 * 32000) = 74175 (rounded down)
>
> Now do the calcuation.
>
> (74.25MHz/1.001) / 74175 * 4096 = 4096045.511 => 32000.35556Hz
>         => error of 0.001111%
>
> Now for the calcuation using the proscribed figures.
>
> (74.25MHz/1.001) / 210937 * 11648 = 4096009.709 => 32000.07585Hz
>         => error of 0.000237%
>

Why would you round down???  Round to the closest.

(74250000 / 1.001 * 4096)  / (128 * 32000.)
=> 74175.82417582418
=> 74176

(74250000 / 1.001) / 74176 * 4096 / 128
=> 31999.924148327947

That's actually the same error as yours: 0.000237%

You're right.  Yours isn't worse, but it's also not any better.

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 6/9] drm: bridge/dw_hdmi: adjust pixel clock values in N calculation
@ 2015-09-05 19:44                         ` Doug Anderson
  0 siblings, 0 replies; 226+ messages in thread
From: Doug Anderson @ 2015-09-05 19:44 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Fabio Estevam, alsa-devel, linux-kernel, dri-devel,
	Jaroslav Kysela, open list:ARM/Rockchip SoC...,
	Mark Brown, Yakir Yang, Andy Yan, linux-arm-kernel

Hi,

On Sat, Sep 5, 2015 at 7:01 AM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
>> If you know the answer, just tell me.  If you're talking about 74.25
>> vs. 32 kHz it is further evidence of what I'm saying.  Note that
>> picking only one of the two listed CTS values again puts you in a
>> worse position for regenerating the proper audio clock then just using
>> the default N=4096.
>
> No it doesn't.
>
> 74.25MHz/1.001 * 4096 / (128 * 32000) = 74175 (rounded down)
>
> Now do the calcuation.
>
> (74.25MHz/1.001) / 74175 * 4096 = 4096045.511 => 32000.35556Hz
>         => error of 0.001111%
>
> Now for the calcuation using the proscribed figures.
>
> (74.25MHz/1.001) / 210937 * 11648 = 4096009.709 => 32000.07585Hz
>         => error of 0.000237%
>

Why would you round down???  Round to the closest.

(74250000 / 1.001 * 4096)  / (128 * 32000.)
=> 74175.82417582418
=> 74176

(74250000 / 1.001) / 74176 * 4096 / 128
=> 31999.924148327947

That's actually the same error as yours: 0.000237%

You're right.  Yours isn't worse, but it's also not any better.
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 6/9] drm: bridge/dw_hdmi: adjust pixel clock values in N calculation
@ 2015-09-05 19:44                         ` Doug Anderson
  0 siblings, 0 replies; 226+ messages in thread
From: Doug Anderson @ 2015-09-05 19:44 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

On Sat, Sep 5, 2015 at 7:01 AM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
>> If you know the answer, just tell me.  If you're talking about 74.25
>> vs. 32 kHz it is further evidence of what I'm saying.  Note that
>> picking only one of the two listed CTS values again puts you in a
>> worse position for regenerating the proper audio clock then just using
>> the default N=4096.
>
> No it doesn't.
>
> 74.25MHz/1.001 * 4096 / (128 * 32000) = 74175 (rounded down)
>
> Now do the calcuation.
>
> (74.25MHz/1.001) / 74175 * 4096 = 4096045.511 => 32000.35556Hz
>         => error of 0.001111%
>
> Now for the calcuation using the proscribed figures.
>
> (74.25MHz/1.001) / 210937 * 11648 = 4096009.709 => 32000.07585Hz
>         => error of 0.000237%
>

Why would you round down???  Round to the closest.

(74250000 / 1.001 * 4096)  / (128 * 32000.)
=> 74175.82417582418
=> 74176

(74250000 / 1.001) / 74176 * 4096 / 128
=> 31999.924148327947

That's actually the same error as yours: 0.000237%

You're right.  Yours isn't worse, but it's also not any better.

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 03/12] gpu: imx: simplify sync polarity setting
  2015-08-08 16:03   ` Russell King
  (?)
@ 2015-10-06 17:57     ` Fabio Estevam
  -1 siblings, 0 replies; 226+ messages in thread
From: Fabio Estevam @ 2015-10-06 17:57 UTC (permalink / raw)
  To: Russell King
  Cc: linux-rockchip, DRI mailing list, linux-kernel, linux-arm-kernel,
	Fabio Estevam, Yakir Yang, Andy Yan

On Sat, Aug 8, 2015 at 1:03 PM, Russell King
<rmk+kernel@arm.linux.org.uk> wrote:
> Use a function to convert the sync pin to a bit mask for the DI_GENERAL
> register, and move this out of the interlace/non-interlace path to the
> common path.
>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

Reviewed-by: Fabio Estevam <fabio.estevam@freescale.com>

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 03/12] gpu: imx: simplify sync polarity setting
@ 2015-10-06 17:57     ` Fabio Estevam
  0 siblings, 0 replies; 226+ messages in thread
From: Fabio Estevam @ 2015-10-06 17:57 UTC (permalink / raw)
  To: Russell King
  Cc: Fabio Estevam, linux-kernel, DRI mailing list, linux-rockchip,
	Andy Yan, linux-arm-kernel

On Sat, Aug 8, 2015 at 1:03 PM, Russell King
<rmk+kernel@arm.linux.org.uk> wrote:
> Use a function to convert the sync pin to a bit mask for the DI_GENERAL
> register, and move this out of the interlace/non-interlace path to the
> common path.
>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

Reviewed-by: Fabio Estevam <fabio.estevam@freescale.com>
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 03/12] gpu: imx: simplify sync polarity setting
@ 2015-10-06 17:57     ` Fabio Estevam
  0 siblings, 0 replies; 226+ messages in thread
From: Fabio Estevam @ 2015-10-06 17:57 UTC (permalink / raw)
  To: linux-arm-kernel

On Sat, Aug 8, 2015 at 1:03 PM, Russell King
<rmk+kernel@arm.linux.org.uk> wrote:
> Use a function to convert the sync pin to a bit mask for the DI_GENERAL
> register, and move this out of the interlace/non-interlace path to the
> common path.
>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

Reviewed-by: Fabio Estevam <fabio.estevam@freescale.com>

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 04/12] gpu: imx: fix support for interlaced modes
  2015-08-08 16:03   ` Russell King
  (?)
@ 2015-10-06 17:59     ` Fabio Estevam
  -1 siblings, 0 replies; 226+ messages in thread
From: Fabio Estevam @ 2015-10-06 17:59 UTC (permalink / raw)
  To: Russell King
  Cc: linux-rockchip, DRI mailing list, linux-kernel, linux-arm-kernel,
	Fabio Estevam, Yakir Yang, Andy Yan

On Sat, Aug 8, 2015 at 1:03 PM, Russell King
<rmk+kernel@arm.linux.org.uk> wrote:
> The support for interlaced video modes seems to be broken; we don't use
> anything other than the vtotal/htotal from the timing information to
> define the various sync counters.
>
> Freescale patches for interlaced video support contain an alternative
> sync counter setup, which we include here.  This setup produces the
> hsync and vsync via the normal counter 2 and 3, but moves the display
> enable signal from counter 5 to counter 6.  Therefore, we need to
> change the display controller setup as well.
>
> The corresponding Freescale patches for this change are:
>   iMX6-HDMI-support-interlaced-display-mode.patch
>   IPU-fine-tuning-the-interlace-display-timing-for-CEA.patch
>
> This produces a working interlace format output from the IPU.
>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

Reviewed-by: Fabio Estevam <fabio.estevam@freescale.com>

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 04/12] gpu: imx: fix support for interlaced modes
@ 2015-10-06 17:59     ` Fabio Estevam
  0 siblings, 0 replies; 226+ messages in thread
From: Fabio Estevam @ 2015-10-06 17:59 UTC (permalink / raw)
  To: Russell King
  Cc: linux-rockchip, DRI mailing list, linux-kernel, linux-arm-kernel,
	Fabio Estevam, Yakir Yang, Andy Yan

On Sat, Aug 8, 2015 at 1:03 PM, Russell King
<rmk+kernel@arm.linux.org.uk> wrote:
> The support for interlaced video modes seems to be broken; we don't use
> anything other than the vtotal/htotal from the timing information to
> define the various sync counters.
>
> Freescale patches for interlaced video support contain an alternative
> sync counter setup, which we include here.  This setup produces the
> hsync and vsync via the normal counter 2 and 3, but moves the display
> enable signal from counter 5 to counter 6.  Therefore, we need to
> change the display controller setup as well.
>
> The corresponding Freescale patches for this change are:
>   iMX6-HDMI-support-interlaced-display-mode.patch
>   IPU-fine-tuning-the-interlace-display-timing-for-CEA.patch
>
> This produces a working interlace format output from the IPU.
>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

Reviewed-by: Fabio Estevam <fabio.estevam@freescale.com>

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 04/12] gpu: imx: fix support for interlaced modes
@ 2015-10-06 17:59     ` Fabio Estevam
  0 siblings, 0 replies; 226+ messages in thread
From: Fabio Estevam @ 2015-10-06 17:59 UTC (permalink / raw)
  To: linux-arm-kernel

On Sat, Aug 8, 2015 at 1:03 PM, Russell King
<rmk+kernel@arm.linux.org.uk> wrote:
> The support for interlaced video modes seems to be broken; we don't use
> anything other than the vtotal/htotal from the timing information to
> define the various sync counters.
>
> Freescale patches for interlaced video support contain an alternative
> sync counter setup, which we include here.  This setup produces the
> hsync and vsync via the normal counter 2 and 3, but moves the display
> enable signal from counter 5 to counter 6.  Therefore, we need to
> change the display controller setup as well.
>
> The corresponding Freescale patches for this change are:
>   iMX6-HDMI-support-interlaced-display-mode.patch
>   IPU-fine-tuning-the-interlace-display-timing-for-CEA.patch
>
> This produces a working interlace format output from the IPU.
>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

Reviewed-by: Fabio Estevam <fabio.estevam@freescale.com>

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 05/12] drm: bridge/dw_hdmi: add support for interlaced video modes
  2015-08-08 16:03   ` Russell King
  (?)
@ 2015-10-06 18:00     ` Fabio Estevam
  -1 siblings, 0 replies; 226+ messages in thread
From: Fabio Estevam @ 2015-10-06 18:00 UTC (permalink / raw)
  To: Russell King
  Cc: linux-rockchip, DRI mailing list, linux-kernel, linux-arm-kernel,
	Fabio Estevam, Yakir Yang, Andy Yan

On Sat, Aug 8, 2015 at 1:03 PM, Russell King
<rmk+kernel@arm.linux.org.uk> wrote:
> Add support for interlaced video modes to the dw_hdmi bridge.  This
> mainly involves halving the vertical parameters to be programmed into
> the bridge registers, and setting the interlace_allowed connector flag.
>
> This brings working 1080i support.  However, 480i and 576i fail to
> work due to the lack of proper pixel repetition support, which is not
> trivial to add due to the tabular PLL parameterisation.  Hence, we
> filter out these modes in our mode_valid() method.
>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

Reviewed-by: Fabio Estevam <fabio.estevam@freescale.com>

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 05/12] drm: bridge/dw_hdmi: add support for interlaced video modes
@ 2015-10-06 18:00     ` Fabio Estevam
  0 siblings, 0 replies; 226+ messages in thread
From: Fabio Estevam @ 2015-10-06 18:00 UTC (permalink / raw)
  To: Russell King
  Cc: Fabio Estevam, linux-kernel, DRI mailing list, linux-rockchip,
	Andy Yan, linux-arm-kernel

On Sat, Aug 8, 2015 at 1:03 PM, Russell King
<rmk+kernel@arm.linux.org.uk> wrote:
> Add support for interlaced video modes to the dw_hdmi bridge.  This
> mainly involves halving the vertical parameters to be programmed into
> the bridge registers, and setting the interlace_allowed connector flag.
>
> This brings working 1080i support.  However, 480i and 576i fail to
> work due to the lack of proper pixel repetition support, which is not
> trivial to add due to the tabular PLL parameterisation.  Hence, we
> filter out these modes in our mode_valid() method.
>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

Reviewed-by: Fabio Estevam <fabio.estevam@freescale.com>
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 05/12] drm: bridge/dw_hdmi: add support for interlaced video modes
@ 2015-10-06 18:00     ` Fabio Estevam
  0 siblings, 0 replies; 226+ messages in thread
From: Fabio Estevam @ 2015-10-06 18:00 UTC (permalink / raw)
  To: linux-arm-kernel

On Sat, Aug 8, 2015 at 1:03 PM, Russell King
<rmk+kernel@arm.linux.org.uk> wrote:
> Add support for interlaced video modes to the dw_hdmi bridge.  This
> mainly involves halving the vertical parameters to be programmed into
> the bridge registers, and setting the interlace_allowed connector flag.
>
> This brings working 1080i support.  However, 480i and 576i fail to
> work due to the lack of proper pixel repetition support, which is not
> trivial to add due to the tabular PLL parameterisation.  Hence, we
> filter out these modes in our mode_valid() method.
>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

Reviewed-by: Fabio Estevam <fabio.estevam@freescale.com>

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 11/12] drm: bridge/dw_hdmi: add connector mode forcing
  2015-08-08 16:04   ` Russell King
  (?)
@ 2015-10-06 18:01     ` Fabio Estevam
  -1 siblings, 0 replies; 226+ messages in thread
From: Fabio Estevam @ 2015-10-06 18:01 UTC (permalink / raw)
  To: Russell King
  Cc: linux-rockchip, DRI mailing list, linux-kernel, linux-arm-kernel,
	Fabio Estevam, Yakir Yang, Andy Yan

On Sat, Aug 8, 2015 at 1:04 PM, Russell King
<rmk+kernel@arm.linux.org.uk> wrote:
> When connected to HDMI sources, some DVI monitors de-assert their HPD
> signal and TDMS loads for one seconds every four seconds when there is
> no signal present on the connection.
>
> Unfortunately, this behaviour is indistinguishable from a proper HDMI
> setup with an AV receiver in the path to the display: the HDMI spec
> requires us to detect HPD deassertions as short as 100ms, which indicate
> that the EDID has changed.
>
> Since it is possible to connect a DVI monitor to an AV receiver and then
> to a HDMI source, merely working around this by detecting the lack of
> HDMI vendor block in the EDID is insufficient - the AV receiver is at
> liberty to modify the EDID as it sees fit, and it will place its own
> parameters into the EDID including the HDMI vendor block.
>
> DRM has support for forcing the state of a connector, which we should
> implement to allow us to work around these broken DVI monitors - we can
> tell DRM to force the connection state to indicate that there is always
> a device connected to work around this problem.  Although this requires
> manual configuration, it is better than nothing at all.
>
> When a forced connection state has been set, there is no point handling
> our RXSENSE interrupts, so disable them in this circumstance.
>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

Reviewed-by: Fabio Estevam@freescale.com

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 11/12] drm: bridge/dw_hdmi: add connector mode forcing
@ 2015-10-06 18:01     ` Fabio Estevam
  0 siblings, 0 replies; 226+ messages in thread
From: Fabio Estevam @ 2015-10-06 18:01 UTC (permalink / raw)
  To: Russell King
  Cc: linux-rockchip, DRI mailing list, linux-kernel, linux-arm-kernel,
	Fabio Estevam, Yakir Yang, Andy Yan

On Sat, Aug 8, 2015 at 1:04 PM, Russell King
<rmk+kernel@arm.linux.org.uk> wrote:
> When connected to HDMI sources, some DVI monitors de-assert their HPD
> signal and TDMS loads for one seconds every four seconds when there is
> no signal present on the connection.
>
> Unfortunately, this behaviour is indistinguishable from a proper HDMI
> setup with an AV receiver in the path to the display: the HDMI spec
> requires us to detect HPD deassertions as short as 100ms, which indicate
> that the EDID has changed.
>
> Since it is possible to connect a DVI monitor to an AV receiver and then
> to a HDMI source, merely working around this by detecting the lack of
> HDMI vendor block in the EDID is insufficient - the AV receiver is at
> liberty to modify the EDID as it sees fit, and it will place its own
> parameters into the EDID including the HDMI vendor block.
>
> DRM has support for forcing the state of a connector, which we should
> implement to allow us to work around these broken DVI monitors - we can
> tell DRM to force the connection state to indicate that there is always
> a device connected to work around this problem.  Although this requires
> manual configuration, it is better than nothing at all.
>
> When a forced connection state has been set, there is no point handling
> our RXSENSE interrupts, so disable them in this circumstance.
>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

Reviewed-by: Fabio Estevam@freescale.com

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 11/12] drm: bridge/dw_hdmi: add connector mode forcing
@ 2015-10-06 18:01     ` Fabio Estevam
  0 siblings, 0 replies; 226+ messages in thread
From: Fabio Estevam @ 2015-10-06 18:01 UTC (permalink / raw)
  To: linux-arm-kernel

On Sat, Aug 8, 2015 at 1:04 PM, Russell King
<rmk+kernel@arm.linux.org.uk> wrote:
> When connected to HDMI sources, some DVI monitors de-assert their HPD
> signal and TDMS loads for one seconds every four seconds when there is
> no signal present on the connection.
>
> Unfortunately, this behaviour is indistinguishable from a proper HDMI
> setup with an AV receiver in the path to the display: the HDMI spec
> requires us to detect HPD deassertions as short as 100ms, which indicate
> that the EDID has changed.
>
> Since it is possible to connect a DVI monitor to an AV receiver and then
> to a HDMI source, merely working around this by detecting the lack of
> HDMI vendor block in the EDID is insufficient - the AV receiver is at
> liberty to modify the EDID as it sees fit, and it will place its own
> parameters into the EDID including the HDMI vendor block.
>
> DRM has support for forcing the state of a connector, which we should
> implement to allow us to work around these broken DVI monitors - we can
> tell DRM to force the connection state to indicate that there is always
> a device connected to work around this problem.  Although this requires
> manual configuration, it is better than nothing at all.
>
> When a forced connection state has been set, there is no point handling
> our RXSENSE interrupts, so disable them in this circumstance.
>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

Reviewed-by: Fabio Estevam at freescale.com

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 12/12] drm: bridge/dw_hdmi: improve HDMI enable/disable handling
  2015-08-08 16:04   ` Russell King
  (?)
@ 2015-10-06 18:03     ` Fabio Estevam
  -1 siblings, 0 replies; 226+ messages in thread
From: Fabio Estevam @ 2015-10-06 18:03 UTC (permalink / raw)
  To: Russell King
  Cc: linux-rockchip, DRI mailing list, linux-kernel, linux-arm-kernel,
	Fabio Estevam, Yakir Yang, Andy Yan

On Sat, Aug 8, 2015 at 1:04 PM, Russell King
<rmk+kernel@arm.linux.org.uk> wrote:
> HDMI sinks are permitted to de-assert and re-assert the HPD signal to
> indicate that their EDID has been updated, which may not involve a
> change of video information.
>
> An example of where such a situation can arise is when an AV receiver
> is connected between the source and the display device.  Events which
> can cause the HPD to be deasserted include:
>
>  * turning on or switching to standby the AV receiver.
>  * turning on or switching to standby the display device.
>
> Each of these can change the entire EDID data, or just a part of the
> EDID data - it's up to the connected HDMI sink to do what they desire
> here.  For example
>
>  - with the AV receiver and display device both in standby, a source
>    connected to the AV receiver may provide its own EDID to the source.
>  - turning on the display device causes the display device's EDID to be
>    made available in an unmodified form to the source.
>  - subsequently turning on the AV receiver then provides a modified
>    version of the display device's EDID.
>
> Moreover, HPD doesn't tell us whether something is actually listening
> on the HDMI TDMS signals.  The phy gives us a set of RXSENSE indications
> which tell us whether there is a sink connected to the TMDS signals.
>
> Currently, we use the HPD signal to enable or disable the HDMI block,
> which is questionable when HPD is used in this manner.  Using the
> RXSENSE would be more appropriate, but there is some bad behaviour
> which needs to be coped with.  The iMX6 implementation lets the TMDS
> signals float when the phy is "powered down", which cause spurious
> interrupts.  Rather than just using RXSENSE, use RXSENSE and HPD
> becoming both active to signal the presence of a device, but loss
> of RXSENSE to indicate that the device has been unplugged.
>
> The side effect of this change is that a sink deasserting the HPD
> signal to cause a re-read of the EDID data will not cause the bridge
> to immediately disable the video signal.
>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

Reviewed-by: Fabio Estevam <fabio.estevam@freescale.com>

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 12/12] drm: bridge/dw_hdmi: improve HDMI enable/disable handling
@ 2015-10-06 18:03     ` Fabio Estevam
  0 siblings, 0 replies; 226+ messages in thread
From: Fabio Estevam @ 2015-10-06 18:03 UTC (permalink / raw)
  To: Russell King
  Cc: Fabio Estevam, linux-kernel, DRI mailing list, linux-rockchip,
	Andy Yan, linux-arm-kernel

On Sat, Aug 8, 2015 at 1:04 PM, Russell King
<rmk+kernel@arm.linux.org.uk> wrote:
> HDMI sinks are permitted to de-assert and re-assert the HPD signal to
> indicate that their EDID has been updated, which may not involve a
> change of video information.
>
> An example of where such a situation can arise is when an AV receiver
> is connected between the source and the display device.  Events which
> can cause the HPD to be deasserted include:
>
>  * turning on or switching to standby the AV receiver.
>  * turning on or switching to standby the display device.
>
> Each of these can change the entire EDID data, or just a part of the
> EDID data - it's up to the connected HDMI sink to do what they desire
> here.  For example
>
>  - with the AV receiver and display device both in standby, a source
>    connected to the AV receiver may provide its own EDID to the source.
>  - turning on the display device causes the display device's EDID to be
>    made available in an unmodified form to the source.
>  - subsequently turning on the AV receiver then provides a modified
>    version of the display device's EDID.
>
> Moreover, HPD doesn't tell us whether something is actually listening
> on the HDMI TDMS signals.  The phy gives us a set of RXSENSE indications
> which tell us whether there is a sink connected to the TMDS signals.
>
> Currently, we use the HPD signal to enable or disable the HDMI block,
> which is questionable when HPD is used in this manner.  Using the
> RXSENSE would be more appropriate, but there is some bad behaviour
> which needs to be coped with.  The iMX6 implementation lets the TMDS
> signals float when the phy is "powered down", which cause spurious
> interrupts.  Rather than just using RXSENSE, use RXSENSE and HPD
> becoming both active to signal the presence of a device, but loss
> of RXSENSE to indicate that the device has been unplugged.
>
> The side effect of this change is that a sink deasserting the HPD
> signal to cause a re-read of the EDID data will not cause the bridge
> to immediately disable the video signal.
>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

Reviewed-by: Fabio Estevam <fabio.estevam@freescale.com>
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 12/12] drm: bridge/dw_hdmi: improve HDMI enable/disable handling
@ 2015-10-06 18:03     ` Fabio Estevam
  0 siblings, 0 replies; 226+ messages in thread
From: Fabio Estevam @ 2015-10-06 18:03 UTC (permalink / raw)
  To: linux-arm-kernel

On Sat, Aug 8, 2015 at 1:04 PM, Russell King
<rmk+kernel@arm.linux.org.uk> wrote:
> HDMI sinks are permitted to de-assert and re-assert the HPD signal to
> indicate that their EDID has been updated, which may not involve a
> change of video information.
>
> An example of where such a situation can arise is when an AV receiver
> is connected between the source and the display device.  Events which
> can cause the HPD to be deasserted include:
>
>  * turning on or switching to standby the AV receiver.
>  * turning on or switching to standby the display device.
>
> Each of these can change the entire EDID data, or just a part of the
> EDID data - it's up to the connected HDMI sink to do what they desire
> here.  For example
>
>  - with the AV receiver and display device both in standby, a source
>    connected to the AV receiver may provide its own EDID to the source.
>  - turning on the display device causes the display device's EDID to be
>    made available in an unmodified form to the source.
>  - subsequently turning on the AV receiver then provides a modified
>    version of the display device's EDID.
>
> Moreover, HPD doesn't tell us whether something is actually listening
> on the HDMI TDMS signals.  The phy gives us a set of RXSENSE indications
> which tell us whether there is a sink connected to the TMDS signals.
>
> Currently, we use the HPD signal to enable or disable the HDMI block,
> which is questionable when HPD is used in this manner.  Using the
> RXSENSE would be more appropriate, but there is some bad behaviour
> which needs to be coped with.  The iMX6 implementation lets the TMDS
> signals float when the phy is "powered down", which cause spurious
> interrupts.  Rather than just using RXSENSE, use RXSENSE and HPD
> becoming both active to signal the presence of a device, but loss
> of RXSENSE to indicate that the device has been unplugged.
>
> The side effect of this change is that a sink deasserting the HPD
> signal to cause a re-read of the EDID data will not cause the bridge
> to immediately disable the video signal.
>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

Reviewed-by: Fabio Estevam <fabio.estevam@freescale.com>

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver
  2015-08-08 16:10     ` Russell King
  (?)
@ 2015-10-06 18:07       ` Fabio Estevam
  -1 siblings, 0 replies; 226+ messages in thread
From: Fabio Estevam @ 2015-10-06 18:07 UTC (permalink / raw)
  To: Russell King
  Cc: linux-rockchip, alsa-devel, DRI mailing list, linux-kernel,
	linux-arm-kernel, Fabio Estevam, Jaroslav Kysela, Mark Brown,
	Yakir Yang, Andy Yan

On Sat, Aug 8, 2015 at 1:10 PM, Russell King
<rmk+kernel@arm.linux.org.uk> wrote:
> Add ALSA based HDMI AHB audio driver for dw_hdmi.  The only buffer
> format supported by the hardware is its own special IEC958 based format,
> which is not compatible with any ALSA format.  To avoid doing too much
> data manipulation within the driver, we support only ALSAs IEC958 LE and
> 24-bit PCM formats for 2 to 6 channels, which we convert to its hardware
> format.
>
> A more desirable solution would be to have this conversion in userspace,
> but ALSA does not appear to allow such transformations outside of
> libasound itself.
>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

I applied this series, but the HDMI audio card is not registered. I
guess I missed the dts pieces. Are the dts patches available?

Thanks

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver
@ 2015-10-06 18:07       ` Fabio Estevam
  0 siblings, 0 replies; 226+ messages in thread
From: Fabio Estevam @ 2015-10-06 18:07 UTC (permalink / raw)
  To: Russell King
  Cc: linux-rockchip, alsa-devel, DRI mailing list, linux-kernel,
	linux-arm-kernel, Fabio Estevam, Jaroslav Kysela, Mark Brown,
	Yakir Yang, Andy Yan

On Sat, Aug 8, 2015 at 1:10 PM, Russell King
<rmk+kernel@arm.linux.org.uk> wrote:
> Add ALSA based HDMI AHB audio driver for dw_hdmi.  The only buffer
> format supported by the hardware is its own special IEC958 based format,
> which is not compatible with any ALSA format.  To avoid doing too much
> data manipulation within the driver, we support only ALSAs IEC958 LE and
> 24-bit PCM formats for 2 to 6 channels, which we convert to its hardware
> format.
>
> A more desirable solution would be to have this conversion in userspace,
> but ALSA does not appear to allow such transformations outside of
> libasound itself.
>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

I applied this series, but the HDMI audio card is not registered. I
guess I missed the dts pieces. Are the dts patches available?

Thanks

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver
@ 2015-10-06 18:07       ` Fabio Estevam
  0 siblings, 0 replies; 226+ messages in thread
From: Fabio Estevam @ 2015-10-06 18:07 UTC (permalink / raw)
  To: linux-arm-kernel

On Sat, Aug 8, 2015 at 1:10 PM, Russell King
<rmk+kernel@arm.linux.org.uk> wrote:
> Add ALSA based HDMI AHB audio driver for dw_hdmi.  The only buffer
> format supported by the hardware is its own special IEC958 based format,
> which is not compatible with any ALSA format.  To avoid doing too much
> data manipulation within the driver, we support only ALSAs IEC958 LE and
> 24-bit PCM formats for 2 to 6 channels, which we convert to its hardware
> format.
>
> A more desirable solution would be to have this conversion in userspace,
> but ALSA does not appear to allow such transformations outside of
> libasound itself.
>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

I applied this series, but the HDMI audio card is not registered. I
guess I missed the dts pieces. Are the dts patches available?

Thanks

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver
  2015-10-06 18:07       ` Fabio Estevam
  (?)
@ 2015-10-06 18:18         ` Russell King - ARM Linux
  -1 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-10-06 18:18 UTC (permalink / raw)
  To: Fabio Estevam
  Cc: linux-rockchip, alsa-devel, DRI mailing list, linux-kernel,
	linux-arm-kernel, Fabio Estevam, Jaroslav Kysela, Mark Brown,
	Yakir Yang, Andy Yan

On Tue, Oct 06, 2015 at 03:07:40PM -0300, Fabio Estevam wrote:
> On Sat, Aug 8, 2015 at 1:10 PM, Russell King
> <rmk+kernel@arm.linux.org.uk> wrote:
> > Add ALSA based HDMI AHB audio driver for dw_hdmi.  The only buffer
> > format supported by the hardware is its own special IEC958 based format,
> > which is not compatible with any ALSA format.  To avoid doing too much
> > data manipulation within the driver, we support only ALSAs IEC958 LE and
> > 24-bit PCM formats for 2 to 6 channels, which we convert to its hardware
> > format.
> >
> > A more desirable solution would be to have this conversion in userspace,
> > but ALSA does not appear to allow such transformations outside of
> > libasound itself.
> >
> > Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> 
> I applied this series, but the HDMI audio card is not registered. I
> guess I missed the dts pieces. Are the dts patches available?

Sorry, I've been out for most of the day.  There's no DT patches required.

The dw_hdmi bridge driver creates its own platform device for the audio,
which should then bind to the dw_hdmi-ahb-audio driver using normal Linux
methods.

I don't know what's wrong with your setup, for me, it just works:

[    1.358829] dwhdmi-imx 120000.hdmi: Detected HDMI controller 0x13:0xa:0xa0:0xc1
[    1.377173] imx-drm display-subsystem: bound 120000.hdmi (ops dw_hdmi_imx_ops)
...
[    2.851343] ALSA device list:
[    2.857364]   #0: DW-HDMI rev 0x0a, irq 21

as it always has done for me.  There's nothing special about it.

There's nothing special about it - it's a standard ALSA PCM audio driver,
it doesn't have any requirements over and above having ALSA and ALSA's PCM
support enabled.  If you didn't have those enabled, but somehow had the
dw_hdmi-ahb-audio set as built-in, you'd get a link time error, so it's
not a configuration issue.

It works for me on all iMX6 platforms (solo, dual-lite, dual and quad).

As it's a straight ALSA device, it doesn't need any codec or any of the
ASoC infrastructure either.

I guess the thing to start looking at is whether the device and driver
appear in /sys/bus/platform/{devices,drivers}/.  You should have:

/sys/bus/platform/devices/dw-hdmi-ahb-audio.0.auto 
/sys/bus/platform/drivers/dw-hdmi-ahb-audio

and obviously the latter should contain the symlink to the device.  If
that is present, then the driver has bound, and it should appear in
/proc/asound/cards.

-- 
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver
@ 2015-10-06 18:18         ` Russell King - ARM Linux
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-10-06 18:18 UTC (permalink / raw)
  To: Fabio Estevam
  Cc: Fabio Estevam, alsa-devel, linux-kernel, DRI mailing list,
	Jaroslav Kysela, linux-rockchip, Mark Brown, Andy Yan,
	linux-arm-kernel

On Tue, Oct 06, 2015 at 03:07:40PM -0300, Fabio Estevam wrote:
> On Sat, Aug 8, 2015 at 1:10 PM, Russell King
> <rmk+kernel@arm.linux.org.uk> wrote:
> > Add ALSA based HDMI AHB audio driver for dw_hdmi.  The only buffer
> > format supported by the hardware is its own special IEC958 based format,
> > which is not compatible with any ALSA format.  To avoid doing too much
> > data manipulation within the driver, we support only ALSAs IEC958 LE and
> > 24-bit PCM formats for 2 to 6 channels, which we convert to its hardware
> > format.
> >
> > A more desirable solution would be to have this conversion in userspace,
> > but ALSA does not appear to allow such transformations outside of
> > libasound itself.
> >
> > Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> 
> I applied this series, but the HDMI audio card is not registered. I
> guess I missed the dts pieces. Are the dts patches available?

Sorry, I've been out for most of the day.  There's no DT patches required.

The dw_hdmi bridge driver creates its own platform device for the audio,
which should then bind to the dw_hdmi-ahb-audio driver using normal Linux
methods.

I don't know what's wrong with your setup, for me, it just works:

[    1.358829] dwhdmi-imx 120000.hdmi: Detected HDMI controller 0x13:0xa:0xa0:0xc1
[    1.377173] imx-drm display-subsystem: bound 120000.hdmi (ops dw_hdmi_imx_ops)
...
[    2.851343] ALSA device list:
[    2.857364]   #0: DW-HDMI rev 0x0a, irq 21

as it always has done for me.  There's nothing special about it.

There's nothing special about it - it's a standard ALSA PCM audio driver,
it doesn't have any requirements over and above having ALSA and ALSA's PCM
support enabled.  If you didn't have those enabled, but somehow had the
dw_hdmi-ahb-audio set as built-in, you'd get a link time error, so it's
not a configuration issue.

It works for me on all iMX6 platforms (solo, dual-lite, dual and quad).

As it's a straight ALSA device, it doesn't need any codec or any of the
ASoC infrastructure either.

I guess the thing to start looking at is whether the device and driver
appear in /sys/bus/platform/{devices,drivers}/.  You should have:

/sys/bus/platform/devices/dw-hdmi-ahb-audio.0.auto 
/sys/bus/platform/drivers/dw-hdmi-ahb-audio

and obviously the latter should contain the symlink to the device.  If
that is present, then the driver has bound, and it should appear in
/proc/asound/cards.

-- 
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver
@ 2015-10-06 18:18         ` Russell King - ARM Linux
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-10-06 18:18 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Oct 06, 2015 at 03:07:40PM -0300, Fabio Estevam wrote:
> On Sat, Aug 8, 2015 at 1:10 PM, Russell King
> <rmk+kernel@arm.linux.org.uk> wrote:
> > Add ALSA based HDMI AHB audio driver for dw_hdmi.  The only buffer
> > format supported by the hardware is its own special IEC958 based format,
> > which is not compatible with any ALSA format.  To avoid doing too much
> > data manipulation within the driver, we support only ALSAs IEC958 LE and
> > 24-bit PCM formats for 2 to 6 channels, which we convert to its hardware
> > format.
> >
> > A more desirable solution would be to have this conversion in userspace,
> > but ALSA does not appear to allow such transformations outside of
> > libasound itself.
> >
> > Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> 
> I applied this series, but the HDMI audio card is not registered. I
> guess I missed the dts pieces. Are the dts patches available?

Sorry, I've been out for most of the day.  There's no DT patches required.

The dw_hdmi bridge driver creates its own platform device for the audio,
which should then bind to the dw_hdmi-ahb-audio driver using normal Linux
methods.

I don't know what's wrong with your setup, for me, it just works:

[    1.358829] dwhdmi-imx 120000.hdmi: Detected HDMI controller 0x13:0xa:0xa0:0xc1
[    1.377173] imx-drm display-subsystem: bound 120000.hdmi (ops dw_hdmi_imx_ops)
...
[    2.851343] ALSA device list:
[    2.857364]   #0: DW-HDMI rev 0x0a, irq 21

as it always has done for me.  There's nothing special about it.

There's nothing special about it - it's a standard ALSA PCM audio driver,
it doesn't have any requirements over and above having ALSA and ALSA's PCM
support enabled.  If you didn't have those enabled, but somehow had the
dw_hdmi-ahb-audio set as built-in, you'd get a link time error, so it's
not a configuration issue.

It works for me on all iMX6 platforms (solo, dual-lite, dual and quad).

As it's a straight ALSA device, it doesn't need any codec or any of the
ASoC infrastructure either.

I guess the thing to start looking at is whether the device and driver
appear in /sys/bus/platform/{devices,drivers}/.  You should have:

/sys/bus/platform/devices/dw-hdmi-ahb-audio.0.auto 
/sys/bus/platform/drivers/dw-hdmi-ahb-audio

and obviously the latter should contain the symlink to the device.  If
that is present, then the driver has bound, and it should appear in
/proc/asound/cards.

-- 
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver
  2015-10-06 18:18         ` Russell King - ARM Linux
  (?)
@ 2015-10-06 18:45           ` Fabio Estevam
  -1 siblings, 0 replies; 226+ messages in thread
From: Fabio Estevam @ 2015-10-06 18:45 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: linux-rockchip, alsa-devel, DRI mailing list, linux-kernel,
	linux-arm-kernel, Fabio Estevam, Jaroslav Kysela, Mark Brown,
	Yakir Yang, Andy Yan

On Tue, Oct 6, 2015 at 3:18 PM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:

> Sorry, I've been out for most of the day.  There's no DT patches required.
>
> The dw_hdmi bridge driver creates its own platform device for the audio,
> which should then bind to the dw_hdmi-ahb-audio driver using normal Linux
> methods.
>
> I don't know what's wrong with your setup, for me, it just works:
>
> [    1.358829] dwhdmi-imx 120000.hdmi: Detected HDMI controller 0x13:0xa:0xa0:0xc1
> [    1.377173] imx-drm display-subsystem: bound 120000.hdmi (ops dw_hdmi_imx_ops)
> ...
> [    2.851343] ALSA device list:
> [    2.857364]   #0: DW-HDMI rev 0x0a, irq 21
>
> as it always has done for me.  There's nothing special about it.

Great, got it to probe now:

[    7.454760] ALSA device list:
[    7.457764]   #0: DW-HDMI rev 0x0a, irq 19
[    7.461990]   #1: wm8962-audio

There was a conflict and I resolved incorrectly here. Will try to play
a wav file via aplay now.

Thanks

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver
@ 2015-10-06 18:45           ` Fabio Estevam
  0 siblings, 0 replies; 226+ messages in thread
From: Fabio Estevam @ 2015-10-06 18:45 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: linux-rockchip, alsa-devel, DRI mailing list, linux-kernel,
	linux-arm-kernel, Fabio Estevam, Jaroslav Kysela, Mark Brown,
	Yakir Yang, Andy Yan

On Tue, Oct 6, 2015 at 3:18 PM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:

> Sorry, I've been out for most of the day.  There's no DT patches required.
>
> The dw_hdmi bridge driver creates its own platform device for the audio,
> which should then bind to the dw_hdmi-ahb-audio driver using normal Linux
> methods.
>
> I don't know what's wrong with your setup, for me, it just works:
>
> [    1.358829] dwhdmi-imx 120000.hdmi: Detected HDMI controller 0x13:0xa:0xa0:0xc1
> [    1.377173] imx-drm display-subsystem: bound 120000.hdmi (ops dw_hdmi_imx_ops)
> ...
> [    2.851343] ALSA device list:
> [    2.857364]   #0: DW-HDMI rev 0x0a, irq 21
>
> as it always has done for me.  There's nothing special about it.

Great, got it to probe now:

[    7.454760] ALSA device list:
[    7.457764]   #0: DW-HDMI rev 0x0a, irq 19
[    7.461990]   #1: wm8962-audio

There was a conflict and I resolved incorrectly here. Will try to play
a wav file via aplay now.

Thanks

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver
@ 2015-10-06 18:45           ` Fabio Estevam
  0 siblings, 0 replies; 226+ messages in thread
From: Fabio Estevam @ 2015-10-06 18:45 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Oct 6, 2015 at 3:18 PM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:

> Sorry, I've been out for most of the day.  There's no DT patches required.
>
> The dw_hdmi bridge driver creates its own platform device for the audio,
> which should then bind to the dw_hdmi-ahb-audio driver using normal Linux
> methods.
>
> I don't know what's wrong with your setup, for me, it just works:
>
> [    1.358829] dwhdmi-imx 120000.hdmi: Detected HDMI controller 0x13:0xa:0xa0:0xc1
> [    1.377173] imx-drm display-subsystem: bound 120000.hdmi (ops dw_hdmi_imx_ops)
> ...
> [    2.851343] ALSA device list:
> [    2.857364]   #0: DW-HDMI rev 0x0a, irq 21
>
> as it always has done for me.  There's nothing special about it.

Great, got it to probe now:

[    7.454760] ALSA device list:
[    7.457764]   #0: DW-HDMI rev 0x0a, irq 19
[    7.461990]   #1: wm8962-audio

There was a conflict and I resolved incorrectly here. Will try to play
a wav file via aplay now.

Thanks

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver
  2015-10-06 18:45           ` Fabio Estevam
  (?)
@ 2015-10-06 18:54             ` Russell King - ARM Linux
  -1 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-10-06 18:54 UTC (permalink / raw)
  To: Fabio Estevam
  Cc: linux-rockchip, alsa-devel, DRI mailing list, linux-kernel,
	linux-arm-kernel, Fabio Estevam, Jaroslav Kysela, Mark Brown,
	Yakir Yang, Andy Yan

[-- Attachment #1: Type: text/plain, Size: 1399 bytes --]

On Tue, Oct 06, 2015 at 03:45:32PM -0300, Fabio Estevam wrote:
> On Tue, Oct 6, 2015 at 3:18 PM, Russell King - ARM Linux
> <linux@arm.linux.org.uk> wrote:
> 
> > Sorry, I've been out for most of the day.  There's no DT patches required.
> >
> > The dw_hdmi bridge driver creates its own platform device for the audio,
> > which should then bind to the dw_hdmi-ahb-audio driver using normal Linux
> > methods.
> >
> > I don't know what's wrong with your setup, for me, it just works:
> >
> > [    1.358829] dwhdmi-imx 120000.hdmi: Detected HDMI controller 0x13:0xa:0xa0:0xc1
> > [    1.377173] imx-drm display-subsystem: bound 120000.hdmi (ops dw_hdmi_imx_ops)
> > ...
> > [    2.851343] ALSA device list:
> > [    2.857364]   #0: DW-HDMI rev 0x0a, irq 21
> >
> > as it always has done for me.  There's nothing special about it.
> 
> Great, got it to probe now:
> 
> [    7.454760] ALSA device list:
> [    7.457764]   #0: DW-HDMI rev 0x0a, irq 19
> [    7.461990]   #1: wm8962-audio
> 
> There was a conflict and I resolved incorrectly here. Will try to play
> a wav file via aplay now.

Make sure you have the ALSA config file, as alsalib won't get on
with dw-hdmi only accepting 24-bit audio without this.  A copy is
attached.  It also tells ALSA how to deal with multi-channel audio
as well.

-- 
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.

[-- Attachment #2: dw-hdmi-ahb-aud.conf --]
[-- Type: text/plain, Size: 3907 bytes --]

#
# All PCM must be 24-bit for easy kernel conversion.
# IEC958 formatted output can be sent directly.
#
# Direct-to-hardware converting to 24-bit output.
#
dw-hdmi-ahb-aud.pcm.hw-s24le {
	@args [ CARD ]
	@args.CARD { type string }
	type linear
	slave.pcm { 
		type hw
		card $CARD
	}
	slave.format S24_LE
}

#
# Dmix hardware 24-bit output.
#
dw-hdmi-ahb-aud.pcm.dmix-s24le {
	@args [ CARD ]
	@args.CARD { type string }
	type plug
	slave.pcm { @func concat strings [ "dmix:" $CARD ",FORMAT=S24_LE" ] }
}

#
# Softvol with dmix output
#
dw-hdmi-ahb-aud.pcm.default {
	@args [ CARD ]
	@args.CARD { type string }
	type asym
	playback.pcm {
		type softvol
		slave.pcm {
			@func refer name { 
				@func concat strings [
					"cards."
					{ @func card_driver card $CARD }
					".pcm.dmix-s24le:CARD=" $CARD
				]
			}
		}
		control {
			name "PCM Playback Volume"
			card $CARD
		}
	}
}

#
# Common output path for front and surround outputs
#
dw-hdmi-ahb-aud.pcm.common.0 {
	@args [ CARD ]
	@args.CARD { type string }
	type asym
	playback.pcm {
		type softvol
		slave.pcm {
			@func refer name { 
				@func concat strings [
					"cards."
					{ @func card_driver card $CARD }
					".pcm.hw-s24le:CARD=" $CARD
				]
			}
		}
		control {
			name "PCM Playback Volume"
			card $CARD
		}
	}
}

<confdir:pcm/front.conf>

dw-hdmi-ahb-aud.pcm.front.0 cards.dw-hdmi-ahb-aud.pcm.common.0

# The mapping of ALSA channels to surround channels is very imprecise.
# ALSA uses a different terminology and speaker placement to the CEA
# surround positioning.  CEA has the positioning of:
#
#			LFE
#	FL	FLC	FC	FRC	FR
#
#
#	RL	RLC	RC	RRC	RR
#
# ALSA's idea is:
#
#			LFE
#		FL	C	FR
#
#		SL		SR
#
#		RL		RR
#
# We do our best to map between these representations.

<confdir:pcm/surround40.conf>

dw-hdmi-ahb-aud.pcm.surround40.0 {
	@args [ CARD ]
	@args.CARD { type string }
	type empty
	slave.pcm {
		@func refer
		name { 
			@func concat strings [
				"cards.dw-hdmi-ahb-aud.pcm.common.0:CARD="
				$CARD
			]
		}
	}
}

# surround 41 and surround50 are (annoyingly) mapped to surround51
# We could do without stacking two 'route' plugins on top of each other

<confdir:pcm/surround41.conf>
<confdir:pcm/surround50.conf>
<confdir:pcm/surround51.conf>

dw-hdmi-ahb-aud.pcm.surround51.0 {
	@args [ CARD ]
	@args.CARD { type string }
	type route
	slave.pcm {
		@func refer
		name { 
			@func concat strings [
				"cards.dw-hdmi-ahb-aud.pcm.common.0:CARD="
				$CARD
			]
		}
	}
	ttable.0.0 1.0 # FL  -> slave 0 -> hdmi 0 FL
	ttable.1.1 1.0 # FR  -> slave 1 -> hdmi 1 FR
	ttable.2.4 1.0 # RL  -> slave 4 -> hdmi 4 RL
	ttable.3.5 1.0 # RR  -> slave 5 -> hdmi 5 RR
	ttable.4.3 1.0 # C   -> slave 3 -> hdmi 3 FC 
	ttable.5.2 1.0 # LFE -> slave 2 -> hdmi 2 LFE
}

<confdir:pcm/surround71.conf>

dw-hdmi-ahb-aud.pcm.surround71.0 {
	@args [ CARD ]
	@args.CARD { type string }
	type route
	slave.pcm {
		@func refer
		name { 
			@func concat strings [
				"cards.dw-hdmi-ahb-aud.pcm.common.0:CARD="
				$CARD
			]
		}
	}
	slave.channels 8
	ttable.0.0 1.0 # FL  -> slave 0 -> hdmi 0 FL
	ttable.1.1 1.0 # FR  -> slave 1 -> hdmi 1 FR
	ttable.2.6 1.0 # RL  -> slave 6 -> hdmi 6 RLC/FLC
	ttable.3.7 1.0 # RR  -> slave 7 -> hdmi 7 RRC/FRC
	ttable.4.3 1.0 # C   -> slave 3 -> hdmi 3 FC
	ttable.5.2 1.0 # LFE -> slave 2 -> hdmi 2 LFE
	ttable.6.4 1.0 # SL  -> slave 4 -> hdmi 4 RL
	ttable.7.5 1.0 # SR  -> slave 5 -> hdmi 5 RR
}

<confdir:pcm/hdmi.conf>

dw-hdmi-ahb-aud.pcm.hdmi.0 {
	@args [ CARD AES0 AES1 AES2 AES3 ]
	@args.CARD { type string }
	@args.AES0 { type integer }
	@args.AES1 { type integer }
	@args.AES2 { type integer }
	@args.AES3 { type integer }
	type iec958
	slave.pcm {
		type hw
		card $CARD
	}
	slave.format IEC958_SUBFRAME_LE
	# $AES3 must be correct for some AV receivers to accept the stream
	status [ $AES0 $AES1 $AES2 $AES3 ]
}

<confdir:pcm/iec958.conf>

dw-hdmi-ahb-aud.pcm.iec958.0 cards.dw-hdmi-ahb-aud.pcm.hdmi.0

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver
@ 2015-10-06 18:54             ` Russell King - ARM Linux
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-10-06 18:54 UTC (permalink / raw)
  To: Fabio Estevam
  Cc: Fabio Estevam, alsa-devel, linux-kernel, DRI mailing list,
	Jaroslav Kysela, linux-rockchip, Mark Brown, Andy Yan,
	linux-arm-kernel

[-- Attachment #1: Type: text/plain, Size: 1399 bytes --]

On Tue, Oct 06, 2015 at 03:45:32PM -0300, Fabio Estevam wrote:
> On Tue, Oct 6, 2015 at 3:18 PM, Russell King - ARM Linux
> <linux@arm.linux.org.uk> wrote:
> 
> > Sorry, I've been out for most of the day.  There's no DT patches required.
> >
> > The dw_hdmi bridge driver creates its own platform device for the audio,
> > which should then bind to the dw_hdmi-ahb-audio driver using normal Linux
> > methods.
> >
> > I don't know what's wrong with your setup, for me, it just works:
> >
> > [    1.358829] dwhdmi-imx 120000.hdmi: Detected HDMI controller 0x13:0xa:0xa0:0xc1
> > [    1.377173] imx-drm display-subsystem: bound 120000.hdmi (ops dw_hdmi_imx_ops)
> > ...
> > [    2.851343] ALSA device list:
> > [    2.857364]   #0: DW-HDMI rev 0x0a, irq 21
> >
> > as it always has done for me.  There's nothing special about it.
> 
> Great, got it to probe now:
> 
> [    7.454760] ALSA device list:
> [    7.457764]   #0: DW-HDMI rev 0x0a, irq 19
> [    7.461990]   #1: wm8962-audio
> 
> There was a conflict and I resolved incorrectly here. Will try to play
> a wav file via aplay now.

Make sure you have the ALSA config file, as alsalib won't get on
with dw-hdmi only accepting 24-bit audio without this.  A copy is
attached.  It also tells ALSA how to deal with multi-channel audio
as well.

-- 
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.

[-- Attachment #2: dw-hdmi-ahb-aud.conf --]
[-- Type: text/plain, Size: 3907 bytes --]

#
# All PCM must be 24-bit for easy kernel conversion.
# IEC958 formatted output can be sent directly.
#
# Direct-to-hardware converting to 24-bit output.
#
dw-hdmi-ahb-aud.pcm.hw-s24le {
	@args [ CARD ]
	@args.CARD { type string }
	type linear
	slave.pcm { 
		type hw
		card $CARD
	}
	slave.format S24_LE
}

#
# Dmix hardware 24-bit output.
#
dw-hdmi-ahb-aud.pcm.dmix-s24le {
	@args [ CARD ]
	@args.CARD { type string }
	type plug
	slave.pcm { @func concat strings [ "dmix:" $CARD ",FORMAT=S24_LE" ] }
}

#
# Softvol with dmix output
#
dw-hdmi-ahb-aud.pcm.default {
	@args [ CARD ]
	@args.CARD { type string }
	type asym
	playback.pcm {
		type softvol
		slave.pcm {
			@func refer name { 
				@func concat strings [
					"cards."
					{ @func card_driver card $CARD }
					".pcm.dmix-s24le:CARD=" $CARD
				]
			}
		}
		control {
			name "PCM Playback Volume"
			card $CARD
		}
	}
}

#
# Common output path for front and surround outputs
#
dw-hdmi-ahb-aud.pcm.common.0 {
	@args [ CARD ]
	@args.CARD { type string }
	type asym
	playback.pcm {
		type softvol
		slave.pcm {
			@func refer name { 
				@func concat strings [
					"cards."
					{ @func card_driver card $CARD }
					".pcm.hw-s24le:CARD=" $CARD
				]
			}
		}
		control {
			name "PCM Playback Volume"
			card $CARD
		}
	}
}

<confdir:pcm/front.conf>

dw-hdmi-ahb-aud.pcm.front.0 cards.dw-hdmi-ahb-aud.pcm.common.0

# The mapping of ALSA channels to surround channels is very imprecise.
# ALSA uses a different terminology and speaker placement to the CEA
# surround positioning.  CEA has the positioning of:
#
#			LFE
#	FL	FLC	FC	FRC	FR
#
#
#	RL	RLC	RC	RRC	RR
#
# ALSA's idea is:
#
#			LFE
#		FL	C	FR
#
#		SL		SR
#
#		RL		RR
#
# We do our best to map between these representations.

<confdir:pcm/surround40.conf>

dw-hdmi-ahb-aud.pcm.surround40.0 {
	@args [ CARD ]
	@args.CARD { type string }
	type empty
	slave.pcm {
		@func refer
		name { 
			@func concat strings [
				"cards.dw-hdmi-ahb-aud.pcm.common.0:CARD="
				$CARD
			]
		}
	}
}

# surround 41 and surround50 are (annoyingly) mapped to surround51
# We could do without stacking two 'route' plugins on top of each other

<confdir:pcm/surround41.conf>
<confdir:pcm/surround50.conf>
<confdir:pcm/surround51.conf>

dw-hdmi-ahb-aud.pcm.surround51.0 {
	@args [ CARD ]
	@args.CARD { type string }
	type route
	slave.pcm {
		@func refer
		name { 
			@func concat strings [
				"cards.dw-hdmi-ahb-aud.pcm.common.0:CARD="
				$CARD
			]
		}
	}
	ttable.0.0 1.0 # FL  -> slave 0 -> hdmi 0 FL
	ttable.1.1 1.0 # FR  -> slave 1 -> hdmi 1 FR
	ttable.2.4 1.0 # RL  -> slave 4 -> hdmi 4 RL
	ttable.3.5 1.0 # RR  -> slave 5 -> hdmi 5 RR
	ttable.4.3 1.0 # C   -> slave 3 -> hdmi 3 FC 
	ttable.5.2 1.0 # LFE -> slave 2 -> hdmi 2 LFE
}

<confdir:pcm/surround71.conf>

dw-hdmi-ahb-aud.pcm.surround71.0 {
	@args [ CARD ]
	@args.CARD { type string }
	type route
	slave.pcm {
		@func refer
		name { 
			@func concat strings [
				"cards.dw-hdmi-ahb-aud.pcm.common.0:CARD="
				$CARD
			]
		}
	}
	slave.channels 8
	ttable.0.0 1.0 # FL  -> slave 0 -> hdmi 0 FL
	ttable.1.1 1.0 # FR  -> slave 1 -> hdmi 1 FR
	ttable.2.6 1.0 # RL  -> slave 6 -> hdmi 6 RLC/FLC
	ttable.3.7 1.0 # RR  -> slave 7 -> hdmi 7 RRC/FRC
	ttable.4.3 1.0 # C   -> slave 3 -> hdmi 3 FC
	ttable.5.2 1.0 # LFE -> slave 2 -> hdmi 2 LFE
	ttable.6.4 1.0 # SL  -> slave 4 -> hdmi 4 RL
	ttable.7.5 1.0 # SR  -> slave 5 -> hdmi 5 RR
}

<confdir:pcm/hdmi.conf>

dw-hdmi-ahb-aud.pcm.hdmi.0 {
	@args [ CARD AES0 AES1 AES2 AES3 ]
	@args.CARD { type string }
	@args.AES0 { type integer }
	@args.AES1 { type integer }
	@args.AES2 { type integer }
	@args.AES3 { type integer }
	type iec958
	slave.pcm {
		type hw
		card $CARD
	}
	slave.format IEC958_SUBFRAME_LE
	# $AES3 must be correct for some AV receivers to accept the stream
	status [ $AES0 $AES1 $AES2 $AES3 ]
}

<confdir:pcm/iec958.conf>

dw-hdmi-ahb-aud.pcm.iec958.0 cards.dw-hdmi-ahb-aud.pcm.hdmi.0

[-- Attachment #3: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver
@ 2015-10-06 18:54             ` Russell King - ARM Linux
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-10-06 18:54 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Oct 06, 2015 at 03:45:32PM -0300, Fabio Estevam wrote:
> On Tue, Oct 6, 2015 at 3:18 PM, Russell King - ARM Linux
> <linux@arm.linux.org.uk> wrote:
> 
> > Sorry, I've been out for most of the day.  There's no DT patches required.
> >
> > The dw_hdmi bridge driver creates its own platform device for the audio,
> > which should then bind to the dw_hdmi-ahb-audio driver using normal Linux
> > methods.
> >
> > I don't know what's wrong with your setup, for me, it just works:
> >
> > [    1.358829] dwhdmi-imx 120000.hdmi: Detected HDMI controller 0x13:0xa:0xa0:0xc1
> > [    1.377173] imx-drm display-subsystem: bound 120000.hdmi (ops dw_hdmi_imx_ops)
> > ...
> > [    2.851343] ALSA device list:
> > [    2.857364]   #0: DW-HDMI rev 0x0a, irq 21
> >
> > as it always has done for me.  There's nothing special about it.
> 
> Great, got it to probe now:
> 
> [    7.454760] ALSA device list:
> [    7.457764]   #0: DW-HDMI rev 0x0a, irq 19
> [    7.461990]   #1: wm8962-audio
> 
> There was a conflict and I resolved incorrectly here. Will try to play
> a wav file via aplay now.

Make sure you have the ALSA config file, as alsalib won't get on
with dw-hdmi only accepting 24-bit audio without this.  A copy is
attached.  It also tells ALSA how to deal with multi-channel audio
as well.

-- 
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.
-------------- next part --------------
#
# All PCM must be 24-bit for easy kernel conversion.
# IEC958 formatted output can be sent directly.
#
# Direct-to-hardware converting to 24-bit output.
#
dw-hdmi-ahb-aud.pcm.hw-s24le {
	@args [ CARD ]
	@args.CARD { type string }
	type linear
	slave.pcm { 
		type hw
		card $CARD
	}
	slave.format S24_LE
}

#
# Dmix hardware 24-bit output.
#
dw-hdmi-ahb-aud.pcm.dmix-s24le {
	@args [ CARD ]
	@args.CARD { type string }
	type plug
	slave.pcm { @func concat strings [ "dmix:" $CARD ",FORMAT=S24_LE" ] }
}

#
# Softvol with dmix output
#
dw-hdmi-ahb-aud.pcm.default {
	@args [ CARD ]
	@args.CARD { type string }
	type asym
	playback.pcm {
		type softvol
		slave.pcm {
			@func refer name { 
				@func concat strings [
					"cards."
					{ @func card_driver card $CARD }
					".pcm.dmix-s24le:CARD=" $CARD
				]
			}
		}
		control {
			name "PCM Playback Volume"
			card $CARD
		}
	}
}

#
# Common output path for front and surround outputs
#
dw-hdmi-ahb-aud.pcm.common.0 {
	@args [ CARD ]
	@args.CARD { type string }
	type asym
	playback.pcm {
		type softvol
		slave.pcm {
			@func refer name { 
				@func concat strings [
					"cards."
					{ @func card_driver card $CARD }
					".pcm.hw-s24le:CARD=" $CARD
				]
			}
		}
		control {
			name "PCM Playback Volume"
			card $CARD
		}
	}
}

<confdir:pcm/front.conf>

dw-hdmi-ahb-aud.pcm.front.0 cards.dw-hdmi-ahb-aud.pcm.common.0

# The mapping of ALSA channels to surround channels is very imprecise.
# ALSA uses a different terminology and speaker placement to the CEA
# surround positioning.  CEA has the positioning of:
#
#			LFE
#	FL	FLC	FC	FRC	FR
#
#
#	RL	RLC	RC	RRC	RR
#
# ALSA's idea is:
#
#			LFE
#		FL	C	FR
#
#		SL		SR
#
#		RL		RR
#
# We do our best to map between these representations.

<confdir:pcm/surround40.conf>

dw-hdmi-ahb-aud.pcm.surround40.0 {
	@args [ CARD ]
	@args.CARD { type string }
	type empty
	slave.pcm {
		@func refer
		name { 
			@func concat strings [
				"cards.dw-hdmi-ahb-aud.pcm.common.0:CARD="
				$CARD
			]
		}
	}
}

# surround 41 and surround50 are (annoyingly) mapped to surround51
# We could do without stacking two 'route' plugins on top of each other

<confdir:pcm/surround41.conf>
<confdir:pcm/surround50.conf>
<confdir:pcm/surround51.conf>

dw-hdmi-ahb-aud.pcm.surround51.0 {
	@args [ CARD ]
	@args.CARD { type string }
	type route
	slave.pcm {
		@func refer
		name { 
			@func concat strings [
				"cards.dw-hdmi-ahb-aud.pcm.common.0:CARD="
				$CARD
			]
		}
	}
	ttable.0.0 1.0 # FL  -> slave 0 -> hdmi 0 FL
	ttable.1.1 1.0 # FR  -> slave 1 -> hdmi 1 FR
	ttable.2.4 1.0 # RL  -> slave 4 -> hdmi 4 RL
	ttable.3.5 1.0 # RR  -> slave 5 -> hdmi 5 RR
	ttable.4.3 1.0 # C   -> slave 3 -> hdmi 3 FC 
	ttable.5.2 1.0 # LFE -> slave 2 -> hdmi 2 LFE
}

<confdir:pcm/surround71.conf>

dw-hdmi-ahb-aud.pcm.surround71.0 {
	@args [ CARD ]
	@args.CARD { type string }
	type route
	slave.pcm {
		@func refer
		name { 
			@func concat strings [
				"cards.dw-hdmi-ahb-aud.pcm.common.0:CARD="
				$CARD
			]
		}
	}
	slave.channels 8
	ttable.0.0 1.0 # FL  -> slave 0 -> hdmi 0 FL
	ttable.1.1 1.0 # FR  -> slave 1 -> hdmi 1 FR
	ttable.2.6 1.0 # RL  -> slave 6 -> hdmi 6 RLC/FLC
	ttable.3.7 1.0 # RR  -> slave 7 -> hdmi 7 RRC/FRC
	ttable.4.3 1.0 # C   -> slave 3 -> hdmi 3 FC
	ttable.5.2 1.0 # LFE -> slave 2 -> hdmi 2 LFE
	ttable.6.4 1.0 # SL  -> slave 4 -> hdmi 4 RL
	ttable.7.5 1.0 # SR  -> slave 5 -> hdmi 5 RR
}

<confdir:pcm/hdmi.conf>

dw-hdmi-ahb-aud.pcm.hdmi.0 {
	@args [ CARD AES0 AES1 AES2 AES3 ]
	@args.CARD { type string }
	@args.AES0 { type integer }
	@args.AES1 { type integer }
	@args.AES2 { type integer }
	@args.AES3 { type integer }
	type iec958
	slave.pcm {
		type hw
		card $CARD
	}
	slave.format IEC958_SUBFRAME_LE
	# $AES3 must be correct for some AV receivers to accept the stream
	status [ $AES0 $AES1 $AES2 $AES3 ]
}

<confdir:pcm/iec958.conf>

dw-hdmi-ahb-aud.pcm.iec958.0 cards.dw-hdmi-ahb-aud.pcm.hdmi.0

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver
  2015-10-06 18:54             ` Russell King - ARM Linux
  (?)
@ 2015-10-06 20:25               ` Fabio Estevam
  -1 siblings, 0 replies; 226+ messages in thread
From: Fabio Estevam @ 2015-10-06 20:25 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: linux-rockchip, alsa-devel, DRI mailing list, linux-kernel,
	linux-arm-kernel, Fabio Estevam, Jaroslav Kysela, Mark Brown,
	Yakir Yang, Andy Yan

On Tue, Oct 6, 2015 at 3:54 PM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:

> Make sure you have the ALSA config file, as alsalib won't get on
> with dw-hdmi only accepting 24-bit audio without this.  A copy is
> attached.  It also tells ALSA how to deal with multi-channel audio
> as well.

Thanks, Russell!

Got audio to play on my HDMI TV :-)

For the entire series:

Tested-by: Fabio Estevam <fabio.estevam@freescale.com>

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver
@ 2015-10-06 20:25               ` Fabio Estevam
  0 siblings, 0 replies; 226+ messages in thread
From: Fabio Estevam @ 2015-10-06 20:25 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Fabio Estevam, alsa-devel, linux-kernel, DRI mailing list,
	Jaroslav Kysela, linux-rockchip, Mark Brown, Andy Yan,
	linux-arm-kernel

On Tue, Oct 6, 2015 at 3:54 PM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:

> Make sure you have the ALSA config file, as alsalib won't get on
> with dw-hdmi only accepting 24-bit audio without this.  A copy is
> attached.  It also tells ALSA how to deal with multi-channel audio
> as well.

Thanks, Russell!

Got audio to play on my HDMI TV :-)

For the entire series:

Tested-by: Fabio Estevam <fabio.estevam@freescale.com>
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver
@ 2015-10-06 20:25               ` Fabio Estevam
  0 siblings, 0 replies; 226+ messages in thread
From: Fabio Estevam @ 2015-10-06 20:25 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Oct 6, 2015 at 3:54 PM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:

> Make sure you have the ALSA config file, as alsalib won't get on
> with dw-hdmi only accepting 24-bit audio without this.  A copy is
> attached.  It also tells ALSA how to deal with multi-channel audio
> as well.

Thanks, Russell!

Got audio to play on my HDMI TV :-)

For the entire series:

Tested-by: Fabio Estevam <fabio.estevam@freescale.com>

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 06/12] drm: bridge/dw_hdmi: clean up HDMI vs DVI mode handling
  2015-08-08 16:03   ` Russell King
  (?)
@ 2015-10-07  3:40     ` Yakir Yang
  -1 siblings, 0 replies; 226+ messages in thread
From: Yakir Yang @ 2015-10-07  3:40 UTC (permalink / raw)
  To: Russell King, linux-rockchip, dri-devel, linux-kernel, linux-arm-kernel
  Cc: Philipp Zabel, Andy Yan, Fabio Estevam, Sascha Hauer,
	Jon Nettleton, David Airlie

Hi Russell & Andy

On 08/09/2015 12:03 AM, Russell King wrote:
> The FSL kernel detects the HDMI vendor id, and uses this to set
> hdmi->edid_cfg.hdmi_cap, which is then used to set mdvi appropriately,
> rather than detecting whether we are outputting a CEA mode.  Update
> the dw_hdmi code to use this logic, but lets eliminate the mdvi
> variable, prefering the more verbose "hdmi->sink_is_hdmi" instead.
>
> Use the generic drm_detect_hdmi_monitor() to detect a HDMI sink.
>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

Actually I have posted similarly changes before, feel better about
this one, and I have backport those 06/12 & 07/12 to chrome-3.14
tree, audio still works rightly when I changing the display resolutions.
So I would like to share:

Tested-by: Yakir Yang <ykk@rock-chips.com>

Besides, Andy, would you like to share your ACK here :)

Best regards,
- Yakir
> ---
>   drivers/gpu/drm/bridge/dw_hdmi.c | 26 ++++++++++++--------------
>   1 file changed, 12 insertions(+), 14 deletions(-)
>
> diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
> index 2e211b8331ed..7f764716f3c4 100644
> --- a/drivers/gpu/drm/bridge/dw_hdmi.c
> +++ b/drivers/gpu/drm/bridge/dw_hdmi.c
> @@ -82,7 +82,6 @@ static const u16 csc_coeff_rgb_in_eitu709[3][4] = {
>   };
>   
>   struct hdmi_vmode {
> -	bool mdvi;
>   	bool mdataenablepolarity;
>   
>   	unsigned int mpixelclock;
> @@ -123,6 +122,7 @@ struct dw_hdmi {
>   
>   	struct i2c_adapter *ddc;
>   	void __iomem *regs;
> +	bool sink_is_hdmi;
>   
>   	spinlock_t audio_lock;
>   	struct mutex audio_mutex;
> @@ -913,11 +913,10 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep,
>   static int dw_hdmi_phy_init(struct dw_hdmi *hdmi)
>   {
>   	int i, ret;
> -	bool cscon = false;
> +	bool cscon;
>   
>   	/*check csc whether needed activated in HDMI mode */
> -	cscon = (is_color_space_conversion(hdmi) &&
> -			!hdmi->hdmi_data.video_mode.mdvi);
> +	cscon = hdmi->sink_is_hdmi && is_color_space_conversion(hdmi);
>   
>   	/* HDMI Phy spec says to do the phy initialization sequence twice */
>   	for (i = 0; i < 2; i++) {
> @@ -1094,9 +1093,9 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
>   		HDMI_FC_INVIDCONF_IN_I_P_INTERLACED :
>   		HDMI_FC_INVIDCONF_IN_I_P_PROGRESSIVE;
>   
> -	inv_val |= (vmode->mdvi ?
> -		HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE :
> -		HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE);
> +	inv_val |= hdmi->sink_is_hdmi ?
> +		HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE :
> +		HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE;
>   
>   	hdmi_writeb(hdmi, inv_val, HDMI_FC_INVIDCONF);
>   
> @@ -1236,10 +1235,8 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
>   
>   	if (!hdmi->vic) {
>   		dev_dbg(hdmi->dev, "Non-CEA mode used in HDMI\n");
> -		hdmi->hdmi_data.video_mode.mdvi = true;
>   	} else {
>   		dev_dbg(hdmi->dev, "CEA mode used vic=%d\n", hdmi->vic);
> -		hdmi->hdmi_data.video_mode.mdvi = false;
>   	}
>   
>   	if ((hdmi->vic == 6) || (hdmi->vic == 7) ||
> @@ -1275,10 +1272,8 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
>   	dw_hdmi_enable_video_path(hdmi);
>   
>   	/* not for DVI mode */
> -	if (hdmi->hdmi_data.video_mode.mdvi) {
> -		dev_dbg(hdmi->dev, "%s DVI mode\n", __func__);
> -	} else {
> -		dev_dbg(hdmi->dev, "%s CEA mode\n", __func__);
> +	if (hdmi->sink_is_hdmi) {
> +		dev_dbg(hdmi->dev, "%s HDMI mode\n", __func__);
>   
>   		/* HDMI Initialization Step E - Configure audio */
>   		hdmi_clk_regenerator_update_pixel_clock(hdmi);
> @@ -1286,6 +1281,8 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
>   
>   		/* HDMI Initialization Step F - Configure AVI InfoFrame */
>   		hdmi_config_AVI(hdmi, mode);
> +	} else {
> +		dev_dbg(hdmi->dev, "%s DVI mode\n", __func__);
>   	}
>   
>   	hdmi_video_packetize(hdmi);
> @@ -1294,7 +1291,7 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
>   	hdmi_tx_hdcp_config(hdmi);
>   
>   	dw_hdmi_clear_overflow(hdmi);
> -	if (hdmi->cable_plugin && !hdmi->hdmi_data.video_mode.mdvi)
> +	if (hdmi->cable_plugin && hdmi->sink_is_hdmi)
>   		hdmi_enable_overflow_interrupts(hdmi);
>   
>   	return 0;
> @@ -1444,6 +1441,7 @@ static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
>   		dev_dbg(hdmi->dev, "got edid: width[%d] x height[%d]\n",
>   			edid->width_cm, edid->height_cm);
>   
> +		hdmi->sink_is_hdmi = drm_detect_hdmi_monitor(edid);
>   		drm_mode_connector_update_edid_property(connector, edid);
>   		ret = drm_add_edid_modes(connector, edid);
>   		kfree(edid);



^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 06/12] drm: bridge/dw_hdmi: clean up HDMI vs DVI mode handling
@ 2015-10-07  3:40     ` Yakir Yang
  0 siblings, 0 replies; 226+ messages in thread
From: Yakir Yang @ 2015-10-07  3:40 UTC (permalink / raw)
  To: Russell King, linux-rockchip, dri-devel, linux-kernel, linux-arm-kernel
  Cc: Fabio Estevam, Andy Yan

Hi Russell & Andy

On 08/09/2015 12:03 AM, Russell King wrote:
> The FSL kernel detects the HDMI vendor id, and uses this to set
> hdmi->edid_cfg.hdmi_cap, which is then used to set mdvi appropriately,
> rather than detecting whether we are outputting a CEA mode.  Update
> the dw_hdmi code to use this logic, but lets eliminate the mdvi
> variable, prefering the more verbose "hdmi->sink_is_hdmi" instead.
>
> Use the generic drm_detect_hdmi_monitor() to detect a HDMI sink.
>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

Actually I have posted similarly changes before, feel better about
this one, and I have backport those 06/12 & 07/12 to chrome-3.14
tree, audio still works rightly when I changing the display resolutions.
So I would like to share:

Tested-by: Yakir Yang <ykk@rock-chips.com>

Besides, Andy, would you like to share your ACK here :)

Best regards,
- Yakir
> ---
>   drivers/gpu/drm/bridge/dw_hdmi.c | 26 ++++++++++++--------------
>   1 file changed, 12 insertions(+), 14 deletions(-)
>
> diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
> index 2e211b8331ed..7f764716f3c4 100644
> --- a/drivers/gpu/drm/bridge/dw_hdmi.c
> +++ b/drivers/gpu/drm/bridge/dw_hdmi.c
> @@ -82,7 +82,6 @@ static const u16 csc_coeff_rgb_in_eitu709[3][4] = {
>   };
>   
>   struct hdmi_vmode {
> -	bool mdvi;
>   	bool mdataenablepolarity;
>   
>   	unsigned int mpixelclock;
> @@ -123,6 +122,7 @@ struct dw_hdmi {
>   
>   	struct i2c_adapter *ddc;
>   	void __iomem *regs;
> +	bool sink_is_hdmi;
>   
>   	spinlock_t audio_lock;
>   	struct mutex audio_mutex;
> @@ -913,11 +913,10 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep,
>   static int dw_hdmi_phy_init(struct dw_hdmi *hdmi)
>   {
>   	int i, ret;
> -	bool cscon = false;
> +	bool cscon;
>   
>   	/*check csc whether needed activated in HDMI mode */
> -	cscon = (is_color_space_conversion(hdmi) &&
> -			!hdmi->hdmi_data.video_mode.mdvi);
> +	cscon = hdmi->sink_is_hdmi && is_color_space_conversion(hdmi);
>   
>   	/* HDMI Phy spec says to do the phy initialization sequence twice */
>   	for (i = 0; i < 2; i++) {
> @@ -1094,9 +1093,9 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
>   		HDMI_FC_INVIDCONF_IN_I_P_INTERLACED :
>   		HDMI_FC_INVIDCONF_IN_I_P_PROGRESSIVE;
>   
> -	inv_val |= (vmode->mdvi ?
> -		HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE :
> -		HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE);
> +	inv_val |= hdmi->sink_is_hdmi ?
> +		HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE :
> +		HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE;
>   
>   	hdmi_writeb(hdmi, inv_val, HDMI_FC_INVIDCONF);
>   
> @@ -1236,10 +1235,8 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
>   
>   	if (!hdmi->vic) {
>   		dev_dbg(hdmi->dev, "Non-CEA mode used in HDMI\n");
> -		hdmi->hdmi_data.video_mode.mdvi = true;
>   	} else {
>   		dev_dbg(hdmi->dev, "CEA mode used vic=%d\n", hdmi->vic);
> -		hdmi->hdmi_data.video_mode.mdvi = false;
>   	}
>   
>   	if ((hdmi->vic == 6) || (hdmi->vic == 7) ||
> @@ -1275,10 +1272,8 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
>   	dw_hdmi_enable_video_path(hdmi);
>   
>   	/* not for DVI mode */
> -	if (hdmi->hdmi_data.video_mode.mdvi) {
> -		dev_dbg(hdmi->dev, "%s DVI mode\n", __func__);
> -	} else {
> -		dev_dbg(hdmi->dev, "%s CEA mode\n", __func__);
> +	if (hdmi->sink_is_hdmi) {
> +		dev_dbg(hdmi->dev, "%s HDMI mode\n", __func__);
>   
>   		/* HDMI Initialization Step E - Configure audio */
>   		hdmi_clk_regenerator_update_pixel_clock(hdmi);
> @@ -1286,6 +1281,8 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
>   
>   		/* HDMI Initialization Step F - Configure AVI InfoFrame */
>   		hdmi_config_AVI(hdmi, mode);
> +	} else {
> +		dev_dbg(hdmi->dev, "%s DVI mode\n", __func__);
>   	}
>   
>   	hdmi_video_packetize(hdmi);
> @@ -1294,7 +1291,7 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
>   	hdmi_tx_hdcp_config(hdmi);
>   
>   	dw_hdmi_clear_overflow(hdmi);
> -	if (hdmi->cable_plugin && !hdmi->hdmi_data.video_mode.mdvi)
> +	if (hdmi->cable_plugin && hdmi->sink_is_hdmi)
>   		hdmi_enable_overflow_interrupts(hdmi);
>   
>   	return 0;
> @@ -1444,6 +1441,7 @@ static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
>   		dev_dbg(hdmi->dev, "got edid: width[%d] x height[%d]\n",
>   			edid->width_cm, edid->height_cm);
>   
> +		hdmi->sink_is_hdmi = drm_detect_hdmi_monitor(edid);
>   		drm_mode_connector_update_edid_property(connector, edid);
>   		ret = drm_add_edid_modes(connector, edid);
>   		kfree(edid);


_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 06/12] drm: bridge/dw_hdmi: clean up HDMI vs DVI mode handling
@ 2015-10-07  3:40     ` Yakir Yang
  0 siblings, 0 replies; 226+ messages in thread
From: Yakir Yang @ 2015-10-07  3:40 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Russell & Andy

On 08/09/2015 12:03 AM, Russell King wrote:
> The FSL kernel detects the HDMI vendor id, and uses this to set
> hdmi->edid_cfg.hdmi_cap, which is then used to set mdvi appropriately,
> rather than detecting whether we are outputting a CEA mode.  Update
> the dw_hdmi code to use this logic, but lets eliminate the mdvi
> variable, prefering the more verbose "hdmi->sink_is_hdmi" instead.
>
> Use the generic drm_detect_hdmi_monitor() to detect a HDMI sink.
>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

Actually I have posted similarly changes before, feel better about
this one, and I have backport those 06/12 & 07/12 to chrome-3.14
tree, audio still works rightly when I changing the display resolutions.
So I would like to share:

Tested-by: Yakir Yang <ykk@rock-chips.com>

Besides, Andy, would you like to share your ACK here :)

Best regards,
- Yakir
> ---
>   drivers/gpu/drm/bridge/dw_hdmi.c | 26 ++++++++++++--------------
>   1 file changed, 12 insertions(+), 14 deletions(-)
>
> diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
> index 2e211b8331ed..7f764716f3c4 100644
> --- a/drivers/gpu/drm/bridge/dw_hdmi.c
> +++ b/drivers/gpu/drm/bridge/dw_hdmi.c
> @@ -82,7 +82,6 @@ static const u16 csc_coeff_rgb_in_eitu709[3][4] = {
>   };
>   
>   struct hdmi_vmode {
> -	bool mdvi;
>   	bool mdataenablepolarity;
>   
>   	unsigned int mpixelclock;
> @@ -123,6 +122,7 @@ struct dw_hdmi {
>   
>   	struct i2c_adapter *ddc;
>   	void __iomem *regs;
> +	bool sink_is_hdmi;
>   
>   	spinlock_t audio_lock;
>   	struct mutex audio_mutex;
> @@ -913,11 +913,10 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep,
>   static int dw_hdmi_phy_init(struct dw_hdmi *hdmi)
>   {
>   	int i, ret;
> -	bool cscon = false;
> +	bool cscon;
>   
>   	/*check csc whether needed activated in HDMI mode */
> -	cscon = (is_color_space_conversion(hdmi) &&
> -			!hdmi->hdmi_data.video_mode.mdvi);
> +	cscon = hdmi->sink_is_hdmi && is_color_space_conversion(hdmi);
>   
>   	/* HDMI Phy spec says to do the phy initialization sequence twice */
>   	for (i = 0; i < 2; i++) {
> @@ -1094,9 +1093,9 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
>   		HDMI_FC_INVIDCONF_IN_I_P_INTERLACED :
>   		HDMI_FC_INVIDCONF_IN_I_P_PROGRESSIVE;
>   
> -	inv_val |= (vmode->mdvi ?
> -		HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE :
> -		HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE);
> +	inv_val |= hdmi->sink_is_hdmi ?
> +		HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE :
> +		HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE;
>   
>   	hdmi_writeb(hdmi, inv_val, HDMI_FC_INVIDCONF);
>   
> @@ -1236,10 +1235,8 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
>   
>   	if (!hdmi->vic) {
>   		dev_dbg(hdmi->dev, "Non-CEA mode used in HDMI\n");
> -		hdmi->hdmi_data.video_mode.mdvi = true;
>   	} else {
>   		dev_dbg(hdmi->dev, "CEA mode used vic=%d\n", hdmi->vic);
> -		hdmi->hdmi_data.video_mode.mdvi = false;
>   	}
>   
>   	if ((hdmi->vic == 6) || (hdmi->vic == 7) ||
> @@ -1275,10 +1272,8 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
>   	dw_hdmi_enable_video_path(hdmi);
>   
>   	/* not for DVI mode */
> -	if (hdmi->hdmi_data.video_mode.mdvi) {
> -		dev_dbg(hdmi->dev, "%s DVI mode\n", __func__);
> -	} else {
> -		dev_dbg(hdmi->dev, "%s CEA mode\n", __func__);
> +	if (hdmi->sink_is_hdmi) {
> +		dev_dbg(hdmi->dev, "%s HDMI mode\n", __func__);
>   
>   		/* HDMI Initialization Step E - Configure audio */
>   		hdmi_clk_regenerator_update_pixel_clock(hdmi);
> @@ -1286,6 +1281,8 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
>   
>   		/* HDMI Initialization Step F - Configure AVI InfoFrame */
>   		hdmi_config_AVI(hdmi, mode);
> +	} else {
> +		dev_dbg(hdmi->dev, "%s DVI mode\n", __func__);
>   	}
>   
>   	hdmi_video_packetize(hdmi);
> @@ -1294,7 +1291,7 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
>   	hdmi_tx_hdcp_config(hdmi);
>   
>   	dw_hdmi_clear_overflow(hdmi);
> -	if (hdmi->cable_plugin && !hdmi->hdmi_data.video_mode.mdvi)
> +	if (hdmi->cable_plugin && hdmi->sink_is_hdmi)
>   		hdmi_enable_overflow_interrupts(hdmi);
>   
>   	return 0;
> @@ -1444,6 +1441,7 @@ static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
>   		dev_dbg(hdmi->dev, "got edid: width[%d] x height[%d]\n",
>   			edid->width_cm, edid->height_cm);
>   
> +		hdmi->sink_is_hdmi = drm_detect_hdmi_monitor(edid);
>   		drm_mode_connector_update_edid_property(connector, edid);
>   		ret = drm_add_edid_modes(connector, edid);
>   		kfree(edid);

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 07/12] drm: bridge/dw_hdmi: enable audio only if sink supports audio
  2015-08-08 16:03   ` Russell King
  (?)
@ 2015-10-07  3:42     ` Yakir Yang
  -1 siblings, 0 replies; 226+ messages in thread
From: Yakir Yang @ 2015-10-07  3:42 UTC (permalink / raw)
  To: Russell King, linux-rockchip, dri-devel, linux-kernel, linux-arm-kernel
  Cc: Philipp Zabel, Andy Yan, Fabio Estevam, Sascha Hauer,
	Jon Nettleton, David Airlie

Hi Russell & Andy

On 08/09/2015 12:03 AM, Russell King wrote:
> Only enable audio support if the sink supports audio in some form, as
> defined via its EDID.  We discover this capability using the generic
> drm_detect_monitor_audio() function.
>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

Some to 06/12 reply.

Tested-by: Yakir Yang <ykk@rock-chips.com>

- Yakir
> ---
>   drivers/gpu/drm/bridge/dw_hdmi.c | 12 +++++++++---
>   1 file changed, 9 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
> index 7f764716f3c4..578d7362cd65 100644
> --- a/drivers/gpu/drm/bridge/dw_hdmi.c
> +++ b/drivers/gpu/drm/bridge/dw_hdmi.c
> @@ -123,6 +123,7 @@ struct dw_hdmi {
>   	struct i2c_adapter *ddc;
>   	void __iomem *regs;
>   	bool sink_is_hdmi;
> +	bool sink_has_audio;
>   
>   	spinlock_t audio_lock;
>   	struct mutex audio_mutex;
> @@ -1271,13 +1272,17 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
>   	/* HDMI Initialization Step B.3 */
>   	dw_hdmi_enable_video_path(hdmi);
>   
> -	/* not for DVI mode */
> -	if (hdmi->sink_is_hdmi) {
> -		dev_dbg(hdmi->dev, "%s HDMI mode\n", __func__);
> +	if (hdmi->sink_has_audio) {
> +		dev_dbg(hdmi->dev, "sink has audio support\n");
>   
>   		/* HDMI Initialization Step E - Configure audio */
>   		hdmi_clk_regenerator_update_pixel_clock(hdmi);
>   		hdmi_enable_audio_clk(hdmi);
> +	}
> +
> +	/* not for DVI mode */
> +	if (hdmi->sink_is_hdmi) {
> +		dev_dbg(hdmi->dev, "%s HDMI mode\n", __func__);
>   
>   		/* HDMI Initialization Step F - Configure AVI InfoFrame */
>   		hdmi_config_AVI(hdmi, mode);
> @@ -1442,6 +1447,7 @@ static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
>   			edid->width_cm, edid->height_cm);
>   
>   		hdmi->sink_is_hdmi = drm_detect_hdmi_monitor(edid);
> +		hdmi->sink_has_audio = drm_detect_monitor_audio(edid);
>   		drm_mode_connector_update_edid_property(connector, edid);
>   		ret = drm_add_edid_modes(connector, edid);
>   		kfree(edid);



^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 07/12] drm: bridge/dw_hdmi: enable audio only if sink supports audio
@ 2015-10-07  3:42     ` Yakir Yang
  0 siblings, 0 replies; 226+ messages in thread
From: Yakir Yang @ 2015-10-07  3:42 UTC (permalink / raw)
  To: Russell King, linux-rockchip, dri-devel, linux-kernel, linux-arm-kernel
  Cc: Fabio Estevam, Andy Yan

Hi Russell & Andy

On 08/09/2015 12:03 AM, Russell King wrote:
> Only enable audio support if the sink supports audio in some form, as
> defined via its EDID.  We discover this capability using the generic
> drm_detect_monitor_audio() function.
>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

Some to 06/12 reply.

Tested-by: Yakir Yang <ykk@rock-chips.com>

- Yakir
> ---
>   drivers/gpu/drm/bridge/dw_hdmi.c | 12 +++++++++---
>   1 file changed, 9 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
> index 7f764716f3c4..578d7362cd65 100644
> --- a/drivers/gpu/drm/bridge/dw_hdmi.c
> +++ b/drivers/gpu/drm/bridge/dw_hdmi.c
> @@ -123,6 +123,7 @@ struct dw_hdmi {
>   	struct i2c_adapter *ddc;
>   	void __iomem *regs;
>   	bool sink_is_hdmi;
> +	bool sink_has_audio;
>   
>   	spinlock_t audio_lock;
>   	struct mutex audio_mutex;
> @@ -1271,13 +1272,17 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
>   	/* HDMI Initialization Step B.3 */
>   	dw_hdmi_enable_video_path(hdmi);
>   
> -	/* not for DVI mode */
> -	if (hdmi->sink_is_hdmi) {
> -		dev_dbg(hdmi->dev, "%s HDMI mode\n", __func__);
> +	if (hdmi->sink_has_audio) {
> +		dev_dbg(hdmi->dev, "sink has audio support\n");
>   
>   		/* HDMI Initialization Step E - Configure audio */
>   		hdmi_clk_regenerator_update_pixel_clock(hdmi);
>   		hdmi_enable_audio_clk(hdmi);
> +	}
> +
> +	/* not for DVI mode */
> +	if (hdmi->sink_is_hdmi) {
> +		dev_dbg(hdmi->dev, "%s HDMI mode\n", __func__);
>   
>   		/* HDMI Initialization Step F - Configure AVI InfoFrame */
>   		hdmi_config_AVI(hdmi, mode);
> @@ -1442,6 +1447,7 @@ static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
>   			edid->width_cm, edid->height_cm);
>   
>   		hdmi->sink_is_hdmi = drm_detect_hdmi_monitor(edid);
> +		hdmi->sink_has_audio = drm_detect_monitor_audio(edid);
>   		drm_mode_connector_update_edid_property(connector, edid);
>   		ret = drm_add_edid_modes(connector, edid);
>   		kfree(edid);


_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 07/12] drm: bridge/dw_hdmi: enable audio only if sink supports audio
@ 2015-10-07  3:42     ` Yakir Yang
  0 siblings, 0 replies; 226+ messages in thread
From: Yakir Yang @ 2015-10-07  3:42 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Russell & Andy

On 08/09/2015 12:03 AM, Russell King wrote:
> Only enable audio support if the sink supports audio in some form, as
> defined via its EDID.  We discover this capability using the generic
> drm_detect_monitor_audio() function.
>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

Some to 06/12 reply.

Tested-by: Yakir Yang <ykk@rock-chips.com>

- Yakir
> ---
>   drivers/gpu/drm/bridge/dw_hdmi.c | 12 +++++++++---
>   1 file changed, 9 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
> index 7f764716f3c4..578d7362cd65 100644
> --- a/drivers/gpu/drm/bridge/dw_hdmi.c
> +++ b/drivers/gpu/drm/bridge/dw_hdmi.c
> @@ -123,6 +123,7 @@ struct dw_hdmi {
>   	struct i2c_adapter *ddc;
>   	void __iomem *regs;
>   	bool sink_is_hdmi;
> +	bool sink_has_audio;
>   
>   	spinlock_t audio_lock;
>   	struct mutex audio_mutex;
> @@ -1271,13 +1272,17 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
>   	/* HDMI Initialization Step B.3 */
>   	dw_hdmi_enable_video_path(hdmi);
>   
> -	/* not for DVI mode */
> -	if (hdmi->sink_is_hdmi) {
> -		dev_dbg(hdmi->dev, "%s HDMI mode\n", __func__);
> +	if (hdmi->sink_has_audio) {
> +		dev_dbg(hdmi->dev, "sink has audio support\n");
>   
>   		/* HDMI Initialization Step E - Configure audio */
>   		hdmi_clk_regenerator_update_pixel_clock(hdmi);
>   		hdmi_enable_audio_clk(hdmi);
> +	}
> +
> +	/* not for DVI mode */
> +	if (hdmi->sink_is_hdmi) {
> +		dev_dbg(hdmi->dev, "%s HDMI mode\n", __func__);
>   
>   		/* HDMI Initialization Step F - Configure AVI InfoFrame */
>   		hdmi_config_AVI(hdmi, mode);
> @@ -1442,6 +1447,7 @@ static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
>   			edid->width_cm, edid->height_cm);
>   
>   		hdmi->sink_is_hdmi = drm_detect_hdmi_monitor(edid);
> +		hdmi->sink_has_audio = drm_detect_monitor_audio(edid);
>   		drm_mode_connector_update_edid_property(connector, edid);
>   		ret = drm_add_edid_modes(connector, edid);
>   		kfree(edid);

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 08/12] drm: bridge/dw_hdmi: avoid enabling interface in mode_set
  2015-08-08 16:04   ` Russell King
@ 2015-10-07  3:50     ` Yakir Yang
  -1 siblings, 0 replies; 226+ messages in thread
From: Yakir Yang @ 2015-10-07  3:50 UTC (permalink / raw)
  To: Russell King, linux-rockchip, dri-devel, linux-kernel, linux-arm-kernel
  Cc: Philipp Zabel, Andy Yan, Fabio Estevam, Sascha Hauer,
	Jon Nettleton, David Airlie



On 08/09/2015 12:04 AM, Russell King wrote:
> On a mode set, DRM makes the following sequence of calls:
> * for_each_encoder
> *   bridge	mode_fixup
> *   encoder	mode_fixup
> * crtc		mode_fixup
> * for_each_encoder
> *   bridge	disable
> *   encoder	prepare
> *   bridge	post_disable
> * disable unused encoders
> * crtc		prepare
> * crtc		mode_set
> * for_each_encoder
> *   encoder	mode_set
> *   bridge	mode_set
> * crtc		commit
> * for_each_encoder
> *   bridge	pre_enable
> *   encoder	commit
> *   bridge	enable
>
> dw_hdmi enables the HDMI output in both the bridge mode_set() and also
> the bridge enable() step.  This is duplicated work - we can avoid the
> setup in mode_set() and just do it in the enable() stage.  This
> simplifies the code a little.
>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

I have noticed that dw_hdmi driver have poweron/poweroff when
driver detect HPD event in irq thread, that's also duplicated works,
would you like to collect that changes into this one:

static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
{
         ......

         if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
                 if (phy_int_pol & HDMI_PHY_HPD) {
                         dev_dbg(hdmi->dev, "EVENT=plugin\n");

                         hdmi_modb(hdmi, 0, HDMI_PHY_HPD, HDMI_PHY_POL0);

                         dw_hdmi_poweron(hdmi);    // no need here
                 } else {
                         dev_dbg(hdmi->dev, "EVENT=plugout\n");

                         hdmi_modb(hdmi, HDMI_PHY_HPD, HDMI_PHY_HPD,
                                   HDMI_PHY_POL0);

                         dw_hdmi_poweroff(hdmi);    // no need here
                 }
                 drm_helper_hpd_irq_event(hdmi->connector.dev);
         }
         ......
}

Thanks,
- Yakir
> ---
>   drivers/gpu/drm/bridge/dw_hdmi.c | 2 --
>   1 file changed, 2 deletions(-)
>
> diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
> index 578d7362cd65..fbac8386552b 100644
> --- a/drivers/gpu/drm/bridge/dw_hdmi.c
> +++ b/drivers/gpu/drm/bridge/dw_hdmi.c
> @@ -1389,8 +1389,6 @@ static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge,
>   {
>   	struct dw_hdmi *hdmi = bridge->driver_private;
>   
> -	dw_hdmi_setup(hdmi, mode);
> -
>   	/* Store the display mode for plugin/DKMS poweron events */
>   	memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode));
>   }



^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 08/12] drm: bridge/dw_hdmi: avoid enabling interface in mode_set
@ 2015-10-07  3:50     ` Yakir Yang
  0 siblings, 0 replies; 226+ messages in thread
From: Yakir Yang @ 2015-10-07  3:50 UTC (permalink / raw)
  To: linux-arm-kernel



On 08/09/2015 12:04 AM, Russell King wrote:
> On a mode set, DRM makes the following sequence of calls:
> * for_each_encoder
> *   bridge	mode_fixup
> *   encoder	mode_fixup
> * crtc		mode_fixup
> * for_each_encoder
> *   bridge	disable
> *   encoder	prepare
> *   bridge	post_disable
> * disable unused encoders
> * crtc		prepare
> * crtc		mode_set
> * for_each_encoder
> *   encoder	mode_set
> *   bridge	mode_set
> * crtc		commit
> * for_each_encoder
> *   bridge	pre_enable
> *   encoder	commit
> *   bridge	enable
>
> dw_hdmi enables the HDMI output in both the bridge mode_set() and also
> the bridge enable() step.  This is duplicated work - we can avoid the
> setup in mode_set() and just do it in the enable() stage.  This
> simplifies the code a little.
>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

I have noticed that dw_hdmi driver have poweron/poweroff when
driver detect HPD event in irq thread, that's also duplicated works,
would you like to collect that changes into this one:

static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
{
         ......

         if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
                 if (phy_int_pol & HDMI_PHY_HPD) {
                         dev_dbg(hdmi->dev, "EVENT=plugin\n");

                         hdmi_modb(hdmi, 0, HDMI_PHY_HPD, HDMI_PHY_POL0);

                         dw_hdmi_poweron(hdmi);    // no need here
                 } else {
                         dev_dbg(hdmi->dev, "EVENT=plugout\n");

                         hdmi_modb(hdmi, HDMI_PHY_HPD, HDMI_PHY_HPD,
                                   HDMI_PHY_POL0);

                         dw_hdmi_poweroff(hdmi);    // no need here
                 }
                 drm_helper_hpd_irq_event(hdmi->connector.dev);
         }
         ......
}

Thanks,
- Yakir
> ---
>   drivers/gpu/drm/bridge/dw_hdmi.c | 2 --
>   1 file changed, 2 deletions(-)
>
> diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
> index 578d7362cd65..fbac8386552b 100644
> --- a/drivers/gpu/drm/bridge/dw_hdmi.c
> +++ b/drivers/gpu/drm/bridge/dw_hdmi.c
> @@ -1389,8 +1389,6 @@ static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge,
>   {
>   	struct dw_hdmi *hdmi = bridge->driver_private;
>   
> -	dw_hdmi_setup(hdmi, mode);
> -
>   	/* Store the display mode for plugin/DKMS poweron events */
>   	memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode));
>   }

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 09/12] drm: bridge/dw_hdmi: rename dw_hdmi_phy_enable_power()
  2015-08-08 16:04   ` Russell King
  (?)
  (?)
@ 2015-10-07  4:00   ` Yakir Yang
  -1 siblings, 0 replies; 226+ messages in thread
From: Yakir Yang @ 2015-10-07  4:00 UTC (permalink / raw)
  To: Russell King, linux-rockchip, dri-devel, linux-kernel, linux-arm-kernel
  Cc: Fabio Estevam, Andy Yan


[-- Attachment #1.1: Type: text/plain, Size: 2524 bytes --]



On 08/09/2015 12:04 AM, Russell King wrote:
> dw_hdmi_phy_enable_power() is not about enabling and disabling power.
> It is about allowing or preventing power-down mode being entered - the
> register is documented as "Power-down enable (active low 0b)."

Same as rockchip hdmi document, great clean, wish I have the
qualification to share review. (If no, that's fine :) )
Reviewed-by: Yakir Yang <ykk@rock-chips.com>

- Yakir
> This can be seen as the bit has no effect when the HDMI phy is
> operational on iMX6 hardware.
>
> Rename the function to dw_hdmi_phy_enable_powerdown() to reflect the
> documentation, make it take a bool for the 'enable' argument, and invert
> the value to be written.
>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> ---
>   drivers/gpu/drm/bridge/dw_hdmi.c | 10 +++++-----
>   1 file changed, 5 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
> index fbac8386552b..7b8a4e942a71 100644
> --- a/drivers/gpu/drm/bridge/dw_hdmi.c
> +++ b/drivers/gpu/drm/bridge/dw_hdmi.c
> @@ -737,9 +737,9 @@ static int hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data,
>   	return 0;
>   }
>   
> -static void dw_hdmi_phy_enable_power(struct dw_hdmi *hdmi, u8 enable)
> +static void dw_hdmi_phy_enable_powerdown(struct dw_hdmi *hdmi, bool enable)
>   {
> -	hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
> +	hdmi_mask_writeb(hdmi, !enable, HDMI_PHY_CONF0,
>   			 HDMI_PHY_CONF0_PDZ_OFFSET,
>   			 HDMI_PHY_CONF0_PDZ_MASK);
>   }
> @@ -879,7 +879,7 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep,
>   	/* REMOVE CLK TERM */
>   	hdmi_phy_i2c_write(hdmi, 0x8000, 0x05);  /* CKCALCTRL */
>   
> -	dw_hdmi_phy_enable_power(hdmi, 1);
> +	dw_hdmi_phy_enable_powerdown(hdmi, false);
>   
>   	/* toggle TMDS enable */
>   	dw_hdmi_phy_enable_tmds(hdmi, 0);
> @@ -924,7 +924,7 @@ static int dw_hdmi_phy_init(struct dw_hdmi *hdmi)
>   		dw_hdmi_phy_sel_data_en_pol(hdmi, 1);
>   		dw_hdmi_phy_sel_interface_control(hdmi, 0);
>   		dw_hdmi_phy_enable_tmds(hdmi, 0);
> -		dw_hdmi_phy_enable_power(hdmi, 0);
> +		dw_hdmi_phy_enable_powerdown(hdmi, true);
>   
>   		/* Enable CSC */
>   		ret = hdmi_phy_configure(hdmi, 0, 8, cscon);
> @@ -1155,7 +1155,7 @@ static void dw_hdmi_phy_disable(struct dw_hdmi *hdmi)
>   		return;
>   
>   	dw_hdmi_phy_enable_tmds(hdmi, 0);
> -	dw_hdmi_phy_enable_power(hdmi, 0);
> +	dw_hdmi_phy_enable_powerdown(hdmi, true);
>   
>   	hdmi->phy_enabled = false;
>   }


[-- Attachment #1.2: Type: text/html, Size: 3169 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 10/12] drm: bridge/dw_hdmi: fix phy enable/disable handling
  2015-08-08 16:04   ` Russell King
  (?)
  (?)
@ 2015-10-07  4:05   ` Yakir Yang
  2015-10-07  9:48       ` Russell King - ARM Linux
  -1 siblings, 1 reply; 226+ messages in thread
From: Yakir Yang @ 2015-10-07  4:05 UTC (permalink / raw)
  To: Russell King, linux-rockchip, dri-devel, linux-kernel, linux-arm-kernel
  Cc: Fabio Estevam, Andy Yan


[-- Attachment #1.1: Type: text/plain, Size: 3783 bytes --]



On 08/09/2015 12:04 AM, Russell King wrote:
> The dw_hdmi enable/disable handling is particularly weak in several
> regards:
> * The hotplug interrupt could call hdmi_poweron() or hdmi_poweroff()
>    while DRM is setting a mode, which could race with a mode being set.
> * Hotplug will always re-enable the phy whenever it detects an active
>    hotplug signal, even if DRM has disabled the output.
>
> Resolve all of these by introducing a mutex to prevent races, and a
> state-tracking bool so we know whether DRM wishes the output to be
> enabled.  We choose to use our own mutex rather than ->struct_mutex
> so that we can still process interrupts in a timely fashion.
>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> ---
>   drivers/gpu/drm/bridge/dw_hdmi.c | 29 ++++++++++++++++++++++-------
>   1 file changed, 22 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
> index 7b8a4e942a71..0ee188930d26 100644
> --- a/drivers/gpu/drm/bridge/dw_hdmi.c
> +++ b/drivers/gpu/drm/bridge/dw_hdmi.c
> @@ -125,6 +125,9 @@ struct dw_hdmi {
>   	bool sink_is_hdmi;
>   	bool sink_has_audio;
>   
> +	struct mutex mutex;		/* for state below and previous_mode */
> +	bool disabled;			/* DRM has disabled our bridge */
> +
>   	spinlock_t audio_lock;
>   	struct mutex audio_mutex;
>   	unsigned int sample_rate;
> @@ -1389,8 +1392,12 @@ static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge,
>   {
>   	struct dw_hdmi *hdmi = bridge->driver_private;
>   
> +	mutex_lock(&hdmi->mutex);
> +
>   	/* Store the display mode for plugin/DKMS poweron events */
>   	memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode));
> +
> +	mutex_unlock(&hdmi->mutex);
>   }
>   
>   static bool dw_hdmi_bridge_mode_fixup(struct drm_bridge *bridge,
> @@ -1404,14 +1411,20 @@ static void dw_hdmi_bridge_disable(struct drm_bridge *bridge)
>   {
>   	struct dw_hdmi *hdmi = bridge->driver_private;
>   
> +	mutex_lock(&hdmi->mutex);
> +	hdmi->disabled = true;
>   	dw_hdmi_poweroff(hdmi);
> +	mutex_unlock(&hdmi->mutex);
>   }
>   
>   static void dw_hdmi_bridge_enable(struct drm_bridge *bridge)
>   {
>   	struct dw_hdmi *hdmi = bridge->driver_private;
>   
> +	mutex_lock(&hdmi->mutex);
>   	dw_hdmi_poweron(hdmi);
> +	hdmi->disabled = false;
> +	mutex_unlock(&hdmi->mutex);
>   }
>   
>   static void dw_hdmi_bridge_nop(struct drm_bridge *bridge)
> @@ -1534,20 +1547,20 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
>   	phy_int_pol = hdmi_readb(hdmi, HDMI_PHY_POL0);
>   
>   	if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
> +		hdmi_modb(hdmi, ~phy_int_pol, HDMI_PHY_HPD, HDMI_PHY_POL0);
> +		mutex_lock(&hdmi->mutex);
>   		if (phy_int_pol & HDMI_PHY_HPD) {
>   			dev_dbg(hdmi->dev, "EVENT=plugin\n");
>   
> -			hdmi_modb(hdmi, 0, HDMI_PHY_HPD, HDMI_PHY_POL0);
> -
> -			dw_hdmi_poweron(hdmi);
> +			if (!hdmi->disabled)
> +				dw_hdmi_poweron(hdmi);
>   		} else {
>   			dev_dbg(hdmi->dev, "EVENT=plugout\n");
>   
> -			hdmi_modb(hdmi, HDMI_PHY_HPD, HDMI_PHY_HPD,
> -				  HDMI_PHY_POL0);
> -
> -			dw_hdmi_poweroff(hdmi);
> +			if (!hdmi->disabled)
> +				dw_hdmi_poweroff(hdmi);

Just like my reply on 08/12, I thought this could be removed, so 
poweron/poweroff
would only be called with bridge->enable/bridge->disable, them maybe no need
mutex here.

Best regards,
- Yakir
>   		}
> +		mutex_unlock(&hdmi->mutex);
>   		drm_helper_hpd_irq_event(hdmi->bridge->dev);
>   	}
>   
> @@ -1617,7 +1630,9 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
>   	hdmi->sample_rate = 48000;
>   	hdmi->ratio = 100;
>   	hdmi->encoder = encoder;
> +	hdmi->disabled = true;
>   
> +	mutex_init(&hdmi->mutex);
>   	mutex_init(&hdmi->audio_mutex);
>   	spin_lock_init(&hdmi->audio_lock);
>   


[-- Attachment #1.2: Type: text/html, Size: 4397 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 08/12] drm: bridge/dw_hdmi: avoid enabling interface in mode_set
  2015-10-07  3:50     ` Yakir Yang
  (?)
@ 2015-10-07  9:18       ` Russell King - ARM Linux
  -1 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-10-07  9:18 UTC (permalink / raw)
  To: Yakir Yang
  Cc: linux-rockchip, dri-devel, linux-kernel, linux-arm-kernel,
	Fabio Estevam, David Airlie, Sascha Hauer, Philipp Zabel,
	Andy Yan, Jon Nettleton

On Wed, Oct 07, 2015 at 11:50:53AM +0800, Yakir Yang wrote:
> 
> 
> On 08/09/2015 12:04 AM, Russell King wrote:
> >On a mode set, DRM makes the following sequence of calls:
> >* for_each_encoder
> >*   bridge	mode_fixup
> >*   encoder	mode_fixup
> >* crtc		mode_fixup
> >* for_each_encoder
> >*   bridge	disable
> >*   encoder	prepare
> >*   bridge	post_disable
> >* disable unused encoders
> >* crtc		prepare
> >* crtc		mode_set
> >* for_each_encoder
> >*   encoder	mode_set
> >*   bridge	mode_set
> >* crtc		commit
> >* for_each_encoder
> >*   bridge	pre_enable
> >*   encoder	commit
> >*   bridge	enable
> >
> >dw_hdmi enables the HDMI output in both the bridge mode_set() and also
> >the bridge enable() step.  This is duplicated work - we can avoid the
> >setup in mode_set() and just do it in the enable() stage.  This
> >simplifies the code a little.
> >
> >Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> 
> I have noticed that dw_hdmi driver have poweron/poweroff when
> driver detect HPD event in irq thread, that's also duplicated works,
> would you like to collect that changes into this one:
> 
> static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
> {
>         ......
> 
>         if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
>                 if (phy_int_pol & HDMI_PHY_HPD) {
>                         dev_dbg(hdmi->dev, "EVENT=plugin\n");
> 
>                         hdmi_modb(hdmi, 0, HDMI_PHY_HPD, HDMI_PHY_POL0);
> 
>                         dw_hdmi_poweron(hdmi);    // no need here
>                 } else {
>                         dev_dbg(hdmi->dev, "EVENT=plugout\n");
> 
>                         hdmi_modb(hdmi, HDMI_PHY_HPD, HDMI_PHY_HPD,
>                                   HDMI_PHY_POL0);
> 
>                         dw_hdmi_poweroff(hdmi);    // no need here
>                 }
>                 drm_helper_hpd_irq_event(hdmi->connector.dev);
>         }
>         ......
> }

I'm very much of the opinion of making small logical changes.  This
patch is one small logical change to the DRM-side logic to get rid
of the identified duplication there without touching anything else.
If removing the above calls to dw_hdmi_poweron()/dw_hdmi_poweroff()
were found to cause a regression, then the whole change would end
up being reverted, which would be annoying.

-- 
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 08/12] drm: bridge/dw_hdmi: avoid enabling interface in mode_set
@ 2015-10-07  9:18       ` Russell King - ARM Linux
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-10-07  9:18 UTC (permalink / raw)
  To: Yakir Yang
  Cc: Fabio Estevam, linux-kernel, dri-devel, linux-rockchip, Andy Yan,
	linux-arm-kernel

On Wed, Oct 07, 2015 at 11:50:53AM +0800, Yakir Yang wrote:
> 
> 
> On 08/09/2015 12:04 AM, Russell King wrote:
> >On a mode set, DRM makes the following sequence of calls:
> >* for_each_encoder
> >*   bridge	mode_fixup
> >*   encoder	mode_fixup
> >* crtc		mode_fixup
> >* for_each_encoder
> >*   bridge	disable
> >*   encoder	prepare
> >*   bridge	post_disable
> >* disable unused encoders
> >* crtc		prepare
> >* crtc		mode_set
> >* for_each_encoder
> >*   encoder	mode_set
> >*   bridge	mode_set
> >* crtc		commit
> >* for_each_encoder
> >*   bridge	pre_enable
> >*   encoder	commit
> >*   bridge	enable
> >
> >dw_hdmi enables the HDMI output in both the bridge mode_set() and also
> >the bridge enable() step.  This is duplicated work - we can avoid the
> >setup in mode_set() and just do it in the enable() stage.  This
> >simplifies the code a little.
> >
> >Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> 
> I have noticed that dw_hdmi driver have poweron/poweroff when
> driver detect HPD event in irq thread, that's also duplicated works,
> would you like to collect that changes into this one:
> 
> static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
> {
>         ......
> 
>         if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
>                 if (phy_int_pol & HDMI_PHY_HPD) {
>                         dev_dbg(hdmi->dev, "EVENT=plugin\n");
> 
>                         hdmi_modb(hdmi, 0, HDMI_PHY_HPD, HDMI_PHY_POL0);
> 
>                         dw_hdmi_poweron(hdmi);    // no need here
>                 } else {
>                         dev_dbg(hdmi->dev, "EVENT=plugout\n");
> 
>                         hdmi_modb(hdmi, HDMI_PHY_HPD, HDMI_PHY_HPD,
>                                   HDMI_PHY_POL0);
> 
>                         dw_hdmi_poweroff(hdmi);    // no need here
>                 }
>                 drm_helper_hpd_irq_event(hdmi->connector.dev);
>         }
>         ......
> }

I'm very much of the opinion of making small logical changes.  This
patch is one small logical change to the DRM-side logic to get rid
of the identified duplication there without touching anything else.
If removing the above calls to dw_hdmi_poweron()/dw_hdmi_poweroff()
were found to cause a regression, then the whole change would end
up being reverted, which would be annoying.

-- 
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 08/12] drm: bridge/dw_hdmi: avoid enabling interface in mode_set
@ 2015-10-07  9:18       ` Russell King - ARM Linux
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-10-07  9:18 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Oct 07, 2015 at 11:50:53AM +0800, Yakir Yang wrote:
> 
> 
> On 08/09/2015 12:04 AM, Russell King wrote:
> >On a mode set, DRM makes the following sequence of calls:
> >* for_each_encoder
> >*   bridge	mode_fixup
> >*   encoder	mode_fixup
> >* crtc		mode_fixup
> >* for_each_encoder
> >*   bridge	disable
> >*   encoder	prepare
> >*   bridge	post_disable
> >* disable unused encoders
> >* crtc		prepare
> >* crtc		mode_set
> >* for_each_encoder
> >*   encoder	mode_set
> >*   bridge	mode_set
> >* crtc		commit
> >* for_each_encoder
> >*   bridge	pre_enable
> >*   encoder	commit
> >*   bridge	enable
> >
> >dw_hdmi enables the HDMI output in both the bridge mode_set() and also
> >the bridge enable() step.  This is duplicated work - we can avoid the
> >setup in mode_set() and just do it in the enable() stage.  This
> >simplifies the code a little.
> >
> >Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> 
> I have noticed that dw_hdmi driver have poweron/poweroff when
> driver detect HPD event in irq thread, that's also duplicated works,
> would you like to collect that changes into this one:
> 
> static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
> {
>         ......
> 
>         if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
>                 if (phy_int_pol & HDMI_PHY_HPD) {
>                         dev_dbg(hdmi->dev, "EVENT=plugin\n");
> 
>                         hdmi_modb(hdmi, 0, HDMI_PHY_HPD, HDMI_PHY_POL0);
> 
>                         dw_hdmi_poweron(hdmi);    // no need here
>                 } else {
>                         dev_dbg(hdmi->dev, "EVENT=plugout\n");
> 
>                         hdmi_modb(hdmi, HDMI_PHY_HPD, HDMI_PHY_HPD,
>                                   HDMI_PHY_POL0);
> 
>                         dw_hdmi_poweroff(hdmi);    // no need here
>                 }
>                 drm_helper_hpd_irq_event(hdmi->connector.dev);
>         }
>         ......
> }

I'm very much of the opinion of making small logical changes.  This
patch is one small logical change to the DRM-side logic to get rid
of the identified duplication there without touching anything else.
If removing the above calls to dw_hdmi_poweron()/dw_hdmi_poweroff()
were found to cause a regression, then the whole change would end
up being reverted, which would be annoying.

-- 
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 08/12] drm: bridge/dw_hdmi: avoid enabling interface in mode_set
  2015-10-07  9:18       ` Russell King - ARM Linux
  (?)
@ 2015-10-07  9:35         ` Yakir Yang
  -1 siblings, 0 replies; 226+ messages in thread
From: Yakir Yang @ 2015-10-07  9:35 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: linux-rockchip, dri-devel, linux-kernel, linux-arm-kernel,
	Fabio Estevam, David Airlie, Sascha Hauer, Philipp Zabel,
	Andy Yan, Jon Nettleton



On 10/07/2015 05:18 PM, Russell King - ARM Linux wrote:
> On Wed, Oct 07, 2015 at 11:50:53AM +0800, Yakir Yang wrote:
>>
>> On 08/09/2015 12:04 AM, Russell King wrote:
>>> On a mode set, DRM makes the following sequence of calls:
>>> * for_each_encoder
>>> *   bridge	mode_fixup
>>> *   encoder	mode_fixup
>>> * crtc		mode_fixup
>>> * for_each_encoder
>>> *   bridge	disable
>>> *   encoder	prepare
>>> *   bridge	post_disable
>>> * disable unused encoders
>>> * crtc		prepare
>>> * crtc		mode_set
>>> * for_each_encoder
>>> *   encoder	mode_set
>>> *   bridge	mode_set
>>> * crtc		commit
>>> * for_each_encoder
>>> *   bridge	pre_enable
>>> *   encoder	commit
>>> *   bridge	enable
>>>
>>> dw_hdmi enables the HDMI output in both the bridge mode_set() and also
>>> the bridge enable() step.  This is duplicated work - we can avoid the
>>> setup in mode_set() and just do it in the enable() stage.  This
>>> simplifies the code a little.
>>>
>>> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
>> I have noticed that dw_hdmi driver have poweron/poweroff when
>> driver detect HPD event in irq thread, that's also duplicated works,
>> would you like to collect that changes into this one:
>>
>> static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
>> {
>>          ......
>>
>>          if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
>>                  if (phy_int_pol & HDMI_PHY_HPD) {
>>                          dev_dbg(hdmi->dev, "EVENT=plugin\n");
>>
>>                          hdmi_modb(hdmi, 0, HDMI_PHY_HPD, HDMI_PHY_POL0);
>>
>>                          dw_hdmi_poweron(hdmi);    // no need here
>>                  } else {
>>                          dev_dbg(hdmi->dev, "EVENT=plugout\n");
>>
>>                          hdmi_modb(hdmi, HDMI_PHY_HPD, HDMI_PHY_HPD,
>>                                    HDMI_PHY_POL0);
>>
>>                          dw_hdmi_poweroff(hdmi);    // no need here
>>                  }
>>                  drm_helper_hpd_irq_event(hdmi->connector.dev);
>>          }
>>          ......
>> }
> I'm very much of the opinion of making small logical changes.  This
> patch is one small logical change to the DRM-side logic to get rid
> of the identified duplication there without touching anything else.
> If removing the above calls to dw_hdmi_poweron()/dw_hdmi_poweroff()
> were found to cause a regression, then the whole change would end
> up being reverted, which would be annoying.

Hmm... Yeah, it do make some driver logical changes, but I
thought that's good, just make a clean on HPD thread, and I
do give lots of test on chrome tree about this changes, guess
a separate patch would be better.

If you don't feel good enough about this, okay, I would give
more test on that changes, and send upstream to request
comment later.

- Yakir


^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 08/12] drm: bridge/dw_hdmi: avoid enabling interface in mode_set
@ 2015-10-07  9:35         ` Yakir Yang
  0 siblings, 0 replies; 226+ messages in thread
From: Yakir Yang @ 2015-10-07  9:35 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Fabio Estevam, linux-kernel, dri-devel, linux-rockchip, Andy Yan,
	linux-arm-kernel



On 10/07/2015 05:18 PM, Russell King - ARM Linux wrote:
> On Wed, Oct 07, 2015 at 11:50:53AM +0800, Yakir Yang wrote:
>>
>> On 08/09/2015 12:04 AM, Russell King wrote:
>>> On a mode set, DRM makes the following sequence of calls:
>>> * for_each_encoder
>>> *   bridge	mode_fixup
>>> *   encoder	mode_fixup
>>> * crtc		mode_fixup
>>> * for_each_encoder
>>> *   bridge	disable
>>> *   encoder	prepare
>>> *   bridge	post_disable
>>> * disable unused encoders
>>> * crtc		prepare
>>> * crtc		mode_set
>>> * for_each_encoder
>>> *   encoder	mode_set
>>> *   bridge	mode_set
>>> * crtc		commit
>>> * for_each_encoder
>>> *   bridge	pre_enable
>>> *   encoder	commit
>>> *   bridge	enable
>>>
>>> dw_hdmi enables the HDMI output in both the bridge mode_set() and also
>>> the bridge enable() step.  This is duplicated work - we can avoid the
>>> setup in mode_set() and just do it in the enable() stage.  This
>>> simplifies the code a little.
>>>
>>> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
>> I have noticed that dw_hdmi driver have poweron/poweroff when
>> driver detect HPD event in irq thread, that's also duplicated works,
>> would you like to collect that changes into this one:
>>
>> static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
>> {
>>          ......
>>
>>          if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
>>                  if (phy_int_pol & HDMI_PHY_HPD) {
>>                          dev_dbg(hdmi->dev, "EVENT=plugin\n");
>>
>>                          hdmi_modb(hdmi, 0, HDMI_PHY_HPD, HDMI_PHY_POL0);
>>
>>                          dw_hdmi_poweron(hdmi);    // no need here
>>                  } else {
>>                          dev_dbg(hdmi->dev, "EVENT=plugout\n");
>>
>>                          hdmi_modb(hdmi, HDMI_PHY_HPD, HDMI_PHY_HPD,
>>                                    HDMI_PHY_POL0);
>>
>>                          dw_hdmi_poweroff(hdmi);    // no need here
>>                  }
>>                  drm_helper_hpd_irq_event(hdmi->connector.dev);
>>          }
>>          ......
>> }
> I'm very much of the opinion of making small logical changes.  This
> patch is one small logical change to the DRM-side logic to get rid
> of the identified duplication there without touching anything else.
> If removing the above calls to dw_hdmi_poweron()/dw_hdmi_poweroff()
> were found to cause a regression, then the whole change would end
> up being reverted, which would be annoying.

Hmm... Yeah, it do make some driver logical changes, but I
thought that's good, just make a clean on HPD thread, and I
do give lots of test on chrome tree about this changes, guess
a separate patch would be better.

If you don't feel good enough about this, okay, I would give
more test on that changes, and send upstream to request
comment later.

- Yakir

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 08/12] drm: bridge/dw_hdmi: avoid enabling interface in mode_set
@ 2015-10-07  9:35         ` Yakir Yang
  0 siblings, 0 replies; 226+ messages in thread
From: Yakir Yang @ 2015-10-07  9:35 UTC (permalink / raw)
  To: linux-arm-kernel



On 10/07/2015 05:18 PM, Russell King - ARM Linux wrote:
> On Wed, Oct 07, 2015 at 11:50:53AM +0800, Yakir Yang wrote:
>>
>> On 08/09/2015 12:04 AM, Russell King wrote:
>>> On a mode set, DRM makes the following sequence of calls:
>>> * for_each_encoder
>>> *   bridge	mode_fixup
>>> *   encoder	mode_fixup
>>> * crtc		mode_fixup
>>> * for_each_encoder
>>> *   bridge	disable
>>> *   encoder	prepare
>>> *   bridge	post_disable
>>> * disable unused encoders
>>> * crtc		prepare
>>> * crtc		mode_set
>>> * for_each_encoder
>>> *   encoder	mode_set
>>> *   bridge	mode_set
>>> * crtc		commit
>>> * for_each_encoder
>>> *   bridge	pre_enable
>>> *   encoder	commit
>>> *   bridge	enable
>>>
>>> dw_hdmi enables the HDMI output in both the bridge mode_set() and also
>>> the bridge enable() step.  This is duplicated work - we can avoid the
>>> setup in mode_set() and just do it in the enable() stage.  This
>>> simplifies the code a little.
>>>
>>> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
>> I have noticed that dw_hdmi driver have poweron/poweroff when
>> driver detect HPD event in irq thread, that's also duplicated works,
>> would you like to collect that changes into this one:
>>
>> static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
>> {
>>          ......
>>
>>          if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
>>                  if (phy_int_pol & HDMI_PHY_HPD) {
>>                          dev_dbg(hdmi->dev, "EVENT=plugin\n");
>>
>>                          hdmi_modb(hdmi, 0, HDMI_PHY_HPD, HDMI_PHY_POL0);
>>
>>                          dw_hdmi_poweron(hdmi);    // no need here
>>                  } else {
>>                          dev_dbg(hdmi->dev, "EVENT=plugout\n");
>>
>>                          hdmi_modb(hdmi, HDMI_PHY_HPD, HDMI_PHY_HPD,
>>                                    HDMI_PHY_POL0);
>>
>>                          dw_hdmi_poweroff(hdmi);    // no need here
>>                  }
>>                  drm_helper_hpd_irq_event(hdmi->connector.dev);
>>          }
>>          ......
>> }
> I'm very much of the opinion of making small logical changes.  This
> patch is one small logical change to the DRM-side logic to get rid
> of the identified duplication there without touching anything else.
> If removing the above calls to dw_hdmi_poweron()/dw_hdmi_poweroff()
> were found to cause a regression, then the whole change would end
> up being reverted, which would be annoying.

Hmm... Yeah, it do make some driver logical changes, but I
thought that's good, just make a clean on HPD thread, and I
do give lots of test on chrome tree about this changes, guess
a separate patch would be better.

If you don't feel good enough about this, okay, I would give
more test on that changes, and send upstream to request
comment later.

- Yakir

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 10/12] drm: bridge/dw_hdmi: fix phy enable/disable handling
  2015-10-07  4:05   ` Yakir Yang
  2015-10-07  9:48       ` Russell King - ARM Linux
@ 2015-10-07  9:48       ` Russell King - ARM Linux
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-10-07  9:48 UTC (permalink / raw)
  To: Yakir Yang
  Cc: linux-rockchip, dri-devel, linux-kernel, linux-arm-kernel,
	Philipp Zabel, Andy Yan, Fabio Estevam, Sascha Hauer,
	Jon Nettleton, David Airlie

On Wed, Oct 07, 2015 at 12:05:37PM +0800, Yakir Yang wrote:
> 
> 
> On 08/09/2015 12:04 AM, Russell King wrote:
> >The dw_hdmi enable/disable handling is particularly weak in several
> >regards:
> >* The hotplug interrupt could call hdmi_poweron() or hdmi_poweroff()
> >   while DRM is setting a mode, which could race with a mode being set.
> >* Hotplug will always re-enable the phy whenever it detects an active
> >   hotplug signal, even if DRM has disabled the output.
> >
> >Resolve all of these by introducing a mutex to prevent races, and a
> >state-tracking bool so we know whether DRM wishes the output to be
> >enabled.  We choose to use our own mutex rather than ->struct_mutex
> >so that we can still process interrupts in a timely fashion.
> >
> >Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> >---
> >  drivers/gpu/drm/bridge/dw_hdmi.c | 29 ++++++++++++++++++++++-------
> >  1 file changed, 22 insertions(+), 7 deletions(-)
> >
> >diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
> >index 7b8a4e942a71..0ee188930d26 100644
> >--- a/drivers/gpu/drm/bridge/dw_hdmi.c
> >+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
> >@@ -125,6 +125,9 @@ struct dw_hdmi {
> >  	bool sink_is_hdmi;
> >  	bool sink_has_audio;
> >+	struct mutex mutex;		/* for state below and previous_mode */
> >+	bool disabled;			/* DRM has disabled our bridge */
> >+
> >  	spinlock_t audio_lock;
> >  	struct mutex audio_mutex;
> >  	unsigned int sample_rate;
> >@@ -1389,8 +1392,12 @@ static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge,
> >  {
> >  	struct dw_hdmi *hdmi = bridge->driver_private;
> >+	mutex_lock(&hdmi->mutex);
> >+
> >  	/* Store the display mode for plugin/DKMS poweron events */
> >  	memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode));
> >+
> >+	mutex_unlock(&hdmi->mutex);
> >  }
> >  static bool dw_hdmi_bridge_mode_fixup(struct drm_bridge *bridge,
> >@@ -1404,14 +1411,20 @@ static void dw_hdmi_bridge_disable(struct drm_bridge *bridge)
> >  {
> >  	struct dw_hdmi *hdmi = bridge->driver_private;
> >+	mutex_lock(&hdmi->mutex);
> >+	hdmi->disabled = true;
> >  	dw_hdmi_poweroff(hdmi);
> >+	mutex_unlock(&hdmi->mutex);
> >  }
> >  static void dw_hdmi_bridge_enable(struct drm_bridge *bridge)
> >  {
> >  	struct dw_hdmi *hdmi = bridge->driver_private;
> >+	mutex_lock(&hdmi->mutex);
> >  	dw_hdmi_poweron(hdmi);
> >+	hdmi->disabled = false;
> >+	mutex_unlock(&hdmi->mutex);
> >  }
> >  static void dw_hdmi_bridge_nop(struct drm_bridge *bridge)
> >@@ -1534,20 +1547,20 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
> >  	phy_int_pol = hdmi_readb(hdmi, HDMI_PHY_POL0);
> >  	if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
> >+		hdmi_modb(hdmi, ~phy_int_pol, HDMI_PHY_HPD, HDMI_PHY_POL0);
> >+		mutex_lock(&hdmi->mutex);
> >  		if (phy_int_pol & HDMI_PHY_HPD) {
> >  			dev_dbg(hdmi->dev, "EVENT=plugin\n");
> >-			hdmi_modb(hdmi, 0, HDMI_PHY_HPD, HDMI_PHY_POL0);
> >-
> >-			dw_hdmi_poweron(hdmi);
> >+			if (!hdmi->disabled)
> >+				dw_hdmi_poweron(hdmi);
> >  		} else {
> >  			dev_dbg(hdmi->dev, "EVENT=plugout\n");
> >-			hdmi_modb(hdmi, HDMI_PHY_HPD, HDMI_PHY_HPD,
> >-				  HDMI_PHY_POL0);
> >-
> >-			dw_hdmi_poweroff(hdmi);
> >+			if (!hdmi->disabled)
> >+				dw_hdmi_poweroff(hdmi);
> 
> Just like my reply on 08/12, I thought this could be removed, so
> poweron/poweroff would only be called with bridge->enable/
> bridge->disable, them maybe no need mutex here.

The bridge enable/disable methods do not get called on hotplug changes.

[    1.363011] dwhdmi-imx 120000.hdmi: Detected HDMI controller 0x13:0xa:0xa0:0xc1
[    1.371341] dwhdmi-imx 120000.hdmi: dw_hdmi_irq(I:RX----,HPD P:RX3210,HPD S:RX----,HPD)
[    1.381345] imx-drm display-subsystem: bound 120000.hdmi (ops dw_hdmi_imx_ops)
[    1.448691] dwhdmi-imx 120000.hdmi: dw_hdmi_bridge_disable()
[    1.450963] dwhdmi-imx 120000.hdmi: dw_hdmi_bridge_enable()

and then unplugging and re-plugging the HDMI cable:

[   68.307505] dwhdmi-imx 120000.hdmi: dw_hdmi_irq(I:RX----,HPD P:RX3210,--- S:RX----,---)
[   73.813970] dwhdmi-imx 120000.hdmi: dw_hdmi_irq(I:RX----,HPD P:RX3210,HPD S:RX----,HPD)

As you can see, during the period of disconnection for five seconds,
dw_hdmi_bridge_disable() was not called.

So, without the code above, we'd be needlessly wasting power with the
bridge enabled, trying to drive a disconnected display.

-- 
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 10/12] drm: bridge/dw_hdmi: fix phy enable/disable handling
@ 2015-10-07  9:48       ` Russell King - ARM Linux
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-10-07  9:48 UTC (permalink / raw)
  To: Yakir Yang
  Cc: Fabio Estevam, linux-kernel, dri-devel, linux-rockchip, Andy Yan,
	linux-arm-kernel

On Wed, Oct 07, 2015 at 12:05:37PM +0800, Yakir Yang wrote:
> 
> 
> On 08/09/2015 12:04 AM, Russell King wrote:
> >The dw_hdmi enable/disable handling is particularly weak in several
> >regards:
> >* The hotplug interrupt could call hdmi_poweron() or hdmi_poweroff()
> >   while DRM is setting a mode, which could race with a mode being set.
> >* Hotplug will always re-enable the phy whenever it detects an active
> >   hotplug signal, even if DRM has disabled the output.
> >
> >Resolve all of these by introducing a mutex to prevent races, and a
> >state-tracking bool so we know whether DRM wishes the output to be
> >enabled.  We choose to use our own mutex rather than ->struct_mutex
> >so that we can still process interrupts in a timely fashion.
> >
> >Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> >---
> >  drivers/gpu/drm/bridge/dw_hdmi.c | 29 ++++++++++++++++++++++-------
> >  1 file changed, 22 insertions(+), 7 deletions(-)
> >
> >diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
> >index 7b8a4e942a71..0ee188930d26 100644
> >--- a/drivers/gpu/drm/bridge/dw_hdmi.c
> >+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
> >@@ -125,6 +125,9 @@ struct dw_hdmi {
> >  	bool sink_is_hdmi;
> >  	bool sink_has_audio;
> >+	struct mutex mutex;		/* for state below and previous_mode */
> >+	bool disabled;			/* DRM has disabled our bridge */
> >+
> >  	spinlock_t audio_lock;
> >  	struct mutex audio_mutex;
> >  	unsigned int sample_rate;
> >@@ -1389,8 +1392,12 @@ static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge,
> >  {
> >  	struct dw_hdmi *hdmi = bridge->driver_private;
> >+	mutex_lock(&hdmi->mutex);
> >+
> >  	/* Store the display mode for plugin/DKMS poweron events */
> >  	memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode));
> >+
> >+	mutex_unlock(&hdmi->mutex);
> >  }
> >  static bool dw_hdmi_bridge_mode_fixup(struct drm_bridge *bridge,
> >@@ -1404,14 +1411,20 @@ static void dw_hdmi_bridge_disable(struct drm_bridge *bridge)
> >  {
> >  	struct dw_hdmi *hdmi = bridge->driver_private;
> >+	mutex_lock(&hdmi->mutex);
> >+	hdmi->disabled = true;
> >  	dw_hdmi_poweroff(hdmi);
> >+	mutex_unlock(&hdmi->mutex);
> >  }
> >  static void dw_hdmi_bridge_enable(struct drm_bridge *bridge)
> >  {
> >  	struct dw_hdmi *hdmi = bridge->driver_private;
> >+	mutex_lock(&hdmi->mutex);
> >  	dw_hdmi_poweron(hdmi);
> >+	hdmi->disabled = false;
> >+	mutex_unlock(&hdmi->mutex);
> >  }
> >  static void dw_hdmi_bridge_nop(struct drm_bridge *bridge)
> >@@ -1534,20 +1547,20 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
> >  	phy_int_pol = hdmi_readb(hdmi, HDMI_PHY_POL0);
> >  	if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
> >+		hdmi_modb(hdmi, ~phy_int_pol, HDMI_PHY_HPD, HDMI_PHY_POL0);
> >+		mutex_lock(&hdmi->mutex);
> >  		if (phy_int_pol & HDMI_PHY_HPD) {
> >  			dev_dbg(hdmi->dev, "EVENT=plugin\n");
> >-			hdmi_modb(hdmi, 0, HDMI_PHY_HPD, HDMI_PHY_POL0);
> >-
> >-			dw_hdmi_poweron(hdmi);
> >+			if (!hdmi->disabled)
> >+				dw_hdmi_poweron(hdmi);
> >  		} else {
> >  			dev_dbg(hdmi->dev, "EVENT=plugout\n");
> >-			hdmi_modb(hdmi, HDMI_PHY_HPD, HDMI_PHY_HPD,
> >-				  HDMI_PHY_POL0);
> >-
> >-			dw_hdmi_poweroff(hdmi);
> >+			if (!hdmi->disabled)
> >+				dw_hdmi_poweroff(hdmi);
> 
> Just like my reply on 08/12, I thought this could be removed, so
> poweron/poweroff would only be called with bridge->enable/
> bridge->disable, them maybe no need mutex here.

The bridge enable/disable methods do not get called on hotplug changes.

[    1.363011] dwhdmi-imx 120000.hdmi: Detected HDMI controller 0x13:0xa:0xa0:0xc1
[    1.371341] dwhdmi-imx 120000.hdmi: dw_hdmi_irq(I:RX----,HPD P:RX3210,HPD S:RX----,HPD)
[    1.381345] imx-drm display-subsystem: bound 120000.hdmi (ops dw_hdmi_imx_ops)
[    1.448691] dwhdmi-imx 120000.hdmi: dw_hdmi_bridge_disable()
[    1.450963] dwhdmi-imx 120000.hdmi: dw_hdmi_bridge_enable()

and then unplugging and re-plugging the HDMI cable:

[   68.307505] dwhdmi-imx 120000.hdmi: dw_hdmi_irq(I:RX----,HPD P:RX3210,--- S:RX----,---)
[   73.813970] dwhdmi-imx 120000.hdmi: dw_hdmi_irq(I:RX----,HPD P:RX3210,HPD S:RX----,HPD)

As you can see, during the period of disconnection for five seconds,
dw_hdmi_bridge_disable() was not called.

So, without the code above, we'd be needlessly wasting power with the
bridge enabled, trying to drive a disconnected display.

-- 
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 10/12] drm: bridge/dw_hdmi: fix phy enable/disable handling
@ 2015-10-07  9:48       ` Russell King - ARM Linux
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-10-07  9:48 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Oct 07, 2015 at 12:05:37PM +0800, Yakir Yang wrote:
> 
> 
> On 08/09/2015 12:04 AM, Russell King wrote:
> >The dw_hdmi enable/disable handling is particularly weak in several
> >regards:
> >* The hotplug interrupt could call hdmi_poweron() or hdmi_poweroff()
> >   while DRM is setting a mode, which could race with a mode being set.
> >* Hotplug will always re-enable the phy whenever it detects an active
> >   hotplug signal, even if DRM has disabled the output.
> >
> >Resolve all of these by introducing a mutex to prevent races, and a
> >state-tracking bool so we know whether DRM wishes the output to be
> >enabled.  We choose to use our own mutex rather than ->struct_mutex
> >so that we can still process interrupts in a timely fashion.
> >
> >Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> >---
> >  drivers/gpu/drm/bridge/dw_hdmi.c | 29 ++++++++++++++++++++++-------
> >  1 file changed, 22 insertions(+), 7 deletions(-)
> >
> >diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
> >index 7b8a4e942a71..0ee188930d26 100644
> >--- a/drivers/gpu/drm/bridge/dw_hdmi.c
> >+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
> >@@ -125,6 +125,9 @@ struct dw_hdmi {
> >  	bool sink_is_hdmi;
> >  	bool sink_has_audio;
> >+	struct mutex mutex;		/* for state below and previous_mode */
> >+	bool disabled;			/* DRM has disabled our bridge */
> >+
> >  	spinlock_t audio_lock;
> >  	struct mutex audio_mutex;
> >  	unsigned int sample_rate;
> >@@ -1389,8 +1392,12 @@ static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge,
> >  {
> >  	struct dw_hdmi *hdmi = bridge->driver_private;
> >+	mutex_lock(&hdmi->mutex);
> >+
> >  	/* Store the display mode for plugin/DKMS poweron events */
> >  	memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode));
> >+
> >+	mutex_unlock(&hdmi->mutex);
> >  }
> >  static bool dw_hdmi_bridge_mode_fixup(struct drm_bridge *bridge,
> >@@ -1404,14 +1411,20 @@ static void dw_hdmi_bridge_disable(struct drm_bridge *bridge)
> >  {
> >  	struct dw_hdmi *hdmi = bridge->driver_private;
> >+	mutex_lock(&hdmi->mutex);
> >+	hdmi->disabled = true;
> >  	dw_hdmi_poweroff(hdmi);
> >+	mutex_unlock(&hdmi->mutex);
> >  }
> >  static void dw_hdmi_bridge_enable(struct drm_bridge *bridge)
> >  {
> >  	struct dw_hdmi *hdmi = bridge->driver_private;
> >+	mutex_lock(&hdmi->mutex);
> >  	dw_hdmi_poweron(hdmi);
> >+	hdmi->disabled = false;
> >+	mutex_unlock(&hdmi->mutex);
> >  }
> >  static void dw_hdmi_bridge_nop(struct drm_bridge *bridge)
> >@@ -1534,20 +1547,20 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
> >  	phy_int_pol = hdmi_readb(hdmi, HDMI_PHY_POL0);
> >  	if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
> >+		hdmi_modb(hdmi, ~phy_int_pol, HDMI_PHY_HPD, HDMI_PHY_POL0);
> >+		mutex_lock(&hdmi->mutex);
> >  		if (phy_int_pol & HDMI_PHY_HPD) {
> >  			dev_dbg(hdmi->dev, "EVENT=plugin\n");
> >-			hdmi_modb(hdmi, 0, HDMI_PHY_HPD, HDMI_PHY_POL0);
> >-
> >-			dw_hdmi_poweron(hdmi);
> >+			if (!hdmi->disabled)
> >+				dw_hdmi_poweron(hdmi);
> >  		} else {
> >  			dev_dbg(hdmi->dev, "EVENT=plugout\n");
> >-			hdmi_modb(hdmi, HDMI_PHY_HPD, HDMI_PHY_HPD,
> >-				  HDMI_PHY_POL0);
> >-
> >-			dw_hdmi_poweroff(hdmi);
> >+			if (!hdmi->disabled)
> >+				dw_hdmi_poweroff(hdmi);
> 
> Just like my reply on 08/12, I thought this could be removed, so
> poweron/poweroff would only be called with bridge->enable/
> bridge->disable, them maybe no need mutex here.

The bridge enable/disable methods do not get called on hotplug changes.

[    1.363011] dwhdmi-imx 120000.hdmi: Detected HDMI controller 0x13:0xa:0xa0:0xc1
[    1.371341] dwhdmi-imx 120000.hdmi: dw_hdmi_irq(I:RX----,HPD P:RX3210,HPD S:RX----,HPD)
[    1.381345] imx-drm display-subsystem: bound 120000.hdmi (ops dw_hdmi_imx_ops)
[    1.448691] dwhdmi-imx 120000.hdmi: dw_hdmi_bridge_disable()
[    1.450963] dwhdmi-imx 120000.hdmi: dw_hdmi_bridge_enable()

and then unplugging and re-plugging the HDMI cable:

[   68.307505] dwhdmi-imx 120000.hdmi: dw_hdmi_irq(I:RX----,HPD P:RX3210,--- S:RX----,---)
[   73.813970] dwhdmi-imx 120000.hdmi: dw_hdmi_irq(I:RX----,HPD P:RX3210,HPD S:RX----,HPD)

As you can see, during the period of disconnection for five seconds,
dw_hdmi_bridge_disable() was not called.

So, without the code above, we'd be needlessly wasting power with the
bridge enabled, trying to drive a disconnected display.

-- 
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 10/12] drm: bridge/dw_hdmi: fix phy enable/disable handling
  2015-10-07  9:48       ` Russell King - ARM Linux
  (?)
  (?)
@ 2015-10-07 10:40       ` Yakir Yang
  2015-10-07 19:17           ` Russell King - ARM Linux
  -1 siblings, 1 reply; 226+ messages in thread
From: Yakir Yang @ 2015-10-07 10:40 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Fabio Estevam, linux-kernel, dri-devel, linux-rockchip, Andy Yan,
	linux-arm-kernel


[-- Attachment #1.1: Type: text/plain, Size: 9800 bytes --]



On 10/07/2015 05:48 PM, Russell King - ARM Linux wrote:
> On Wed, Oct 07, 2015 at 12:05:37PM +0800, Yakir Yang wrote:
>>
>> On 08/09/2015 12:04 AM, Russell King wrote:
>>> The dw_hdmi enable/disable handling is particularly weak in several
>>> regards:
>>> * The hotplug interrupt could call hdmi_poweron() or hdmi_poweroff()
>>>    while DRM is setting a mode, which could race with a mode being set.
>>> * Hotplug will always re-enable the phy whenever it detects an active
>>>    hotplug signal, even if DRM has disabled the output.
>>>
>>> Resolve all of these by introducing a mutex to prevent races, and a
>>> state-tracking bool so we know whether DRM wishes the output to be
>>> enabled.  We choose to use our own mutex rather than ->struct_mutex
>>> so that we can still process interrupts in a timely fashion.
>>>
>>> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
>>> ---
>>>   drivers/gpu/drm/bridge/dw_hdmi.c | 29 ++++++++++++++++++++++-------
>>>   1 file changed, 22 insertions(+), 7 deletions(-)
>>>
>>> diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
>>> index 7b8a4e942a71..0ee188930d26 100644
>>> --- a/drivers/gpu/drm/bridge/dw_hdmi.c
>>> +++ b/drivers/gpu/drm/bridge/dw_hdmi.c
>>> @@ -125,6 +125,9 @@ struct dw_hdmi {
>>>   	bool sink_is_hdmi;
>>>   	bool sink_has_audio;
>>> +	struct mutex mutex;		/* for state below and previous_mode */
>>> +	bool disabled;			/* DRM has disabled our bridge */
>>> +
>>>   	spinlock_t audio_lock;
>>>   	struct mutex audio_mutex;
>>>   	unsigned int sample_rate;
>>> @@ -1389,8 +1392,12 @@ static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge,
>>>   {
>>>   	struct dw_hdmi *hdmi = bridge->driver_private;
>>> +	mutex_lock(&hdmi->mutex);
>>> +
>>>   	/* Store the display mode for plugin/DKMS poweron events */
>>>   	memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode));
>>> +
>>> +	mutex_unlock(&hdmi->mutex);
>>>   }
>>>   static bool dw_hdmi_bridge_mode_fixup(struct drm_bridge *bridge,
>>> @@ -1404,14 +1411,20 @@ static void dw_hdmi_bridge_disable(struct drm_bridge *bridge)
>>>   {
>>>   	struct dw_hdmi *hdmi = bridge->driver_private;
>>> +	mutex_lock(&hdmi->mutex);
>>> +	hdmi->disabled = true;
>>>   	dw_hdmi_poweroff(hdmi);
>>> +	mutex_unlock(&hdmi->mutex);
>>>   }
>>>   static void dw_hdmi_bridge_enable(struct drm_bridge *bridge)
>>>   {
>>>   	struct dw_hdmi *hdmi = bridge->driver_private;
>>> +	mutex_lock(&hdmi->mutex);
>>>   	dw_hdmi_poweron(hdmi);
>>> +	hdmi->disabled = false;
>>> +	mutex_unlock(&hdmi->mutex);
>>>   }
>>>   static void dw_hdmi_bridge_nop(struct drm_bridge *bridge)
>>> @@ -1534,20 +1547,20 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
>>>   	phy_int_pol = hdmi_readb(hdmi, HDMI_PHY_POL0);
>>>   	if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
>>> +		hdmi_modb(hdmi, ~phy_int_pol, HDMI_PHY_HPD, HDMI_PHY_POL0);
>>> +		mutex_lock(&hdmi->mutex);
>>>   		if (phy_int_pol & HDMI_PHY_HPD) {
>>>   			dev_dbg(hdmi->dev, "EVENT=plugin\n");
>>> -			hdmi_modb(hdmi, 0, HDMI_PHY_HPD, HDMI_PHY_POL0);
>>> -
>>> -			dw_hdmi_poweron(hdmi);
>>> +			if (!hdmi->disabled)
>>> +				dw_hdmi_poweron(hdmi);
>>>   		} else {
>>>   			dev_dbg(hdmi->dev, "EVENT=plugout\n");
>>> -			hdmi_modb(hdmi, HDMI_PHY_HPD, HDMI_PHY_HPD,
>>> -				  HDMI_PHY_POL0);
>>> -
>>> -			dw_hdmi_poweroff(hdmi);
>>> +			if (!hdmi->disabled)
>>> +				dw_hdmi_poweroff(hdmi);
>> Just like my reply on 08/12, I thought this could be removed, so
>> poweron/poweroff would only be called with bridge->enable/
>> bridge->disable, them maybe no need mutex here.
> The bridge enable/disable methods do not get called on hotplug changes.
>
> [    1.363011] dwhdmi-imx 120000.hdmi: Detected HDMI controller 0x13:0xa:0xa0:0xc1
> [    1.371341] dwhdmi-imx 120000.hdmi: dw_hdmi_irq(I:RX----,HPD P:RX3210,HPD S:RX----,HPD)
> [    1.381345] imx-drm display-subsystem: bound 120000.hdmi (ops dw_hdmi_imx_ops)
> [    1.448691] dwhdmi-imx 120000.hdmi: dw_hdmi_bridge_disable()
> [    1.450963] dwhdmi-imx 120000.hdmi: dw_hdmi_bridge_enable()
>
> and then unplugging and re-plugging the HDMI cable:
>
> [   68.307505] dwhdmi-imx 120000.hdmi: dw_hdmi_irq(I:RX----,HPD P:RX3210,--- S:RX----,---)
> [   73.813970] dwhdmi-imx 120000.hdmi: dw_hdmi_irq(I:RX----,HPD P:RX3210,HPD S:RX----,HPD)
>
> As you can see, during the period of disconnection for five seconds,
> dw_hdmi_bridge_disable() was not called.
>
> So, without the code above, we'd be needlessly wasting power with the
> bridge enabled, trying to drive a disconnected display.

Strangely, I do see bridge enable/disable in my side, past the log and 
dump_stack bellow.

And I guess your HDMI maybe not really hot pluged, could you confirm 
that the "status"
of HDMI display card have been changed between "connected" and 
"disconnected".


Do see bridge_disable when I unpluging the HDMI cable
[   16.358717] dwhdmi-rockchip ff980000.hdmi: EVENT=plugout
[   20.613221] [<c010e030>] (unwind_backtrace) from [<c010a4e0>] 
(show_stack+0x20/0x24)
[   20.631561] [<c010a4e0>] (show_stack) from [<c0896828>] 
(dump_stack+0x70/0x8c)
[   20.649337] [<c0896828>] (dump_stack) from [<c0414038>] 
(dw_hdmi_bridge_disable+0x1c/0x88)
[   20.668178] [<c0414038>] (dw_hdmi_bridge_disable) from [<c03e3888>] 
(drm_encoder_disable+0x34/0x78)
[   20.687857] [<c03e3888>] (drm_encoder_disable) from [<c03e3b1c>] 
(__drm_helper_disable_unused_functions+0x68/0xe4)
[   20.708975] [<c03e3b1c>] (__drm_helper_disable_unused_functions) from 
[<c03e4320>] (drm_crtc_helper_set_config+0x128/0x85c)
[   20.731180] [<c03e4320>] (drm_crtc_helper_set_config) from 
[<c03f5e3c>] (drm_mode_set_config_internal+0x58/0xdc)
[   20.752507] [<c03f5e3c>] (drm_mode_set_config_internal) from 
[<c0405ed0>] (commit_crtc_state+0x124/0x1ec)
[   20.773342] [<c0405ed0>] (commit_crtc_state) from [<c04055d4>] 
(atomic_commit.isra.3+0x5c/0xc8)
[   20.793397] [<c04055d4>] (atomic_commit.isra.3) from [<c040565c>] 
(drm_atomic_commit+0x1c/0x20)
[   20.813467] [<c040565c>] (drm_atomic_commit) from [<c03fa480>] 
(drm_mode_setcrtc+0x324/0x3e4)
[   20.833379] [<c03fa480>] (drm_mode_setcrtc) from [<c03eb320>] 
(drm_ioctl+0x304/0x478)
[   20.852557] [<c03eb320>] (drm_ioctl) from [<c021f024>] 
(do_vfs_ioctl+0x494/0x5a8)
[   20.871377] [<c021f024>] (do_vfs_ioctl) from [<c021f194>] 
(SyS_ioctl+0x5c/0x84)
[   20.890038] [<c021f194>] (SyS_ioctl) from [<c010646c>] 
(__sys_trace_return+0x0/0x14)
[   16.890157] ------- YAKIR: dw_hdmi_bridge_disable:1656
[   16.897907] ------- YAKIR: dw_hdmi_poweroff:1631

Also see bridge have been disabled once before try to bridge_enable when 
I try re-plugging the HDMI cable
[   20.494292] dwhdmi-rockchip ff980000.hdmi: EVENT=plugin
[   12.599389] [<c010e030>] (unwind_backtrace) from [<c010a4e0>] 
(show_stack+0x20/0x24)
[   12.612238] [<c010a4e0>] (show_stack) from [<c0896828>] 
(dump_stack+0x70/0x8c)
[   12.624609] [<c0896828>] (dump_stack) from [<c0414038>] 
(dw_hdmi_bridge_disable+0x1c/0x88)
[   12.638055] [<c0414038>] (dw_hdmi_bridge_disable) from [<c03e3dd4>] 
(drm_crtc_helper_set_mode+0x20c/0x4e0)
[   12.652878] [<c03e3dd4>] (drm_crtc_helper_set_mode) from [<c03e47ec>] 
(drm_crtc_helper_set_config+0x5f4/0x85c)
[   12.668019] [<c03e47ec>] (drm_crtc_helper_set_config) from 
[<c03f5e3c>] (drm_mode_set_config_internal+0x58/0xdc)
[   12.683442] [<c03f5e3c>] (drm_mode_set_config_internal) from 
[<c0405ed0>] (commit_crtc_state+0x124/0x1ec)
[   12.698287] [<c0405ed0>] (commit_crtc_state) from [<c04055d4>] 
(atomic_commit.isra.3+0x5c/0xc8)
[   12.712295] [<c04055d4>] (atomic_commit.isra.3) from [<c040565c>] 
(drm_atomic_commit+0x1c/0x20)
[   12.726258] [<c040565c>] (drm_atomic_commit) from [<c03fa480>] 
(drm_mode_setcrtc+0x324/0x3e4)
[   12.740226] [<c03fa480>] (drm_mode_setcrtc) from [<c03eb320>] 
(drm_ioctl+0x304/0x478)
[   12.753651] [<c03eb320>] (drm_ioctl) from [<c021f024>] 
(do_vfs_ioctl+0x494/0x5a8)
[   12.766794] [<c021f024>] (do_vfs_ioctl) from [<c021f194>] 
(SyS_ioctl+0x5c/0x84)
[   12.779861] [<c021f194>] (SyS_ioctl) from [<c010646c>] 
(__sys_trace_return+0x0/0x14)
[   21.059320] ------- YAKIR: dw_hdmi_bridge_disable:1656
[   21.068272] ------- YAKIR: dw_hdmi_poweroff:1631
[   21.076714] rockchip-vop ff930000.vop: start latency exceeded, new 
value 9333 ns
[   21.088037] rockchip-vop ff930000.vop: Attached to iommu domain
[   12.871336] [<c010e030>] (unwind_backtrace) from [<c010a4e0>] 
(show_stack+0x20/0x24)
[   12.882067] [<c010a4e0>] (show_stack) from [<c0896828>] 
(dump_stack+0x70/0x8c)
[   12.892311] [<c0896828>] (dump_stack) from [<c0414134>] 
(dw_hdmi_bridge_enable+0x3c/0xf28)
[   12.903569] [<c0414134>] (dw_hdmi_bridge_enable) from [<c03e3ff4>] 
(drm_crtc_helper_set_mode+0x42c/0x4e0)
[   12.916226] [<c03e3ff4>] (drm_crtc_helper_set_mode) from [<c03e47ec>] 
(drm_crtc_helper_set_config+0x5f4/0x85c)
[   12.929450] [<c03e47ec>] (drm_crtc_helper_set_config) from 
[<c03f5e3c>] (drm_mode_set_config_internal+0x58/0xdc)
[   12.942883] [<c03f5e3c>] (drm_mode_set_config_internal) from 
[<c0405ed0>] (commit_crtc_state+0x124/0x1ec)
[   12.955784] [<c0405ed0>] (commit_crtc_state) from [<c04055d4>] 
(atomic_commit.isra.3+0x5c/0xc8)
[   12.967753] [<c04055d4>] (atomic_commit.isra.3) from [<c040565c>] 
(drm_atomic_commit+0x1c/0x20)
[   12.979705] [<c040565c>] (drm_atomic_commit) from [<c03fa480>] 
(drm_mode_setcrtc+0x324/0x3e4)
[   12.991568] [<c03fa480>] (drm_mode_setcrtc) from [<c03eb320>] 
(drm_ioctl+0x304/0x478)
[   13.002817] [<c03eb320>] (drm_ioctl) from [<c021f024>] 
(do_vfs_ioctl+0x494/0x5a8)
[   13.013766] [<c021f024>] (do_vfs_ioctl) from [<c021f194>] 
(SyS_ioctl+0x5c/0x84)
[   13.024655] [<c021f194>] (SyS_ioctl) from [<c010646c>] 
(__sys_trace_return+0x0/0x14)
[   21.114556] ------- YAKIR: dw_hdmi_bridge_enable:1664
[   21.122889] ------- YAKIR: dw_hdmi_poweron:1625


- Yakir


[-- Attachment #1.2: Type: text/html, Size: 12007 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 10/12] drm: bridge/dw_hdmi: fix phy enable/disable handling
  2015-10-07 10:40       ` Yakir Yang
  2015-10-07 19:17           ` Russell King - ARM Linux
@ 2015-10-07 19:17           ` Russell King - ARM Linux
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-10-07 19:17 UTC (permalink / raw)
  To: Yakir Yang
  Cc: linux-rockchip, dri-devel, linux-kernel, linux-arm-kernel,
	Philipp Zabel, Andy Yan, Fabio Estevam, Sascha Hauer,
	Jon Nettleton, David Airlie

On Wed, Oct 07, 2015 at 06:40:11PM +0800, Yakir Yang wrote:
> 
> 
> On 10/07/2015 05:48 PM, Russell King - ARM Linux wrote:
> >On Wed, Oct 07, 2015 at 12:05:37PM +0800, Yakir Yang wrote:
> >>
> >>On 08/09/2015 12:04 AM, Russell King wrote:
> >>>The dw_hdmi enable/disable handling is particularly weak in several
> >>>regards:
> >>>* The hotplug interrupt could call hdmi_poweron() or hdmi_poweroff()
> >>>   while DRM is setting a mode, which could race with a mode being set.
> >>>* Hotplug will always re-enable the phy whenever it detects an active
> >>>   hotplug signal, even if DRM has disabled the output.
> >>>
> >>>Resolve all of these by introducing a mutex to prevent races, and a
> >>>state-tracking bool so we know whether DRM wishes the output to be
> >>>enabled.  We choose to use our own mutex rather than ->struct_mutex
> >>>so that we can still process interrupts in a timely fashion.
> >>>
> >>>Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> >>>---
> >>>  drivers/gpu/drm/bridge/dw_hdmi.c | 29 ++++++++++++++++++++++-------
> >>>  1 file changed, 22 insertions(+), 7 deletions(-)
> >>>
> >>>diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
> >>>index 7b8a4e942a71..0ee188930d26 100644
> >>>--- a/drivers/gpu/drm/bridge/dw_hdmi.c
> >>>+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
> >>>@@ -125,6 +125,9 @@ struct dw_hdmi {
> >>>  	bool sink_is_hdmi;
> >>>  	bool sink_has_audio;
> >>>+	struct mutex mutex;		/* for state below and previous_mode */
> >>>+	bool disabled;			/* DRM has disabled our bridge */
> >>>+
> >>>  	spinlock_t audio_lock;
> >>>  	struct mutex audio_mutex;
> >>>  	unsigned int sample_rate;
> >>>@@ -1389,8 +1392,12 @@ static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge,
> >>>  {
> >>>  	struct dw_hdmi *hdmi = bridge->driver_private;
> >>>+	mutex_lock(&hdmi->mutex);
> >>>+
> >>>  	/* Store the display mode for plugin/DKMS poweron events */
> >>>  	memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode));
> >>>+
> >>>+	mutex_unlock(&hdmi->mutex);
> >>>  }
> >>>  static bool dw_hdmi_bridge_mode_fixup(struct drm_bridge *bridge,
> >>>@@ -1404,14 +1411,20 @@ static void dw_hdmi_bridge_disable(struct drm_bridge *bridge)
> >>>  {
> >>>  	struct dw_hdmi *hdmi = bridge->driver_private;
> >>>+	mutex_lock(&hdmi->mutex);
> >>>+	hdmi->disabled = true;
> >>>  	dw_hdmi_poweroff(hdmi);
> >>>+	mutex_unlock(&hdmi->mutex);
> >>>  }
> >>>  static void dw_hdmi_bridge_enable(struct drm_bridge *bridge)
> >>>  {
> >>>  	struct dw_hdmi *hdmi = bridge->driver_private;
> >>>+	mutex_lock(&hdmi->mutex);
> >>>  	dw_hdmi_poweron(hdmi);
> >>>+	hdmi->disabled = false;
> >>>+	mutex_unlock(&hdmi->mutex);
> >>>  }
> >>>  static void dw_hdmi_bridge_nop(struct drm_bridge *bridge)
> >>>@@ -1534,20 +1547,20 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
> >>>  	phy_int_pol = hdmi_readb(hdmi, HDMI_PHY_POL0);
> >>>  	if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
> >>>+		hdmi_modb(hdmi, ~phy_int_pol, HDMI_PHY_HPD, HDMI_PHY_POL0);
> >>>+		mutex_lock(&hdmi->mutex);
> >>>  		if (phy_int_pol & HDMI_PHY_HPD) {
> >>>  			dev_dbg(hdmi->dev, "EVENT=plugin\n");
> >>>-			hdmi_modb(hdmi, 0, HDMI_PHY_HPD, HDMI_PHY_POL0);
> >>>-
> >>>-			dw_hdmi_poweron(hdmi);
> >>>+			if (!hdmi->disabled)
> >>>+				dw_hdmi_poweron(hdmi);
> >>>  		} else {
> >>>  			dev_dbg(hdmi->dev, "EVENT=plugout\n");
> >>>-			hdmi_modb(hdmi, HDMI_PHY_HPD, HDMI_PHY_HPD,
> >>>-				  HDMI_PHY_POL0);
> >>>-
> >>>-			dw_hdmi_poweroff(hdmi);
> >>>+			if (!hdmi->disabled)
> >>>+				dw_hdmi_poweroff(hdmi);
> >>Just like my reply on 08/12, I thought this could be removed, so
> >>poweron/poweroff would only be called with bridge->enable/
> >>bridge->disable, them maybe no need mutex here.
> >The bridge enable/disable methods do not get called on hotplug changes.
> >
> >[    1.363011] dwhdmi-imx 120000.hdmi: Detected HDMI controller 0x13:0xa:0xa0:0xc1
> >[    1.371341] dwhdmi-imx 120000.hdmi: dw_hdmi_irq(I:RX----,HPD P:RX3210,HPD S:RX----,HPD)
> >[    1.381345] imx-drm display-subsystem: bound 120000.hdmi (ops dw_hdmi_imx_ops)
> >[    1.448691] dwhdmi-imx 120000.hdmi: dw_hdmi_bridge_disable()
> >[    1.450963] dwhdmi-imx 120000.hdmi: dw_hdmi_bridge_enable()
> >
> >and then unplugging and re-plugging the HDMI cable:
> >
> >[   68.307505] dwhdmi-imx 120000.hdmi: dw_hdmi_irq(I:RX----,HPD P:RX3210,--- S:RX----,---)
> >[   73.813970] dwhdmi-imx 120000.hdmi: dw_hdmi_irq(I:RX----,HPD P:RX3210,HPD S:RX----,HPD)
> >
> >As you can see, during the period of disconnection for five seconds,
> >dw_hdmi_bridge_disable() was not called.
> >
> >So, without the code above, we'd be needlessly wasting power with the
> >bridge enabled, trying to drive a disconnected display.
> 
> Strangely, I do see bridge enable/disable in my side, past the log and
> dump_stack bellow.
> 
> And I guess your HDMI maybe not really hot pluged, could you confirm that
> the "status" of HDMI display card have been changed between "connected"
> and "disconnected".

It does.

> Do see bridge_disable when I unpluging the HDMI cable
> [   16.358717] dwhdmi-rockchip ff980000.hdmi: EVENT=plugout
> [   20.613221] [<c010e030>] (unwind_backtrace) from [<c010a4e0>]
> (show_stack+0x20/0x24)
> [   20.631561] [<c010a4e0>] (show_stack) from [<c0896828>]
> (dump_stack+0x70/0x8c)
> [   20.649337] [<c0896828>] (dump_stack) from [<c0414038>]
> (dw_hdmi_bridge_disable+0x1c/0x88)
> [   20.668178] [<c0414038>] (dw_hdmi_bridge_disable) from [<c03e3888>]
> (drm_encoder_disable+0x34/0x78)
> [   20.687857] [<c03e3888>] (drm_encoder_disable) from [<c03e3b1c>]
> (__drm_helper_disable_unused_functions+0x68/0xe4)
> [   20.708975] [<c03e3b1c>] (__drm_helper_disable_unused_functions) from
> [<c03e4320>] (drm_crtc_helper_set_config+0x128/0x85c)
> [   20.731180] [<c03e4320>] (drm_crtc_helper_set_config) from [<c03f5e3c>]
> (drm_mode_set_config_internal+0x58/0xdc)
> [   20.752507] [<c03f5e3c>] (drm_mode_set_config_internal) from [<c0405ed0>]
> (commit_crtc_state+0x124/0x1ec)
> [   20.773342] [<c0405ed0>] (commit_crtc_state) from [<c04055d4>]
> (atomic_commit.isra.3+0x5c/0xc8)
> [   20.793397] [<c04055d4>] (atomic_commit.isra.3) from [<c040565c>]
> (drm_atomic_commit+0x1c/0x20)
> [   20.813467] [<c040565c>] (drm_atomic_commit) from [<c03fa480>]
> (drm_mode_setcrtc+0x324/0x3e4)
> [   20.833379] [<c03fa480>] (drm_mode_setcrtc) from [<c03eb320>]
> (drm_ioctl+0x304/0x478)
> [   20.852557] [<c03eb320>] (drm_ioctl) from [<c021f024>]
> (do_vfs_ioctl+0x494/0x5a8)
> [   20.871377] [<c021f024>] (do_vfs_ioctl) from [<c021f194>]
> (SyS_ioctl+0x5c/0x84)
> [   20.890038] [<c021f194>] (SyS_ioctl) from [<c010646c>]
> (__sys_trace_return+0x0/0x14)

Your userspace is issuing an ioctl to disable the output.  I guess you
have other active outputs besides HDMI.

-- 
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 10/12] drm: bridge/dw_hdmi: fix phy enable/disable handling
@ 2015-10-07 19:17           ` Russell King - ARM Linux
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-10-07 19:17 UTC (permalink / raw)
  To: Yakir Yang
  Cc: Fabio Estevam, linux-kernel, dri-devel, linux-rockchip, Andy Yan,
	linux-arm-kernel

On Wed, Oct 07, 2015 at 06:40:11PM +0800, Yakir Yang wrote:
> 
> 
> On 10/07/2015 05:48 PM, Russell King - ARM Linux wrote:
> >On Wed, Oct 07, 2015 at 12:05:37PM +0800, Yakir Yang wrote:
> >>
> >>On 08/09/2015 12:04 AM, Russell King wrote:
> >>>The dw_hdmi enable/disable handling is particularly weak in several
> >>>regards:
> >>>* The hotplug interrupt could call hdmi_poweron() or hdmi_poweroff()
> >>>   while DRM is setting a mode, which could race with a mode being set.
> >>>* Hotplug will always re-enable the phy whenever it detects an active
> >>>   hotplug signal, even if DRM has disabled the output.
> >>>
> >>>Resolve all of these by introducing a mutex to prevent races, and a
> >>>state-tracking bool so we know whether DRM wishes the output to be
> >>>enabled.  We choose to use our own mutex rather than ->struct_mutex
> >>>so that we can still process interrupts in a timely fashion.
> >>>
> >>>Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> >>>---
> >>>  drivers/gpu/drm/bridge/dw_hdmi.c | 29 ++++++++++++++++++++++-------
> >>>  1 file changed, 22 insertions(+), 7 deletions(-)
> >>>
> >>>diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
> >>>index 7b8a4e942a71..0ee188930d26 100644
> >>>--- a/drivers/gpu/drm/bridge/dw_hdmi.c
> >>>+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
> >>>@@ -125,6 +125,9 @@ struct dw_hdmi {
> >>>  	bool sink_is_hdmi;
> >>>  	bool sink_has_audio;
> >>>+	struct mutex mutex;		/* for state below and previous_mode */
> >>>+	bool disabled;			/* DRM has disabled our bridge */
> >>>+
> >>>  	spinlock_t audio_lock;
> >>>  	struct mutex audio_mutex;
> >>>  	unsigned int sample_rate;
> >>>@@ -1389,8 +1392,12 @@ static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge,
> >>>  {
> >>>  	struct dw_hdmi *hdmi = bridge->driver_private;
> >>>+	mutex_lock(&hdmi->mutex);
> >>>+
> >>>  	/* Store the display mode for plugin/DKMS poweron events */
> >>>  	memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode));
> >>>+
> >>>+	mutex_unlock(&hdmi->mutex);
> >>>  }
> >>>  static bool dw_hdmi_bridge_mode_fixup(struct drm_bridge *bridge,
> >>>@@ -1404,14 +1411,20 @@ static void dw_hdmi_bridge_disable(struct drm_bridge *bridge)
> >>>  {
> >>>  	struct dw_hdmi *hdmi = bridge->driver_private;
> >>>+	mutex_lock(&hdmi->mutex);
> >>>+	hdmi->disabled = true;
> >>>  	dw_hdmi_poweroff(hdmi);
> >>>+	mutex_unlock(&hdmi->mutex);
> >>>  }
> >>>  static void dw_hdmi_bridge_enable(struct drm_bridge *bridge)
> >>>  {
> >>>  	struct dw_hdmi *hdmi = bridge->driver_private;
> >>>+	mutex_lock(&hdmi->mutex);
> >>>  	dw_hdmi_poweron(hdmi);
> >>>+	hdmi->disabled = false;
> >>>+	mutex_unlock(&hdmi->mutex);
> >>>  }
> >>>  static void dw_hdmi_bridge_nop(struct drm_bridge *bridge)
> >>>@@ -1534,20 +1547,20 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
> >>>  	phy_int_pol = hdmi_readb(hdmi, HDMI_PHY_POL0);
> >>>  	if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
> >>>+		hdmi_modb(hdmi, ~phy_int_pol, HDMI_PHY_HPD, HDMI_PHY_POL0);
> >>>+		mutex_lock(&hdmi->mutex);
> >>>  		if (phy_int_pol & HDMI_PHY_HPD) {
> >>>  			dev_dbg(hdmi->dev, "EVENT=plugin\n");
> >>>-			hdmi_modb(hdmi, 0, HDMI_PHY_HPD, HDMI_PHY_POL0);
> >>>-
> >>>-			dw_hdmi_poweron(hdmi);
> >>>+			if (!hdmi->disabled)
> >>>+				dw_hdmi_poweron(hdmi);
> >>>  		} else {
> >>>  			dev_dbg(hdmi->dev, "EVENT=plugout\n");
> >>>-			hdmi_modb(hdmi, HDMI_PHY_HPD, HDMI_PHY_HPD,
> >>>-				  HDMI_PHY_POL0);
> >>>-
> >>>-			dw_hdmi_poweroff(hdmi);
> >>>+			if (!hdmi->disabled)
> >>>+				dw_hdmi_poweroff(hdmi);
> >>Just like my reply on 08/12, I thought this could be removed, so
> >>poweron/poweroff would only be called with bridge->enable/
> >>bridge->disable, them maybe no need mutex here.
> >The bridge enable/disable methods do not get called on hotplug changes.
> >
> >[    1.363011] dwhdmi-imx 120000.hdmi: Detected HDMI controller 0x13:0xa:0xa0:0xc1
> >[    1.371341] dwhdmi-imx 120000.hdmi: dw_hdmi_irq(I:RX----,HPD P:RX3210,HPD S:RX----,HPD)
> >[    1.381345] imx-drm display-subsystem: bound 120000.hdmi (ops dw_hdmi_imx_ops)
> >[    1.448691] dwhdmi-imx 120000.hdmi: dw_hdmi_bridge_disable()
> >[    1.450963] dwhdmi-imx 120000.hdmi: dw_hdmi_bridge_enable()
> >
> >and then unplugging and re-plugging the HDMI cable:
> >
> >[   68.307505] dwhdmi-imx 120000.hdmi: dw_hdmi_irq(I:RX----,HPD P:RX3210,--- S:RX----,---)
> >[   73.813970] dwhdmi-imx 120000.hdmi: dw_hdmi_irq(I:RX----,HPD P:RX3210,HPD S:RX----,HPD)
> >
> >As you can see, during the period of disconnection for five seconds,
> >dw_hdmi_bridge_disable() was not called.
> >
> >So, without the code above, we'd be needlessly wasting power with the
> >bridge enabled, trying to drive a disconnected display.
> 
> Strangely, I do see bridge enable/disable in my side, past the log and
> dump_stack bellow.
> 
> And I guess your HDMI maybe not really hot pluged, could you confirm that
> the "status" of HDMI display card have been changed between "connected"
> and "disconnected".

It does.

> Do see bridge_disable when I unpluging the HDMI cable
> [   16.358717] dwhdmi-rockchip ff980000.hdmi: EVENT=plugout
> [   20.613221] [<c010e030>] (unwind_backtrace) from [<c010a4e0>]
> (show_stack+0x20/0x24)
> [   20.631561] [<c010a4e0>] (show_stack) from [<c0896828>]
> (dump_stack+0x70/0x8c)
> [   20.649337] [<c0896828>] (dump_stack) from [<c0414038>]
> (dw_hdmi_bridge_disable+0x1c/0x88)
> [   20.668178] [<c0414038>] (dw_hdmi_bridge_disable) from [<c03e3888>]
> (drm_encoder_disable+0x34/0x78)
> [   20.687857] [<c03e3888>] (drm_encoder_disable) from [<c03e3b1c>]
> (__drm_helper_disable_unused_functions+0x68/0xe4)
> [   20.708975] [<c03e3b1c>] (__drm_helper_disable_unused_functions) from
> [<c03e4320>] (drm_crtc_helper_set_config+0x128/0x85c)
> [   20.731180] [<c03e4320>] (drm_crtc_helper_set_config) from [<c03f5e3c>]
> (drm_mode_set_config_internal+0x58/0xdc)
> [   20.752507] [<c03f5e3c>] (drm_mode_set_config_internal) from [<c0405ed0>]
> (commit_crtc_state+0x124/0x1ec)
> [   20.773342] [<c0405ed0>] (commit_crtc_state) from [<c04055d4>]
> (atomic_commit.isra.3+0x5c/0xc8)
> [   20.793397] [<c04055d4>] (atomic_commit.isra.3) from [<c040565c>]
> (drm_atomic_commit+0x1c/0x20)
> [   20.813467] [<c040565c>] (drm_atomic_commit) from [<c03fa480>]
> (drm_mode_setcrtc+0x324/0x3e4)
> [   20.833379] [<c03fa480>] (drm_mode_setcrtc) from [<c03eb320>]
> (drm_ioctl+0x304/0x478)
> [   20.852557] [<c03eb320>] (drm_ioctl) from [<c021f024>]
> (do_vfs_ioctl+0x494/0x5a8)
> [   20.871377] [<c021f024>] (do_vfs_ioctl) from [<c021f194>]
> (SyS_ioctl+0x5c/0x84)
> [   20.890038] [<c021f194>] (SyS_ioctl) from [<c010646c>]
> (__sys_trace_return+0x0/0x14)

Your userspace is issuing an ioctl to disable the output.  I guess you
have other active outputs besides HDMI.

-- 
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 10/12] drm: bridge/dw_hdmi: fix phy enable/disable handling
@ 2015-10-07 19:17           ` Russell King - ARM Linux
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-10-07 19:17 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Oct 07, 2015 at 06:40:11PM +0800, Yakir Yang wrote:
> 
> 
> On 10/07/2015 05:48 PM, Russell King - ARM Linux wrote:
> >On Wed, Oct 07, 2015 at 12:05:37PM +0800, Yakir Yang wrote:
> >>
> >>On 08/09/2015 12:04 AM, Russell King wrote:
> >>>The dw_hdmi enable/disable handling is particularly weak in several
> >>>regards:
> >>>* The hotplug interrupt could call hdmi_poweron() or hdmi_poweroff()
> >>>   while DRM is setting a mode, which could race with a mode being set.
> >>>* Hotplug will always re-enable the phy whenever it detects an active
> >>>   hotplug signal, even if DRM has disabled the output.
> >>>
> >>>Resolve all of these by introducing a mutex to prevent races, and a
> >>>state-tracking bool so we know whether DRM wishes the output to be
> >>>enabled.  We choose to use our own mutex rather than ->struct_mutex
> >>>so that we can still process interrupts in a timely fashion.
> >>>
> >>>Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> >>>---
> >>>  drivers/gpu/drm/bridge/dw_hdmi.c | 29 ++++++++++++++++++++++-------
> >>>  1 file changed, 22 insertions(+), 7 deletions(-)
> >>>
> >>>diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
> >>>index 7b8a4e942a71..0ee188930d26 100644
> >>>--- a/drivers/gpu/drm/bridge/dw_hdmi.c
> >>>+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
> >>>@@ -125,6 +125,9 @@ struct dw_hdmi {
> >>>  	bool sink_is_hdmi;
> >>>  	bool sink_has_audio;
> >>>+	struct mutex mutex;		/* for state below and previous_mode */
> >>>+	bool disabled;			/* DRM has disabled our bridge */
> >>>+
> >>>  	spinlock_t audio_lock;
> >>>  	struct mutex audio_mutex;
> >>>  	unsigned int sample_rate;
> >>>@@ -1389,8 +1392,12 @@ static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge,
> >>>  {
> >>>  	struct dw_hdmi *hdmi = bridge->driver_private;
> >>>+	mutex_lock(&hdmi->mutex);
> >>>+
> >>>  	/* Store the display mode for plugin/DKMS poweron events */
> >>>  	memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode));
> >>>+
> >>>+	mutex_unlock(&hdmi->mutex);
> >>>  }
> >>>  static bool dw_hdmi_bridge_mode_fixup(struct drm_bridge *bridge,
> >>>@@ -1404,14 +1411,20 @@ static void dw_hdmi_bridge_disable(struct drm_bridge *bridge)
> >>>  {
> >>>  	struct dw_hdmi *hdmi = bridge->driver_private;
> >>>+	mutex_lock(&hdmi->mutex);
> >>>+	hdmi->disabled = true;
> >>>  	dw_hdmi_poweroff(hdmi);
> >>>+	mutex_unlock(&hdmi->mutex);
> >>>  }
> >>>  static void dw_hdmi_bridge_enable(struct drm_bridge *bridge)
> >>>  {
> >>>  	struct dw_hdmi *hdmi = bridge->driver_private;
> >>>+	mutex_lock(&hdmi->mutex);
> >>>  	dw_hdmi_poweron(hdmi);
> >>>+	hdmi->disabled = false;
> >>>+	mutex_unlock(&hdmi->mutex);
> >>>  }
> >>>  static void dw_hdmi_bridge_nop(struct drm_bridge *bridge)
> >>>@@ -1534,20 +1547,20 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
> >>>  	phy_int_pol = hdmi_readb(hdmi, HDMI_PHY_POL0);
> >>>  	if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
> >>>+		hdmi_modb(hdmi, ~phy_int_pol, HDMI_PHY_HPD, HDMI_PHY_POL0);
> >>>+		mutex_lock(&hdmi->mutex);
> >>>  		if (phy_int_pol & HDMI_PHY_HPD) {
> >>>  			dev_dbg(hdmi->dev, "EVENT=plugin\n");
> >>>-			hdmi_modb(hdmi, 0, HDMI_PHY_HPD, HDMI_PHY_POL0);
> >>>-
> >>>-			dw_hdmi_poweron(hdmi);
> >>>+			if (!hdmi->disabled)
> >>>+				dw_hdmi_poweron(hdmi);
> >>>  		} else {
> >>>  			dev_dbg(hdmi->dev, "EVENT=plugout\n");
> >>>-			hdmi_modb(hdmi, HDMI_PHY_HPD, HDMI_PHY_HPD,
> >>>-				  HDMI_PHY_POL0);
> >>>-
> >>>-			dw_hdmi_poweroff(hdmi);
> >>>+			if (!hdmi->disabled)
> >>>+				dw_hdmi_poweroff(hdmi);
> >>Just like my reply on 08/12, I thought this could be removed, so
> >>poweron/poweroff would only be called with bridge->enable/
> >>bridge->disable, them maybe no need mutex here.
> >The bridge enable/disable methods do not get called on hotplug changes.
> >
> >[    1.363011] dwhdmi-imx 120000.hdmi: Detected HDMI controller 0x13:0xa:0xa0:0xc1
> >[    1.371341] dwhdmi-imx 120000.hdmi: dw_hdmi_irq(I:RX----,HPD P:RX3210,HPD S:RX----,HPD)
> >[    1.381345] imx-drm display-subsystem: bound 120000.hdmi (ops dw_hdmi_imx_ops)
> >[    1.448691] dwhdmi-imx 120000.hdmi: dw_hdmi_bridge_disable()
> >[    1.450963] dwhdmi-imx 120000.hdmi: dw_hdmi_bridge_enable()
> >
> >and then unplugging and re-plugging the HDMI cable:
> >
> >[   68.307505] dwhdmi-imx 120000.hdmi: dw_hdmi_irq(I:RX----,HPD P:RX3210,--- S:RX----,---)
> >[   73.813970] dwhdmi-imx 120000.hdmi: dw_hdmi_irq(I:RX----,HPD P:RX3210,HPD S:RX----,HPD)
> >
> >As you can see, during the period of disconnection for five seconds,
> >dw_hdmi_bridge_disable() was not called.
> >
> >So, without the code above, we'd be needlessly wasting power with the
> >bridge enabled, trying to drive a disconnected display.
> 
> Strangely, I do see bridge enable/disable in my side, past the log and
> dump_stack bellow.
> 
> And I guess your HDMI maybe not really hot pluged, could you confirm that
> the "status" of HDMI display card have been changed between "connected"
> and "disconnected".

It does.

> Do see bridge_disable when I unpluging the HDMI cable
> [   16.358717] dwhdmi-rockchip ff980000.hdmi: EVENT=plugout
> [   20.613221] [<c010e030>] (unwind_backtrace) from [<c010a4e0>]
> (show_stack+0x20/0x24)
> [   20.631561] [<c010a4e0>] (show_stack) from [<c0896828>]
> (dump_stack+0x70/0x8c)
> [   20.649337] [<c0896828>] (dump_stack) from [<c0414038>]
> (dw_hdmi_bridge_disable+0x1c/0x88)
> [   20.668178] [<c0414038>] (dw_hdmi_bridge_disable) from [<c03e3888>]
> (drm_encoder_disable+0x34/0x78)
> [   20.687857] [<c03e3888>] (drm_encoder_disable) from [<c03e3b1c>]
> (__drm_helper_disable_unused_functions+0x68/0xe4)
> [   20.708975] [<c03e3b1c>] (__drm_helper_disable_unused_functions) from
> [<c03e4320>] (drm_crtc_helper_set_config+0x128/0x85c)
> [   20.731180] [<c03e4320>] (drm_crtc_helper_set_config) from [<c03f5e3c>]
> (drm_mode_set_config_internal+0x58/0xdc)
> [   20.752507] [<c03f5e3c>] (drm_mode_set_config_internal) from [<c0405ed0>]
> (commit_crtc_state+0x124/0x1ec)
> [   20.773342] [<c0405ed0>] (commit_crtc_state) from [<c04055d4>]
> (atomic_commit.isra.3+0x5c/0xc8)
> [   20.793397] [<c04055d4>] (atomic_commit.isra.3) from [<c040565c>]
> (drm_atomic_commit+0x1c/0x20)
> [   20.813467] [<c040565c>] (drm_atomic_commit) from [<c03fa480>]
> (drm_mode_setcrtc+0x324/0x3e4)
> [   20.833379] [<c03fa480>] (drm_mode_setcrtc) from [<c03eb320>]
> (drm_ioctl+0x304/0x478)
> [   20.852557] [<c03eb320>] (drm_ioctl) from [<c021f024>]
> (do_vfs_ioctl+0x494/0x5a8)
> [   20.871377] [<c021f024>] (do_vfs_ioctl) from [<c021f194>]
> (SyS_ioctl+0x5c/0x84)
> [   20.890038] [<c021f194>] (SyS_ioctl) from [<c010646c>]
> (__sys_trace_return+0x0/0x14)

Your userspace is issuing an ioctl to disable the output.  I guess you
have other active outputs besides HDMI.

-- 
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 10/12] drm: bridge/dw_hdmi: fix phy enable/disable handling
  2015-10-07 19:17           ` Russell King - ARM Linux
  (?)
@ 2015-10-08  8:35             ` Yakir Yang
  -1 siblings, 0 replies; 226+ messages in thread
From: Yakir Yang @ 2015-10-08  8:35 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: linux-rockchip, dri-devel, linux-kernel, linux-arm-kernel,
	Philipp Zabel, Andy Yan, Fabio Estevam, Sascha Hauer,
	Jon Nettleton, David Airlie


Oh, I haven't noticed that those patches already have been
merged into linux-next  :-)


On 10/08/2015 03:17 AM, Russell King - ARM Linux wrote:
> On Wed, Oct 07, 2015 at 06:40:11PM +0800, Yakir Yang wrote:
>>
>> On 10/07/2015 05:48 PM, Russell King - ARM Linux wrote:
>>> On Wed, Oct 07, 2015 at 12:05:37PM +0800, Yakir Yang wrote:
>>>> On 08/09/2015 12:04 AM, Russell King wrote:
>>>>> The dw_hdmi enable/disable handling is particularly weak in several
>>>>> regards:
>>>>> * The hotplug interrupt could call hdmi_poweron() or hdmi_poweroff()
>>>>>    while DRM is setting a mode, which could race with a mode being set.
>>>>> * Hotplug will always re-enable the phy whenever it detects an active
>>>>>    hotplug signal, even if DRM has disabled the output.
>>>>>
>>>>> Resolve all of these by introducing a mutex to prevent races, and a
>>>>> state-tracking bool so we know whether DRM wishes the output to be
>>>>> enabled.  We choose to use our own mutex rather than ->struct_mutex
>>>>> so that we can still process interrupts in a timely fashion.
>>>>>
>>>>> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
>>>>> ---
>>>>>   drivers/gpu/drm/bridge/dw_hdmi.c | 29 ++++++++++++++++++++++-------
>>>>>   1 file changed, 22 insertions(+), 7 deletions(-)
>>>>>
>>>>> diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
>>>>> index 7b8a4e942a71..0ee188930d26 100644
>>>>> --- a/drivers/gpu/drm/bridge/dw_hdmi.c
>>>>> +++ b/drivers/gpu/drm/bridge/dw_hdmi.c
>>>>> @@ -125,6 +125,9 @@ struct dw_hdmi {
>>>>>   	bool sink_is_hdmi;
>>>>>   	bool sink_has_audio;
>>>>> +	struct mutex mutex;		/* for state below and previous_mode */
>>>>> +	bool disabled;			/* DRM has disabled our bridge */
>>>>> +
>>>>>   	spinlock_t audio_lock;
>>>>>   	struct mutex audio_mutex;
>>>>>   	unsigned int sample_rate;
>>>>> @@ -1389,8 +1392,12 @@ static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge,
>>>>>   {
>>>>>   	struct dw_hdmi *hdmi = bridge->driver_private;
>>>>> +	mutex_lock(&hdmi->mutex);
>>>>> +
>>>>>   	/* Store the display mode for plugin/DKMS poweron events */
>>>>>   	memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode));
>>>>> +
>>>>> +	mutex_unlock(&hdmi->mutex);
>>>>>   }
>>>>>   static bool dw_hdmi_bridge_mode_fixup(struct drm_bridge *bridge,
>>>>> @@ -1404,14 +1411,20 @@ static void dw_hdmi_bridge_disable(struct drm_bridge *bridge)
>>>>>   {
>>>>>   	struct dw_hdmi *hdmi = bridge->driver_private;
>>>>> +	mutex_lock(&hdmi->mutex);
>>>>> +	hdmi->disabled = true;
>>>>>   	dw_hdmi_poweroff(hdmi);
>>>>> +	mutex_unlock(&hdmi->mutex);
>>>>>   }
>>>>>   static void dw_hdmi_bridge_enable(struct drm_bridge *bridge)
>>>>>   {
>>>>>   	struct dw_hdmi *hdmi = bridge->driver_private;
>>>>> +	mutex_lock(&hdmi->mutex);
>>>>>   	dw_hdmi_poweron(hdmi);
>>>>> +	hdmi->disabled = false;
>>>>> +	mutex_unlock(&hdmi->mutex);
>>>>>   }
>>>>>   static void dw_hdmi_bridge_nop(struct drm_bridge *bridge)
>>>>> @@ -1534,20 +1547,20 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
>>>>>   	phy_int_pol = hdmi_readb(hdmi, HDMI_PHY_POL0);
>>>>>   	if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
>>>>> +		hdmi_modb(hdmi, ~phy_int_pol, HDMI_PHY_HPD, HDMI_PHY_POL0);
>>>>> +		mutex_lock(&hdmi->mutex);
>>>>>   		if (phy_int_pol & HDMI_PHY_HPD) {
>>>>>   			dev_dbg(hdmi->dev, "EVENT=plugin\n");
>>>>> -			hdmi_modb(hdmi, 0, HDMI_PHY_HPD, HDMI_PHY_POL0);
>>>>> -
>>>>> -			dw_hdmi_poweron(hdmi);
>>>>> +			if (!hdmi->disabled)
>>>>> +				dw_hdmi_poweron(hdmi);
>>>>>   		} else {
>>>>>   			dev_dbg(hdmi->dev, "EVENT=plugout\n");
>>>>> -			hdmi_modb(hdmi, HDMI_PHY_HPD, HDMI_PHY_HPD,
>>>>> -				  HDMI_PHY_POL0);
>>>>> -
>>>>> -			dw_hdmi_poweroff(hdmi);
>>>>> +			if (!hdmi->disabled)
>>>>> +				dw_hdmi_poweroff(hdmi);
>>>> Just like my reply on 08/12, I thought this could be removed, so
>>>> poweron/poweroff would only be called with bridge->enable/
>>>> bridge->disable, them maybe no need mutex here.
>>> The bridge enable/disable methods do not get called on hotplug changes.
>>>
>>> [    1.363011] dwhdmi-imx 120000.hdmi: Detected HDMI controller 0x13:0xa:0xa0:0xc1
>>> [    1.371341] dwhdmi-imx 120000.hdmi: dw_hdmi_irq(I:RX----,HPD P:RX3210,HPD S:RX----,HPD)
>>> [    1.381345] imx-drm display-subsystem: bound 120000.hdmi (ops dw_hdmi_imx_ops)
>>> [    1.448691] dwhdmi-imx 120000.hdmi: dw_hdmi_bridge_disable()
>>> [    1.450963] dwhdmi-imx 120000.hdmi: dw_hdmi_bridge_enable()
>>>
>>> and then unplugging and re-plugging the HDMI cable:
>>>
>>> [   68.307505] dwhdmi-imx 120000.hdmi: dw_hdmi_irq(I:RX----,HPD P:RX3210,--- S:RX----,---)
>>> [   73.813970] dwhdmi-imx 120000.hdmi: dw_hdmi_irq(I:RX----,HPD P:RX3210,HPD S:RX----,HPD)
>>>
>>> As you can see, during the period of disconnection for five seconds,
>>> dw_hdmi_bridge_disable() was not called.
>>>
>>> So, without the code above, we'd be needlessly wasting power with the
>>> bridge enabled, trying to drive a disconnected display.
>> Strangely, I do see bridge enable/disable in my side, past the log and
>> dump_stack bellow.
>>
>> And I guess your HDMI maybe not really hot pluged, could you confirm that
>> the "status" of HDMI display card have been changed between "connected"
>> and "disconnected".
> It does.
>
>> Do see bridge_disable when I unpluging the HDMI cable
>> [   16.358717] dwhdmi-rockchip ff980000.hdmi: EVENT=plugout
>> [   20.613221] [<c010e030>] (unwind_backtrace) from [<c010a4e0>]
>> (show_stack+0x20/0x24)
>> [   20.631561] [<c010a4e0>] (show_stack) from [<c0896828>]
>> (dump_stack+0x70/0x8c)
>> [   20.649337] [<c0896828>] (dump_stack) from [<c0414038>]
>> (dw_hdmi_bridge_disable+0x1c/0x88)
>> [   20.668178] [<c0414038>] (dw_hdmi_bridge_disable) from [<c03e3888>]
>> (drm_encoder_disable+0x34/0x78)
>> [   20.687857] [<c03e3888>] (drm_encoder_disable) from [<c03e3b1c>]
>> (__drm_helper_disable_unused_functions+0x68/0xe4)
>> [   20.708975] [<c03e3b1c>] (__drm_helper_disable_unused_functions) from
>> [<c03e4320>] (drm_crtc_helper_set_config+0x128/0x85c)
>> [   20.731180] [<c03e4320>] (drm_crtc_helper_set_config) from [<c03f5e3c>]
>> (drm_mode_set_config_internal+0x58/0xdc)
>> [   20.752507] [<c03f5e3c>] (drm_mode_set_config_internal) from [<c0405ed0>]
>> (commit_crtc_state+0x124/0x1ec)
>> [   20.773342] [<c0405ed0>] (commit_crtc_state) from [<c04055d4>]
>> (atomic_commit.isra.3+0x5c/0xc8)
>> [   20.793397] [<c04055d4>] (atomic_commit.isra.3) from [<c040565c>]
>> (drm_atomic_commit+0x1c/0x20)
>> [   20.813467] [<c040565c>] (drm_atomic_commit) from [<c03fa480>]
>> (drm_mode_setcrtc+0x324/0x3e4)
>> [   20.833379] [<c03fa480>] (drm_mode_setcrtc) from [<c03eb320>]
>> (drm_ioctl+0x304/0x478)
>> [   20.852557] [<c03eb320>] (drm_ioctl) from [<c021f024>]
>> (do_vfs_ioctl+0x494/0x5a8)
>> [   20.871377] [<c021f024>] (do_vfs_ioctl) from [<c021f194>]
>> (SyS_ioctl+0x5c/0x84)
>> [   20.890038] [<c021f194>] (SyS_ioctl) from [<c010646c>]
>> (__sys_trace_return+0x0/0x14)
> Your userspace is issuing an ioctl to disable the output.  I guess you
> have other active outputs besides HDMI.
>
>

Yeah, I do have another active eDP screen, but after removed the eDP
display card, I still see the bridge enable/disabled have been called.

I try to track the some userspace code, but due to little knowledge about
ChomeOS code, still can't found something directly. As drm framework
won't make bridge disabled when connector plug out, so feel free to agree
this isn't duplicate work.

Thanks,
- Yakir


^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 10/12] drm: bridge/dw_hdmi: fix phy enable/disable handling
@ 2015-10-08  8:35             ` Yakir Yang
  0 siblings, 0 replies; 226+ messages in thread
From: Yakir Yang @ 2015-10-08  8:35 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Fabio Estevam, linux-kernel, dri-devel, linux-rockchip, Andy Yan,
	linux-arm-kernel


Oh, I haven't noticed that those patches already have been
merged into linux-next  :-)


On 10/08/2015 03:17 AM, Russell King - ARM Linux wrote:
> On Wed, Oct 07, 2015 at 06:40:11PM +0800, Yakir Yang wrote:
>>
>> On 10/07/2015 05:48 PM, Russell King - ARM Linux wrote:
>>> On Wed, Oct 07, 2015 at 12:05:37PM +0800, Yakir Yang wrote:
>>>> On 08/09/2015 12:04 AM, Russell King wrote:
>>>>> The dw_hdmi enable/disable handling is particularly weak in several
>>>>> regards:
>>>>> * The hotplug interrupt could call hdmi_poweron() or hdmi_poweroff()
>>>>>    while DRM is setting a mode, which could race with a mode being set.
>>>>> * Hotplug will always re-enable the phy whenever it detects an active
>>>>>    hotplug signal, even if DRM has disabled the output.
>>>>>
>>>>> Resolve all of these by introducing a mutex to prevent races, and a
>>>>> state-tracking bool so we know whether DRM wishes the output to be
>>>>> enabled.  We choose to use our own mutex rather than ->struct_mutex
>>>>> so that we can still process interrupts in a timely fashion.
>>>>>
>>>>> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
>>>>> ---
>>>>>   drivers/gpu/drm/bridge/dw_hdmi.c | 29 ++++++++++++++++++++++-------
>>>>>   1 file changed, 22 insertions(+), 7 deletions(-)
>>>>>
>>>>> diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
>>>>> index 7b8a4e942a71..0ee188930d26 100644
>>>>> --- a/drivers/gpu/drm/bridge/dw_hdmi.c
>>>>> +++ b/drivers/gpu/drm/bridge/dw_hdmi.c
>>>>> @@ -125,6 +125,9 @@ struct dw_hdmi {
>>>>>   	bool sink_is_hdmi;
>>>>>   	bool sink_has_audio;
>>>>> +	struct mutex mutex;		/* for state below and previous_mode */
>>>>> +	bool disabled;			/* DRM has disabled our bridge */
>>>>> +
>>>>>   	spinlock_t audio_lock;
>>>>>   	struct mutex audio_mutex;
>>>>>   	unsigned int sample_rate;
>>>>> @@ -1389,8 +1392,12 @@ static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge,
>>>>>   {
>>>>>   	struct dw_hdmi *hdmi = bridge->driver_private;
>>>>> +	mutex_lock(&hdmi->mutex);
>>>>> +
>>>>>   	/* Store the display mode for plugin/DKMS poweron events */
>>>>>   	memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode));
>>>>> +
>>>>> +	mutex_unlock(&hdmi->mutex);
>>>>>   }
>>>>>   static bool dw_hdmi_bridge_mode_fixup(struct drm_bridge *bridge,
>>>>> @@ -1404,14 +1411,20 @@ static void dw_hdmi_bridge_disable(struct drm_bridge *bridge)
>>>>>   {
>>>>>   	struct dw_hdmi *hdmi = bridge->driver_private;
>>>>> +	mutex_lock(&hdmi->mutex);
>>>>> +	hdmi->disabled = true;
>>>>>   	dw_hdmi_poweroff(hdmi);
>>>>> +	mutex_unlock(&hdmi->mutex);
>>>>>   }
>>>>>   static void dw_hdmi_bridge_enable(struct drm_bridge *bridge)
>>>>>   {
>>>>>   	struct dw_hdmi *hdmi = bridge->driver_private;
>>>>> +	mutex_lock(&hdmi->mutex);
>>>>>   	dw_hdmi_poweron(hdmi);
>>>>> +	hdmi->disabled = false;
>>>>> +	mutex_unlock(&hdmi->mutex);
>>>>>   }
>>>>>   static void dw_hdmi_bridge_nop(struct drm_bridge *bridge)
>>>>> @@ -1534,20 +1547,20 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
>>>>>   	phy_int_pol = hdmi_readb(hdmi, HDMI_PHY_POL0);
>>>>>   	if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
>>>>> +		hdmi_modb(hdmi, ~phy_int_pol, HDMI_PHY_HPD, HDMI_PHY_POL0);
>>>>> +		mutex_lock(&hdmi->mutex);
>>>>>   		if (phy_int_pol & HDMI_PHY_HPD) {
>>>>>   			dev_dbg(hdmi->dev, "EVENT=plugin\n");
>>>>> -			hdmi_modb(hdmi, 0, HDMI_PHY_HPD, HDMI_PHY_POL0);
>>>>> -
>>>>> -			dw_hdmi_poweron(hdmi);
>>>>> +			if (!hdmi->disabled)
>>>>> +				dw_hdmi_poweron(hdmi);
>>>>>   		} else {
>>>>>   			dev_dbg(hdmi->dev, "EVENT=plugout\n");
>>>>> -			hdmi_modb(hdmi, HDMI_PHY_HPD, HDMI_PHY_HPD,
>>>>> -				  HDMI_PHY_POL0);
>>>>> -
>>>>> -			dw_hdmi_poweroff(hdmi);
>>>>> +			if (!hdmi->disabled)
>>>>> +				dw_hdmi_poweroff(hdmi);
>>>> Just like my reply on 08/12, I thought this could be removed, so
>>>> poweron/poweroff would only be called with bridge->enable/
>>>> bridge->disable, them maybe no need mutex here.
>>> The bridge enable/disable methods do not get called on hotplug changes.
>>>
>>> [    1.363011] dwhdmi-imx 120000.hdmi: Detected HDMI controller 0x13:0xa:0xa0:0xc1
>>> [    1.371341] dwhdmi-imx 120000.hdmi: dw_hdmi_irq(I:RX----,HPD P:RX3210,HPD S:RX----,HPD)
>>> [    1.381345] imx-drm display-subsystem: bound 120000.hdmi (ops dw_hdmi_imx_ops)
>>> [    1.448691] dwhdmi-imx 120000.hdmi: dw_hdmi_bridge_disable()
>>> [    1.450963] dwhdmi-imx 120000.hdmi: dw_hdmi_bridge_enable()
>>>
>>> and then unplugging and re-plugging the HDMI cable:
>>>
>>> [   68.307505] dwhdmi-imx 120000.hdmi: dw_hdmi_irq(I:RX----,HPD P:RX3210,--- S:RX----,---)
>>> [   73.813970] dwhdmi-imx 120000.hdmi: dw_hdmi_irq(I:RX----,HPD P:RX3210,HPD S:RX----,HPD)
>>>
>>> As you can see, during the period of disconnection for five seconds,
>>> dw_hdmi_bridge_disable() was not called.
>>>
>>> So, without the code above, we'd be needlessly wasting power with the
>>> bridge enabled, trying to drive a disconnected display.
>> Strangely, I do see bridge enable/disable in my side, past the log and
>> dump_stack bellow.
>>
>> And I guess your HDMI maybe not really hot pluged, could you confirm that
>> the "status" of HDMI display card have been changed between "connected"
>> and "disconnected".
> It does.
>
>> Do see bridge_disable when I unpluging the HDMI cable
>> [   16.358717] dwhdmi-rockchip ff980000.hdmi: EVENT=plugout
>> [   20.613221] [<c010e030>] (unwind_backtrace) from [<c010a4e0>]
>> (show_stack+0x20/0x24)
>> [   20.631561] [<c010a4e0>] (show_stack) from [<c0896828>]
>> (dump_stack+0x70/0x8c)
>> [   20.649337] [<c0896828>] (dump_stack) from [<c0414038>]
>> (dw_hdmi_bridge_disable+0x1c/0x88)
>> [   20.668178] [<c0414038>] (dw_hdmi_bridge_disable) from [<c03e3888>]
>> (drm_encoder_disable+0x34/0x78)
>> [   20.687857] [<c03e3888>] (drm_encoder_disable) from [<c03e3b1c>]
>> (__drm_helper_disable_unused_functions+0x68/0xe4)
>> [   20.708975] [<c03e3b1c>] (__drm_helper_disable_unused_functions) from
>> [<c03e4320>] (drm_crtc_helper_set_config+0x128/0x85c)
>> [   20.731180] [<c03e4320>] (drm_crtc_helper_set_config) from [<c03f5e3c>]
>> (drm_mode_set_config_internal+0x58/0xdc)
>> [   20.752507] [<c03f5e3c>] (drm_mode_set_config_internal) from [<c0405ed0>]
>> (commit_crtc_state+0x124/0x1ec)
>> [   20.773342] [<c0405ed0>] (commit_crtc_state) from [<c04055d4>]
>> (atomic_commit.isra.3+0x5c/0xc8)
>> [   20.793397] [<c04055d4>] (atomic_commit.isra.3) from [<c040565c>]
>> (drm_atomic_commit+0x1c/0x20)
>> [   20.813467] [<c040565c>] (drm_atomic_commit) from [<c03fa480>]
>> (drm_mode_setcrtc+0x324/0x3e4)
>> [   20.833379] [<c03fa480>] (drm_mode_setcrtc) from [<c03eb320>]
>> (drm_ioctl+0x304/0x478)
>> [   20.852557] [<c03eb320>] (drm_ioctl) from [<c021f024>]
>> (do_vfs_ioctl+0x494/0x5a8)
>> [   20.871377] [<c021f024>] (do_vfs_ioctl) from [<c021f194>]
>> (SyS_ioctl+0x5c/0x84)
>> [   20.890038] [<c021f194>] (SyS_ioctl) from [<c010646c>]
>> (__sys_trace_return+0x0/0x14)
> Your userspace is issuing an ioctl to disable the output.  I guess you
> have other active outputs besides HDMI.
>
>

Yeah, I do have another active eDP screen, but after removed the eDP
display card, I still see the bridge enable/disabled have been called.

I try to track the some userspace code, but due to little knowledge about
ChomeOS code, still can't found something directly. As drm framework
won't make bridge disabled when connector plug out, so feel free to agree
this isn't duplicate work.

Thanks,
- Yakir

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 10/12] drm: bridge/dw_hdmi: fix phy enable/disable handling
@ 2015-10-08  8:35             ` Yakir Yang
  0 siblings, 0 replies; 226+ messages in thread
From: Yakir Yang @ 2015-10-08  8:35 UTC (permalink / raw)
  To: linux-arm-kernel


Oh, I haven't noticed that those patches already have been
merged into linux-next  :-)


On 10/08/2015 03:17 AM, Russell King - ARM Linux wrote:
> On Wed, Oct 07, 2015 at 06:40:11PM +0800, Yakir Yang wrote:
>>
>> On 10/07/2015 05:48 PM, Russell King - ARM Linux wrote:
>>> On Wed, Oct 07, 2015 at 12:05:37PM +0800, Yakir Yang wrote:
>>>> On 08/09/2015 12:04 AM, Russell King wrote:
>>>>> The dw_hdmi enable/disable handling is particularly weak in several
>>>>> regards:
>>>>> * The hotplug interrupt could call hdmi_poweron() or hdmi_poweroff()
>>>>>    while DRM is setting a mode, which could race with a mode being set.
>>>>> * Hotplug will always re-enable the phy whenever it detects an active
>>>>>    hotplug signal, even if DRM has disabled the output.
>>>>>
>>>>> Resolve all of these by introducing a mutex to prevent races, and a
>>>>> state-tracking bool so we know whether DRM wishes the output to be
>>>>> enabled.  We choose to use our own mutex rather than ->struct_mutex
>>>>> so that we can still process interrupts in a timely fashion.
>>>>>
>>>>> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
>>>>> ---
>>>>>   drivers/gpu/drm/bridge/dw_hdmi.c | 29 ++++++++++++++++++++++-------
>>>>>   1 file changed, 22 insertions(+), 7 deletions(-)
>>>>>
>>>>> diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
>>>>> index 7b8a4e942a71..0ee188930d26 100644
>>>>> --- a/drivers/gpu/drm/bridge/dw_hdmi.c
>>>>> +++ b/drivers/gpu/drm/bridge/dw_hdmi.c
>>>>> @@ -125,6 +125,9 @@ struct dw_hdmi {
>>>>>   	bool sink_is_hdmi;
>>>>>   	bool sink_has_audio;
>>>>> +	struct mutex mutex;		/* for state below and previous_mode */
>>>>> +	bool disabled;			/* DRM has disabled our bridge */
>>>>> +
>>>>>   	spinlock_t audio_lock;
>>>>>   	struct mutex audio_mutex;
>>>>>   	unsigned int sample_rate;
>>>>> @@ -1389,8 +1392,12 @@ static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge,
>>>>>   {
>>>>>   	struct dw_hdmi *hdmi = bridge->driver_private;
>>>>> +	mutex_lock(&hdmi->mutex);
>>>>> +
>>>>>   	/* Store the display mode for plugin/DKMS poweron events */
>>>>>   	memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode));
>>>>> +
>>>>> +	mutex_unlock(&hdmi->mutex);
>>>>>   }
>>>>>   static bool dw_hdmi_bridge_mode_fixup(struct drm_bridge *bridge,
>>>>> @@ -1404,14 +1411,20 @@ static void dw_hdmi_bridge_disable(struct drm_bridge *bridge)
>>>>>   {
>>>>>   	struct dw_hdmi *hdmi = bridge->driver_private;
>>>>> +	mutex_lock(&hdmi->mutex);
>>>>> +	hdmi->disabled = true;
>>>>>   	dw_hdmi_poweroff(hdmi);
>>>>> +	mutex_unlock(&hdmi->mutex);
>>>>>   }
>>>>>   static void dw_hdmi_bridge_enable(struct drm_bridge *bridge)
>>>>>   {
>>>>>   	struct dw_hdmi *hdmi = bridge->driver_private;
>>>>> +	mutex_lock(&hdmi->mutex);
>>>>>   	dw_hdmi_poweron(hdmi);
>>>>> +	hdmi->disabled = false;
>>>>> +	mutex_unlock(&hdmi->mutex);
>>>>>   }
>>>>>   static void dw_hdmi_bridge_nop(struct drm_bridge *bridge)
>>>>> @@ -1534,20 +1547,20 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
>>>>>   	phy_int_pol = hdmi_readb(hdmi, HDMI_PHY_POL0);
>>>>>   	if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
>>>>> +		hdmi_modb(hdmi, ~phy_int_pol, HDMI_PHY_HPD, HDMI_PHY_POL0);
>>>>> +		mutex_lock(&hdmi->mutex);
>>>>>   		if (phy_int_pol & HDMI_PHY_HPD) {
>>>>>   			dev_dbg(hdmi->dev, "EVENT=plugin\n");
>>>>> -			hdmi_modb(hdmi, 0, HDMI_PHY_HPD, HDMI_PHY_POL0);
>>>>> -
>>>>> -			dw_hdmi_poweron(hdmi);
>>>>> +			if (!hdmi->disabled)
>>>>> +				dw_hdmi_poweron(hdmi);
>>>>>   		} else {
>>>>>   			dev_dbg(hdmi->dev, "EVENT=plugout\n");
>>>>> -			hdmi_modb(hdmi, HDMI_PHY_HPD, HDMI_PHY_HPD,
>>>>> -				  HDMI_PHY_POL0);
>>>>> -
>>>>> -			dw_hdmi_poweroff(hdmi);
>>>>> +			if (!hdmi->disabled)
>>>>> +				dw_hdmi_poweroff(hdmi);
>>>> Just like my reply on 08/12, I thought this could be removed, so
>>>> poweron/poweroff would only be called with bridge->enable/
>>>> bridge->disable, them maybe no need mutex here.
>>> The bridge enable/disable methods do not get called on hotplug changes.
>>>
>>> [    1.363011] dwhdmi-imx 120000.hdmi: Detected HDMI controller 0x13:0xa:0xa0:0xc1
>>> [    1.371341] dwhdmi-imx 120000.hdmi: dw_hdmi_irq(I:RX----,HPD P:RX3210,HPD S:RX----,HPD)
>>> [    1.381345] imx-drm display-subsystem: bound 120000.hdmi (ops dw_hdmi_imx_ops)
>>> [    1.448691] dwhdmi-imx 120000.hdmi: dw_hdmi_bridge_disable()
>>> [    1.450963] dwhdmi-imx 120000.hdmi: dw_hdmi_bridge_enable()
>>>
>>> and then unplugging and re-plugging the HDMI cable:
>>>
>>> [   68.307505] dwhdmi-imx 120000.hdmi: dw_hdmi_irq(I:RX----,HPD P:RX3210,--- S:RX----,---)
>>> [   73.813970] dwhdmi-imx 120000.hdmi: dw_hdmi_irq(I:RX----,HPD P:RX3210,HPD S:RX----,HPD)
>>>
>>> As you can see, during the period of disconnection for five seconds,
>>> dw_hdmi_bridge_disable() was not called.
>>>
>>> So, without the code above, we'd be needlessly wasting power with the
>>> bridge enabled, trying to drive a disconnected display.
>> Strangely, I do see bridge enable/disable in my side, past the log and
>> dump_stack bellow.
>>
>> And I guess your HDMI maybe not really hot pluged, could you confirm that
>> the "status" of HDMI display card have been changed between "connected"
>> and "disconnected".
> It does.
>
>> Do see bridge_disable when I unpluging the HDMI cable
>> [   16.358717] dwhdmi-rockchip ff980000.hdmi: EVENT=plugout
>> [   20.613221] [<c010e030>] (unwind_backtrace) from [<c010a4e0>]
>> (show_stack+0x20/0x24)
>> [   20.631561] [<c010a4e0>] (show_stack) from [<c0896828>]
>> (dump_stack+0x70/0x8c)
>> [   20.649337] [<c0896828>] (dump_stack) from [<c0414038>]
>> (dw_hdmi_bridge_disable+0x1c/0x88)
>> [   20.668178] [<c0414038>] (dw_hdmi_bridge_disable) from [<c03e3888>]
>> (drm_encoder_disable+0x34/0x78)
>> [   20.687857] [<c03e3888>] (drm_encoder_disable) from [<c03e3b1c>]
>> (__drm_helper_disable_unused_functions+0x68/0xe4)
>> [   20.708975] [<c03e3b1c>] (__drm_helper_disable_unused_functions) from
>> [<c03e4320>] (drm_crtc_helper_set_config+0x128/0x85c)
>> [   20.731180] [<c03e4320>] (drm_crtc_helper_set_config) from [<c03f5e3c>]
>> (drm_mode_set_config_internal+0x58/0xdc)
>> [   20.752507] [<c03f5e3c>] (drm_mode_set_config_internal) from [<c0405ed0>]
>> (commit_crtc_state+0x124/0x1ec)
>> [   20.773342] [<c0405ed0>] (commit_crtc_state) from [<c04055d4>]
>> (atomic_commit.isra.3+0x5c/0xc8)
>> [   20.793397] [<c04055d4>] (atomic_commit.isra.3) from [<c040565c>]
>> (drm_atomic_commit+0x1c/0x20)
>> [   20.813467] [<c040565c>] (drm_atomic_commit) from [<c03fa480>]
>> (drm_mode_setcrtc+0x324/0x3e4)
>> [   20.833379] [<c03fa480>] (drm_mode_setcrtc) from [<c03eb320>]
>> (drm_ioctl+0x304/0x478)
>> [   20.852557] [<c03eb320>] (drm_ioctl) from [<c021f024>]
>> (do_vfs_ioctl+0x494/0x5a8)
>> [   20.871377] [<c021f024>] (do_vfs_ioctl) from [<c021f194>]
>> (SyS_ioctl+0x5c/0x84)
>> [   20.890038] [<c021f194>] (SyS_ioctl) from [<c010646c>]
>> (__sys_trace_return+0x0/0x14)
> Your userspace is issuing an ioctl to disable the output.  I guess you
> have other active outputs besides HDMI.
>
>

Yeah, I do have another active eDP screen, but after removed the eDP
display card, I still see the bridge enable/disabled have been called.

I try to track the some userspace code, but due to little knowledge about
ChomeOS code, still can't found something directly. As drm framework
won't make bridge disabled when connector plug out, so feel free to agree
this isn't duplicate work.

Thanks,
- Yakir

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver
  2015-10-06 20:25               ` Fabio Estevam
  (?)
@ 2015-10-09 16:00                 ` Russell King - ARM Linux
  -1 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-10-09 16:00 UTC (permalink / raw)
  To: Fabio Estevam
  Cc: linux-rockchip, alsa-devel, DRI mailing list, linux-kernel,
	linux-arm-kernel, Fabio Estevam, Jaroslav Kysela, Mark Brown,
	Yakir Yang, Andy Yan

On Tue, Oct 06, 2015 at 05:25:16PM -0300, Fabio Estevam wrote:
> On Tue, Oct 6, 2015 at 3:54 PM, Russell King - ARM Linux
> <linux@arm.linux.org.uk> wrote:
> 
> > Make sure you have the ALSA config file, as alsalib won't get on
> > with dw-hdmi only accepting 24-bit audio without this.  A copy is
> > attached.  It also tells ALSA how to deal with multi-channel audio
> > as well.
> 
> Thanks, Russell!
> 
> Got audio to play on my HDMI TV :-)
> 
> For the entire series:
> 
> Tested-by: Fabio Estevam <fabio.estevam@freescale.com>

Just to confirm - that's for _all_ of these 8 patches, including the
changes to the ACR code in the last four patches, and you're happy that
I send all of these:

drm: bridge/dw_hdmi-ahb-audio: add audio driver
drm: bridge/dw_hdmi-ahb-audio: parse ELD from HDMI driver
drm: bridge/dw_hdmi-ahb-audio: basic support for multi-channel PCM audio
drm: bridge/dw_hdmi-ahb-audio: allow larger buffer sizes
drm: bridge/dw_hdmi: avoid being recursive in N calculation
drm: bridge/dw_hdmi: adjust pixel clock values in N calculation
drm: bridge/dw_hdmi: remove ratio support from ACR code
drm: bridge/dw_hdmi: replace CTS calculation for the ACR

-- 
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver
@ 2015-10-09 16:00                 ` Russell King - ARM Linux
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-10-09 16:00 UTC (permalink / raw)
  To: Fabio Estevam
  Cc: Fabio Estevam, alsa-devel, linux-kernel, DRI mailing list,
	Jaroslav Kysela, linux-rockchip, Mark Brown, Andy Yan,
	linux-arm-kernel

On Tue, Oct 06, 2015 at 05:25:16PM -0300, Fabio Estevam wrote:
> On Tue, Oct 6, 2015 at 3:54 PM, Russell King - ARM Linux
> <linux@arm.linux.org.uk> wrote:
> 
> > Make sure you have the ALSA config file, as alsalib won't get on
> > with dw-hdmi only accepting 24-bit audio without this.  A copy is
> > attached.  It also tells ALSA how to deal with multi-channel audio
> > as well.
> 
> Thanks, Russell!
> 
> Got audio to play on my HDMI TV :-)
> 
> For the entire series:
> 
> Tested-by: Fabio Estevam <fabio.estevam@freescale.com>

Just to confirm - that's for _all_ of these 8 patches, including the
changes to the ACR code in the last four patches, and you're happy that
I send all of these:

drm: bridge/dw_hdmi-ahb-audio: add audio driver
drm: bridge/dw_hdmi-ahb-audio: parse ELD from HDMI driver
drm: bridge/dw_hdmi-ahb-audio: basic support for multi-channel PCM audio
drm: bridge/dw_hdmi-ahb-audio: allow larger buffer sizes
drm: bridge/dw_hdmi: avoid being recursive in N calculation
drm: bridge/dw_hdmi: adjust pixel clock values in N calculation
drm: bridge/dw_hdmi: remove ratio support from ACR code
drm: bridge/dw_hdmi: replace CTS calculation for the ACR

-- 
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver
@ 2015-10-09 16:00                 ` Russell King - ARM Linux
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-10-09 16:00 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Oct 06, 2015 at 05:25:16PM -0300, Fabio Estevam wrote:
> On Tue, Oct 6, 2015 at 3:54 PM, Russell King - ARM Linux
> <linux@arm.linux.org.uk> wrote:
> 
> > Make sure you have the ALSA config file, as alsalib won't get on
> > with dw-hdmi only accepting 24-bit audio without this.  A copy is
> > attached.  It also tells ALSA how to deal with multi-channel audio
> > as well.
> 
> Thanks, Russell!
> 
> Got audio to play on my HDMI TV :-)
> 
> For the entire series:
> 
> Tested-by: Fabio Estevam <fabio.estevam@freescale.com>

Just to confirm - that's for _all_ of these 8 patches, including the
changes to the ACR code in the last four patches, and you're happy that
I send all of these:

drm: bridge/dw_hdmi-ahb-audio: add audio driver
drm: bridge/dw_hdmi-ahb-audio: parse ELD from HDMI driver
drm: bridge/dw_hdmi-ahb-audio: basic support for multi-channel PCM audio
drm: bridge/dw_hdmi-ahb-audio: allow larger buffer sizes
drm: bridge/dw_hdmi: avoid being recursive in N calculation
drm: bridge/dw_hdmi: adjust pixel clock values in N calculation
drm: bridge/dw_hdmi: remove ratio support from ACR code
drm: bridge/dw_hdmi: replace CTS calculation for the ACR

-- 
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver
  2015-10-09 16:00                 ` Russell King - ARM Linux
  (?)
@ 2015-10-09 16:02                   ` Fabio Estevam
  -1 siblings, 0 replies; 226+ messages in thread
From: Fabio Estevam @ 2015-10-09 16:02 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: linux-rockchip, alsa-devel, DRI mailing list, linux-kernel,
	linux-arm-kernel, Fabio Estevam, Jaroslav Kysela, Mark Brown,
	Yakir Yang, Andy Yan

On Fri, Oct 9, 2015 at 1:00 PM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:

>> Thanks, Russell!
>>
>> Got audio to play on my HDMI TV :-)
>>
>> For the entire series:
>>
>> Tested-by: Fabio Estevam <fabio.estevam@freescale.com>
>
> Just to confirm - that's for _all_ of these 8 patches, including the
> changes to the ACR code in the last four patches, and you're happy that
> I send all of these:
>
> drm: bridge/dw_hdmi-ahb-audio: add audio driver
> drm: bridge/dw_hdmi-ahb-audio: parse ELD from HDMI driver
> drm: bridge/dw_hdmi-ahb-audio: basic support for multi-channel PCM audio
> drm: bridge/dw_hdmi-ahb-audio: allow larger buffer sizes
> drm: bridge/dw_hdmi: avoid being recursive in N calculation
> drm: bridge/dw_hdmi: adjust pixel clock values in N calculation
> drm: bridge/dw_hdmi: remove ratio support from ACR code
> drm: bridge/dw_hdmi: replace CTS calculation for the ACR

That's correct. Thanks, Russell

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver
@ 2015-10-09 16:02                   ` Fabio Estevam
  0 siblings, 0 replies; 226+ messages in thread
From: Fabio Estevam @ 2015-10-09 16:02 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Fabio Estevam, alsa-devel, linux-kernel, DRI mailing list,
	Jaroslav Kysela, linux-rockchip, Mark Brown, Andy Yan,
	linux-arm-kernel

On Fri, Oct 9, 2015 at 1:00 PM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:

>> Thanks, Russell!
>>
>> Got audio to play on my HDMI TV :-)
>>
>> For the entire series:
>>
>> Tested-by: Fabio Estevam <fabio.estevam@freescale.com>
>
> Just to confirm - that's for _all_ of these 8 patches, including the
> changes to the ACR code in the last four patches, and you're happy that
> I send all of these:
>
> drm: bridge/dw_hdmi-ahb-audio: add audio driver
> drm: bridge/dw_hdmi-ahb-audio: parse ELD from HDMI driver
> drm: bridge/dw_hdmi-ahb-audio: basic support for multi-channel PCM audio
> drm: bridge/dw_hdmi-ahb-audio: allow larger buffer sizes
> drm: bridge/dw_hdmi: avoid being recursive in N calculation
> drm: bridge/dw_hdmi: adjust pixel clock values in N calculation
> drm: bridge/dw_hdmi: remove ratio support from ACR code
> drm: bridge/dw_hdmi: replace CTS calculation for the ACR

That's correct. Thanks, Russell
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver
@ 2015-10-09 16:02                   ` Fabio Estevam
  0 siblings, 0 replies; 226+ messages in thread
From: Fabio Estevam @ 2015-10-09 16:02 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Oct 9, 2015 at 1:00 PM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:

>> Thanks, Russell!
>>
>> Got audio to play on my HDMI TV :-)
>>
>> For the entire series:
>>
>> Tested-by: Fabio Estevam <fabio.estevam@freescale.com>
>
> Just to confirm - that's for _all_ of these 8 patches, including the
> changes to the ACR code in the last four patches, and you're happy that
> I send all of these:
>
> drm: bridge/dw_hdmi-ahb-audio: add audio driver
> drm: bridge/dw_hdmi-ahb-audio: parse ELD from HDMI driver
> drm: bridge/dw_hdmi-ahb-audio: basic support for multi-channel PCM audio
> drm: bridge/dw_hdmi-ahb-audio: allow larger buffer sizes
> drm: bridge/dw_hdmi: avoid being recursive in N calculation
> drm: bridge/dw_hdmi: adjust pixel clock values in N calculation
> drm: bridge/dw_hdmi: remove ratio support from ACR code
> drm: bridge/dw_hdmi: replace CTS calculation for the ACR

That's correct. Thanks, Russell

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver
  2015-10-09 16:02                   ` Fabio Estevam
  (?)
@ 2015-10-09 16:11                     ` Russell King - ARM Linux
  -1 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-10-09 16:11 UTC (permalink / raw)
  To: Fabio Estevam
  Cc: linux-rockchip, alsa-devel, DRI mailing list, linux-kernel,
	linux-arm-kernel, Fabio Estevam, Jaroslav Kysela, Mark Brown,
	Yakir Yang, Andy Yan

On Fri, Oct 09, 2015 at 01:02:11PM -0300, Fabio Estevam wrote:
> On Fri, Oct 9, 2015 at 1:00 PM, Russell King - ARM Linux
> <linux@arm.linux.org.uk> wrote:
> 
> >> Thanks, Russell!
> >>
> >> Got audio to play on my HDMI TV :-)
> >>
> >> For the entire series:
> >>
> >> Tested-by: Fabio Estevam <fabio.estevam@freescale.com>
> >
> > Just to confirm - that's for _all_ of these 8 patches, including the
> > changes to the ACR code in the last four patches, and you're happy that
> > I send all of these:
> >
> > drm: bridge/dw_hdmi-ahb-audio: add audio driver
> > drm: bridge/dw_hdmi-ahb-audio: parse ELD from HDMI driver
> > drm: bridge/dw_hdmi-ahb-audio: basic support for multi-channel PCM audio
> > drm: bridge/dw_hdmi-ahb-audio: allow larger buffer sizes
> > drm: bridge/dw_hdmi: avoid being recursive in N calculation
> > drm: bridge/dw_hdmi: adjust pixel clock values in N calculation
> > drm: bridge/dw_hdmi: remove ratio support from ACR code
> > drm: bridge/dw_hdmi: replace CTS calculation for the ACR
> 
> That's correct. Thanks, Russell

Thanks.  I'll drop that set into linux-next tonight, along with the TDA998x
and Armada DRM patches that haven't seen an airing there yet - before asking
David to pull them next week (the timescale has slipped...)

-- 
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [PATCH 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver
@ 2015-10-09 16:11                     ` Russell King - ARM Linux
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-10-09 16:11 UTC (permalink / raw)
  To: Fabio Estevam
  Cc: Fabio Estevam, alsa-devel, linux-kernel, DRI mailing list,
	Jaroslav Kysela, linux-rockchip, Mark Brown, Andy Yan,
	linux-arm-kernel

On Fri, Oct 09, 2015 at 01:02:11PM -0300, Fabio Estevam wrote:
> On Fri, Oct 9, 2015 at 1:00 PM, Russell King - ARM Linux
> <linux@arm.linux.org.uk> wrote:
> 
> >> Thanks, Russell!
> >>
> >> Got audio to play on my HDMI TV :-)
> >>
> >> For the entire series:
> >>
> >> Tested-by: Fabio Estevam <fabio.estevam@freescale.com>
> >
> > Just to confirm - that's for _all_ of these 8 patches, including the
> > changes to the ACR code in the last four patches, and you're happy that
> > I send all of these:
> >
> > drm: bridge/dw_hdmi-ahb-audio: add audio driver
> > drm: bridge/dw_hdmi-ahb-audio: parse ELD from HDMI driver
> > drm: bridge/dw_hdmi-ahb-audio: basic support for multi-channel PCM audio
> > drm: bridge/dw_hdmi-ahb-audio: allow larger buffer sizes
> > drm: bridge/dw_hdmi: avoid being recursive in N calculation
> > drm: bridge/dw_hdmi: adjust pixel clock values in N calculation
> > drm: bridge/dw_hdmi: remove ratio support from ACR code
> > drm: bridge/dw_hdmi: replace CTS calculation for the ACR
> 
> That's correct. Thanks, Russell

Thanks.  I'll drop that set into linux-next tonight, along with the TDA998x
and Armada DRM patches that haven't seen an airing there yet - before asking
David to pull them next week (the timescale has slipped...)

-- 
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [PATCH 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver
@ 2015-10-09 16:11                     ` Russell King - ARM Linux
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2015-10-09 16:11 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Oct 09, 2015 at 01:02:11PM -0300, Fabio Estevam wrote:
> On Fri, Oct 9, 2015 at 1:00 PM, Russell King - ARM Linux
> <linux@arm.linux.org.uk> wrote:
> 
> >> Thanks, Russell!
> >>
> >> Got audio to play on my HDMI TV :-)
> >>
> >> For the entire series:
> >>
> >> Tested-by: Fabio Estevam <fabio.estevam@freescale.com>
> >
> > Just to confirm - that's for _all_ of these 8 patches, including the
> > changes to the ACR code in the last four patches, and you're happy that
> > I send all of these:
> >
> > drm: bridge/dw_hdmi-ahb-audio: add audio driver
> > drm: bridge/dw_hdmi-ahb-audio: parse ELD from HDMI driver
> > drm: bridge/dw_hdmi-ahb-audio: basic support for multi-channel PCM audio
> > drm: bridge/dw_hdmi-ahb-audio: allow larger buffer sizes
> > drm: bridge/dw_hdmi: avoid being recursive in N calculation
> > drm: bridge/dw_hdmi: adjust pixel clock values in N calculation
> > drm: bridge/dw_hdmi: remove ratio support from ACR code
> > drm: bridge/dw_hdmi: replace CTS calculation for the ACR
> 
> That's correct. Thanks, Russell

Thanks.  I'll drop that set into linux-next tonight, along with the TDA998x
and Armada DRM patches that haven't seen an airing there yet - before asking
David to pull them next week (the timescale has slipped...)

-- 
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [alsa-devel] [PATCH 0/9] dw-hdmi audio support
  2015-08-27  8:42     ` Philipp Zabel
@ 2016-01-05 15:40       ` Jean-Michel Hautbois
  -1 siblings, 0 replies; 226+ messages in thread
From: Jean-Michel Hautbois @ 2016-01-05 15:40 UTC (permalink / raw)
  To: Philipp Zabel
  Cc: Russell King - ARM Linux, Fabio Estevam, alsa-devel,
	David Airlie, Jon Nettleton, linux-kernel, ML dri-devel,
	linux-rockchip, Yakir Yang, Andy Yan, Sascha Hauer,
	linux-arm-kernel

Hi Russell,

2015-08-27 10:42 GMT+02:00 Philipp Zabel <p.zabel@pengutronix.de>:
>
> Am Samstag, den 08.08.2015, 17:09 +0100 schrieb Russell King - ARM
> Linux:
> > Following on from the previous sub-series, this sub-series adds audio
> > support to dw-hdmi.
> >
> > The two different variants are now in this patch: AHB audio support
> > found on iMX6 platforms, and I2S support found on Rockchip patches.
> > Thanks to Yakir Yang for contributing the I2S support.
> >
> > I suspect that there is still some discussion to be had on this
> > series, though I would like to see it moving forward so that we can
> > get something merged.
>
> Tested-by: Philipp Zabel <p.zabel@pengutronix.de>
> on i.MX6 GK802 via HDMI connected to a TV (stereo only).
>
> except for the i2s patch, which is broken in this series.
>
> regards
> Philipp


What is the status of this series ?
I would like to use audio output in HDMI on my i.MX6 board, but I
don't know if you have some pending WIP on this ?

Thanks,
JM

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [alsa-devel] [PATCH 0/9] dw-hdmi audio support
@ 2016-01-05 15:40       ` Jean-Michel Hautbois
  0 siblings, 0 replies; 226+ messages in thread
From: Jean-Michel Hautbois @ 2016-01-05 15:40 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Russell,

2015-08-27 10:42 GMT+02:00 Philipp Zabel <p.zabel@pengutronix.de>:
>
> Am Samstag, den 08.08.2015, 17:09 +0100 schrieb Russell King - ARM
> Linux:
> > Following on from the previous sub-series, this sub-series adds audio
> > support to dw-hdmi.
> >
> > The two different variants are now in this patch: AHB audio support
> > found on iMX6 platforms, and I2S support found on Rockchip patches.
> > Thanks to Yakir Yang for contributing the I2S support.
> >
> > I suspect that there is still some discussion to be had on this
> > series, though I would like to see it moving forward so that we can
> > get something merged.
>
> Tested-by: Philipp Zabel <p.zabel@pengutronix.de>
> on i.MX6 GK802 via HDMI connected to a TV (stereo only).
>
> except for the i2s patch, which is broken in this series.
>
> regards
> Philipp


What is the status of this series ?
I would like to use audio output in HDMI on my i.MX6 board, but I
don't know if you have some pending WIP on this ?

Thanks,
JM

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [alsa-devel] [PATCH 0/9] dw-hdmi audio support
  2016-01-05 15:40       ` Jean-Michel Hautbois
  (?)
@ 2016-01-05 15:54         ` Fabio Estevam
  -1 siblings, 0 replies; 226+ messages in thread
From: Fabio Estevam @ 2016-01-05 15:54 UTC (permalink / raw)
  To: Jean-Michel Hautbois
  Cc: Philipp Zabel, Fabio Estevam, alsa-devel,
	Russell King - ARM Linux, linux-kernel, ML dri-devel,
	linux-rockchip, Andy Yan, linux-arm-kernel

On Tue, Jan 5, 2016 at 1:40 PM, Jean-Michel Hautbois
<jean-michel.hautbois@veo-labs.com> wrote:

> What is the status of this series ?
> I would like to use audio output in HDMI on my i.MX6 board, but I
> don't know if you have some pending WIP on this ?

This series is in mainline since 4.4-rc1.

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [alsa-devel] [PATCH 0/9] dw-hdmi audio support
@ 2016-01-05 15:54         ` Fabio Estevam
  0 siblings, 0 replies; 226+ messages in thread
From: Fabio Estevam @ 2016-01-05 15:54 UTC (permalink / raw)
  To: Jean-Michel Hautbois
  Cc: Fabio Estevam, alsa-devel, Russell King - ARM Linux,
	linux-kernel, ML dri-devel, linux-rockchip, Andy Yan,
	linux-arm-kernel

On Tue, Jan 5, 2016 at 1:40 PM, Jean-Michel Hautbois
<jean-michel.hautbois@veo-labs.com> wrote:

> What is the status of this series ?
> I would like to use audio output in HDMI on my i.MX6 board, but I
> don't know if you have some pending WIP on this ?

This series is in mainline since 4.4-rc1.
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [alsa-devel] [PATCH 0/9] dw-hdmi audio support
@ 2016-01-05 15:54         ` Fabio Estevam
  0 siblings, 0 replies; 226+ messages in thread
From: Fabio Estevam @ 2016-01-05 15:54 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Jan 5, 2016 at 1:40 PM, Jean-Michel Hautbois
<jean-michel.hautbois@veo-labs.com> wrote:

> What is the status of this series ?
> I would like to use audio output in HDMI on my i.MX6 board, but I
> don't know if you have some pending WIP on this ?

This series is in mainline since 4.4-rc1.

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [alsa-devel] [PATCH 0/9] dw-hdmi audio support
  2016-01-05 15:40       ` Jean-Michel Hautbois
  (?)
@ 2016-01-05 16:04         ` Russell King - ARM Linux
  -1 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2016-01-05 16:04 UTC (permalink / raw)
  To: Jean-Michel Hautbois
  Cc: Philipp Zabel, Fabio Estevam, alsa-devel, David Airlie,
	Jon Nettleton, linux-kernel, ML dri-devel, linux-rockchip,
	Yakir Yang, Andy Yan, Sascha Hauer, linux-arm-kernel

On Tue, Jan 05, 2016 at 04:40:54PM +0100, Jean-Michel Hautbois wrote:
> Hi Russell,
> 
> 2015-08-27 10:42 GMT+02:00 Philipp Zabel <p.zabel@pengutronix.de>:
> >
> > Am Samstag, den 08.08.2015, 17:09 +0100 schrieb Russell King - ARM
> > Linux:
> > > Following on from the previous sub-series, this sub-series adds audio
> > > support to dw-hdmi.
> > >
> > > The two different variants are now in this patch: AHB audio support
> > > found on iMX6 platforms, and I2S support found on Rockchip patches.
> > > Thanks to Yakir Yang for contributing the I2S support.
> > >
> > > I suspect that there is still some discussion to be had on this
> > > series, though I would like to see it moving forward so that we can
> > > get something merged.
> >
> > Tested-by: Philipp Zabel <p.zabel@pengutronix.de>
> > on i.MX6 GK802 via HDMI connected to a TV (stereo only).
> >
> > except for the i2s patch, which is broken in this series.
> >
> > regards
> > Philipp
> 
> 
> What is the status of this series ?
> I would like to use audio output in HDMI on my i.MX6 board, but I
> don't know if you have some pending WIP on this ?

The I2S part has been dropped.  The DesignWare HDMI block is configurable
when it is synthesized - it can contain either the AHB audio interface or
an I2S interface.  Freescale chose to synthesize it with the AHB audio
interface, and this is what my patches are geared up to provide.

On Rockchip devices, they chose to synthesize it with the I2S audio
block, and so they need a different driver for it.  Yakir Yang has been
working on that, but I've not seen anything recently.  After merging
his I2S patch, I found some problems and decided with Yakir that the
best thing to do was to drop it.

So, the result is we support HDMI audio on iMX6.

The changes were merged into mainline during the 4.4 merge window, so
Linux 4.4 will support iMX6 HDMI audio.

-- 
RMK's Patch system: http://www.arm.linux.org.uk/developer/patches/
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [alsa-devel] [PATCH 0/9] dw-hdmi audio support
@ 2016-01-05 16:04         ` Russell King - ARM Linux
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2016-01-05 16:04 UTC (permalink / raw)
  To: Jean-Michel Hautbois
  Cc: Fabio Estevam, alsa-devel, linux-kernel, ML dri-devel,
	linux-rockchip, Andy Yan, linux-arm-kernel

On Tue, Jan 05, 2016 at 04:40:54PM +0100, Jean-Michel Hautbois wrote:
> Hi Russell,
> 
> 2015-08-27 10:42 GMT+02:00 Philipp Zabel <p.zabel@pengutronix.de>:
> >
> > Am Samstag, den 08.08.2015, 17:09 +0100 schrieb Russell King - ARM
> > Linux:
> > > Following on from the previous sub-series, this sub-series adds audio
> > > support to dw-hdmi.
> > >
> > > The two different variants are now in this patch: AHB audio support
> > > found on iMX6 platforms, and I2S support found on Rockchip patches.
> > > Thanks to Yakir Yang for contributing the I2S support.
> > >
> > > I suspect that there is still some discussion to be had on this
> > > series, though I would like to see it moving forward so that we can
> > > get something merged.
> >
> > Tested-by: Philipp Zabel <p.zabel@pengutronix.de>
> > on i.MX6 GK802 via HDMI connected to a TV (stereo only).
> >
> > except for the i2s patch, which is broken in this series.
> >
> > regards
> > Philipp
> 
> 
> What is the status of this series ?
> I would like to use audio output in HDMI on my i.MX6 board, but I
> don't know if you have some pending WIP on this ?

The I2S part has been dropped.  The DesignWare HDMI block is configurable
when it is synthesized - it can contain either the AHB audio interface or
an I2S interface.  Freescale chose to synthesize it with the AHB audio
interface, and this is what my patches are geared up to provide.

On Rockchip devices, they chose to synthesize it with the I2S audio
block, and so they need a different driver for it.  Yakir Yang has been
working on that, but I've not seen anything recently.  After merging
his I2S patch, I found some problems and decided with Yakir that the
best thing to do was to drop it.

So, the result is we support HDMI audio on iMX6.

The changes were merged into mainline during the 4.4 merge window, so
Linux 4.4 will support iMX6 HDMI audio.

-- 
RMK's Patch system: http://www.arm.linux.org.uk/developer/patches/
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [alsa-devel] [PATCH 0/9] dw-hdmi audio support
@ 2016-01-05 16:04         ` Russell King - ARM Linux
  0 siblings, 0 replies; 226+ messages in thread
From: Russell King - ARM Linux @ 2016-01-05 16:04 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Jan 05, 2016 at 04:40:54PM +0100, Jean-Michel Hautbois wrote:
> Hi Russell,
> 
> 2015-08-27 10:42 GMT+02:00 Philipp Zabel <p.zabel@pengutronix.de>:
> >
> > Am Samstag, den 08.08.2015, 17:09 +0100 schrieb Russell King - ARM
> > Linux:
> > > Following on from the previous sub-series, this sub-series adds audio
> > > support to dw-hdmi.
> > >
> > > The two different variants are now in this patch: AHB audio support
> > > found on iMX6 platforms, and I2S support found on Rockchip patches.
> > > Thanks to Yakir Yang for contributing the I2S support.
> > >
> > > I suspect that there is still some discussion to be had on this
> > > series, though I would like to see it moving forward so that we can
> > > get something merged.
> >
> > Tested-by: Philipp Zabel <p.zabel@pengutronix.de>
> > on i.MX6 GK802 via HDMI connected to a TV (stereo only).
> >
> > except for the i2s patch, which is broken in this series.
> >
> > regards
> > Philipp
> 
> 
> What is the status of this series ?
> I would like to use audio output in HDMI on my i.MX6 board, but I
> don't know if you have some pending WIP on this ?

The I2S part has been dropped.  The DesignWare HDMI block is configurable
when it is synthesized - it can contain either the AHB audio interface or
an I2S interface.  Freescale chose to synthesize it with the AHB audio
interface, and this is what my patches are geared up to provide.

On Rockchip devices, they chose to synthesize it with the I2S audio
block, and so they need a different driver for it.  Yakir Yang has been
working on that, but I've not seen anything recently.  After merging
his I2S patch, I found some problems and decided with Yakir that the
best thing to do was to drop it.

So, the result is we support HDMI audio on iMX6.

The changes were merged into mainline during the 4.4 merge window, so
Linux 4.4 will support iMX6 HDMI audio.

-- 
RMK's Patch system: http://www.arm.linux.org.uk/developer/patches/
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [alsa-devel] [PATCH 0/9] dw-hdmi audio support
  2016-01-05 16:04         ` Russell King - ARM Linux
  (?)
@ 2016-01-07  8:21           ` Jean-Michel Hautbois
  -1 siblings, 0 replies; 226+ messages in thread
From: Jean-Michel Hautbois @ 2016-01-07  8:21 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Philipp Zabel, Fabio Estevam, alsa-devel, David Airlie,
	Jon Nettleton, linux-kernel, ML dri-devel, linux-rockchip,
	Yakir Yang, Andy Yan, Sascha Hauer, linux-arm-kernel

Hi Russell,

2016-01-05 17:04 GMT+01:00 Russell King - ARM Linux <linux@arm.linux.org.uk>:
> On Tue, Jan 05, 2016 at 04:40:54PM +0100, Jean-Michel Hautbois wrote:
>> Hi Russell,
>>
>> 2015-08-27 10:42 GMT+02:00 Philipp Zabel <p.zabel@pengutronix.de>:
>> >
>> > Am Samstag, den 08.08.2015, 17:09 +0100 schrieb Russell King - ARM
>> > Linux:
>> > > Following on from the previous sub-series, this sub-series adds audio
>> > > support to dw-hdmi.
>> > >
>> > > The two different variants are now in this patch: AHB audio support
>> > > found on iMX6 platforms, and I2S support found on Rockchip patches.
>> > > Thanks to Yakir Yang for contributing the I2S support.
>> > >
>> > > I suspect that there is still some discussion to be had on this
>> > > series, though I would like to see it moving forward so that we can
>> > > get something merged.
>> >
>> > Tested-by: Philipp Zabel <p.zabel@pengutronix.de>
>> > on i.MX6 GK802 via HDMI connected to a TV (stereo only).
>> >
>> > except for the i2s patch, which is broken in this series.
>> >
>> > regards
>> > Philipp
>>
>>
>> What is the status of this series ?
>> I would like to use audio output in HDMI on my i.MX6 board, but I
>> don't know if you have some pending WIP on this ?
>
> The I2S part has been dropped.  The DesignWare HDMI block is configurable
> when it is synthesized - it can contain either the AHB audio interface or
> an I2S interface.  Freescale chose to synthesize it with the AHB audio
> interface, and this is what my patches are geared up to provide.
>
> On Rockchip devices, they chose to synthesize it with the I2S audio
> block, and so they need a different driver for it.  Yakir Yang has been
> working on that, but I've not seen anything recently.  After merging
> his I2S patch, I found some problems and decided with Yakir that the
> best thing to do was to drop it.
>
> So, the result is we support HDMI audio on iMX6.
>
> The changes were merged into mainline during the 4.4 merge window, so
> Linux 4.4 will support iMX6 HDMI audio.
>

Thank you for this detailed answer. I will rebase onto 4.4 then :).

JM

^ permalink raw reply	[flat|nested] 226+ messages in thread

* Re: [alsa-devel] [PATCH 0/9] dw-hdmi audio support
@ 2016-01-07  8:21           ` Jean-Michel Hautbois
  0 siblings, 0 replies; 226+ messages in thread
From: Jean-Michel Hautbois @ 2016-01-07  8:21 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Fabio Estevam, alsa-devel, linux-kernel, ML dri-devel,
	linux-rockchip, Andy Yan, linux-arm-kernel

Hi Russell,

2016-01-05 17:04 GMT+01:00 Russell King - ARM Linux <linux@arm.linux.org.uk>:
> On Tue, Jan 05, 2016 at 04:40:54PM +0100, Jean-Michel Hautbois wrote:
>> Hi Russell,
>>
>> 2015-08-27 10:42 GMT+02:00 Philipp Zabel <p.zabel@pengutronix.de>:
>> >
>> > Am Samstag, den 08.08.2015, 17:09 +0100 schrieb Russell King - ARM
>> > Linux:
>> > > Following on from the previous sub-series, this sub-series adds audio
>> > > support to dw-hdmi.
>> > >
>> > > The two different variants are now in this patch: AHB audio support
>> > > found on iMX6 platforms, and I2S support found on Rockchip patches.
>> > > Thanks to Yakir Yang for contributing the I2S support.
>> > >
>> > > I suspect that there is still some discussion to be had on this
>> > > series, though I would like to see it moving forward so that we can
>> > > get something merged.
>> >
>> > Tested-by: Philipp Zabel <p.zabel@pengutronix.de>
>> > on i.MX6 GK802 via HDMI connected to a TV (stereo only).
>> >
>> > except for the i2s patch, which is broken in this series.
>> >
>> > regards
>> > Philipp
>>
>>
>> What is the status of this series ?
>> I would like to use audio output in HDMI on my i.MX6 board, but I
>> don't know if you have some pending WIP on this ?
>
> The I2S part has been dropped.  The DesignWare HDMI block is configurable
> when it is synthesized - it can contain either the AHB audio interface or
> an I2S interface.  Freescale chose to synthesize it with the AHB audio
> interface, and this is what my patches are geared up to provide.
>
> On Rockchip devices, they chose to synthesize it with the I2S audio
> block, and so they need a different driver for it.  Yakir Yang has been
> working on that, but I've not seen anything recently.  After merging
> his I2S patch, I found some problems and decided with Yakir that the
> best thing to do was to drop it.
>
> So, the result is we support HDMI audio on iMX6.
>
> The changes were merged into mainline during the 4.4 merge window, so
> Linux 4.4 will support iMX6 HDMI audio.
>

Thank you for this detailed answer. I will rebase onto 4.4 then :).

JM
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 226+ messages in thread

* [alsa-devel] [PATCH 0/9] dw-hdmi audio support
@ 2016-01-07  8:21           ` Jean-Michel Hautbois
  0 siblings, 0 replies; 226+ messages in thread
From: Jean-Michel Hautbois @ 2016-01-07  8:21 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Russell,

2016-01-05 17:04 GMT+01:00 Russell King - ARM Linux <linux@arm.linux.org.uk>:
> On Tue, Jan 05, 2016 at 04:40:54PM +0100, Jean-Michel Hautbois wrote:
>> Hi Russell,
>>
>> 2015-08-27 10:42 GMT+02:00 Philipp Zabel <p.zabel@pengutronix.de>:
>> >
>> > Am Samstag, den 08.08.2015, 17:09 +0100 schrieb Russell King - ARM
>> > Linux:
>> > > Following on from the previous sub-series, this sub-series adds audio
>> > > support to dw-hdmi.
>> > >
>> > > The two different variants are now in this patch: AHB audio support
>> > > found on iMX6 platforms, and I2S support found on Rockchip patches.
>> > > Thanks to Yakir Yang for contributing the I2S support.
>> > >
>> > > I suspect that there is still some discussion to be had on this
>> > > series, though I would like to see it moving forward so that we can
>> > > get something merged.
>> >
>> > Tested-by: Philipp Zabel <p.zabel@pengutronix.de>
>> > on i.MX6 GK802 via HDMI connected to a TV (stereo only).
>> >
>> > except for the i2s patch, which is broken in this series.
>> >
>> > regards
>> > Philipp
>>
>>
>> What is the status of this series ?
>> I would like to use audio output in HDMI on my i.MX6 board, but I
>> don't know if you have some pending WIP on this ?
>
> The I2S part has been dropped.  The DesignWare HDMI block is configurable
> when it is synthesized - it can contain either the AHB audio interface or
> an I2S interface.  Freescale chose to synthesize it with the AHB audio
> interface, and this is what my patches are geared up to provide.
>
> On Rockchip devices, they chose to synthesize it with the I2S audio
> block, and so they need a different driver for it.  Yakir Yang has been
> working on that, but I've not seen anything recently.  After merging
> his I2S patch, I found some problems and decided with Yakir that the
> best thing to do was to drop it.
>
> So, the result is we support HDMI audio on iMX6.
>
> The changes were merged into mainline during the 4.4 merge window, so
> Linux 4.4 will support iMX6 HDMI audio.
>

Thank you for this detailed answer. I will rebase onto 4.4 then :).

JM

^ permalink raw reply	[flat|nested] 226+ messages in thread

end of thread, other threads:[~2016-01-07  8:22 UTC | newest]

Thread overview: 226+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-08-08 16:02 [PATCH 00/12] dw-hdmi development Russell King - ARM Linux
2015-08-08 16:02 ` Russell King - ARM Linux
2015-08-08 16:02 ` Russell King - ARM Linux
2015-08-08 16:03 ` [PATCH 01/12] drm: bridge/dw_hdmi: remove pixel repetition setting for all VICs Russell King
2015-08-08 16:03   ` Russell King
2015-08-08 16:03 ` [PATCH 02/12] drm: bridge/dw_hdmi: don't support any pixel doubled modes Russell King
2015-08-08 16:03   ` Russell King
2015-08-08 16:03 ` [PATCH 03/12] gpu: imx: simplify sync polarity setting Russell King
2015-08-08 16:03   ` Russell King
2015-10-06 17:57   ` Fabio Estevam
2015-10-06 17:57     ` Fabio Estevam
2015-10-06 17:57     ` Fabio Estevam
2015-08-08 16:03 ` [PATCH 04/12] gpu: imx: fix support for interlaced modes Russell King
2015-08-08 16:03   ` Russell King
2015-08-27  8:39   ` Philipp Zabel
2015-08-27  8:39     ` Philipp Zabel
2015-08-27  8:39     ` Philipp Zabel
2015-08-27  8:54     ` Russell King - ARM Linux
2015-08-27  8:54       ` Russell King - ARM Linux
2015-08-27  8:54       ` Russell King - ARM Linux
2015-08-27  9:40       ` Philipp Zabel
2015-08-27  9:40         ` Philipp Zabel
2015-08-27  9:40         ` Philipp Zabel
2015-10-06 17:59   ` Fabio Estevam
2015-10-06 17:59     ` Fabio Estevam
2015-10-06 17:59     ` Fabio Estevam
2015-08-08 16:03 ` [PATCH 05/12] drm: bridge/dw_hdmi: add support for interlaced video modes Russell King
2015-08-08 16:03   ` Russell King
2015-08-08 16:03   ` Russell King
2015-10-06 18:00   ` Fabio Estevam
2015-10-06 18:00     ` Fabio Estevam
2015-10-06 18:00     ` Fabio Estevam
2015-08-08 16:03 ` [PATCH 06/12] drm: bridge/dw_hdmi: clean up HDMI vs DVI mode handling Russell King
2015-08-08 16:03   ` Russell King
2015-10-07  3:40   ` Yakir Yang
2015-10-07  3:40     ` Yakir Yang
2015-10-07  3:40     ` Yakir Yang
2015-08-08 16:03 ` [PATCH 07/12] drm: bridge/dw_hdmi: enable audio only if sink supports audio Russell King
2015-08-08 16:03   ` Russell King
2015-10-07  3:42   ` Yakir Yang
2015-10-07  3:42     ` Yakir Yang
2015-10-07  3:42     ` Yakir Yang
2015-08-08 16:04 ` [PATCH 08/12] drm: bridge/dw_hdmi: avoid enabling interface in mode_set Russell King
2015-08-08 16:04   ` Russell King
2015-10-07  3:50   ` Yakir Yang
2015-10-07  3:50     ` Yakir Yang
2015-10-07  9:18     ` Russell King - ARM Linux
2015-10-07  9:18       ` Russell King - ARM Linux
2015-10-07  9:18       ` Russell King - ARM Linux
2015-10-07  9:35       ` Yakir Yang
2015-10-07  9:35         ` Yakir Yang
2015-10-07  9:35         ` Yakir Yang
2015-08-08 16:04 ` [PATCH 09/12] drm: bridge/dw_hdmi: rename dw_hdmi_phy_enable_power() Russell King
2015-08-08 16:04   ` Russell King
2015-08-08 16:04   ` Russell King
2015-10-07  4:00   ` Yakir Yang
2015-08-08 16:04 ` [PATCH 10/12] drm: bridge/dw_hdmi: fix phy enable/disable handling Russell King
2015-08-08 16:04   ` Russell King
2015-08-08 16:04   ` Russell King
2015-10-07  4:05   ` Yakir Yang
2015-10-07  9:48     ` Russell King - ARM Linux
2015-10-07  9:48       ` Russell King - ARM Linux
2015-10-07  9:48       ` Russell King - ARM Linux
2015-10-07 10:40       ` Yakir Yang
2015-10-07 19:17         ` Russell King - ARM Linux
2015-10-07 19:17           ` Russell King - ARM Linux
2015-10-07 19:17           ` Russell King - ARM Linux
2015-10-08  8:35           ` Yakir Yang
2015-10-08  8:35             ` Yakir Yang
2015-10-08  8:35             ` Yakir Yang
2015-08-08 16:04 ` [PATCH 11/12] drm: bridge/dw_hdmi: add connector mode forcing Russell King
2015-08-08 16:04   ` Russell King
2015-08-08 16:04   ` Russell King
2015-10-06 18:01   ` Fabio Estevam
2015-10-06 18:01     ` Fabio Estevam
2015-10-06 18:01     ` Fabio Estevam
2015-08-08 16:04 ` [PATCH 12/12] drm: bridge/dw_hdmi: improve HDMI enable/disable handling Russell King
2015-08-08 16:04   ` Russell King
2015-10-06 18:03   ` Fabio Estevam
2015-10-06 18:03     ` Fabio Estevam
2015-10-06 18:03     ` Fabio Estevam
2015-08-08 16:09 ` [PATCH 0/9] dw-hdmi audio support Russell King - ARM Linux
2015-08-08 16:09   ` Russell King - ARM Linux
2015-08-08 16:09   ` Russell King - ARM Linux
2015-08-08 16:10   ` [PATCH 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver Russell King
2015-08-08 16:10     ` Russell King
2015-08-08 16:10     ` Russell King
2015-08-10 10:05     ` Takashi Iwai
2015-08-10 10:05       ` Takashi Iwai
2015-08-10 10:05       ` Takashi Iwai
2015-08-10 10:39       ` Russell King - ARM Linux
2015-08-10 10:39         ` Russell King - ARM Linux
2015-08-10 10:39         ` Russell King - ARM Linux
2015-08-10 12:23         ` Takashi Iwai
2015-08-10 12:23           ` Takashi Iwai
2015-08-10 12:23           ` Takashi Iwai
2015-08-10 16:49           ` Russell King - ARM Linux
2015-08-10 16:49             ` Russell King - ARM Linux
2015-08-10 16:49             ` Russell King - ARM Linux
2015-08-10 18:16             ` Mark Brown
2015-08-10 18:16               ` Mark Brown
2015-08-14 13:54       ` [PATCH v2 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver David Airlie <airlied@linux.ie>, Sascha Hauer <s.hauer@pengutronix.de>, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, Jaroslav Kysela <perex@perex.cz>, linux-rockchip@lists.infradead.org, Mark Brown <broonie@kernel.org>, Philipp Zabel <p.zabel@pengutronix.de>, Yakir Yang <ykk@rock-chips.com>, Andy Yan <andy.yan@rock-chips.com>, Jon Nettleton <jon.nettleton@gmail.com>, linux-arm-kernel@lists.infradead.org Russell King
2015-08-14 13:54         ` Russell King
2015-08-14 13:54         ` Russell King
2015-08-14 14:04       ` [PATCH v2 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver Russell King
2015-08-14 14:04         ` Russell King
2015-08-14 14:04         ` Russell King
2015-08-14 14:34         ` [alsa-devel] " Takashi Iwai
2015-08-14 14:34           ` Takashi Iwai
2015-08-14 14:34           ` Takashi Iwai
2015-10-06 18:07     ` [PATCH " Fabio Estevam
2015-10-06 18:07       ` Fabio Estevam
2015-10-06 18:07       ` Fabio Estevam
2015-10-06 18:18       ` Russell King - ARM Linux
2015-10-06 18:18         ` Russell King - ARM Linux
2015-10-06 18:18         ` Russell King - ARM Linux
2015-10-06 18:45         ` Fabio Estevam
2015-10-06 18:45           ` Fabio Estevam
2015-10-06 18:45           ` Fabio Estevam
2015-10-06 18:54           ` Russell King - ARM Linux
2015-10-06 18:54             ` Russell King - ARM Linux
2015-10-06 18:54             ` Russell King - ARM Linux
2015-10-06 20:25             ` Fabio Estevam
2015-10-06 20:25               ` Fabio Estevam
2015-10-06 20:25               ` Fabio Estevam
2015-10-09 16:00               ` Russell King - ARM Linux
2015-10-09 16:00                 ` Russell King - ARM Linux
2015-10-09 16:00                 ` Russell King - ARM Linux
2015-10-09 16:02                 ` Fabio Estevam
2015-10-09 16:02                   ` Fabio Estevam
2015-10-09 16:02                   ` Fabio Estevam
2015-10-09 16:11                   ` Russell King - ARM Linux
2015-10-09 16:11                     ` Russell King - ARM Linux
2015-10-09 16:11                     ` Russell King - ARM Linux
2015-08-08 16:10   ` [PATCH 2/9] drm: bridge/dw_hdmi-ahb-audio: parse ELD from HDMI driver Russell King
2015-08-08 16:10     ` Russell King
2015-08-08 16:10     ` Russell King
2015-08-08 16:10   ` [PATCH 3/9] drm: bridge/dw_hdmi-ahb-audio: basic support for multi-channel PCM audio Russell King
2015-08-08 16:10     ` Russell King
2015-08-08 16:10   ` [PATCH 4/9] drm: bridge/dw_hdmi-ahb-audio: allow larger buffer sizes Russell King
2015-08-08 16:10     ` Russell King
2015-08-08 16:10     ` Russell King
2015-08-08 16:10   ` [PATCH 5/9] drm: bridge/dw_hdmi: avoid being recursive in N calculation Russell King
2015-08-08 16:10     ` Russell King
2015-08-08 16:10     ` Russell King
2015-09-04 17:50     ` Doug Anderson
2015-09-04 17:50       ` Doug Anderson
2015-09-04 17:50       ` Doug Anderson
2015-08-08 16:10   ` [PATCH 6/9] drm: bridge/dw_hdmi: adjust pixel clock values " Russell King
2015-08-08 16:10     ` Russell King
2015-08-08 16:10     ` Russell King
2015-09-04 18:21     ` Doug Anderson
2015-09-04 18:21       ` Doug Anderson
2015-09-04 18:21       ` Doug Anderson
2015-09-04 19:48       ` Doug Anderson
2015-09-04 19:48         ` Doug Anderson
2015-09-04 19:48         ` Doug Anderson
2015-09-04 21:24         ` Russell King - ARM Linux
2015-09-04 21:24           ` Russell King - ARM Linux
2015-09-04 21:24           ` Russell King - ARM Linux
2015-09-04 23:50           ` Doug Anderson
2015-09-04 23:50             ` Doug Anderson
2015-09-04 23:50             ` Doug Anderson
2015-09-05  0:27             ` Russell King - ARM Linux
2015-09-05  0:27               ` Russell King - ARM Linux
2015-09-05  0:27               ` Russell King - ARM Linux
2015-09-05  2:03               ` Doug Anderson
2015-09-05  2:03                 ` Doug Anderson
2015-09-05  2:03                 ` Doug Anderson
2015-09-05  8:31                 ` Russell King - ARM Linux
2015-09-05  8:31                   ` Russell King - ARM Linux
2015-09-05  8:31                   ` Russell King - ARM Linux
2015-09-05 13:46                   ` Doug Anderson
2015-09-05 13:46                     ` Doug Anderson
2015-09-05 13:46                     ` Doug Anderson
2015-09-05 14:01                     ` Russell King - ARM Linux
2015-09-05 14:01                       ` Russell King - ARM Linux
2015-09-05 14:01                       ` Russell King - ARM Linux
2015-09-05 19:44                       ` Doug Anderson
2015-09-05 19:44                         ` Doug Anderson
2015-09-05 19:44                         ` Doug Anderson
2015-09-05  8:34                 ` Russell King - ARM Linux
2015-09-05  8:34                   ` Russell King - ARM Linux
2015-09-05  8:34                   ` Russell King - ARM Linux
2015-09-05 13:50                   ` Doug Anderson
2015-09-05 13:50                     ` Doug Anderson
2015-09-05 13:50                     ` Doug Anderson
2015-08-08 16:10   ` [PATCH 7/9] drm: bridge/dw_hdmi: remove ratio support from ACR code Russell King
2015-08-08 16:10     ` Russell King
2015-08-08 16:10     ` Russell King
2015-09-04 18:24     ` Doug Anderson
2015-09-04 18:24       ` Doug Anderson
2015-09-04 18:24       ` Doug Anderson
2015-08-08 16:10   ` [PATCH 8/9] drm: bridge/dw_hdmi: replace CTS calculation for the ACR Russell King
2015-08-08 16:10     ` Russell King
2015-09-04 20:00     ` Doug Anderson
2015-09-04 20:00       ` Doug Anderson
2015-09-04 20:00       ` Doug Anderson
2015-08-08 16:10   ` [PATCH 9/9] drm: bridge/dw_hdmi-i2s-audio: add audio driver Russell King
2015-08-08 16:10     ` Russell King
2015-08-10 15:48     ` Russell King - ARM Linux
2015-08-10 15:48       ` Russell King - ARM Linux
2015-08-10 15:48       ` Russell King - ARM Linux
2015-08-10 16:26       ` Yakir Yang
2015-08-10 16:26         ` Yakir Yang
2015-08-10 16:26         ` Yakir Yang
2015-08-27  8:42   ` [PATCH 0/9] dw-hdmi audio support Philipp Zabel
2015-08-27  8:42     ` Philipp Zabel
2015-08-27  8:42     ` Philipp Zabel
2016-01-05 15:40     ` [alsa-devel] " Jean-Michel Hautbois
2016-01-05 15:40       ` Jean-Michel Hautbois
2016-01-05 15:54       ` Fabio Estevam
2016-01-05 15:54         ` Fabio Estevam
2016-01-05 15:54         ` Fabio Estevam
2016-01-05 16:04       ` Russell King - ARM Linux
2016-01-05 16:04         ` Russell King - ARM Linux
2016-01-05 16:04         ` Russell King - ARM Linux
2016-01-07  8:21         ` Jean-Michel Hautbois
2016-01-07  8:21           ` Jean-Michel Hautbois
2016-01-07  8:21           ` Jean-Michel Hautbois
2015-08-10 12:21 ` [PATCH 00/12] dw-hdmi development Thierry Reding
2015-08-10 12:21   ` Thierry Reding
2015-08-10 12:21   ` Thierry Reding
2015-08-18 10:37   ` Russell King - ARM Linux
2015-08-18 10:37     ` Russell King - ARM Linux
2015-08-18 10:37     ` Russell King - ARM Linux

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.