We found Designware hdmi driver only support audio clock config, we can not play sound through it. To add Designware HDMI Audio support, we make those patch set: 1): modify n/cts config order, according to dw_hdmi document. 2): add Audio Sample Channel Status config interfaces to dw_hdmi driver. 3): add audio support for more display resolutions(eg. 800x600). 4): add audio support for No-CEA display resolutions. 5): fixed dw_hdmi irq bug, add irq control to suspend/resume interfaces. 6): add suspend/resume callback for dw_hdmi rockchip driver. 7): filter interlace mode in rockchip vop driver. 8): add hdmi audio config interfaces to dw_hdmi driver. 9): creat "dw_hdmi-audio" platform device in dw_hdmi driver. 10): add codec driver for hdmi audio, callback dw_hdmi audio config functions. 11): add sound driver for hdmi audio, creat hdmi audio sound card. 12): add dt-bings file and add hdmi_audio node to corresponding dt file.
Changes in v2: - adjust n/cts setting order - Add audio sample channel status setting - Add irq control to suspend/resume interfaces - Add suspend/resume support for dw_hdmi_rockchip driver - filter interlace display mode for rockchip vop - add more n/cts combinations for more display resolutions - enable audio support for No-CEA display mode - Add audio config interfaces to dw_hdmi driver - Update the audio control interfaces - Update dw_hdmi audio control interfaces, and adjust jack report process - give "codec-name" & "codec-dai-name" an const name - remove codec-name and codec-dai-name - rename rockchip,rockchip-hdmi-audio.txt to rockchip,rockchip-dw-hdmi-audio.txt
Yakir Yang (12): drm: bridge/dw_hdmi: adjust n/cts setting order drm: bridge/dw_hdmi: add audio sample channel status setting drm: bridge/dw_hdmi: add irq control to suspend/resume drm: rockchip/dw_hdmi_rockchip: add resume/suspend support drm: rockchip/vop: filter interlace display mode drm: bridge/dw_hdmi: add audio support for more display resolutions drm: bridge/dw_hdmi: enable audio support for No-CEA display resolutions drm: bridge/dw_hdmi: add audio config interfaces drm: bridge/dw_hdmi: creat dw-hdmi-audio platform device ASoC: dw-hdmi-audio: add codec driver for dw hdmi audio ASoC: rockchip-hdmi-audio: add sound driver for hdmi audio dt-bindings: Add documentation for Rockchip dw-hdmi-audio
.../sound/rockchip,rockchip-dw-hdmi-audio.txt | 12 + drivers/gpu/drm/bridge/dw_hdmi.c | 276 ++++++++++++++++-- drivers/gpu/drm/bridge/dw_hdmi.h | 32 +++ drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 13 + drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 3 +- include/drm/bridge/dw_hdmi.h | 45 +++ sound/soc/codecs/Kconfig | 4 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/dw-hdmi-audio.c | 314 +++++++++++++++++++++ sound/soc/codecs/dw-hdmi-audio.h | 17 ++ sound/soc/rockchip/Kconfig | 9 + sound/soc/rockchip/Makefile | 2 + sound/soc/rockchip/rockchip_hdmi_audio.c | 196 +++++++++++++ 13 files changed, 895 insertions(+), 30 deletions(-) create mode 100644 Documentation/devicetree/bindings/sound/rockchip,rockchip-dw-hdmi-audio.txt create mode 100644 sound/soc/codecs/dw-hdmi-audio.c create mode 100644 sound/soc/codecs/dw-hdmi-audio.h create mode 100644 sound/soc/rockchip/rockchip_hdmi_audio.c
For Designerware HDMI, the following write sequence is recommended: 1. aud_n3 (set bit ncts_atomic_write if desired) 2. aud_cts3 (set CTS_manual and CTS value if desired/enabled) 3. aud_cts2 (required in CTS_manual) 4. aud_cts1 (required in CTS_manual) 5. aud_n3 (bit ncts_atomic_write with same value as in step 1.) 6. aud_n2 7. aud_n1
Signed-off-by: Yakir Yang ykk@rock-chips.com --- Changes in v2: - adjust n/cts setting order
drivers/gpu/drm/bridge/dw_hdmi.c | 37 +++++++++++++++++++++---------------- drivers/gpu/drm/bridge/dw_hdmi.h | 6 ++++++ 2 files changed, 27 insertions(+), 16 deletions(-)
diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c index cd6a706..423addc 100644 --- a/drivers/gpu/drm/bridge/dw_hdmi.c +++ b/drivers/gpu/drm/bridge/dw_hdmi.c @@ -177,26 +177,31 @@ static void hdmi_mask_writeb(struct dw_hdmi *hdmi, u8 data, unsigned int reg, hdmi_modb(hdmi, data << shift, mask, reg); }
-static void hdmi_set_clock_regenerator_n(struct dw_hdmi *hdmi, - unsigned int value) +static void hdmi_regenerate_n_cts(struct dw_hdmi *hdmi, unsigned int n, + unsigned int cts) { - hdmi_writeb(hdmi, value & 0xff, HDMI_AUD_N1); - hdmi_writeb(hdmi, (value >> 8) & 0xff, HDMI_AUD_N2); - hdmi_writeb(hdmi, (value >> 16) & 0x0f, HDMI_AUD_N3); + /* set ncts_atomic_write */ + hdmi_modb(hdmi, HDMI_AUD_N3_NCTS_ATOMIC_WRITE_SET, + HDMI_AUD_N3_NCTS_ATOMIC_WRITE_MASK, HDMI_AUD_N3); + + /* set cts manual */ + hdmi_modb(hdmi, HDMI_AUD_CTS3_CTS_MANUAL, + HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3);
/* nshift factor = 0 */ hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_N_SHIFT_MASK, HDMI_AUD_CTS3); -} - -static void hdmi_regenerate_cts(struct dw_hdmi *hdmi, unsigned int cts) -{ - /* Must be set/cleared first */ - hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3);
- hdmi_writeb(hdmi, cts & 0xff, HDMI_AUD_CTS1); + /* set cts values */ + hdmi_modb(hdmi, ((cts >> 16) & HDMI_AUD_CTS3_AUDCTS19_16_MASK), + HDMI_AUD_CTS3_AUDCTS19_16_MASK, HDMI_AUD_CTS3); hdmi_writeb(hdmi, (cts >> 8) & 0xff, HDMI_AUD_CTS2); - hdmi_writeb(hdmi, ((cts >> 16) & HDMI_AUD_CTS3_AUDCTS19_16_MASK) | - HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3); + hdmi_writeb(hdmi, cts & 0xff, HDMI_AUD_CTS1); + + /* set n values */ + hdmi_modb(hdmi, (n >> 16) & HDMI_AUD_N3_AUDN19_16_MASK, + HDMI_AUD_N3_AUDN19_16_MASK, HDMI_AUD_N3); + hdmi_writeb(hdmi, (n >> 8) & 0xff, HDMI_AUD_N2); + hdmi_writeb(hdmi, n & 0xff, HDMI_AUD_N1); }
static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk, @@ -355,8 +360,8 @@ static void hdmi_set_clk_regenerator(struct dw_hdmi *hdmi, __func__, hdmi->sample_rate, hdmi->ratio, pixel_clk, clk_n, clk_cts);
- hdmi_set_clock_regenerator_n(hdmi, clk_n); - hdmi_regenerate_cts(hdmi, clk_cts); + hdmi_regenerate_n_cts(hdmi, clk_n, clk_cts); + hdmi_set_schnl(hdmi); }
static void hdmi_init_clk_regenerator(struct dw_hdmi *hdmi) diff --git a/drivers/gpu/drm/bridge/dw_hdmi.h b/drivers/gpu/drm/bridge/dw_hdmi.h index 175dbc8..bc53452 100644 --- a/drivers/gpu/drm/bridge/dw_hdmi.h +++ b/drivers/gpu/drm/bridge/dw_hdmi.h @@ -884,6 +884,12 @@ enum { HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL = 0x08, HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_MASK = 0x04,
+/* AUD_N3 field values */ + HDMI_AUD_N3_NCTS_ATOMIC_WRITE_MASK = 0x80, + HDMI_AUD_N3_NCTS_ATOMIC_WRITE_SET = 0x80, + HDMI_AUD_N3_NCTS_ATOMIC_WRITE_CLEAR = 0x00, + HDMI_AUD_N3_AUDN19_16_MASK = 0x0f, + /* AUD_CTS3 field values */ HDMI_AUD_CTS3_N_SHIFT_OFFSET = 5, HDMI_AUD_CTS3_N_SHIFT_MASK = 0xe0,
On Fri, Jan 30, 2015 at 06:25:30AM -0500, Yakir Yang wrote:
For Designerware HDMI, the following write sequence is recommended:
- aud_n3 (set bit ncts_atomic_write if desired)
- aud_cts3 (set CTS_manual and CTS value if desired/enabled)
- aud_cts2 (required in CTS_manual)
- aud_cts1 (required in CTS_manual)
- aud_n3 (bit ncts_atomic_write with same value as in step 1.)
- aud_n2
- aud_n1
Signed-off-by: Yakir Yang ykk@rock-chips.com
Changes in v2:
- adjust n/cts setting order
drivers/gpu/drm/bridge/dw_hdmi.c | 37 +++++++++++++++++++++---------------- drivers/gpu/drm/bridge/dw_hdmi.h | 6 ++++++ 2 files changed, 27 insertions(+), 16 deletions(-)
diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c index cd6a706..423addc 100644 --- a/drivers/gpu/drm/bridge/dw_hdmi.c +++ b/drivers/gpu/drm/bridge/dw_hdmi.c @@ -177,26 +177,31 @@ static void hdmi_mask_writeb(struct dw_hdmi *hdmi, u8 data, unsigned int reg, hdmi_modb(hdmi, data << shift, mask, reg); }
-static void hdmi_set_clock_regenerator_n(struct dw_hdmi *hdmi,
unsigned int value)
+static void hdmi_regenerate_n_cts(struct dw_hdmi *hdmi, unsigned int n,
unsigned int cts)
{
- hdmi_writeb(hdmi, value & 0xff, HDMI_AUD_N1);
- hdmi_writeb(hdmi, (value >> 8) & 0xff, HDMI_AUD_N2);
- hdmi_writeb(hdmi, (value >> 16) & 0x0f, HDMI_AUD_N3);
- /* set ncts_atomic_write */
- hdmi_modb(hdmi, HDMI_AUD_N3_NCTS_ATOMIC_WRITE_SET,
HDMI_AUD_N3_NCTS_ATOMIC_WRITE_MASK, HDMI_AUD_N3);
Bits 7:4 of N3 are marked up in the iMX6 manuals as "reserved". We need some clarification from FSL whether this matters, but at the moment my opinion on that is this should be conditionalised against the IP version.
Setting bit 7 as you do above may not cause any harm on iMX6, but on the other hand, we shouldn't be setting bits which are marked as reserved.
/* set cts manual */
hdmi_modb(hdmi, HDMI_AUD_CTS3_CTS_MANUAL,
HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3);
/* nshift factor = 0 */ hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_N_SHIFT_MASK, HDMI_AUD_CTS3);
-}
-static void hdmi_regenerate_cts(struct dw_hdmi *hdmi, unsigned int cts) -{
/* Must be set/cleared first */
hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3);
hdmi_writeb(hdmi, cts & 0xff, HDMI_AUD_CTS1);
- /* set cts values */
- hdmi_modb(hdmi, ((cts >> 16) & HDMI_AUD_CTS3_AUDCTS19_16_MASK),
hdmi_writeb(hdmi, (cts >> 8) & 0xff, HDMI_AUD_CTS2);HDMI_AUD_CTS3_AUDCTS19_16_MASK, HDMI_AUD_CTS3);
- hdmi_writeb(hdmi, ((cts >> 16) & HDMI_AUD_CTS3_AUDCTS19_16_MASK) |
HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3);
- hdmi_writeb(hdmi, cts & 0xff, HDMI_AUD_CTS1);
- /* set n values */
- hdmi_modb(hdmi, (n >> 16) & HDMI_AUD_N3_AUDN19_16_MASK,
HDMI_AUD_N3_AUDN19_16_MASK, HDMI_AUD_N3);
- hdmi_writeb(hdmi, (n >> 8) & 0xff, HDMI_AUD_N2);
- hdmi_writeb(hdmi, n & 0xff, HDMI_AUD_N1);
This is definitely moving things in the right direction. However, I would ask that the read-modify-writes to HDMI_AUD_N3 are open-coded rather than using hdmi_modb(), and consolidated. We really don't need to keep re-reading this register.
I'd also like to check that this does not cause a regression on iMX6 SoCs with my audio driver; I'm aware that there are some issues to do with how these registers are written, and the published errata workaround in the iMX6 errata documentation doesn't seem to always work.
}
static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk, @@ -355,8 +360,8 @@ static void hdmi_set_clk_regenerator(struct dw_hdmi *hdmi, __func__, hdmi->sample_rate, hdmi->ratio, pixel_clk, clk_n, clk_cts);
- hdmi_set_clock_regenerator_n(hdmi, clk_n);
- hdmi_regenerate_cts(hdmi, clk_cts);
- hdmi_regenerate_n_cts(hdmi, clk_n, clk_cts);
- hdmi_set_schnl(hdmi);
I'd prefer the addition of hdmi_set_schnl() to be in the patch which adds that new function. However, as I mentioned in my reply to that patch, other versions of this IP do not have these registers, and it may not be safe to write to them. We need to find a way to know whether this IP has the AHB DMA support or not and act accordingly.
On 01/31/2015 06:07 AM, Russell King - ARM Linux wrote:
On Fri, Jan 30, 2015 at 06:25:30AM -0500, Yakir Yang wrote:
For Designerware HDMI, the following write sequence is recommended:
- aud_n3 (set bit ncts_atomic_write if desired)
- aud_cts3 (set CTS_manual and CTS value if desired/enabled)
- aud_cts2 (required in CTS_manual)
- aud_cts1 (required in CTS_manual)
- aud_n3 (bit ncts_atomic_write with same value as in step 1.)
- aud_n2
- aud_n1
Signed-off-by: Yakir Yang ykk@rock-chips.com
Changes in v2:
adjust n/cts setting order
drivers/gpu/drm/bridge/dw_hdmi.c | 37 +++++++++++++++++++++---------------- drivers/gpu/drm/bridge/dw_hdmi.h | 6 ++++++ 2 files changed, 27 insertions(+), 16 deletions(-)
diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c index cd6a706..423addc 100644 --- a/drivers/gpu/drm/bridge/dw_hdmi.c +++ b/drivers/gpu/drm/bridge/dw_hdmi.c @@ -177,26 +177,31 @@ static void hdmi_mask_writeb(struct dw_hdmi *hdmi, u8 data, unsigned int reg, hdmi_modb(hdmi, data << shift, mask, reg); }
-static void hdmi_set_clock_regenerator_n(struct dw_hdmi *hdmi,
unsigned int value)
+static void hdmi_regenerate_n_cts(struct dw_hdmi *hdmi, unsigned int n,
{unsigned int cts)
- hdmi_writeb(hdmi, value & 0xff, HDMI_AUD_N1);
- hdmi_writeb(hdmi, (value >> 8) & 0xff, HDMI_AUD_N2);
- hdmi_writeb(hdmi, (value >> 16) & 0x0f, HDMI_AUD_N3);
- /* set ncts_atomic_write */
- hdmi_modb(hdmi, HDMI_AUD_N3_NCTS_ATOMIC_WRITE_SET,
HDMI_AUD_N3_NCTS_ATOMIC_WRITE_MASK, HDMI_AUD_N3);
Bits 7:4 of N3 are marked up in the iMX6 manuals as "reserved". We need some clarification from FSL whether this matters, but at the moment my opinion on that is this should be conditionalised against the IP version.
Should we take the design_id(0x13 on iMX6, 0x20 on rk3288) to separate it.
Setting bit 7 as you do above may not cause any harm on iMX6, but on the other hand, we shouldn't be setting bits which are marked as reserved.
/* set cts manual */
hdmi_modb(hdmi, HDMI_AUD_CTS3_CTS_MANUAL,
HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3);
/* nshift factor = 0 */ hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_N_SHIFT_MASK, HDMI_AUD_CTS3);
-}
-static void hdmi_regenerate_cts(struct dw_hdmi *hdmi, unsigned int cts) -{
/* Must be set/cleared first */
hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3);
hdmi_writeb(hdmi, cts & 0xff, HDMI_AUD_CTS1);
- /* set cts values */
- hdmi_modb(hdmi, ((cts >> 16) & HDMI_AUD_CTS3_AUDCTS19_16_MASK),
hdmi_writeb(hdmi, (cts >> 8) & 0xff, HDMI_AUD_CTS2);HDMI_AUD_CTS3_AUDCTS19_16_MASK, HDMI_AUD_CTS3);
- hdmi_writeb(hdmi, ((cts >> 16) & HDMI_AUD_CTS3_AUDCTS19_16_MASK) |
HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3);
- hdmi_writeb(hdmi, cts & 0xff, HDMI_AUD_CTS1);
- /* set n values */
- hdmi_modb(hdmi, (n >> 16) & HDMI_AUD_N3_AUDN19_16_MASK,
HDMI_AUD_N3_AUDN19_16_MASK, HDMI_AUD_N3);
- hdmi_writeb(hdmi, (n >> 8) & 0xff, HDMI_AUD_N2);
- hdmi_writeb(hdmi, n & 0xff, HDMI_AUD_N1);
This is definitely moving things in the right direction. However, I would ask that the read-modify-writes to HDMI_AUD_N3 are open-coded rather than using hdmi_modb(), and consolidated. We really don't need to keep re-reading this register.
Maybe we also can take design_id to separate it here.
I'd also like to check that this does not cause a regression on iMX6 SoCs with my audio driver; I'm aware that there are some issues to do with how these registers are written, and the published errata workaround in the iMX6 errata documentation doesn't seem to always work.
}
static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk, @@ -355,8 +360,8 @@ static void hdmi_set_clk_regenerator(struct dw_hdmi *hdmi, __func__, hdmi->sample_rate, hdmi->ratio, pixel_clk, clk_n, clk_cts);
- hdmi_set_clock_regenerator_n(hdmi, clk_n);
- hdmi_regenerate_cts(hdmi, clk_cts);
- hdmi_regenerate_n_cts(hdmi, clk_n, clk_cts);
- hdmi_set_schnl(hdmi);
I'd prefer the addition of hdmi_set_schnl() to be in the patch which adds that new function. However, as I mentioned in my reply to that patch, other versions of this IP do not have these registers, and it may not be safe to write to them. We need to find a way to know whether this IP has the AHB DMA support or not and act accordingly.
When transmitting IEC60985 linear PCM audio, we configure the Aduio Sample Channel Status information of all the channel status bits in the IEC60958 frame.
Signed-off-by: Yakir Yang ykk@rock-chips.com --- Changes in v2: - Add audio sample channel status setting
drivers/gpu/drm/bridge/dw_hdmi.c | 41 ++++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/bridge/dw_hdmi.h | 20 ++++++++++++++++++++ 2 files changed, 61 insertions(+)
diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c index 423addc..2ded957 100644 --- a/drivers/gpu/drm/bridge/dw_hdmi.c +++ b/drivers/gpu/drm/bridge/dw_hdmi.c @@ -204,6 +204,47 @@ static void hdmi_regenerate_n_cts(struct dw_hdmi *hdmi, unsigned int n, hdmi_writeb(hdmi, n & 0xff, HDMI_AUD_N1); }
+static void hdmi_set_schnl(struct dw_hdmi *hdmi) +{ + u8 aud_schnl_samplerate; + + switch (hdmi->sample_rate) { + case 32000: + aud_schnl_samplerate = HDMI_FC_AUDSCHNLS7_SMPRATE_32K; + break; + case 44100: + aud_schnl_samplerate = HDMI_FC_AUDSCHNLS7_SMPRATE_44K1; + break; + case 48000: + aud_schnl_samplerate = HDMI_FC_AUDSCHNLS7_SMPRATE_48K; + break; + case 88200: + aud_schnl_samplerate = HDMI_FC_AUDSCHNLS7_SMPRATE_88K2; + break; + case 96000: + aud_schnl_samplerate = HDMI_FC_AUDSCHNLS7_SMPRATE_96K; + break; + case 176400: + aud_schnl_samplerate = HDMI_FC_AUDSCHNLS7_SMPRATE_176K4; + break; + case 192000: + aud_schnl_samplerate = HDMI_FC_AUDSCHNLS7_SMPRATE_192K; + break; + case 768000: + aud_schnl_samplerate = HDMI_FC_AUDSCHNLS7_SMPRATE_768K; + break; + default: + aud_schnl_samplerate = HDMI_FC_AUDSCHNLS7_SMPRATE_44K1; + break; + } + + /* set channel status register */ + hdmi_modb(hdmi, aud_schnl_samplerate, + HDMI_FC_AUDSCHNLS7_SMPRATE_MASK, HDMI_FC_AUDSCHNLS7); + hdmi_writeb(hdmi, ((~aud_schnl_samplerate) << 4) | 0x2, + HDMI_FC_AUDSCHNLS8); +} + static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk, unsigned int ratio) { diff --git a/drivers/gpu/drm/bridge/dw_hdmi.h b/drivers/gpu/drm/bridge/dw_hdmi.h index bc53452..603e645 100644 --- a/drivers/gpu/drm/bridge/dw_hdmi.h +++ b/drivers/gpu/drm/bridge/dw_hdmi.h @@ -162,6 +162,17 @@ #define HDMI_FC_SPDDEVICEINF 0x1062 #define HDMI_FC_AUDSCONF 0x1063 #define HDMI_FC_AUDSSTAT 0x1064 +#define HDMI_FC_AUDSV 0x1065 +#define HDMI_FC_AUDSU 0x1066 +#define HDMI_FC_AUDSCHNLS0 0x1067 +#define HDMI_FC_AUDSCHNLS1 0x1068 +#define HDMI_FC_AUDSCHNLS2 0x1069 +#define HDMI_FC_AUDSCHNLS3 0x106a +#define HDMI_FC_AUDSCHNLS4 0x106b +#define HDMI_FC_AUDSCHNLS5 0x106c +#define HDMI_FC_AUDSCHNLS6 0x106d +#define HDMI_FC_AUDSCHNLS7 0x106e +#define HDMI_FC_AUDSCHNLS8 0x106f #define HDMI_FC_DATACH0FILL 0x1070 #define HDMI_FC_DATACH1FILL 0x1071 #define HDMI_FC_DATACH2FILL 0x1072 @@ -731,6 +742,15 @@ enum { /* HDMI_FC_AUDSCHNLS7 field values */ HDMI_FC_AUDSCHNLS7_ACCURACY_OFFSET = 4, HDMI_FC_AUDSCHNLS7_ACCURACY_MASK = 0x30, + HDMI_FC_AUDSCHNLS7_SMPRATE_MASK = 0x0f, + HDMI_FC_AUDSCHNLS7_SMPRATE_192K = 0xe, + HDMI_FC_AUDSCHNLS7_SMPRATE_176K4 = 0xc, + HDMI_FC_AUDSCHNLS7_SMPRATE_96K = 0xa, + HDMI_FC_AUDSCHNLS7_SMPRATE_768K = 0x9, + HDMI_FC_AUDSCHNLS7_SMPRATE_88K2 = 0x8, + HDMI_FC_AUDSCHNLS7_SMPRATE_32K = 0x3, + HDMI_FC_AUDSCHNLS7_SMPRATE_48K = 0x2, + HDMI_FC_AUDSCHNLS7_SMPRATE_44K1 = 0x0,
/* HDMI_FC_AUDSCHNLS8 field values */ HDMI_FC_AUDSCHNLS8_ORIGSAMPFREQ_MASK = 0xf0,
On Fri, Jan 30, 2015 at 06:27:10AM -0500, Yakir Yang wrote:
When transmitting IEC60985 linear PCM audio, we configure the Aduio Sample Channel Status information of all the channel status bits in the IEC60958 frame.
Signed-off-by: Yakir Yang ykk@rock-chips.com
Changes in v2:
- Add audio sample channel status setting
See my comments on
"[PATCH 01/11] drm: bridge/dw_hdmi: add audio sample channel status setting"
sent immediately prior to this series. This patch appears to be a copy of that patch.
On 01/31/2015 06:08 AM, Russell King - ARM Linux wrote:
On Fri, Jan 30, 2015 at 06:27:10AM -0500, Yakir Yang wrote:
When transmitting IEC60985 linear PCM audio, we configure the Aduio Sample Channel Status information of all the channel status bits in the IEC60958 frame.
Signed-off-by: Yakir Yang ykk@rock-chips.com
Changes in v2:
- Add audio sample channel status setting
See my comments on
"[PATCH 01/11] drm: bridge/dw_hdmi: add audio sample channel status setting"
sent immediately prior to this series. This patch appears to be a copy of that patch.
"[PATCH 01/11] drm: bridge/dw_hdmi: add audio sample channel status setting"
should not be send, it's my mistaken, i will remove it.
Thanks. : )
On Sat, Jan 31, 2015 at 06:22:19AM -0500, Yang Kuankuan wrote:
On 01/31/2015 06:08 AM, Russell King - ARM Linux wrote:
On Fri, Jan 30, 2015 at 06:27:10AM -0500, Yakir Yang wrote:
When transmitting IEC60985 linear PCM audio, we configure the Aduio Sample Channel Status information of all the channel status bits in the IEC60958 frame.
Signed-off-by: Yakir Yang ykk@rock-chips.com
Changes in v2:
- Add audio sample channel status setting
See my comments on
"[PATCH 01/11] drm: bridge/dw_hdmi: add audio sample channel status setting"
sent immediately prior to this series. This patch appears to be a copy of that patch.
"[PATCH 01/11] drm: bridge/dw_hdmi: add audio sample channel status setting"
should not be send, it's my mistaken, i will remove it.
Please see my comments on the above patch for comments on _this_ patch. All my comments on the mistakenly sent 01/11 also apply to 02/12.
Thanks.
when kernel enter into suspend, cpus will shutdown, hdmi registers will reset invisibly. After kernel resume, drm core will call the bridge enable function. All of hdmi registers will be setup again except the interrupt registers. In that case we should mute all the interrupt in suspend stage, and umnute the interrupt we need in the resume stage.
Signed-off-by: Yakir Yang ykk@rock-chips.com --- Changes in v2: - Add irq control to suspend/resume interfaces
drivers/gpu/drm/bridge/dw_hdmi.c | 43 ++++++++++++++++++++++++++++++++++++++++ include/drm/bridge/dw_hdmi.h | 2 ++ 2 files changed, 45 insertions(+)
diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c index 2ded957..13f26af 100644 --- a/drivers/gpu/drm/bridge/dw_hdmi.c +++ b/drivers/gpu/drm/bridge/dw_hdmi.c @@ -1745,6 +1745,49 @@ void dw_hdmi_unbind(struct device *dev, struct device *master, void *data) } EXPORT_SYMBOL_GPL(dw_hdmi_unbind);
+int dw_hdmi_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct dw_hdmi *hdmi = dev_get_drvdata(&pdev->dev); + u8 ih_mute; + + /* Disable all interrupts */ + hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0); + + /* Disable top level interrupt bits in HDMI block */ + ih_mute = hdmi_readb(hdmi, HDMI_IH_MUTE) | + HDMI_IH_MUTE_MUTE_WAKEUP_INTERRUPT | + HDMI_IH_MUTE_MUTE_ALL_INTERRUPT; + + hdmi_writeb(hdmi, ih_mute, HDMI_IH_MUTE); + + return 0; +} +EXPORT_SYMBOL_GPL(dw_hdmi_suspend); + +int dw_hdmi_resume(struct platform_device *pdev) +{ + struct dw_hdmi *hdmi = dev_get_drvdata(&pdev->dev); + + initialize_hdmi_ih_mutes(hdmi); + + dw_hdmi_fb_registered(hdmi); + + /* + * Configure registers related to HDMI interrupt + * generation before registering IRQ. + */ + hdmi_writeb(hdmi, HDMI_PHY_HPD, HDMI_PHY_POL0); + + /* Clear Hotplug interrupts */ + hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0); + + /* Unmute interrupts */ + hdmi_writeb(hdmi, ~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0); + + return 0; +} +EXPORT_SYMBOL_GPL(dw_hdmi_resume); + MODULE_AUTHOR("Sascha Hauer s.hauer@pengutronix.de"); MODULE_AUTHOR("Andy Yan andy.yan@rock-chips.com"); MODULE_AUTHOR("Yakir Yang ykk@rock-chips.com"); diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h index 5a4f490..8476cfc 100644 --- a/include/drm/bridge/dw_hdmi.h +++ b/include/drm/bridge/dw_hdmi.h @@ -53,6 +53,8 @@ struct dw_hdmi_plat_data { struct drm_display_mode *mode); };
+int dw_hdmi_resume(struct platform_device *pdev); +int dw_hdmi_suspend(struct platform_device *pdev, pm_message_t state); void dw_hdmi_unbind(struct device *dev, struct device *master, void *data); int dw_hdmi_bind(struct device *dev, struct device *master, void *data, struct drm_encoder *encoder,
On Fri, Jan 30, 2015 at 06:28:00AM -0500, Yakir Yang wrote:
diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c index 2ded957..13f26af 100644 --- a/drivers/gpu/drm/bridge/dw_hdmi.c +++ b/drivers/gpu/drm/bridge/dw_hdmi.c @@ -1745,6 +1745,49 @@ void dw_hdmi_unbind(struct device *dev, struct device *master, void *data) } EXPORT_SYMBOL_GPL(dw_hdmi_unbind);
+int dw_hdmi_suspend(struct platform_device *pdev, pm_message_t state) +{
...
+int dw_hdmi_resume(struct platform_device *pdev) +{
Please arrange for these functions to take a struct device rather than a struct platform_device. The generic part of this driver should remain bus-agnostic.
Thanks.
On 01/31/2015 06:11 AM, Russell King - ARM Linux wrote:
On Fri, Jan 30, 2015 at 06:28:00AM -0500, Yakir Yang wrote:
diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c index 2ded957..13f26af 100644 --- a/drivers/gpu/drm/bridge/dw_hdmi.c +++ b/drivers/gpu/drm/bridge/dw_hdmi.c @@ -1745,6 +1745,49 @@ void dw_hdmi_unbind(struct device *dev, struct device *master, void *data) } EXPORT_SYMBOL_GPL(dw_hdmi_unbind);
+int dw_hdmi_suspend(struct platform_device *pdev, pm_message_t state) +{
...
+int dw_hdmi_resume(struct platform_device *pdev) +{
Please arrange for these functions to take a struct device rather than a struct platform_device. The generic part of this driver should remain bus-agnostic.
Thanks.
okay, is it good to replace "struct platform_device" with "struct device" ?
Thanks : )
Signed-off-by: Yakir Yang ykk@rock-chips.com --- Changes in v2: - Add suspend/resume support for dw_hdmi_rockchip driver
drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 13 +++++++++++++ 1 file changed, 13 insertions(+)
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c index d236faa..2f8bacb 100644 --- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c @@ -323,9 +323,22 @@ static int dw_hdmi_rockchip_remove(struct platform_device *pdev) return 0; }
+static int dw_hdmi_rockchip_suspend(struct platform_device *pdev, + pm_message_t state) +{ + return dw_hdmi_suspend(pdev, state); +} + +static int dw_hdmi_rockchip_resume(struct platform_device *pdev) +{ + return dw_hdmi_resume(pdev); +} + static struct platform_driver dw_hdmi_rockchip_pltfm_driver = { .probe = dw_hdmi_rockchip_probe, .remove = dw_hdmi_rockchip_remove, + .resume = dw_hdmi_rockchip_resume, + .suspend = dw_hdmi_rockchip_suspend, .driver = { .name = "dwhdmi-rockchip", .of_match_table = dw_hdmi_rockchip_dt_ids,
On Fri, Jan 30, 2015 at 06:28:59AM -0500, Yakir Yang wrote:
Signed-off-by: Yakir Yang ykk@rock-chips.com
Changes in v2:
- Add suspend/resume support for dw_hdmi_rockchip driver
drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 13 +++++++++++++ 1 file changed, 13 insertions(+)
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c index d236faa..2f8bacb 100644 --- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c @@ -323,9 +323,22 @@ static int dw_hdmi_rockchip_remove(struct platform_device *pdev) return 0; }
+static int dw_hdmi_rockchip_suspend(struct platform_device *pdev,
pm_message_t state)
+{
- return dw_hdmi_suspend(pdev, state);
+}
+static int dw_hdmi_rockchip_resume(struct platform_device *pdev) +{
- return dw_hdmi_resume(pdev);
+}
static struct platform_driver dw_hdmi_rockchip_pltfm_driver = { .probe = dw_hdmi_rockchip_probe, .remove = dw_hdmi_rockchip_remove,
- .resume = dw_hdmi_rockchip_resume,
- .suspend = dw_hdmi_rockchip_suspend, .driver = { .name = "dwhdmi-rockchip", .of_match_table = dw_hdmi_rockchip_dt_ids,
Using the power management operations (setting the .pm member in the embedded struct device_driver) is preferred over using the .resume and .suspend methods.
Please update this patch to use the preferred method. Thanks.
On 01/31/2015 06:13 AM, Russell King - ARM Linux wrote:
On Fri, Jan 30, 2015 at 06:28:59AM -0500, Yakir Yang wrote:
Signed-off-by: Yakir Yang ykk@rock-chips.com
Changes in v2:
Add suspend/resume support for dw_hdmi_rockchip driver
drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 13 +++++++++++++ 1 file changed, 13 insertions(+)
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c index d236faa..2f8bacb 100644 --- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c @@ -323,9 +323,22 @@ static int dw_hdmi_rockchip_remove(struct platform_device *pdev) return 0; }
+static int dw_hdmi_rockchip_suspend(struct platform_device *pdev,
pm_message_t state)
+{
- return dw_hdmi_suspend(pdev, state);
+}
+static int dw_hdmi_rockchip_resume(struct platform_device *pdev) +{
- return dw_hdmi_resume(pdev);
+}
- static struct platform_driver dw_hdmi_rockchip_pltfm_driver = { .probe = dw_hdmi_rockchip_probe, .remove = dw_hdmi_rockchip_remove,
- .resume = dw_hdmi_rockchip_resume,
- .suspend = dw_hdmi_rockchip_suspend, .driver = { .name = "dwhdmi-rockchip", .of_match_table = dw_hdmi_rockchip_dt_ids,
Using the power management operations (setting the .pm member in the embedded struct device_driver) is preferred over using the .resume and .suspend methods.
Please update this patch to use the preferred method. Thanks.
Okay, I will fixed it in next version v3.
Thanks. : )
RK3288's VOP do not support INTERLACE display mode, so we should remove those modes out of mode_ok list.
Signed-off-by: Yakir Yang ykk@rock-chips.com --- Changes in v2: - filter interlace display mode for rockchip vop
drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index 9a5c571..bedab42 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -808,7 +808,8 @@ static bool vop_crtc_mode_fixup(struct drm_crtc *crtc, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { - if (adjusted_mode->htotal == 0 || adjusted_mode->vtotal == 0) + if (adjusted_mode->htotal == 0 || adjusted_mode->vtotal == 0 || + (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE)) return false;
return true;
Hi ykk,
On Fri, Jan 30, 2015 at 7:29 PM, Yakir Yang ykk@rock-chips.com wrote:
RK3288's VOP do not support INTERLACE display mode, so we should remove those modes out of mode_ok list.
Signed-off-by: Yakir Yang ykk@rock-chips.com
Reviewed-by: Daniel Kurtz djkurtz@chromium.org
Can you move this patch out of your hdmi audio patch set, and send it separately? This fix is independent of the others.
Thanks!
Changes in v2:
- filter interlace display mode for rockchip vop
drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index 9a5c571..bedab42 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -808,7 +808,8 @@ static bool vop_crtc_mode_fixup(struct drm_crtc *crtc, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) {
if (adjusted_mode->htotal == 0 || adjusted_mode->vtotal == 0)
if (adjusted_mode->htotal == 0 || adjusted_mode->vtotal == 0 ||
(adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE)) return false; return true;
-- 2.1.2
-- You received this message because you are subscribed to the Google Groups "rockchip-discuss" group. To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/rockchip-discuss/1422617388....
To unsubscribe from this group and stop receiving emails from it, send an email to rockchip-discuss+unsubscribe@chromium.org.
On 02/02/2015 03:00 AM, Daniel Kurtz wrote:
Hi ykk,
On Fri, Jan 30, 2015 at 7:29 PM, Yakir Yang ykk@rock-chips.com wrote:
RK3288's VOP do not support INTERLACE display mode, so we should remove those modes out of mode_ok list.
Signed-off-by: Yakir Yang ykk@rock-chips.com
Reviewed-by: Daniel Kurtz djkurtz@chromium.org
Can you move this patch out of your hdmi audio patch set, and send it separately? This fix is independent of the others.
Thanks!
Okay. : )
Changes in v2:
filter interlace display mode for rockchip vop
drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index 9a5c571..bedab42 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -808,7 +808,8 @@ static bool vop_crtc_mode_fixup(struct drm_crtc *crtc, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) {
if (adjusted_mode->htotal == 0 || adjusted_mode->vtotal == 0)
if (adjusted_mode->htotal == 0 || adjusted_mode->vtotal == 0 ||
(adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE)) return false; return true;
-- 2.1.2
-- You received this message because you are subscribed to the Google Groups "rockchip-discuss" group. To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/rockchip-discuss/1422617388....
To unsubscribe from this group and stop receiving emails from it, send an email to rockchip-discuss+unsubscribe@chromium.org.
Add more n/cts values, in that case we can support audio for more display resolutions (128 * SampleRate = PixelClock * N / CTS).
Signed-off-by: Yakir Yang ykk@rock-chips.com --- Changes in v2: - add more n/cts combinations for more display resolutions
drivers/gpu/drm/bridge/dw_hdmi.c | 58 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+)
diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c index 13f26af..e07723c 100644 --- a/drivers/gpu/drm/bridge/dw_hdmi.c +++ b/drivers/gpu/drm/bridge/dw_hdmi.c @@ -265,8 +265,24 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk, case 44100: if (pixel_clk == 25170000) n = 7007; + else if (pixel_clk == 25175000) + n = 28224; + else if (pixel_clk == 40000000) + n = 7056; + else if (pixel_clk == 54000000) + n = 6272; + else if (pixel_clk == 65000000) + n = 7056; else if (pixel_clk == 74170000) n = 17836; + else if (pixel_clk == 74250000) + n = 6272; + else if (pixel_clk == 83500000) + n = 7056; + else if (pixel_clk == 106500000) + n = 4074; + else if (pixel_clk == 108000000) + n = 4018; else if (pixel_clk == 148350000) n = (ratio == 150) ? 17836 : 8918; else @@ -276,10 +292,26 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk, case 48000: if (pixel_clk == 25170000) n = (ratio == 150) ? 9152 : 6864; + else if (pixel_clk == 25175000) + n = 12288; else if (pixel_clk == 27020000) n = (ratio == 150) ? 8192 : 6144; + else if (pixel_clk == 40000000) + n = 6144; + else if (pixel_clk == 54000000) + n = 6144; + else if (pixel_clk == 65000000) + n = 6144; else if (pixel_clk == 74170000) n = 11648; + else if (pixel_clk == 74250000) + n = 6144; + else if (pixel_clk == 83500000) + n = 6144; + else if (pixel_clk == 106500000) + n = 6144; + else if (pixel_clk == 108000000) + n = 6144; else if (pixel_clk == 148350000) n = (ratio == 150) ? 11648 : 5824; else @@ -327,10 +359,18 @@ static unsigned int hdmi_compute_cts(unsigned int freq, unsigned long pixel_clk, case 96000: case 192000: switch (pixel_clk) { + case 25175000: + cts = 50350; + break; case 25200000: case 27000000: + case 40000000: case 54000000: + case 65000000: case 74250000: + case 83500000: + case 106500000: + case 108000000: case 148500000: cts = pixel_clk / 1000; break; @@ -351,18 +391,36 @@ static unsigned int hdmi_compute_cts(unsigned int freq, unsigned long pixel_clk, case 88200: case 176400: switch (pixel_clk) { + case 25175000: + cts = 125875; + break; case 25200000: cts = 28000; break; case 27000000: cts = 30000; break; + case 40000000: + cts = 50000; + break; case 54000000: cts = 60000; break; + case 65000000: + cts = 81250; + break; case 74250000: cts = 82500; break; + case 83500000: + cts = 104375; + break; + case 106500000: + cts = 88750; + break; + case 108000000: + cts = 76875; + break; case 148500000: cts = 165000; break;
On Fri, Jan 30, 2015 at 06:30:39AM -0500, Yakir Yang wrote:
Add more n/cts values, in that case we can support audio for more display resolutions (128 * SampleRate = PixelClock * N / CTS).
Where do these come from? The iMX6 manuals give the set which are already in the driver, and says that others are not supported - to quote...
"Table below shows the CTS and N values for the supported standard. All other TMDS clocks are not supported, the TMDS clocks divided or multiplied by 1,001 coefficients are not supported."
and the table lists values for TMDS clocks of 25.2, 27, 54, 74.25, 148.5 and 297MHz.
This will need to be tested on iMX6 to validate whether it works. If it doesn't work, we need to conditionalise the new TMDS clocks with the IP version.
On 01/31/2015 06:20 AM, Russell King - ARM Linux wrote:
On Fri, Jan 30, 2015 at 06:30:39AM -0500, Yakir Yang wrote:
Add more n/cts values, in that case we can support audio for more display resolutions (128 * SampleRate = PixelClock * N / CTS).
Where do these come from? The iMX6 manuals give the set which are already in the driver, and says that others are not supported - to quote...
"Table below shows the CTS and N values for the supported standard. All other TMDS clocks are not supported, the TMDS clocks divided or multiplied by 1,001 coefficients are not supported."
and the table lists values for TMDS clocks of 25.2, 27, 54, 74.25, 148.5 and 297MHz.
This will need to be tested on iMX6 to validate whether it works. If it doesn't work, we need to conditionalise the new TMDS clocks with the IP version.
I just want to add audio support for some No-CEA display resolutions, and actually it can works on rk3288 platform. If it can not work on iMX6 platform, i will add IP verison select in next version v3.
Thanks. : )
If the monitor support audio, so we should support audio for it, even if the display resolution is No-CEA mode.
Signed-off-by: Yakir Yang ykk@rock-chips.com --- Changes in v2: - enable audio support for No-CEA display mode
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 e07723c..8cc7b3d 100644 --- a/drivers/gpu/drm/bridge/dw_hdmi.c +++ b/drivers/gpu/drm/bridge/dw_hdmi.c @@ -1286,13 +1286,10 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
hdmi->vic = drm_match_cea_mode(mode);
- if (!hdmi->vic) { + if (hdmi->hdmi_data.video_mode.mdvi) dev_dbg(hdmi->dev, "Non-CEA mode used in HDMI\n"); - hdmi->hdmi_data.video_mode.mdvi = true; - } else { + 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) || (hdmi->vic == 21) || (hdmi->vic == 22) || @@ -1496,6 +1493,7 @@ static int dw_hdmi_connector_get_modes(struct drm_connector *connector) { struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi, connector); + struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode; struct edid *edid; int ret;
@@ -1507,6 +1505,8 @@ 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);
+ vmode->mdvi = !drm_detect_hdmi_monitor(edid); + drm_mode_connector_update_edid_property(connector, edid); ret = drm_add_edid_modes(connector, edid); kfree(edid);
On Fri, Jan 30, 2015 at 06:31:33AM -0500, Yakir Yang wrote:
If the monitor support audio, so we should support audio for it, even if the display resolution is No-CEA mode.
I can't find where it was documented at the moment, but I seem to remember reading somewhere that the iMX6 SoC version of this IP doesn't support audio with non-CEA modes - though, maybe that's a result of the limited TMDS clocks which are supported.
This is a change which will need testing on iMX6.
Designware HDMI supports four interfaces to config hdmi audio (I2S, S/PDIF, Generic Parallel Audio, AHB Audio DMA), but rk3288 only support two ways to config hdmi audio(I2S, S/PDIF), So we take I2S as hdmi audio operation interfaces.
Signed-off-by: Yakir Yang ykk@rock-chips.com --- Changes in v2: - Add audio config interfaces to dw_hdmi driver
drivers/gpu/drm/bridge/dw_hdmi.c | 70 +++++++++++++++++++++++++++++++++------- drivers/gpu/drm/bridge/dw_hdmi.h | 6 ++++ include/drm/bridge/dw_hdmi.h | 30 +++++++++++++++++ 3 files changed, 95 insertions(+), 11 deletions(-)
diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c index 8cc7b3d..25c1678 100644 --- a/drivers/gpu/drm/bridge/dw_hdmi.c +++ b/drivers/gpu/drm/bridge/dw_hdmi.c @@ -16,6 +16,7 @@ #include <linux/err.h> #include <linux/clk.h> #include <linux/hdmi.h> +#include <linux/mutex.h> #include <linux/of_device.h>
#include <drm/drm_of.h> @@ -126,7 +127,10 @@ struct dw_hdmi { struct i2c_adapter *ddc; void __iomem *regs;
- unsigned int sample_rate; + struct hdmi_audio_fmt aud_fmt; + struct mutex audio_mutex; + bool audio_enable; + int ratio;
void (*write)(struct dw_hdmi *hdmi, u8 val, int offset); @@ -208,7 +212,7 @@ static void hdmi_set_schnl(struct dw_hdmi *hdmi) { u8 aud_schnl_samplerate;
- switch (hdmi->sample_rate) { + switch (hdmi->aud_fmt.sample_rate) { case 32000: aud_schnl_samplerate = HDMI_FC_AUDSCHNLS7_SMPRATE_32K; break; @@ -444,9 +448,9 @@ static void hdmi_set_clk_regenerator(struct dw_hdmi *hdmi, { unsigned int clk_n, clk_cts;
- clk_n = hdmi_compute_n(hdmi->sample_rate, pixel_clk, + clk_n = hdmi_compute_n(hdmi->aud_fmt.sample_rate, pixel_clk, hdmi->ratio); - clk_cts = hdmi_compute_cts(hdmi->sample_rate, pixel_clk, + clk_cts = hdmi_compute_cts(hdmi->aud_fmt.sample_rate, pixel_clk, hdmi->ratio);
if (!clk_cts) { @@ -456,7 +460,7 @@ static void hdmi_set_clk_regenerator(struct dw_hdmi *hdmi, }
dev_dbg(hdmi->dev, "%s: samplerate=%d ratio=%d pixelclk=%lu N=%d cts=%d\n", - __func__, hdmi->sample_rate, hdmi->ratio, + __func__, hdmi->aud_fmt.sample_rate, hdmi->ratio, pixel_clk, clk_n, clk_cts);
hdmi_regenerate_n_cts(hdmi, clk_n, clk_cts); @@ -1023,6 +1027,25 @@ static void hdmi_tx_hdcp_config(struct dw_hdmi *hdmi) HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_MASK, HDMI_A_HDCPCFG1); }
+static void hdmi_config_audio(struct dw_hdmi *hdmi, + struct hdmi_audio_fmt *aud_fmt) +{ + if (aud_fmt) + hdmi->aud_fmt = *aud_fmt; + + hdmi_modb(hdmi, AUDIO_CONF0_INTERFACE_II2S, + AUDIO_CONF0_INTERFACE_MSK, HDMI_AUD_CONF0); + + hdmi_modb(hdmi, hdmi->aud_fmt.chan_num, AUDIO_CONF0_I2SINEN_MSK, + HDMI_AUD_CONF0); + + hdmi_modb(hdmi, hdmi->aud_fmt.word_length, AUDIO_CONF1_DATWIDTH_MSK, + HDMI_AUD_CONF1); + + hdmi_modb(hdmi, hdmi->aud_fmt.dai_fmt, AUDIO_CONF1_DATAMODE_MSK, + HDMI_AUD_CONF1); +} + static void hdmi_config_AVI(struct dw_hdmi *hdmi) { u8 val, pix_fmt, under_scan; @@ -1212,6 +1235,34 @@ static void dw_hdmi_phy_disable(struct dw_hdmi *hdmi) hdmi->phy_enabled = false; }
+void hdmi_audio_clk_enable(struct dw_hdmi *hdmi) +{ + if (hdmi->audio_enable) + return; + + mutex_lock(&hdmi->audio_mutex); + hdmi->audio_enable = true; + hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS); + mutex_unlock(&hdmi->audio_mutex); +} + +void hdmi_audio_clk_disable(struct dw_hdmi *hdmi) +{ + if (!hdmi->audio_enable) + return; + + mutex_lock(&hdmi->audio_mutex); + hdmi_modb(hdmi, HDMI_MC_CLKDIS_AUDCLK_DISABLE, + HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS); + hdmi->audio_enable = false; + mutex_unlock(&hdmi->audio_mutex); +} + +bool hdmi_get_connect_status(struct dw_hdmi *hdmi) +{ + return (hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD) ? true : false; +} + /* HDMI Initialization Step B.4 */ static void dw_hdmi_enable_video_path(struct dw_hdmi *hdmi) { @@ -1240,11 +1291,8 @@ static void dw_hdmi_enable_video_path(struct dw_hdmi *hdmi) clkdis &= ~HDMI_MC_CLKDIS_CSCCLK_DISABLE; hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS); } -}
-static void hdmi_enable_audio_clk(struct dw_hdmi *hdmi) -{ - hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS); + hdmi_audio_clk_disable(hdmi); }
/* Workaround to clear the overflow condition */ @@ -1342,7 +1390,7 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
/* HDMI Initialization Step E - Configure audio */ hdmi_clk_regenerator_update_pixel_clock(hdmi); - hdmi_enable_audio_clk(hdmi); + hdmi_audio_clk_enable(hdmi);
/* HDMI Initialization Step F - Configure AVI InfoFrame */ hdmi_config_AVI(hdmi); @@ -1669,7 +1717,7 @@ int dw_hdmi_bind(struct device *dev, struct device *master, hdmi->plat_data = plat_data; hdmi->dev = dev; hdmi->dev_type = plat_data->dev_type; - hdmi->sample_rate = 48000; + hdmi->aud_fmt.sample_rate = 48000; hdmi->ratio = 100; hdmi->encoder = encoder;
diff --git a/drivers/gpu/drm/bridge/dw_hdmi.h b/drivers/gpu/drm/bridge/dw_hdmi.h index 603e645..3ab14b6 100644 --- a/drivers/gpu/drm/bridge/dw_hdmi.h +++ b/drivers/gpu/drm/bridge/dw_hdmi.h @@ -904,6 +904,12 @@ enum { HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL = 0x08, HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_MASK = 0x04,
+/* AUD_CONF0 field values */ + AUDIO_CONF0_INTERFACE_MSK = 0x20, + AUDIO_CONF0_INTERFACE_II2S = 0x20, + AUDIO_CONF0_INTERFACE_SPDIF = 0x00, + AUDIO_CONF0_INTERFACE_GPA = 0x00, + /* AUD_N3 field values */ HDMI_AUD_N3_NCTS_ATOMIC_WRITE_MASK = 0x80, HDMI_AUD_N3_NCTS_ATOMIC_WRITE_SET = 0x80, diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h index 8476cfc..aafa447 100644 --- a/include/drm/bridge/dw_hdmi.h +++ b/include/drm/bridge/dw_hdmi.h @@ -44,6 +44,36 @@ struct dw_hdmi_sym_term { u16 term; /*transmission termination value*/ };
+enum hdmi_audio_wordlength { + AUDIO_WORDLENGTH_16BIT = 16, + AUDIO_WORDLENGTH_24BIT = 24, + AUDIO_CONF1_DATWIDTH_MSK = 0x1F, +}; + +enum hdmi_audio_daifmt { + AUDIO_DAIFMT_IIS = 0x00, + AUDIO_DAIFMT_RIGHT_J = 0x20, + AUDIO_DAIFMT_LEFT_J = 0x40, + AUDIO_DAIFMT_BURST_1 = 0x60, + AUDIO_DAIFMT_BURST_2 = 0x80, + AUDIO_CONF1_DATAMODE_MSK = 0xE0, +}; + +enum hdmi_audio_channelnum { + AUDIO_CHANNELNUM_2 = 0x01, + AUDIO_CHANNELNUM_4 = 0x03, + AUDIO_CHANNELNUM_6 = 0x07, + AUDIO_CHANNELNUM_8 = 0x0F, + AUDIO_CONF0_I2SINEN_MSK = 0x0F, +}; + +struct hdmi_audio_fmt { + unsigned int sample_rate; + enum hdmi_audio_daifmt dai_fmt; + enum hdmi_audio_channelnum chan_num; + enum hdmi_audio_wordlength word_length; +}; + struct dw_hdmi_plat_data { enum dw_hdmi_devtype dev_type; const struct dw_hdmi_mpll_config *mpll_cfg;
On Fri, Jan 30, 2015 at 06:32:23AM -0500, Yakir Yang wrote:
+static void hdmi_config_audio(struct dw_hdmi *hdmi,
struct hdmi_audio_fmt *aud_fmt)
+{
- if (aud_fmt)
hdmi->aud_fmt = *aud_fmt;
- hdmi_modb(hdmi, AUDIO_CONF0_INTERFACE_II2S,
AUDIO_CONF0_INTERFACE_MSK, HDMI_AUD_CONF0);
- hdmi_modb(hdmi, hdmi->aud_fmt.chan_num, AUDIO_CONF0_I2SINEN_MSK,
HDMI_AUD_CONF0);
- hdmi_modb(hdmi, hdmi->aud_fmt.word_length, AUDIO_CONF1_DATWIDTH_MSK,
HDMI_AUD_CONF1);
- hdmi_modb(hdmi, hdmi->aud_fmt.dai_fmt, AUDIO_CONF1_DATAMODE_MSK,
HDMI_AUD_CONF1);
These registers are not defined on iMX6 SoCs, so this probably needs to be conditionalised with the dw-hdmi IP version.
+void hdmi_audio_clk_enable(struct dw_hdmi *hdmi) +{
- if (hdmi->audio_enable)
return;
- mutex_lock(&hdmi->audio_mutex);
- hdmi->audio_enable = true;
- hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS);
- mutex_unlock(&hdmi->audio_mutex);
This is racy. The test needs to be within the mutex-protected region.
+}
+void hdmi_audio_clk_disable(struct dw_hdmi *hdmi) +{
- if (!hdmi->audio_enable)
return;
- mutex_lock(&hdmi->audio_mutex);
- hdmi_modb(hdmi, HDMI_MC_CLKDIS_AUDCLK_DISABLE,
HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS);
- hdmi->audio_enable = false;
- mutex_unlock(&hdmi->audio_mutex);
Ditto.
On 01/31/2015 06:48 AM, Russell King - ARM Linux wrote:
On Fri, Jan 30, 2015 at 06:32:23AM -0500, Yakir Yang wrote:
+static void hdmi_config_audio(struct dw_hdmi *hdmi,
struct hdmi_audio_fmt *aud_fmt)
+{
- if (aud_fmt)
hdmi->aud_fmt = *aud_fmt;
- hdmi_modb(hdmi, AUDIO_CONF0_INTERFACE_II2S,
AUDIO_CONF0_INTERFACE_MSK, HDMI_AUD_CONF0);
- hdmi_modb(hdmi, hdmi->aud_fmt.chan_num, AUDIO_CONF0_I2SINEN_MSK,
HDMI_AUD_CONF0);
- hdmi_modb(hdmi, hdmi->aud_fmt.word_length, AUDIO_CONF1_DATWIDTH_MSK,
HDMI_AUD_CONF1);
- hdmi_modb(hdmi, hdmi->aud_fmt.dai_fmt, AUDIO_CONF1_DATAMODE_MSK,
HDMI_AUD_CONF1);
These registers are not defined on iMX6 SoCs, so this probably needs to be conditionalised with the dw-hdmi IP version.
okay, i will fixed what you have suggest in cover-letter first.
Thanks. : )
+void hdmi_audio_clk_enable(struct dw_hdmi *hdmi) +{
- if (hdmi->audio_enable)
return;
- mutex_lock(&hdmi->audio_mutex);
- hdmi->audio_enable = true;
- hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS);
- mutex_unlock(&hdmi->audio_mutex);
This is racy. The test needs to be within the mutex-protected region.
This function will be called by other driver (dw-hdmi-audio), both modify the variable "hdmi->audio_enable", so i add the mutex-protected.
+}
+void hdmi_audio_clk_disable(struct dw_hdmi *hdmi) +{
- if (!hdmi->audio_enable)
return;
- mutex_lock(&hdmi->audio_mutex);
- hdmi_modb(hdmi, HDMI_MC_CLKDIS_AUDCLK_DISABLE,
HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS);
- hdmi->audio_enable = false;
- mutex_unlock(&hdmi->audio_mutex);
Ditto.
Hi ykk,
On Sat, Jan 31, 2015 at 10:34 PM, Yang Kuankuan ykk@rock-chips.com wrote:
On 01/31/2015 06:48 AM, Russell King - ARM Linux wrote:
+void hdmi_audio_clk_enable(struct dw_hdmi *hdmi) +{
if (hdmi->audio_enable)
return;
mutex_lock(&hdmi->audio_mutex);
hdmi->audio_enable = true;
hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE,
HDMI_MC_CLKDIS);
mutex_unlock(&hdmi->audio_mutex);
This is racy. The test needs to be within the mutex-protected region.
This function will be called by other driver (dw-hdmi-audio), both modify the variable "hdmi->audio_enable", so i add the mutex-protected.
From your comment it isn't clear whether you understand what Russell meant.
He is say you should do the following:
{ mutex_lock(&hdmi->audio_mutex);
if (hdmi->audio_enable) { mutex_unlock(&hdmi->audio_mutex); return; } hdmi->audio_enable = true; hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS);
mutex_unlock(&hdmi->audio_mutex); }
By the way, it doesn't matter that the function is called from another driver. What matters is that this function can be called concurrently on multiple different threads of execution to change the hdmi audio enable state.
From DRM land, it is called with DRM lock held when enabling/disabling
hdmi audio (mode_set / DPMS). It is also called from audio land, when enabling/disabling audio in response to some audio events (userspace ioctls?). I'm not sure exactly how the audio side works, or what locks are involved, but this mutex synchronizes calls from these two worlds to ensure that "hdmi->audio_enable" field always matches the current (intended) status of the hdmi audio clock. This would be useful, for example, if you needed to temporarily disable all HDMI clocks during a mode set, and then restore the audio clock to its pre-mode_set state:
// temporarily disable hdmi audio clk dw_hdmi_audio_clk_disable(hdmi); // do mode_set ... dw_hdmi_audio_clk_reenable(hdmi);
static void dw_hdmi_audio_clk_reenable() { mutex_lock() if (hdmi->audio_enable) dw_hdmi_audio_clk_enable(hdmi) mutex_unlock() }
However, AFAICT, the "hdmi->audio_enable" field is never actually used like this here or in later patches, so it and the mutex do not seem to actually be doing anything useful. In this patch it is probably better to just drop the mutex and audio_enable, and add them as a preparatory patch in the patch set where they will actually be used.
-Dan
On Mon, Feb 02, 2015 at 12:02:50PM +0800, Daniel Kurtz wrote:
Hi ykk,
On Sat, Jan 31, 2015 at 10:34 PM, Yang Kuankuan ykk@rock-chips.com wrote:
On 01/31/2015 06:48 AM, Russell King - ARM Linux wrote:
+void hdmi_audio_clk_enable(struct dw_hdmi *hdmi) +{
if (hdmi->audio_enable)
return;
mutex_lock(&hdmi->audio_mutex);
hdmi->audio_enable = true;
hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE,
HDMI_MC_CLKDIS);
mutex_unlock(&hdmi->audio_mutex);
This is racy. The test needs to be within the mutex-protected region.
This function will be called by other driver (dw-hdmi-audio), both modify the variable "hdmi->audio_enable", so i add the mutex-protected.
From your comment it isn't clear whether you understand what Russell meant.
He is say you should do the following:
{ mutex_lock(&hdmi->audio_mutex);
if (hdmi->audio_enable) { mutex_unlock(&hdmi->audio_mutex); return; } hdmi->audio_enable = true; hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS); mutex_unlock(&hdmi->audio_mutex);
}
Yes, however, I prefer this kind of layout:
mutex_lock(&hdmi->audio_mutex);
if (!hdmi->audio_enable) { hdmi->audio_enable = true; hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS); }
mutex_unlock(&hdmi->audio_mutex);
but that's a matter of personal opinion. The important thing is that the testing and setting of the flag are both within the protected region.
However, there are other bugs here: what if the audio driver is calling the sample rate setting function at the same time that a mode switch is occuring. We actually need a mutex to protect more than just the audio_enable flag.
By the way, it doesn't matter that the function is called from another driver. What matters is that this function can be called concurrently on multiple different threads of execution to change the hdmi audio enable state.
From DRM land, it is called with DRM lock held when enabling/disabling
hdmi audio (mode_set / DPMS). It is also called from audio land, when enabling/disabling audio in response to some audio events (userspace ioctls?). I'm not sure exactly how the audio side works, or what locks are involved, but this mutex synchronizes calls from these two worlds to ensure that "hdmi->audio_enable" field always matches the current (intended) status of the hdmi audio clock. This would be useful, for example, if you needed to temporarily disable all HDMI clocks during a mode set, and then restore the audio clock to its pre-mode_set state:
There's some rather quirky comments in the driver right now which make me uneasy about changing things too much.
My initial idea would be that audio should remain disabled until the audio driver wants it enabled, and the CTS/N values should either be left alone, or set to a value which disables them (there is an iMX6 errata which says to set N=0 initially, but as seems common with iMX6 errata, I see no code implementing the method specified in the documentation - I have found code implementing something similar though.)
However, there is this in the binding function:
/* * To prevent overflows in HDMI_IH_FC_STAT2, set the clk regenerator * N and cts values before enabling phy */ hdmi_init_clk_regenerator(hdmi);
which sets the N/CTS values assuming a 74.25MHz video clock and a 48kHz sample rate. I've always wondered why this is necessary (I haven't experimented with that yet.)
Then there's this in the mode set function:
/* HDMI Initialization Step E - Configure audio */ hdmi_clk_regenerator_update_pixel_clock(hdmi); hdmi_enable_audio_clk(hdmi);
Where these "steps" come from, I've no idea (I can't find any documentation which specifies them - maybe its from the Synopsis documentation?) but this has always raised the question "what if audio is not enabled?"
On 02/02/2015 06:53 AM, Russell King - ARM Linux wrote:
On Mon, Feb 02, 2015 at 12:02:50PM +0800, Daniel Kurtz wrote:
Hi ykk,
On Sat, Jan 31, 2015 at 10:34 PM, Yang Kuankuan ykk@rock-chips.com wrote:
On 01/31/2015 06:48 AM, Russell King - ARM Linux wrote:
+void hdmi_audio_clk_enable(struct dw_hdmi *hdmi) +{
if (hdmi->audio_enable)
return;
mutex_lock(&hdmi->audio_mutex);
hdmi->audio_enable = true;
hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE,
HDMI_MC_CLKDIS);
mutex_unlock(&hdmi->audio_mutex);
This is racy. The test needs to be within the mutex-protected region.
This function will be called by other driver (dw-hdmi-audio), both modify the variable "hdmi->audio_enable", so i add the mutex-protected. From your comment it isn't clear whether you understand what Russell meant.
He is say you should do the following:
{ mutex_lock(&hdmi->audio_mutex);
if (hdmi->audio_enable) { mutex_unlock(&hdmi->audio_mutex); return; } hdmi->audio_enable = true; hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS); mutex_unlock(&hdmi->audio_mutex);
}
Yes, however, I prefer this kind of layout:
mutex_lock(&hdmi->audio_mutex);
if (!hdmi->audio_enable) { hdmi->audio_enable = true; hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS); }
mutex_unlock(&hdmi->audio_mutex);
but that's a matter of personal opinion. The important thing is that the testing and setting of the flag are both within the protected region.
However, there are other bugs here: what if the audio driver is calling the sample rate setting function at the same time that a mode switch is occuring. We actually need a mutex to protect more than just the audio_enable flag.
Okay, got it.
Thanks. : )
By the way, it doesn't matter that the function is called from another driver. What matters is that this function can be called concurrently on multiple different threads of execution to change the hdmi audio enable state.
From DRM land, it is called with DRM lock held when enabling/disabling
hdmi audio (mode_set / DPMS). It is also called from audio land, when enabling/disabling audio in response to some audio events (userspace ioctls?). I'm not sure exactly how the audio side works, or what locks are involved, but this mutex synchronizes calls from these two worlds to ensure that "hdmi->audio_enable" field always matches the current (intended) status of the hdmi audio clock. This would be useful, for example, if you needed to temporarily disable all HDMI clocks during a mode set, and then restore the audio clock to its pre-mode_set state:
There's some rather quirky comments in the driver right now which make me uneasy about changing things too much.
My initial idea would be that audio should remain disabled until the audio driver wants it enabled, and the CTS/N values should either be left alone, or set to a value which disables them (there is an iMX6 errata which says to set N=0 initially, but as seems common with iMX6 errata, I see no code implementing the method specified in the documentation - I have found code implementing something similar though.)
I am confused with the way that audio driver realize the display resolution-changing. If audio driver cannot knowing that, then audio clock may be closed permanently ?
However, there is this in the binding function:
/* * To prevent overflows in HDMI_IH_FC_STAT2, set the clk regenerator * N and cts values before enabling phy */ hdmi_init_clk_regenerator(hdmi);
which sets the N/CTS values assuming a 74.25MHz video clock and a 48kHz sample rate. I've always wondered why this is necessary (I haven't experimented with that yet.)
Then there's this in the mode set function:
/* HDMI Initialization Step E - Configure audio */ hdmi_clk_regenerator_update_pixel_clock(hdmi); hdmi_enable_audio_clk(hdmi);
Where these "steps" come from, I've no idea (I can't find any documentation which specifies them - maybe its from the Synopsis documentation?) but this has always raised the question "what if audio is not enabled?"
static void hdmi_keep_audio_clk_status(struct dw_hdmi *hdmi) { mutex_lock(&hdmi->audio_mutex);
if (hdmi->audio_enable) hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS); else hdmi_modb(hdmi, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS);
mutex_unlock(&hdmi->audio_mutex); }
/* HDMI Initialization Step E - Configure audio */ hdmi_clk_regenerator_update_pixel_clock(hdmi); hdmi_keep_audio_clk_status(hdmi);
keep audio status maybe suitable here.
On Mon, Feb 02, 2015 at 07:32:05AM -0500, Yang Kuankuan wrote:
On 02/02/2015 06:53 AM, Russell King - ARM Linux wrote:
On Mon, Feb 02, 2015 at 12:02:50PM +0800, Daniel Kurtz wrote:
Hi ykk,
On Sat, Jan 31, 2015 at 10:34 PM, Yang Kuankuan ykk@rock-chips.com wrote:
On 01/31/2015 06:48 AM, Russell King - ARM Linux wrote:
+void hdmi_audio_clk_enable(struct dw_hdmi *hdmi) +{
if (hdmi->audio_enable)
return;
mutex_lock(&hdmi->audio_mutex);
hdmi->audio_enable = true;
hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE,
HDMI_MC_CLKDIS);
mutex_unlock(&hdmi->audio_mutex);
This is racy. The test needs to be within the mutex-protected region.
This function will be called by other driver (dw-hdmi-audio), both modify the variable "hdmi->audio_enable", so i add the mutex-protected. From your comment it isn't clear whether you understand what Russell meant.
He is say you should do the following:
{ mutex_lock(&hdmi->audio_mutex);
if (hdmi->audio_enable) { mutex_unlock(&hdmi->audio_mutex); return; } hdmi->audio_enable = true; hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS); mutex_unlock(&hdmi->audio_mutex);
}
Yes, however, I prefer this kind of layout:
mutex_lock(&hdmi->audio_mutex); if (!hdmi->audio_enable) { hdmi->audio_enable = true; hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS); }
mutex_unlock(&hdmi->audio_mutex);
but that's a matter of personal opinion. The important thing is that the testing and setting of the flag are both within the protected region.
However, there are other bugs here: what if the audio driver is calling the sample rate setting function at the same time that a mode switch is occuring. We actually need a mutex to protect more than just the audio_enable flag.
Okay, got it.
Thanks. : )
I've been moving my code closer to what you have posted. I've split up your patches into smaller steps so each change can be evaluated on iMX6. So far:
drm: bridge/dw_hdmi: combine hdmi_set_clock_regenerator_n() and hdmi_regenerate_cts()
This patch _just_ combines the two functions without any other changes.
drm: bridge/dw_hdmi: protect n/cts setting with a mutex
This patch _just_ adds a mutex to protect the calls to hdmi_set_clk_regenerator(), since we will need to call that from both the DRM and audio drivers.
drm: bridge/dw_hdmi: adjust n/cts setting order
This patch changes the order to: - write CTS3 CTS_manual = 0 - write CTS3 N_shift = 0 - write CTS3 CTS value - write CTS2 CTS value - write CTS1 CTS value - write N3 N value - write N2 N value - write N1 N value which is broadly what you're doing, but without the initial N3 write and without CTS_manual=1. I've asked Freescale whether they can clarify what effect adding those would have on their SoCs.
Note: the mutex in my second patch needs to be a spinlock - as it seems my new workaround for iMX6 ERR005174 needs to call the CTS/N setting functions in an irqs-off region (from the ALSA ->trigger callback.) That will need further rework of the CTS/N code to reduce the size of the spinlock protected region.
Incidentally, your Synopsis IP indicates that it is the same revision as the iMX6's IP revision which suffers from this ERR005174 errata. I think you need to check whether this errata applies on your SoC too. Seach for "iMX6 ERR005174" in google.
By the way, it doesn't matter that the function is called from another driver. What matters is that this function can be called concurrently on multiple different threads of execution to change the hdmi audio enable state.
From DRM land, it is called with DRM lock held when enabling/disabling
hdmi audio (mode_set / DPMS). It is also called from audio land, when enabling/disabling audio in response to some audio events (userspace ioctls?). I'm not sure exactly how the audio side works, or what locks are involved, but this mutex synchronizes calls from these two worlds to ensure that "hdmi->audio_enable" field always matches the current (intended) status of the hdmi audio clock. This would be useful, for example, if you needed to temporarily disable all HDMI clocks during a mode set, and then restore the audio clock to its pre-mode_set state:
There's some rather quirky comments in the driver right now which make me uneasy about changing things too much.
My initial idea would be that audio should remain disabled until the audio driver wants it enabled, and the CTS/N values should either be left alone, or set to a value which disables them (there is an iMX6 errata which says to set N=0 initially, but as seems common with iMX6 errata, I see no code implementing the method specified in the documentation - I have found code implementing something similar though.)
I am confused with the way that audio driver realize the display resolution-changing. If audio driver cannot knowing that, then audio clock may be closed permanently ?
The audio driver shouldn't care about the display resolution. That should be the responsibility of the dw_hdmi core - as it is at the moment.
static void hdmi_keep_audio_clk_status(struct dw_hdmi *hdmi) { mutex_lock(&hdmi->audio_mutex);
if (hdmi->audio_enable) hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS); else hdmi_modb(hdmi, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS); mutex_unlock(&hdmi->audio_mutex);
}
/* HDMI Initialization Step E - Configure audio */ hdmi_clk_regenerator_update_pixel_clock(hdmi); hdmi_keep_audio_clk_status(hdmi);
keep audio status maybe suitable here.
What I don't know right now is what triggers this overflow indication in HDMI_IH_FC_STAT2, and whether the code I quoted (which fakes the audio setup) is actually necessary.
In other words: - is it necessary to have the audio clock enabled when video is enabled to prevent the overflow indication? We don't know. Therefore, we can't say whether it is permitted to disable the audio clock when audio is inactive. - is it necessary to program a dummy CTS/N value for 74.25MHz/48kHz, or can we program a non-zero CTS value and a zero N at initialisation until the audio driver comes up? Again, we don't know.
What we do know is that as the driver stands, it works for video output. With my changes for AHB audio support on the iMX6 (which includes some errata workarounds found in the iMX6 errata documentation to avoid FIFO issues), we have working audio there.
On 02/02/2015 08:09 AM, Russell King - ARM Linux wrote:
On Mon, Feb 02, 2015 at 07:32:05AM -0500, Yang Kuankuan wrote:
On 02/02/2015 06:53 AM, Russell King - ARM Linux wrote:
On Mon, Feb 02, 2015 at 12:02:50PM +0800, Daniel Kurtz wrote:
Hi ykk,
On Sat, Jan 31, 2015 at 10:34 PM, Yang Kuankuan ykk@rock-chips.com wrote:
On 01/31/2015 06:48 AM, Russell King - ARM Linux wrote:
> +void hdmi_audio_clk_enable(struct dw_hdmi *hdmi) > +{ > + if (hdmi->audio_enable) > + return; > + > + mutex_lock(&hdmi->audio_mutex); > + hdmi->audio_enable = true; > + hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, > HDMI_MC_CLKDIS); > + mutex_unlock(&hdmi->audio_mutex); This is racy. The test needs to be within the mutex-protected region.
This function will be called by other driver (dw-hdmi-audio), both modify the variable "hdmi->audio_enable", so i add the mutex-protected. From your comment it isn't clear whether you understand what Russell meant.
He is say you should do the following:
{ mutex_lock(&hdmi->audio_mutex);
if (hdmi->audio_enable) { mutex_unlock(&hdmi->audio_mutex); return; } hdmi->audio_enable = true; hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS); mutex_unlock(&hdmi->audio_mutex);
}
Yes, however, I prefer this kind of layout:
mutex_lock(&hdmi->audio_mutex); if (!hdmi->audio_enable) { hdmi->audio_enable = true; hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS); }
mutex_unlock(&hdmi->audio_mutex);
but that's a matter of personal opinion. The important thing is that the testing and setting of the flag are both within the protected region.
However, there are other bugs here: what if the audio driver is calling the sample rate setting function at the same time that a mode switch is occuring. We actually need a mutex to protect more than just the audio_enable flag.
Okay, got it.
Thanks. : )
I've been moving my code closer to what you have posted. I've split up your patches into smaller steps so each change can be evaluated on iMX6. So far:
Thank you very much. : )
drm: bridge/dw_hdmi: combine hdmi_set_clock_regenerator_n() and hdmi_regenerate_cts()
This patch _just_ combines the two functions without any other changes.
drm: bridge/dw_hdmi: protect n/cts setting with a mutex
This patch _just_ adds a mutex to protect the calls to hdmi_set_clk_regenerator(), since we will need to call that from both the DRM and audio drivers.
drm: bridge/dw_hdmi: adjust n/cts setting order
This patch changes the order to:
- write CTS3 CTS_manual = 0
- write CTS3 N_shift = 0
- write CTS3 CTS value
- write CTS2 CTS value
- write CTS1 CTS value
- write N3 N value
- write N2 N value
- write N1 N value
which is broadly what you're doing, but without the initial N3 write and without CTS_manual=1. I've asked Freescale whether they can clarify what effect adding those would have on their SoCs.
Note: the mutex in my second patch needs to be a spinlock - as it seems my new workaround for iMX6 ERR005174 needs to call the CTS/N setting functions in an irqs-off region (from the ALSA ->trigger callback.) That will need further rework of the CTS/N code to reduce the size of the spinlock protected region.
Incidentally, your Synopsis IP indicates that it is the same revision as the iMX6's IP revision which suffers from this ERR005174 errata. I think you need to check whether this errata applies on your SoC too. Seach for "iMX6 ERR005174" in google.
By the way, it doesn't matter that the function is called from another driver. What matters is that this function can be called concurrently on multiple different threads of execution to change the hdmi audio enable state.
From DRM land, it is called with DRM lock held when enabling/disabling
hdmi audio (mode_set / DPMS). It is also called from audio land, when enabling/disabling audio in response to some audio events (userspace ioctls?). I'm not sure exactly how the audio side works, or what locks are involved, but this mutex synchronizes calls from these two worlds to ensure that "hdmi->audio_enable" field always matches the current (intended) status of the hdmi audio clock. This would be useful, for example, if you needed to temporarily disable all HDMI clocks during a mode set, and then restore the audio clock to its pre-mode_set state:
There's some rather quirky comments in the driver right now which make me uneasy about changing things too much.
My initial idea would be that audio should remain disabled until the audio driver wants it enabled, and the CTS/N values should either be left alone, or set to a value which disables them (there is an iMX6 errata which says to set N=0 initially, but as seems common with iMX6 errata, I see no code implementing the method specified in the documentation - I have found code implementing something similar though.)
I am confused with the way that audio driver realize the display resolution-changing. If audio driver cannot knowing that, then audio clock may be closed permanently ?
The audio driver shouldn't care about the display resolution. That should be the responsibility of the dw_hdmi core - as it is at the moment.
Do you mean that we should disable audio clock and deinit the n/cts values, until we meet the audio enable single like this.
if (hdmi->vmode.mdiv) { /* HDMI Initialization Step E - Configure audio */ hdmi_clk_regenerator_update_pixel_clock(hdmi); hdmi_enable_audio_clk(hdmi); }
static void hdmi_keep_audio_clk_status(struct dw_hdmi *hdmi) { mutex_lock(&hdmi->audio_mutex);
if (hdmi->audio_enable) hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS); else hdmi_modb(hdmi, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS); mutex_unlock(&hdmi->audio_mutex);
}
/* HDMI Initialization Step E - Configure audio */ hdmi_clk_regenerator_update_pixel_clock(hdmi); hdmi_keep_audio_clk_status(hdmi);
keep audio status maybe suitable here.
What I don't know right now is what triggers this overflow indication in HDMI_IH_FC_STAT2, and whether the code I quoted (which fakes the audio setup) is actually necessary.
In other words:
- is it necessary to have the audio clock enabled when video is enabled to prevent the overflow indication? We don't know. Therefore, we can't say whether it is permitted to disable the audio clock when audio is inactive.
- is it necessary to program a dummy CTS/N value for 74.25MHz/48kHz, or can we program a non-zero CTS value and a zero N at initialisation until the audio driver comes up? Again, we don't know.
What we do know is that as the driver stands, it works for video output. With my changes for AHB audio support on the iMX6 (which includes some errata workarounds found in the iMX6 errata documentation to avoid FIFO issues), we have working audio there.
I don't know the effect of overflow indication in HDMI_IH_FC_STAT2, seems the irq function have not handle the FC_STAT2 interrupt in dw_hdmi driver, also not found in your dw-hdmi-audio driver.
But I will talk with our IC colleges, - is it necessary to have the audio clock enabled when video is enabled to prevent the overflow indication ? If it is not necessary, maybe we can keep the audio status in mode_set.
- Also is it necessary to program a dummy CTS/N value for 74.25MHz/48kHz, or can we program a non-zero CTS value and a zero N at initialisation until the audio driver comes up
However, there is this in the binding function:
- To prevent overflows in HDMI_IH_FC_STAT2, set the clk regenerator
- N and cts values before enabling phy
*/ hdmi_init_clk_regenerator(hdmi);
which sets the N/CTS values assuming a 74.25MHz video clock and a 48kHz sample rate. I've always wondered why this is necessary (I haven't experimented with that yet.)
Then there's this in the mode set function:
/* HDMI Initialization Step E - Configure audio */ hdmi_clk_regenerator_update_pixel_clock(hdmi); hdmi_enable_audio_clk(hdmi);
Where these "steps" come from, I've no idea (I can't find any documentation which specifies them - maybe its from the Synopsis documentation?) but this has always raised the question "what if audio is not enabled?"
On 02/02/2015 08:09 AM, Russell King - ARM Linux wrote:
On Mon, Feb 02, 2015 at 07:32:05AM -0500, Yang Kuankuan wrote:
On 02/02/2015 06:53 AM, Russell King - ARM Linux wrote:
On Mon, Feb 02, 2015 at 12:02:50PM +0800, Daniel Kurtz wrote:
Hi ykk,
On Sat, Jan 31, 2015 at 10:34 PM, Yang Kuankuan ykk@rock-chips.com wrote:
On 01/31/2015 06:48 AM, Russell King - ARM Linux wrote:
> +void hdmi_audio_clk_enable(struct dw_hdmi *hdmi) > +{ > + if (hdmi->audio_enable) > + return; > + > + mutex_lock(&hdmi->audio_mutex); > + hdmi->audio_enable = true; > + hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, > HDMI_MC_CLKDIS); > + mutex_unlock(&hdmi->audio_mutex); This is racy. The test needs to be within the mutex-protected region.
This function will be called by other driver (dw-hdmi-audio), both modify the variable "hdmi->audio_enable", so i add the mutex-protected. From your comment it isn't clear whether you understand what Russell meant.
He is say you should do the following:
{ mutex_lock(&hdmi->audio_mutex);
if (hdmi->audio_enable) { mutex_unlock(&hdmi->audio_mutex); return; } hdmi->audio_enable = true; hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS); mutex_unlock(&hdmi->audio_mutex);
}
Yes, however, I prefer this kind of layout:
mutex_lock(&hdmi->audio_mutex); if (!hdmi->audio_enable) { hdmi->audio_enable = true; hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS); }
mutex_unlock(&hdmi->audio_mutex);
but that's a matter of personal opinion. The important thing is that the testing and setting of the flag are both within the protected region.
However, there are other bugs here: what if the audio driver is calling the sample rate setting function at the same time that a mode switch is occuring. We actually need a mutex to protect more than just the audio_enable flag.
Okay, got it.
Thanks. : )
I've been moving my code closer to what you have posted. I've split up your patches into smaller steps so each change can be evaluated on iMX6. So far:
drm: bridge/dw_hdmi: combine hdmi_set_clock_regenerator_n() and hdmi_regenerate_cts()
This patch _just_ combines the two functions without any other changes.
drm: bridge/dw_hdmi: protect n/cts setting with a mutex
This patch _just_ adds a mutex to protect the calls to hdmi_set_clk_regenerator(), since we will need to call that from both the DRM and audio drivers.
drm: bridge/dw_hdmi: adjust n/cts setting order
This patch changes the order to:
- write CTS3 CTS_manual = 0
- write CTS3 N_shift = 0
- write CTS3 CTS value
- write CTS2 CTS value
- write CTS1 CTS value
- write N3 N value
- write N2 N value
- write N1 N value
which is broadly what you're doing, but without the initial N3 write and without CTS_manual=1. I've asked Freescale whether they can clarify what effect adding those would have on their SoCs.
Note: the mutex in my second patch needs to be a spinlock - as it seems my new workaround for iMX6 ERR005174 needs to call the CTS/N setting functions in an irqs-off region (from the ALSA ->trigger callback.) That will need further rework of the CTS/N code to reduce the size of the spinlock protected region.
Incidentally, your Synopsis IP indicates that it is the same revision as the iMX6's IP revision which suffers from this ERR005174 errata. I think you need to check whether this errata applies on your SoC too. Seach for "iMX6 ERR005174" in google.
Thank you very much. : ) I will take this order in next v3.
By the way, it doesn't matter that the function is called from another driver. What matters is that this function can be called concurrently on multiple different threads of execution to change the hdmi audio enable state.
From DRM land, it is called with DRM lock held when enabling/disabling
hdmi audio (mode_set / DPMS). It is also called from audio land, when enabling/disabling audio in response to some audio events (userspace ioctls?). I'm not sure exactly how the audio side works, or what locks are involved, but this mutex synchronizes calls from these two worlds to ensure that "hdmi->audio_enable" field always matches the current (intended) status of the hdmi audio clock. This would be useful, for example, if you needed to temporarily disable all HDMI clocks during a mode set, and then restore the audio clock to its pre-mode_set state:
There's some rather quirky comments in the driver right now which make me uneasy about changing things too much.
My initial idea would be that audio should remain disabled until the audio driver wants it enabled, and the CTS/N values should either be left alone, or set to a value which disables them (there is an iMX6 errata which says to set N=0 initially, but as seems common with iMX6 errata, I see no code implementing the method specified in the documentation - I have found code implementing something similar though.)
I am confused with the way that audio driver realize the display resolution-changing. If audio driver cannot knowing that, then audio clock may be closed permanently ?
The audio driver shouldn't care about the display resolution. That should be the responsibility of the dw_hdmi core - as it is at the moment.
Do you mean that we should disable audio clock and deinit the n/cts values, until we meet the audio enable single like this.
static void hdmi_keep_audio_clk_status(struct dw_hdmi *hdmi) { mutex_lock(&hdmi->audio_mutex);
if (hdmi->audio_enable) hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS); else hdmi_modb(hdmi, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS); mutex_unlock(&hdmi->audio_mutex);
}
/* HDMI Initialization Step E - Configure audio */ hdmi_clk_regenerator_update_pixel_clock(hdmi); hdmi_keep_audio_clk_status(hdmi);
keep audio status maybe suitable here.
What I don't know right now is what triggers this overflow indication in HDMI_IH_FC_STAT2, and whether the code I quoted (which fakes the audio setup) is actually necessary.
In other words:
- is it necessary to have the audio clock enabled when video is enabled to prevent the overflow indication? We don't know. Therefore, we can't say whether it is permitted to disable the audio clock when audio is inactive.
- is it necessary to program a dummy CTS/N value for 74.25MHz/48kHz, or can we program a non-zero CTS value and a zero N at initialisation until the audio driver comes up? Again, we don't know.
What we do know is that as the driver stands, it works for video output. With my changes for AHB audio support on the iMX6 (which includes some errata workarounds found in the iMX6 errata documentation to avoid FIFO issues), we have working audio there.
creat dw-hdmi-audio device dynamically in probe function, and transfer some interfaces to dw-hdmi-audio driver for setting hdmi audio format & control hdmi audio clock.
Signed-off-by: Yakir Yang ykk@rock-chips.com --- Changes in v2: - Update the audio control interfaces
drivers/gpu/drm/bridge/dw_hdmi.c | 23 +++++++++++++++++++++++ include/drm/bridge/dw_hdmi.h | 13 +++++++++++++ 2 files changed, 36 insertions(+)
diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c index 25c1678..75e3029 100644 --- a/drivers/gpu/drm/bridge/dw_hdmi.c +++ b/drivers/gpu/drm/bridge/dw_hdmi.c @@ -107,6 +107,7 @@ struct dw_hdmi { struct drm_encoder *encoder; struct drm_bridge *bridge;
+ struct platform_device *audio_pdev; enum dw_hdmi_devtype dev_type; struct device *dev; struct clk *isfr_clk; @@ -1703,6 +1704,8 @@ int dw_hdmi_bind(struct device *dev, struct device *master, struct resource *iores, int irq, const struct dw_hdmi_plat_data *plat_data) { + struct platform_device_info pdevinfo; + struct dw_hdmi_audio_data audio; struct drm_device *drm = data; struct device_node *np = dev->of_node; struct device_node *ddc_node; @@ -1824,6 +1827,23 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
dev_set_drvdata(dev, hdmi);
+ memset(&pdevinfo, 0, sizeof(pdevinfo)); + pdevinfo.parent = dev; + pdevinfo.id = PLATFORM_DEVID_NONE; + + audio.irq = irq; + audio.dw = hdmi; + audio.config_audio = hdmi_config_audio; + audio.enable_clk = hdmi_audio_clk_enable; + audio.disable_clk = hdmi_audio_clk_disable; + audio.get_connect_status = hdmi_get_connect_status; + + pdevinfo.name = "dw-hdmi-audio"; + pdevinfo.data = &audio; + pdevinfo.size_data = sizeof(audio); + pdevinfo.dma_mask = DMA_BIT_MASK(32); + hdmi->audio_pdev = platform_device_register_full(&pdevinfo); + return 0;
err_iahb: @@ -1839,6 +1859,9 @@ void dw_hdmi_unbind(struct device *dev, struct device *master, void *data) { struct dw_hdmi *hdmi = dev_get_drvdata(dev);
+ if (!IS_ERR(hdmi->audio_pdev)) + platform_device_unregister(hdmi->audio_pdev); + /* Disable all interrupts */ hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h index aafa447..2f29112 100644 --- a/include/drm/bridge/dw_hdmi.h +++ b/include/drm/bridge/dw_hdmi.h @@ -12,6 +12,8 @@
#include <drm/drmP.h>
+struct dw_hdmi; + enum { DW_HDMI_RES_8, DW_HDMI_RES_10, @@ -74,6 +76,17 @@ struct hdmi_audio_fmt { enum hdmi_audio_wordlength word_length; };
+struct dw_hdmi_audio_data { + struct dw_hdmi *dw; + + int irq; + bool (*get_connect_status)(struct dw_hdmi *hdmi); + + void (*enable_clk)(struct dw_hdmi *hdmi); + void (*disable_clk)(struct dw_hdmi *hdmi); + void (*config_audio)(struct dw_hdmi *hdmi, struct hdmi_audio_fmt *aud); +}; + struct dw_hdmi_plat_data { enum dw_hdmi_devtype dev_type; const struct dw_hdmi_mpll_config *mpll_cfg;
On Fri, Jan 30, 2015 at 06:23:51AM -0500, Yakir Yang wrote:
We found Designware hdmi driver only support audio clock config, we can not play sound through it. To add Designware HDMI Audio support, we make those patch set: 1): modify n/cts config order, according to dw_hdmi document. 2): add Audio Sample Channel Status config interfaces to dw_hdmi driver. 3): add audio support for more display resolutions(eg. 800x600). 4): add audio support for No-CEA display resolutions. 5): fixed dw_hdmi irq bug, add irq control to suspend/resume interfaces. 6): add suspend/resume callback for dw_hdmi rockchip driver. 7): filter interlace mode in rockchip vop driver. 8): add hdmi audio config interfaces to dw_hdmi driver. 9): creat "dw_hdmi-audio" platform device in dw_hdmi driver. 10): add codec driver for hdmi audio, callback dw_hdmi audio config functions. 11): add sound driver for hdmi audio, creat hdmi audio sound card. 12): add dt-bings file and add hdmi_audio node to corresponding dt file.
I think the overall issue with this patch is working out how to support both the iMX6 version of this IP, and the Rockchip version of the IP.
These two hardware IPs seem to be configured at synthesis time with entirely different audio architectures, which change which registers are available, and sometimes which bits in the registers are present, which makes it more difficult to come up with a unified audio driver.
Also, I think that it would be a good idea to start documenting which registers are available in which versions of the IP in dw_hdmi.h, otherwise I can see that it's going to be very easy for someone to assume that some register or bit which is available in one IP is present on all.
The CONFIGx_ID register values for the iMX6 SoC are:
CONFIG0_ID 0x8f CONFIG1_ID 0x01 CONFIG2_ID 0xf2 CONFIG3_ID 0x02
CONFIG0_ID appears to contain bits which indicate whether the IP supports I2S and SPDIF mode. Presumably your IP has bit 4 set for I2S, and maybe bit 5 for SPDIF?
CONFIG1_ID bit 0 indicates whether the AHB interface is present, which is presumably zero for your IP?
CONFIG3_ID bit 0 indicates whether "generic parallel audio, GPAUD" is present.
Could you provide (in addition to the values printed in the message I requested in another reply) the values of the CONFIGx_ID registers please, and whether any of the bits in there are documented as being applicable to audio.
Thanks.
On 01/31/2015 07:00 AM, Russell King - ARM Linux wrote:
On Fri, Jan 30, 2015 at 06:23:51AM -0500, Yakir Yang wrote:
We found Designware hdmi driver only support audio clock config, we can not play sound through it. To add Designware HDMI Audio support, we make those patch set: 1): modify n/cts config order, according to dw_hdmi document. 2): add Audio Sample Channel Status config interfaces to dw_hdmi driver. 3): add audio support for more display resolutions(eg. 800x600). 4): add audio support for No-CEA display resolutions. 5): fixed dw_hdmi irq bug, add irq control to suspend/resume interfaces. 6): add suspend/resume callback for dw_hdmi rockchip driver. 7): filter interlace mode in rockchip vop driver. 8): add hdmi audio config interfaces to dw_hdmi driver. 9): creat "dw_hdmi-audio" platform device in dw_hdmi driver. 10): add codec driver for hdmi audio, callback dw_hdmi audio config functions. 11): add sound driver for hdmi audio, creat hdmi audio sound card. 12): add dt-bings file and add hdmi_audio node to corresponding dt file.
I think the overall issue with this patch is working out how to support both the iMX6 version of this IP, and the Rockchip version of the IP.
These two hardware IPs seem to be configured at synthesis time with entirely different audio architectures, which change which registers are available, and sometimes which bits in the registers are present, which makes it more difficult to come up with a unified audio driver.
Also, I think that it would be a good idea to start documenting which registers are available in which versions of the IP in dw_hdmi.h, otherwise I can see that it's going to be very easy for someone to assume that some register or bit which is available in one IP is present on all.
The CONFIGx_ID register values for the iMX6 SoC are:
CONFIG0_ID 0x8f CONFIG1_ID 0x01 CONFIG2_ID 0xf2 CONFIG3_ID 0x02
CONFIG0_ID appears to contain bits which indicate whether the IP supports I2S and SPDIF mode. Presumably your IP has bit 4 set for I2S, and maybe bit 5 for SPDIF?
CONFIG1_ID bit 0 indicates whether the AHB interface is present, which is presumably zero for your IP?
CONFIG3_ID bit 0 indicates whether "generic parallel audio, GPAUD" is present.
Could you provide (in addition to the values printed in the message I requested in another reply) the values of the CONFIGx_ID registers please, and whether any of the bits in there are documented as being applicable to audio.
Thanks.
The IP version on rk3288 : dwhdmi-rockchip ff980000.hdmi: Detected HDMI controller 0x20:0xa:0xa0:0xc1
The CONFIGx_ID register values for the rk3288 soc are: CONFIG0_ID 0xbf CONFIG1_ID 0x22 CONFIG2_ID 0xc2 CONFIG3_ID 0x0
After looking at iMX6 DQRM, i found only CONFIG1_ID & CONFIG3_ID are different.
CONFIG1_ID bit 5 indicates whether the HDMI2.0 is present. Bit 1 indicates whether configuration interface is APB interface.
CONFIG3_ID bit 3 indicates whether the AHBAUDDMA is present. Bit 1 indicates whether Generic Parallel Audio is present.
dri-devel@lists.freedesktop.org