The purpose of this patchset is to improve the support of the i2s interface of the synopsys hdmi controller.
Once applied, the interface should support all the usual i2s bus formats, 8 channels playback and properly setup the channel number and allocation in the infoframes.
Also, the dw-hdmi i2s interface will now provide the eld to the generic hdmi-codec so it can expose the related controls to user space.
This work was inspired by Jonas Karlman's work, available here [0].
This was tested the Amlogic meson-g12a-sei510 platform. For this specific platform, which uses codec2codec links, there is a runtime dependency for patch 8 on this ASoC series [1].
Changes since v1 [2]: * Fix copy size in .get_eld()
[0]: https://github.com/Kwiboo/linux-rockchip/commits/rockchip-5.2-for-libreelec-... [1]: https://lkml.kernel.org/r/20190725165949.29699-1-jbrunet@baylibre.com [2]: https://lkml.kernel.org/r/20190805134102.24173-1-jbrunet@baylibre.com
Jerome Brunet (8): drm/bridge: dw-hdmi-i2s: support more i2s format drm/bridge: dw-hdmi: move audio channel setup out of ahb drm/bridge: dw-hdmi: set channel count in the infoframes drm/bridge: dw-hdmi-i2s: enable lpcm multi channels drm/bridge: dw-hdmi-i2s: set the channel allocation drm/bridge: dw-hdmi-i2s: reset audio fifo before applying new params drm/bridge: dw-hdmi-i2s: enable only the required i2s lanes drm/bridge: dw-hdmi-i2s: add .get_eld support
.../drm/bridge/synopsys/dw-hdmi-ahb-audio.c | 20 ++----- .../gpu/drm/bridge/synopsys/dw-hdmi-audio.h | 1 + .../drm/bridge/synopsys/dw-hdmi-i2s-audio.c | 60 +++++++++++++++++-- drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 37 ++++++++++++ drivers/gpu/drm/bridge/synopsys/dw-hdmi.h | 13 +++- include/drm/bridge/dw_hdmi.h | 2 + 6 files changed, 108 insertions(+), 25 deletions(-)
The dw-hdmi-i2s supports more formats than just regular i2s. Add support for left justified, right justified and dsp modes A and B.
Reviewed-by: Jonas Karlman jonas@kwiboo.se Signed-off-by: Jerome Brunet jbrunet@baylibre.com --- .../drm/bridge/synopsys/dw-hdmi-i2s-audio.c | 26 ++++++++++++++++--- drivers/gpu/drm/bridge/synopsys/dw-hdmi.h | 6 +++-- 2 files changed, 27 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c index 5cbb71a866d5..2b624cff541d 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c @@ -44,9 +44,8 @@ static int dw_hdmi_i2s_hw_params(struct device *dev, void *data, u8 inputclkfs = 0;
/* it cares I2S only */ - if ((fmt->fmt != HDMI_I2S) || - (fmt->bit_clk_master | fmt->frame_clk_master)) { - dev_err(dev, "unsupported format/settings\n"); + if (fmt->bit_clk_master | fmt->frame_clk_master) { + dev_err(dev, "unsupported clock settings\n"); return -EINVAL; }
@@ -63,6 +62,27 @@ static int dw_hdmi_i2s_hw_params(struct device *dev, void *data, break; }
+ switch (fmt->fmt) { + case HDMI_I2S: + conf1 |= HDMI_AUD_CONF1_MODE_I2S; + break; + case HDMI_RIGHT_J: + conf1 |= HDMI_AUD_CONF1_MODE_RIGHT_J; + break; + case HDMI_LEFT_J: + conf1 |= HDMI_AUD_CONF1_MODE_LEFT_J; + break; + case HDMI_DSP_A: + conf1 |= HDMI_AUD_CONF1_MODE_BURST_1; + break; + case HDMI_DSP_B: + conf1 |= HDMI_AUD_CONF1_MODE_BURST_2; + break; + default: + dev_err(dev, "unsupported format\n"); + return -EINVAL; + } + dw_hdmi_set_sample_rate(hdmi, hparms->sample_rate);
hdmi_write(audio, inputclkfs, HDMI_AUD_INPUTCLKFS); diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h index 4e3ec09d3ca4..091d7c28aa17 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h @@ -869,8 +869,10 @@ enum {
/* AUD_CONF1 field values */ HDMI_AUD_CONF1_MODE_I2S = 0x00, - HDMI_AUD_CONF1_MODE_RIGHT_J = 0x02, - HDMI_AUD_CONF1_MODE_LEFT_J = 0x04, + HDMI_AUD_CONF1_MODE_RIGHT_J = 0x20, + HDMI_AUD_CONF1_MODE_LEFT_J = 0x40, + HDMI_AUD_CONF1_MODE_BURST_1 = 0x60, + HDMI_AUD_CONF1_MODE_BURST_2 = 0x80, HDMI_AUD_CONF1_WIDTH_16 = 0x10, HDMI_AUD_CONF1_WIDTH_24 = 0x18,
Part of the channel count setup done in dw-hdmi ahb should actually be done whatever the interface providing the data.
Reviewed-by: Jonas Karlman jonas@kwiboo.se Let's move it to dw-hdmi driver instead.
Signed-off-by: Jerome Brunet jbrunet@baylibre.com --- .../drm/bridge/synopsys/dw-hdmi-ahb-audio.c | 20 +++--------- drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 32 +++++++++++++++++++ include/drm/bridge/dw_hdmi.h | 2 ++ 3 files changed, 38 insertions(+), 16 deletions(-)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-ahb-audio.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-ahb-audio.c index a494186ae6ce..2b7539701b42 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-ahb-audio.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-ahb-audio.c @@ -63,10 +63,6 @@ enum { 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, @@ -403,7 +399,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, layout, ca; + u8 threshold, conf0, conf1, ca;
/* Setup as per 3.0.5 FSL 4.1.0 BSP */ switch (dw->revision) { @@ -434,20 +430,12 @@ static int dw_hdmi_prepare(struct snd_pcm_substream *substream) 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); + + dw_hdmi_set_channel_count(dw->data.hdmi, runtime->channels); + dw_hdmi_set_channel_allocation(dw->data.hdmi, ca);
switch (runtime->format) { case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE: diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index 218a7b2308f7..be6d6819bef4 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -645,6 +645,38 @@ void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate) } EXPORT_SYMBOL_GPL(dw_hdmi_set_sample_rate);
+void dw_hdmi_set_channel_count(struct dw_hdmi *hdmi, unsigned int cnt) +{ + u8 layout; + + mutex_lock(&hdmi->audio_mutex); + + /* + * For >2 channel PCM audio, we need to select layout 1 + * and set an appropriate channel map. + */ + if (cnt > 2) + layout = HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_LAYOUT1; + else + layout = HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_LAYOUT0; + + hdmi_modb(hdmi, layout, HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_MASK, + HDMI_FC_AUDSCONF); + + mutex_unlock(&hdmi->audio_mutex); +} +EXPORT_SYMBOL_GPL(dw_hdmi_set_channel_count); + +void dw_hdmi_set_channel_allocation(struct dw_hdmi *hdmi, unsigned int ca) +{ + mutex_lock(&hdmi->audio_mutex); + + hdmi_writeb(hdmi, ca, HDMI_FC_AUDICONF2); + + mutex_unlock(&hdmi->audio_mutex); +} +EXPORT_SYMBOL_GPL(dw_hdmi_set_channel_allocation); + static void hdmi_enable_audio_clk(struct dw_hdmi *hdmi, bool enable) { if (enable) diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h index c402364aec0d..cf528c289857 100644 --- a/include/drm/bridge/dw_hdmi.h +++ b/include/drm/bridge/dw_hdmi.h @@ -155,6 +155,8 @@ void dw_hdmi_resume(struct dw_hdmi *hdmi); void dw_hdmi_setup_rx_sense(struct dw_hdmi *hdmi, bool hpd, bool rx_sense);
void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate); +void dw_hdmi_set_channel_count(struct dw_hdmi *hdmi, unsigned int cnt); +void dw_hdmi_set_channel_allocation(struct dw_hdmi *hdmi, unsigned int ca); void dw_hdmi_audio_enable(struct dw_hdmi *hdmi); void dw_hdmi_audio_disable(struct dw_hdmi *hdmi); void dw_hdmi_set_high_tmds_clock_ratio(struct dw_hdmi *hdmi);
Set the number of channel in the infoframes
Reviewed-by: Jonas Karlman jonas@kwiboo.se Signed-off-by: Jerome Brunet jbrunet@baylibre.com --- drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index be6d6819bef4..bed4bb017afd 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -663,6 +663,10 @@ void dw_hdmi_set_channel_count(struct dw_hdmi *hdmi, unsigned int cnt) hdmi_modb(hdmi, layout, HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_MASK, HDMI_FC_AUDSCONF);
+ /* Set the audio infoframes channel count */ + hdmi_modb(hdmi, (cnt - 1) << HDMI_FC_AUDICONF0_CC_OFFSET, + HDMI_FC_AUDICONF0_CC_MASK, HDMI_FC_AUDICONF0); + mutex_unlock(&hdmi->audio_mutex); } EXPORT_SYMBOL_GPL(dw_hdmi_set_channel_count);
Properly setup the channel count and layout in dw-hdmi i2s driver so we are not limited to 2 channels.
Also correct the maximum channel reported by the DAI from 6 to 8 ch
Reviewed-by: Jonas Karlman jonas@kwiboo.se Signed-off-by: Jerome Brunet jbrunet@baylibre.com --- drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c index 2b624cff541d..caf8aed78fea 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c @@ -84,6 +84,7 @@ static int dw_hdmi_i2s_hw_params(struct device *dev, void *data, }
dw_hdmi_set_sample_rate(hdmi, hparms->sample_rate); + dw_hdmi_set_channel_count(hdmi, hparms->channels);
hdmi_write(audio, inputclkfs, HDMI_AUD_INPUTCLKFS); hdmi_write(audio, conf0, HDMI_AUD_CONF0); @@ -139,7 +140,7 @@ static int snd_dw_hdmi_probe(struct platform_device *pdev)
pdata.ops = &dw_hdmi_i2s_ops; pdata.i2s = 1; - pdata.max_i2s_channels = 6; + pdata.max_i2s_channels = 8; pdata.data = audio;
memset(&pdevinfo, 0, sizeof(pdevinfo));
setup the channel allocation provided by the generic hdmi-codec driver
Reviewed-by: Jonas Karlman jonas@kwiboo.se Signed-off-by: Jerome Brunet jbrunet@baylibre.com --- drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c index caf8aed78fea..0864dee8d180 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c @@ -85,6 +85,7 @@ static int dw_hdmi_i2s_hw_params(struct device *dev, void *data,
dw_hdmi_set_sample_rate(hdmi, hparms->sample_rate); dw_hdmi_set_channel_count(hdmi, hparms->channels); + dw_hdmi_set_channel_allocation(hdmi, hparms->cea.channel_allocation);
hdmi_write(audio, inputclkfs, HDMI_AUD_INPUTCLKFS); hdmi_write(audio, conf0, HDMI_AUD_CONF0);
When changing the audio hw params, reset the audio fifo to make sure any old remaining data is flushed.
The databook mentions that such reset should be followed by a reset of the i2s block to make sure the samples stay aligned
Reviewed-by: Jonas Karlman jonas@kwiboo.se Signed-off-by: Jerome Brunet jbrunet@baylibre.com --- drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c | 6 ++++-- drivers/gpu/drm/bridge/synopsys/dw-hdmi.h | 1 + 2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c index 0864dee8d180..41bee0099578 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c @@ -49,6 +49,10 @@ static int dw_hdmi_i2s_hw_params(struct device *dev, void *data, return -EINVAL; }
+ /* Reset the FIFOs before applying new params */ + hdmi_write(audio, HDMI_AUD_CONF0_SW_RESET, HDMI_AUD_CONF0); + hdmi_write(audio, (u8)~HDMI_MC_SWRSTZ_I2SSWRST_REQ, HDMI_MC_SWRSTZ); + inputclkfs = HDMI_AUD_INPUTCLKFS_64FS; conf0 = HDMI_AUD_CONF0_I2S_ALL_ENABLE;
@@ -102,8 +106,6 @@ static void dw_hdmi_i2s_audio_shutdown(struct device *dev, void *data) struct dw_hdmi *hdmi = audio->hdmi;
dw_hdmi_audio_disable(hdmi); - - hdmi_write(audio, HDMI_AUD_CONF0_SW_RESET, HDMI_AUD_CONF0); }
static int dw_hdmi_i2s_get_dai_id(struct snd_soc_component *component, diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h index 091d7c28aa17..a272fa393ae6 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h @@ -940,6 +940,7 @@ enum { HDMI_MC_CLKDIS_PIXELCLK_DISABLE = 0x1,
/* MC_SWRSTZ field values */ + HDMI_MC_SWRSTZ_I2SSWRST_REQ = 0x08, HDMI_MC_SWRSTZ_TMDSSWRST_REQ = 0x02,
/* MC_FLOWCTRL field values */
Enable the i2s lanes depending on the number of channel in the stream
Reviewed-by: Jonas Karlman jonas@kwiboo.se Signed-off-by: Jerome Brunet jbrunet@baylibre.com --- .../gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c | 15 ++++++++++++++- drivers/gpu/drm/bridge/synopsys/dw-hdmi.h | 6 +++++- 2 files changed, 19 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c index 41bee0099578..b8ece9c1ba2c 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c @@ -54,7 +54,20 @@ static int dw_hdmi_i2s_hw_params(struct device *dev, void *data, hdmi_write(audio, (u8)~HDMI_MC_SWRSTZ_I2SSWRST_REQ, HDMI_MC_SWRSTZ);
inputclkfs = HDMI_AUD_INPUTCLKFS_64FS; - conf0 = HDMI_AUD_CONF0_I2S_ALL_ENABLE; + conf0 = (HDMI_AUD_CONF0_I2S_SELECT | HDMI_AUD_CONF0_I2S_EN0); + + /* Enable the required i2s lanes */ + switch (hparms->channels) { + case 7 ... 8: + conf0 |= HDMI_AUD_CONF0_I2S_EN3; + /* Fall-thru */ + case 5 ... 6: + conf0 |= HDMI_AUD_CONF0_I2S_EN2; + /* Fall-thru */ + case 3 ... 4: + conf0 |= HDMI_AUD_CONF0_I2S_EN1; + /* Fall-thru */ + }
switch (hparms->sample_width) { case 16: diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h index a272fa393ae6..6988f12d89d9 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h @@ -865,7 +865,11 @@ enum {
/* AUD_CONF0 field values */ HDMI_AUD_CONF0_SW_RESET = 0x80, - HDMI_AUD_CONF0_I2S_ALL_ENABLE = 0x2F, + HDMI_AUD_CONF0_I2S_SELECT = 0x20, + HDMI_AUD_CONF0_I2S_EN3 = 0x08, + HDMI_AUD_CONF0_I2S_EN2 = 0x04, + HDMI_AUD_CONF0_I2S_EN1 = 0x02, + HDMI_AUD_CONF0_I2S_EN0 = 0x01,
/* AUD_CONF1 field values */ HDMI_AUD_CONF1_MODE_I2S = 0x00,
Provide the eld to the generic hdmi-codec driver. This will let the driver enforce the maximum channel number and set the channel allocation depending on the hdmi sink.
Cc: Jonas Karlman jonas@kwiboo.se Signed-off-by: Jerome Brunet jbrunet@baylibre.com --- drivers/gpu/drm/bridge/synopsys/dw-hdmi-audio.h | 1 + drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c | 11 +++++++++++ drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 1 + 3 files changed, 13 insertions(+)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-audio.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-audio.h index 63b5756f463b..cb07dc0da5a7 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-audio.h +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-audio.h @@ -14,6 +14,7 @@ struct dw_hdmi_audio_data {
struct dw_hdmi_i2s_audio_data { struct dw_hdmi *hdmi; + u8 *eld;
void (*write)(struct dw_hdmi *hdmi, u8 val, int offset); u8 (*read)(struct dw_hdmi *hdmi, int offset); diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c index b8ece9c1ba2c..62e737b81462 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c @@ -10,6 +10,7 @@ #include <linux/module.h>
#include <drm/bridge/dw_hdmi.h> +#include <drm/drm_crtc.h>
#include <sound/hdmi-codec.h>
@@ -121,6 +122,15 @@ static void dw_hdmi_i2s_audio_shutdown(struct device *dev, void *data) dw_hdmi_audio_disable(hdmi); }
+static int dw_hdmi_i2s_get_eld(struct device *dev, void *data, uint8_t *buf, + size_t len) +{ + struct dw_hdmi_i2s_audio_data *audio = data; + + memcpy(buf, audio->eld, min(sizeof(MAX_ELD_BYTES), len)); + return 0; +} + static int dw_hdmi_i2s_get_dai_id(struct snd_soc_component *component, struct device_node *endpoint) { @@ -144,6 +154,7 @@ static int dw_hdmi_i2s_get_dai_id(struct snd_soc_component *component, static struct hdmi_codec_ops dw_hdmi_i2s_ops = { .hw_params = dw_hdmi_i2s_hw_params, .audio_shutdown = dw_hdmi_i2s_audio_shutdown, + .get_eld = dw_hdmi_i2s_get_eld, .get_dai_id = dw_hdmi_i2s_get_dai_id, };
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index bed4bb017afd..8df69c9dbfad 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -2797,6 +2797,7 @@ __dw_hdmi_probe(struct platform_device *pdev, struct dw_hdmi_i2s_audio_data audio;
audio.hdmi = hdmi; + audio.eld = hdmi->connector.eld; audio.write = hdmi_writeb; audio.read = hdmi_readb; hdmi->enable_audio = dw_hdmi_i2s_audio_enable;
Provide the eld to the generic hdmi-codec driver. This will let the driver enforce the maximum channel number and set the channel allocation depending on the hdmi sink.
Cc: Jonas Karlman jonas@kwiboo.se Signed-off-by: Jerome Brunet jbrunet@baylibre.com --- drivers/gpu/drm/bridge/synopsys/dw-hdmi-audio.h | 1 + drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c | 11 +++++++++++ drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 1 + 3 files changed, 13 insertions(+)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-audio.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-audio.h index 63b5756f463b..cb07dc0da5a7 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-audio.h +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-audio.h @@ -14,6 +14,7 @@ struct dw_hdmi_audio_data {
struct dw_hdmi_i2s_audio_data { struct dw_hdmi *hdmi; + u8 *eld;
void (*write)(struct dw_hdmi *hdmi, u8 val, int offset); u8 (*read)(struct dw_hdmi *hdmi, int offset); diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c index b8ece9c1ba2c..1d15cf9b6821 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c @@ -10,6 +10,7 @@ #include <linux/module.h>
#include <drm/bridge/dw_hdmi.h> +#include <drm/drm_crtc.h>
#include <sound/hdmi-codec.h>
@@ -121,6 +122,15 @@ static void dw_hdmi_i2s_audio_shutdown(struct device *dev, void *data) dw_hdmi_audio_disable(hdmi); }
+static int dw_hdmi_i2s_get_eld(struct device *dev, void *data, uint8_t *buf, + size_t len) +{ + struct dw_hdmi_i2s_audio_data *audio = data; + + memcpy(buf, audio->eld, min_t(size_t, MAX_ELD_BYTES, len)); + return 0; +} + static int dw_hdmi_i2s_get_dai_id(struct snd_soc_component *component, struct device_node *endpoint) { @@ -144,6 +154,7 @@ static int dw_hdmi_i2s_get_dai_id(struct snd_soc_component *component, static struct hdmi_codec_ops dw_hdmi_i2s_ops = { .hw_params = dw_hdmi_i2s_hw_params, .audio_shutdown = dw_hdmi_i2s_audio_shutdown, + .get_eld = dw_hdmi_i2s_get_eld, .get_dai_id = dw_hdmi_i2s_get_dai_id, };
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index bed4bb017afd..8df69c9dbfad 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -2797,6 +2797,7 @@ __dw_hdmi_probe(struct platform_device *pdev, struct dw_hdmi_i2s_audio_data audio;
audio.hdmi = hdmi; + audio.eld = hdmi->connector.eld; audio.write = hdmi_writeb; audio.read = hdmi_readb; hdmi->enable_audio = dw_hdmi_i2s_audio_enable;
On 2019-08-12 14:50, Jerome Brunet wrote:
Provide the eld to the generic hdmi-codec driver. This will let the driver enforce the maximum channel number and set the channel allocation depending on the hdmi sink.
Cc: Jonas Karlman jonas@kwiboo.se Signed-off-by: Jerome Brunet jbrunet@baylibre.com
Tested on Rockchip RK3288/RK3328 devices, full ELD is now available to userspace. Please note that the r-b line in patch 2 is mixed in middle of commit message.
Reviewed-by: Jonas Karlman jonas@kwiboo.se
Regards, Jonas
drivers/gpu/drm/bridge/synopsys/dw-hdmi-audio.h | 1 + drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c | 11 +++++++++++ drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 1 + 3 files changed, 13 insertions(+)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-audio.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-audio.h index 63b5756f463b..cb07dc0da5a7 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-audio.h +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-audio.h @@ -14,6 +14,7 @@ struct dw_hdmi_audio_data {
struct dw_hdmi_i2s_audio_data { struct dw_hdmi *hdmi;
u8 *eld;
void (*write)(struct dw_hdmi *hdmi, u8 val, int offset); u8 (*read)(struct dw_hdmi *hdmi, int offset);
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c index b8ece9c1ba2c..1d15cf9b6821 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c @@ -10,6 +10,7 @@ #include <linux/module.h>
#include <drm/bridge/dw_hdmi.h> +#include <drm/drm_crtc.h>
#include <sound/hdmi-codec.h>
@@ -121,6 +122,15 @@ static void dw_hdmi_i2s_audio_shutdown(struct device *dev, void *data) dw_hdmi_audio_disable(hdmi); }
+static int dw_hdmi_i2s_get_eld(struct device *dev, void *data, uint8_t *buf,
size_t len)
+{
- struct dw_hdmi_i2s_audio_data *audio = data;
- memcpy(buf, audio->eld, min_t(size_t, MAX_ELD_BYTES, len));
- return 0;
+}
static int dw_hdmi_i2s_get_dai_id(struct snd_soc_component *component, struct device_node *endpoint) { @@ -144,6 +154,7 @@ static int dw_hdmi_i2s_get_dai_id(struct snd_soc_component *component, static struct hdmi_codec_ops dw_hdmi_i2s_ops = { .hw_params = dw_hdmi_i2s_hw_params, .audio_shutdown = dw_hdmi_i2s_audio_shutdown,
- .get_eld = dw_hdmi_i2s_get_eld, .get_dai_id = dw_hdmi_i2s_get_dai_id,
};
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index bed4bb017afd..8df69c9dbfad 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -2797,6 +2797,7 @@ __dw_hdmi_probe(struct platform_device *pdev, struct dw_hdmi_i2s_audio_data audio;
audio.hdmi = hdmi;
audio.write = hdmi_writeb; audio.read = hdmi_readb; hdmi->enable_audio = dw_hdmi_i2s_audio_enable;audio.eld = hdmi->connector.eld;
On 14/08/2019 12:08, Jonas Karlman wrote:
On 2019-08-12 14:50, Jerome Brunet wrote:
Provide the eld to the generic hdmi-codec driver. This will let the driver enforce the maximum channel number and set the channel allocation depending on the hdmi sink.
Cc: Jonas Karlman jonas@kwiboo.se Signed-off-by: Jerome Brunet jbrunet@baylibre.com
Tested on Rockchip RK3288/RK3328 devices, full ELD is now available to userspace. Please note that the r-b line in patch 2 is mixed in middle of commit message.
Reviewed-by: Jonas Karlman jonas@kwiboo.se
Regards, Jonas
drivers/gpu/drm/bridge/synopsys/dw-hdmi-audio.h | 1 + drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c | 11 +++++++++++ drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 1 + 3 files changed, 13 insertions(+)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-audio.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-audio.h index 63b5756f463b..cb07dc0da5a7 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-audio.h +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-audio.h @@ -14,6 +14,7 @@ struct dw_hdmi_audio_data {
struct dw_hdmi_i2s_audio_data { struct dw_hdmi *hdmi;
u8 *eld;
void (*write)(struct dw_hdmi *hdmi, u8 val, int offset); u8 (*read)(struct dw_hdmi *hdmi, int offset);
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c index b8ece9c1ba2c..1d15cf9b6821 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c @@ -10,6 +10,7 @@ #include <linux/module.h>
#include <drm/bridge/dw_hdmi.h> +#include <drm/drm_crtc.h>
#include <sound/hdmi-codec.h>
@@ -121,6 +122,15 @@ static void dw_hdmi_i2s_audio_shutdown(struct device *dev, void *data) dw_hdmi_audio_disable(hdmi); }
+static int dw_hdmi_i2s_get_eld(struct device *dev, void *data, uint8_t *buf,
size_t len)
+{
- struct dw_hdmi_i2s_audio_data *audio = data;
- memcpy(buf, audio->eld, min_t(size_t, MAX_ELD_BYTES, len));
- return 0;
+}
static int dw_hdmi_i2s_get_dai_id(struct snd_soc_component *component, struct device_node *endpoint) { @@ -144,6 +154,7 @@ static int dw_hdmi_i2s_get_dai_id(struct snd_soc_component *component, static struct hdmi_codec_ops dw_hdmi_i2s_ops = { .hw_params = dw_hdmi_i2s_hw_params, .audio_shutdown = dw_hdmi_i2s_audio_shutdown,
- .get_eld = dw_hdmi_i2s_get_eld, .get_dai_id = dw_hdmi_i2s_get_dai_id,
};
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index bed4bb017afd..8df69c9dbfad 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -2797,6 +2797,7 @@ __dw_hdmi_probe(struct platform_device *pdev, struct dw_hdmi_i2s_audio_data audio;
audio.hdmi = hdmi;
audio.write = hdmi_writeb; audio.read = hdmi_readb; hdmi->enable_audio = dw_hdmi_i2s_audio_enable;audio.eld = hdmi->connector.eld;
Applying the whole serie, with this resent patch 8 and the patch 2 commit log fixed to drm-misc-next.
Thanks,
Neil
Hi,
On 12/08/2019 14:07, Jerome Brunet wrote:
The purpose of this patchset is to improve the support of the i2s interface of the synopsys hdmi controller.
Once applied, the interface should support all the usual i2s bus formats, 8 channels playback and properly setup the channel number and allocation in the infoframes.
Also, the dw-hdmi i2s interface will now provide the eld to the generic hdmi-codec so it can expose the related controls to user space.
This work was inspired by Jonas Karlman's work, available here [0].
This was tested the Amlogic meson-g12a-sei510 platform. For this specific platform, which uses codec2codec links, there is a runtime dependency for patch 8 on this ASoC series [1].
Changes since v1 [2]:
- Fix copy size in .get_eld()
Jerome Brunet (8): drm/bridge: dw-hdmi-i2s: support more i2s format drm/bridge: dw-hdmi: move audio channel setup out of ahb drm/bridge: dw-hdmi: set channel count in the infoframes drm/bridge: dw-hdmi-i2s: enable lpcm multi channels drm/bridge: dw-hdmi-i2s: set the channel allocation drm/bridge: dw-hdmi-i2s: reset audio fifo before applying new params drm/bridge: dw-hdmi-i2s: enable only the required i2s lanes drm/bridge: dw-hdmi-i2s: add .get_eld support
Reviewed-by: Neil Armstrong narmstrong@baylibre.com
Jonas, is patch 8 ok for you now ? If yes I'll apply them to drm-misc-next.
Neil
.../drm/bridge/synopsys/dw-hdmi-ahb-audio.c | 20 ++----- .../gpu/drm/bridge/synopsys/dw-hdmi-audio.h | 1 + .../drm/bridge/synopsys/dw-hdmi-i2s-audio.c | 60 +++++++++++++++++-- drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 37 ++++++++++++ drivers/gpu/drm/bridge/synopsys/dw-hdmi.h | 13 +++- include/drm/bridge/dw_hdmi.h | 2 + 6 files changed, 108 insertions(+), 25 deletions(-)
On Mon 12 Aug 2019 at 14:19, Neil Armstrong narmstrong@baylibre.com wrote:
Hi,
On 12/08/2019 14:07, Jerome Brunet wrote:
The purpose of this patchset is to improve the support of the i2s interface of the synopsys hdmi controller.
Once applied, the interface should support all the usual i2s bus formats, 8 channels playback and properly setup the channel number and allocation in the infoframes.
Also, the dw-hdmi i2s interface will now provide the eld to the generic hdmi-codec so it can expose the related controls to user space.
This work was inspired by Jonas Karlman's work, available here [0].
This was tested the Amlogic meson-g12a-sei510 platform. For this specific platform, which uses codec2codec links, there is a runtime dependency for patch 8 on this ASoC series [1].
Changes since v1 [2]:
- Fix copy size in .get_eld()
Jerome Brunet (8): drm/bridge: dw-hdmi-i2s: support more i2s format drm/bridge: dw-hdmi: move audio channel setup out of ahb drm/bridge: dw-hdmi: set channel count in the infoframes drm/bridge: dw-hdmi-i2s: enable lpcm multi channels drm/bridge: dw-hdmi-i2s: set the channel allocation drm/bridge: dw-hdmi-i2s: reset audio fifo before applying new params drm/bridge: dw-hdmi-i2s: enable only the required i2s lanes drm/bridge: dw-hdmi-i2s: add .get_eld support
Reviewed-by: Neil Armstrong narmstrong@baylibre.com
Jonas, is patch 8 ok for you now ? If yes I'll apply them to drm-misc-next.
Please don't ! I did not pick the right change and what I just sent is just crazy, give me a second to resend
Neil
.../drm/bridge/synopsys/dw-hdmi-ahb-audio.c | 20 ++----- .../gpu/drm/bridge/synopsys/dw-hdmi-audio.h | 1 + .../drm/bridge/synopsys/dw-hdmi-i2s-audio.c | 60 +++++++++++++++++-- drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 37 ++++++++++++ drivers/gpu/drm/bridge/synopsys/dw-hdmi.h | 13 +++- include/drm/bridge/dw_hdmi.h | 2 + 6 files changed, 108 insertions(+), 25 deletions(-)
dri-devel@lists.freedesktop.org