[alsa-devel] [PATCH v5 0/9] ASoC: Add mediatek HDMI codec support
Hi,
these patches add an interface for Jyri's generic hdmi-codec driver [1] to the mediatek hdmi bridge driver. Since there haven't been many comments after the last submission, the patches are mostly unchanged. A new patch adds HDMI dai-links to the recently merged mt8173-rt5650 machine driver and I have updated the HDMI notification patch (and renamed patch 3 to mention the mt8173-rt5650-rt5676 machine driver).
Changes since v4: - Update hdmi notifier to latest version - Added HDMI dai-links to new mt8173-rt5650 machine driver
The mediatek drm patches apply on top of https://patchwork.kernel.org/patch/8337071/ ("drm/mediatek: Add HDMI support"), and need this patch: [2] https://patchwork.kernel.org/patch/8091531/ ("video: hdmi: add helper function for N and CTS") The asoc patches depend on these two patches: https://patchwork.kernel.org/patch/7215121/ ("ALSA: pcm: add IEC958 channel status helper for hw_params") [1] https://patchwork.kernel.org/patch/7215271/ ("ASoC: hdmi-codec: Add hdmi-codec for external HDMI-encoders")
regards Philipp
Koro Chen (1): ASoC: mediatek: Add HDMI dai-links in the mt8173-rt5650-rt5676 machine driver
PC Liao (1): ASoC: mediatek: Add HDMI dai-links to the mt8173-rt5650 machine driver
Philipp Zabel (7): drm/mediatek: hdmi: Add audio interface to the hdmi-codec driver ASoC: mediatek: address dai link array entries by enum video: rmk's HDMI notification prototype drm/mediatek: hdmi: issue notifications ASoC: hdmi-codec: Use HDMI notifications to add jack support ASoC: hdmi-codec: Add ELD control drm/mediatek: hdmi: use helper function for N and CTS calculation
.../bindings/sound/mt8173-rt5650-rt5676.txt | 5 +- .../devicetree/bindings/sound/mt8173-rt5650.txt | 5 +- drivers/gpu/drm/mediatek/Kconfig | 1 + drivers/gpu/drm/mediatek/mtk_cec.c | 11 ++ drivers/gpu/drm/mediatek/mtk_drm_hdmi_drv.c | 148 +++++++++++++++++ drivers/gpu/drm/mediatek/mtk_hdmi.c | 89 +++++----- drivers/gpu/drm/mediatek/mtk_hdmi.h | 10 +- drivers/gpu/drm/mediatek/mtk_hdmi_hw.c | 179 ++++----------------- drivers/gpu/drm/mediatek/mtk_hdmi_hw.h | 4 +- drivers/video/Kconfig | 3 + drivers/video/Makefile | 1 + drivers/video/hdmi-notifier.c | 61 +++++++ include/linux/hdmi-notifier.h | 44 +++++ include/sound/hdmi-codec.h | 6 + sound/soc/codecs/hdmi-codec.c | 123 +++++++++++++- sound/soc/mediatek/Kconfig | 1 + sound/soc/mediatek/mt8173-rt5650-rt5676.c | 66 +++++++- sound/soc/mediatek/mt8173-rt5650.c | 47 ++++++ 18 files changed, 587 insertions(+), 217 deletions(-) create mode 100644 drivers/video/hdmi-notifier.c create mode 100644 include/linux/hdmi-notifier.h
Add the interface needed by Jyri's generic hdmi-codec driver [1] to start or stop audio playback and to retrieve ELD (EDID like data) to limit the supported audio formats to the HDMI sink capabilities.
[1] https://patchwork.kernel.org/patch/7215271/ ("ASoC: hdmi-codec: Add hdmi-codec for external HDMI-encoders")
Signed-off-by: Philipp Zabel p.zabel@pengutronix.de Signed-off-by: Jie Qiu jie.qiu@mediatek.com --- drivers/gpu/drm/mediatek/Kconfig | 1 + drivers/gpu/drm/mediatek/mtk_drm_hdmi_drv.c | 145 ++++++++++++++++++++++++++++ drivers/gpu/drm/mediatek/mtk_hdmi.c | 89 +++++++++-------- drivers/gpu/drm/mediatek/mtk_hdmi.h | 10 +- drivers/gpu/drm/mediatek/mtk_hdmi_hw.c | 94 +++--------------- drivers/gpu/drm/mediatek/mtk_hdmi_hw.h | 4 +- 6 files changed, 207 insertions(+), 136 deletions(-)
diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig index e2ff158..74f1cc7 100644 --- a/drivers/gpu/drm/mediatek/Kconfig +++ b/drivers/gpu/drm/mediatek/Kconfig @@ -16,6 +16,7 @@ config DRM_MEDIATEK config DRM_MEDIATEK_HDMI tristate "DRM HDMI Support for Mediatek SoCs" depends on DRM_MEDIATEK + select SND_SOC_HDMI_CODEC if SND_SOC select GENERIC_PHY help DRM/KMS HDMI driver for Mediatek SoCs diff --git a/drivers/gpu/drm/mediatek/mtk_drm_hdmi_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_hdmi_drv.c index ff661e0..96ad53a 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_hdmi_drv.c +++ b/drivers/gpu/drm/mediatek/mtk_drm_hdmi_drv.c @@ -26,6 +26,7 @@ #include <linux/of_graph.h> #include <linux/phy/phy.h> #include <linux/platform_device.h> +#include <sound/hdmi-codec.h> #include "mtk_cec.h" #include "mtk_hdmi.h" #include "mtk_hdmi_hw.h" @@ -421,6 +422,148 @@ static int mtk_hdmi_dt_parse_pdata(struct mtk_hdmi *hdmi, return 0; }
+/* + * HDMI audio codec callbacks + */ + +static int mtk_hdmi_audio_hw_params(struct device *dev, + struct hdmi_codec_daifmt *daifmt, + struct hdmi_codec_params *params) +{ + struct mtk_hdmi *hdmi = dev_get_drvdata(dev); + struct hdmi_audio_param hdmi_params; + unsigned int chan = params->cea.channels; + + dev_dbg(hdmi->dev, "%s: %u Hz, %d bit, %d channels\n", __func__, + params->sample_rate, params->sample_width, chan); + + if (!hdmi->bridge.encoder) + return -ENODEV; + + switch (chan) { + case 2: + hdmi_params.aud_input_chan_type = HDMI_AUD_CHAN_TYPE_2_0; + break; + case 4: + hdmi_params.aud_input_chan_type = HDMI_AUD_CHAN_TYPE_4_0; + break; + case 6: + hdmi_params.aud_input_chan_type = HDMI_AUD_CHAN_TYPE_5_1; + break; + case 8: + hdmi_params.aud_input_chan_type = HDMI_AUD_CHAN_TYPE_7_1; + break; + default: + dev_err(hdmi->dev, "channel[%d] not supported!\n", chan); + return -EINVAL; + } + + switch (params->sample_rate) { + case 32000: + case 44100: + case 48000: + case 88200: + case 96000: + case 176400: + case 192000: + break; + default: + dev_err(hdmi->dev, "rate[%d] not supported!\n", + params->sample_rate); + return -EINVAL; + } + + switch (daifmt->fmt) { + case HDMI_I2S: + hdmi_params.aud_codec = HDMI_AUDIO_CODING_TYPE_PCM; + hdmi_params.aud_sampe_size = HDMI_AUDIO_SAMPLE_SIZE_16; + hdmi_params.aud_input_type = HDMI_AUD_INPUT_I2S; + hdmi_params.aud_i2s_fmt = HDMI_I2S_MODE_I2S_24BIT; + hdmi_params.aud_mclk = HDMI_AUD_MCLK_128FS; + break; + default: + dev_err(hdmi->dev, "%s: Invalid DAI format %d\n", __func__, + daifmt->fmt); + return -EINVAL; + } + + memcpy(&hdmi_params.codec_params, params, + sizeof(hdmi_params.codec_params)); + + mtk_hdmi_audio_set_param(hdmi, &hdmi_params); + + return 0; +} + +static int mtk_hdmi_audio_startup(struct device *dev, + void (*abort_cb)(struct device *dev)) +{ + struct mtk_hdmi *hdmi = dev_get_drvdata(dev); + + dev_dbg(dev, "%s\n", __func__); + + mtk_hdmi_audio_enable(hdmi); + + return 0; +} + +static void mtk_hdmi_audio_shutdown(struct device *dev) +{ + struct mtk_hdmi *hdmi = dev_get_drvdata(dev); + + dev_dbg(dev, "%s\n", __func__); + + mtk_hdmi_audio_disable(hdmi); +} + +int mtk_hdmi_audio_digital_mute(struct device *dev, bool enable) +{ + struct mtk_hdmi *hdmi = dev_get_drvdata(dev); + + dev_dbg(dev, "%s(%d)\n", __func__, enable); + + mtk_hdmi_hw_aud_mute(hdmi, enable); + + return 0; +} + +static int mtk_hdmi_audio_get_eld(struct device *dev, uint8_t *buf, size_t len) +{ + struct mtk_hdmi *hdmi = dev_get_drvdata(dev); + + dev_dbg(dev, "%s\n", __func__); + + memcpy(buf, hdmi->conn.eld, min(sizeof(hdmi->conn.eld), len)); + + return 0; +} + +static const struct hdmi_codec_ops mtk_hdmi_audio_codec_ops = { + .hw_params = mtk_hdmi_audio_hw_params, + .audio_startup = mtk_hdmi_audio_startup, + .audio_shutdown = mtk_hdmi_audio_shutdown, + .digital_mute = mtk_hdmi_audio_digital_mute, + .get_eld = mtk_hdmi_audio_get_eld, +}; + +static void mtk_hdmi_register_audio_driver(struct device *dev) +{ + struct hdmi_codec_pdata codec_data = { + .ops = &mtk_hdmi_audio_codec_ops, + .max_i2s_channels = 2, + .i2s = 1, + }; + struct platform_device *pdev; + + pdev = platform_device_register_data(dev, HDMI_CODEC_DRV_NAME, + PLATFORM_DEVID_AUTO, &codec_data, + sizeof(codec_data)); + if (IS_ERR(pdev)) + return; + + DRM_INFO("%s driver bound to HDMI\n", HDMI_CODEC_DRV_NAME); +} + static int mtk_drm_hdmi_probe(struct platform_device *pdev) { struct mtk_hdmi *hdmi; @@ -452,6 +595,8 @@ static int mtk_drm_hdmi_probe(struct platform_device *pdev) return ret; }
+ mtk_hdmi_register_audio_driver(dev); + hdmi->bridge.funcs = &mtk_hdmi_bridge_funcs; hdmi->bridge.of_node = pdev->dev.of_node; ret = drm_bridge_add(&hdmi->bridge); diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c index 30ec7b5..573c48b 100644 --- a/drivers/gpu/drm/mediatek/mtk_hdmi.c +++ b/drivers/gpu/drm/mediatek/mtk_hdmi.c @@ -197,46 +197,30 @@ static int mtk_hdmi_aud_set_input(struct mtk_hdmi *hdmi) static int mtk_hdmi_aud_set_src(struct mtk_hdmi *hdmi, struct drm_display_mode *display_mode) { + unsigned int sample_rate = hdmi->aud_param.codec_params.sample_rate; mtk_hdmi_aud_on_off_hw_ncts(hdmi, false);
if (hdmi->aud_param.aud_input_type == HDMI_AUD_INPUT_I2S) { - switch (hdmi->aud_param.aud_hdmi_fs) { - case HDMI_AUDIO_SAMPLE_FREQUENCY_32000: - case HDMI_AUDIO_SAMPLE_FREQUENCY_44100: - case HDMI_AUDIO_SAMPLE_FREQUENCY_48000: - case HDMI_AUDIO_SAMPLE_FREQUENCY_88200: - case HDMI_AUDIO_SAMPLE_FREQUENCY_96000: + switch (sample_rate) { + case 32000: + case 44100: + case 48000: + case 88200: + case 96000: mtk_hdmi_hw_aud_src_off(hdmi); /* mtk_hdmi_hw_aud_src_enable(hdmi, false); */ - mtk_hdmi_hw_aud_set_mclk( - hdmi, - hdmi->aud_param.aud_mclk); + mtk_hdmi_hw_aud_set_mclk(hdmi, + hdmi->aud_param.aud_mclk); mtk_hdmi_hw_aud_aclk_inv_enable(hdmi, false); break; default: break; } } else { - switch (hdmi->aud_param.iec_frame_fs) { - case HDMI_IEC_32K: - hdmi->aud_param.aud_hdmi_fs = - HDMI_AUDIO_SAMPLE_FREQUENCY_32000; - mtk_hdmi_hw_aud_src_off(hdmi); - mtk_hdmi_hw_aud_set_mclk(hdmi, - HDMI_AUD_MCLK_128FS); - mtk_hdmi_hw_aud_aclk_inv_enable(hdmi, false); - break; - case HDMI_IEC_48K: - hdmi->aud_param.aud_hdmi_fs = - HDMI_AUDIO_SAMPLE_FREQUENCY_48000; - mtk_hdmi_hw_aud_src_off(hdmi); - mtk_hdmi_hw_aud_set_mclk(hdmi, - HDMI_AUD_MCLK_128FS); - mtk_hdmi_hw_aud_aclk_inv_enable(hdmi, false); - break; - case HDMI_IEC_44K: - hdmi->aud_param.aud_hdmi_fs = - HDMI_AUDIO_SAMPLE_FREQUENCY_44100; + switch (sample_rate) { + case 32000: + case 44100: + case 48000: mtk_hdmi_hw_aud_src_off(hdmi); mtk_hdmi_hw_aud_set_mclk(hdmi, HDMI_AUD_MCLK_128FS); @@ -246,20 +230,10 @@ static int mtk_hdmi_aud_set_src(struct mtk_hdmi *hdmi, break; } } - mtk_hdmi_hw_aud_set_ncts(hdmi, hdmi->aud_param.aud_hdmi_fs, - display_mode->clock);
- mtk_hdmi_hw_aud_src_reenable(hdmi); - return 0; -} + mtk_hdmi_hw_aud_set_ncts(hdmi, sample_rate, display_mode->clock);
-static int mtk_hdmi_aud_set_chnl_status(struct mtk_hdmi *hdmi) -{ - mtk_hdmi_hw_aud_set_channel_status( - hdmi, - hdmi->aud_param.hdmi_l_channel_state, - hdmi->aud_param.hdmi_r_channel_state, - hdmi->aud_param.aud_hdmi_fs); + mtk_hdmi_hw_aud_src_reenable(hdmi); return 0; }
@@ -271,7 +245,8 @@ static int mtk_hdmi_aud_output_config(struct mtk_hdmi *hdmi,
mtk_hdmi_aud_set_input(hdmi); mtk_hdmi_aud_set_src(hdmi, display_mode); - mtk_hdmi_aud_set_chnl_status(hdmi); + mtk_hdmi_hw_aud_set_channel_status(hdmi, + hdmi->aud_param.codec_params.iec.status);
usleep_range(50, 100);
@@ -413,15 +388,11 @@ int mtk_hdmi_output_init(struct mtk_hdmi *hdmi) hdmi->csp = HDMI_COLORSPACE_RGB; hdmi->output = true; aud_param->aud_codec = HDMI_AUDIO_CODING_TYPE_PCM; - aud_param->aud_hdmi_fs = HDMI_AUDIO_SAMPLE_FREQUENCY_48000; aud_param->aud_sampe_size = HDMI_AUDIO_SAMPLE_SIZE_16; aud_param->aud_input_type = HDMI_AUD_INPUT_I2S; aud_param->aud_i2s_fmt = HDMI_I2S_MODE_I2S_24BIT; aud_param->aud_mclk = HDMI_AUD_MCLK_128FS; - aud_param->iec_frame_fs = HDMI_IEC_48K; aud_param->aud_input_chan_type = HDMI_AUD_CHAN_TYPE_2_0; - aud_param->hdmi_l_channel_state[2] = 2; - aud_param->hdmi_r_channel_state[2] = 2; hdmi->init = true;
return 0; @@ -439,6 +410,32 @@ void mtk_hdmi_power_off(struct mtk_hdmi *hdmi) mtk_hdmi_hw_make_reg_writable(hdmi, false); }
+void mtk_hdmi_audio_enable(struct mtk_hdmi *hdmi) +{ + mtk_hdmi_aud_enable_packet(hdmi, true); + hdmi->audio_enable = true; +} + +void mtk_hdmi_audio_disable(struct mtk_hdmi *hdmi) +{ + mtk_hdmi_aud_enable_packet(hdmi, false); + hdmi->audio_enable = false; +} + +int mtk_hdmi_audio_set_param(struct mtk_hdmi *hdmi, + struct hdmi_audio_param *param) +{ + if (!hdmi->audio_enable) { + dev_err(hdmi->dev, "hdmi audio is in disable state!\n"); + return -EINVAL; + } + dev_dbg(hdmi->dev, "codec:%d, input:%d, channel:%d, fs:%d\n", + param->aud_codec, param->aud_input_type, + param->aud_input_chan_type, param->codec_params.sample_rate); + memcpy(&hdmi->aud_param, param, sizeof(*param)); + return mtk_hdmi_aud_output_config(hdmi, &hdmi->mode); +} + int mtk_hdmi_output_set_display_mode(struct mtk_hdmi *hdmi, struct drm_display_mode *mode) { diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.h b/drivers/gpu/drm/mediatek/mtk_hdmi.h index 9403915..832f238 100644 --- a/drivers/gpu/drm/mediatek/mtk_hdmi.h +++ b/drivers/gpu/drm/mediatek/mtk_hdmi.h @@ -19,6 +19,7 @@ #include <linux/io.h> #include <linux/kernel.h> #include <linux/types.h> +#include <sound/hdmi-codec.h>
struct clk; struct device; @@ -144,15 +145,12 @@ enum hdmi_aud_channel_swap_type {
struct hdmi_audio_param { enum hdmi_audio_coding_type aud_codec; - enum hdmi_audio_sample_frequency aud_hdmi_fs; enum hdmi_audio_sample_size aud_sampe_size; enum hdmi_aud_input_type aud_input_type; enum hdmi_aud_i2s_fmt aud_i2s_fmt; enum hdmi_aud_mclk aud_mclk; - enum hdmi_aud_iec_frame_rate iec_frame_fs; enum hdmi_aud_channel_type aud_input_chan_type; - u8 hdmi_l_channel_state[6]; - u8 hdmi_r_channel_state[6]; + struct hdmi_codec_params codec_params; };
struct mtk_hdmi { @@ -201,6 +199,10 @@ int mtk_hdmi_output_set_display_mode(struct mtk_hdmi *hdmi, struct drm_display_mode *mode); void mtk_hdmi_power_on(struct mtk_hdmi *hdmi); void mtk_hdmi_power_off(struct mtk_hdmi *hdmi); +void mtk_hdmi_audio_enable(struct mtk_hdmi *hctx); +void mtk_hdmi_audio_disable(struct mtk_hdmi *hctx); +int mtk_hdmi_audio_set_param(struct mtk_hdmi *hctx, + struct hdmi_audio_param *param); #if defined(CONFIG_DEBUG_FS) int mtk_drm_hdmi_debugfs_init(struct mtk_hdmi *hdmi); void mtk_drm_hdmi_debugfs_exit(struct mtk_hdmi *hdmi); diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_hw.c b/drivers/gpu/drm/mediatek/mtk_hdmi_hw.c index ea4e35f..6acbe77 100644 --- a/drivers/gpu/drm/mediatek/mtk_hdmi_hw.c +++ b/drivers/gpu/drm/mediatek/mtk_hdmi_hw.c @@ -395,90 +395,18 @@ void mtk_hdmi_hw_aud_set_input_type(struct mtk_hdmi *hdmi, }
void mtk_hdmi_hw_aud_set_channel_status(struct mtk_hdmi *hdmi, - u8 *l_chan_status, u8 *r_chan_status, - enum hdmi_audio_sample_frequency - aud_hdmi_fs) -{ - u8 l_status[5]; - u8 r_status[5]; - u8 val = 0; - - l_status[0] = l_chan_status[0]; - l_status[1] = l_chan_status[1]; - l_status[2] = l_chan_status[2]; - r_status[0] = r_chan_status[0]; - r_status[1] = r_chan_status[1]; - r_status[2] = r_chan_status[2]; - - l_status[0] &= ~0x02; - r_status[0] &= ~0x02; - - val = l_chan_status[3] & 0xf0; - switch (aud_hdmi_fs) { - case HDMI_AUDIO_SAMPLE_FREQUENCY_32000: - val |= 0x03; - break; - case HDMI_AUDIO_SAMPLE_FREQUENCY_44100: - break; - case HDMI_AUDIO_SAMPLE_FREQUENCY_88200: - val |= 0x08; - break; - case HDMI_AUDIO_SAMPLE_FREQUENCY_96000: - val |= 0x0a; - break; - case HDMI_AUDIO_SAMPLE_FREQUENCY_48000: - val |= 0x02; - break; - default: - val |= 0x02; - break; - } - l_status[3] = val; - r_status[3] = val; - - val = l_chan_status[4]; - val |= ((~(l_status[3] & 0x0f)) << 4); - l_status[4] = val; - r_status[4] = val; - - val = l_status[0]; - mtk_hdmi_write(hdmi, GRL_I2S_C_STA0, val); - mtk_hdmi_write(hdmi, GRL_L_STATUS_0, val); - - val = r_status[0]; - mtk_hdmi_write(hdmi, GRL_R_STATUS_0, val); - - val = l_status[1]; - mtk_hdmi_write(hdmi, GRL_I2S_C_STA1, val); - mtk_hdmi_write(hdmi, GRL_L_STATUS_1, val); - - val = r_status[1]; - mtk_hdmi_write(hdmi, GRL_R_STATUS_1, val); - - val = l_status[2]; - mtk_hdmi_write(hdmi, GRL_I2S_C_STA2, val); - mtk_hdmi_write(hdmi, GRL_L_STATUS_2, val); - - val = r_status[2]; - mtk_hdmi_write(hdmi, GRL_R_STATUS_2, val); - - val = l_status[3]; - mtk_hdmi_write(hdmi, GRL_I2S_C_STA3, val); - mtk_hdmi_write(hdmi, GRL_L_STATUS_3, val); - - val = r_status[3]; - mtk_hdmi_write(hdmi, GRL_R_STATUS_3, val); - - val = l_status[4]; - mtk_hdmi_write(hdmi, GRL_I2S_C_STA4, val); - mtk_hdmi_write(hdmi, GRL_L_STATUS_4, val); - - val = r_status[4]; - mtk_hdmi_write(hdmi, GRL_R_STATUS_4, val); + u8 *channel_status) +{ + int i;
- for (val = 0; val < 19; val++) { - mtk_hdmi_write(hdmi, GRL_L_STATUS_5 + val * 4, 0); - mtk_hdmi_write(hdmi, GRL_R_STATUS_5 + val * 4, 0); + for (i = 0; i < 5; i++) { + mtk_hdmi_write(hdmi, GRL_I2S_C_STA0 + i * 4, channel_status[i]); + mtk_hdmi_write(hdmi, GRL_L_STATUS_0 + i * 4, channel_status[i]); + mtk_hdmi_write(hdmi, GRL_R_STATUS_0 + i * 4, channel_status[i]); + } + for (; i < 24; i++) { + mtk_hdmi_write(hdmi, GRL_L_STATUS_0 + i * 4, 0); + mtk_hdmi_write(hdmi, GRL_R_STATUS_0 + i * 4, 0); } }
diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_hw.h b/drivers/gpu/drm/mediatek/mtk_hdmi_hw.h index 9013219..a3e1bbc 100644 --- a/drivers/gpu/drm/mediatek/mtk_hdmi_hw.h +++ b/drivers/gpu/drm/mediatek/mtk_hdmi_hw.h @@ -46,9 +46,7 @@ void mtk_hdmi_hw_aud_set_i2s_chan_num(struct mtk_hdmi *hdmi, void mtk_hdmi_hw_aud_set_input_type(struct mtk_hdmi *hdmi, enum hdmi_aud_input_type input_type); void mtk_hdmi_hw_aud_set_channel_status(struct mtk_hdmi *hdmi, - u8 *l_chan_status, u8 *r_chan_staus, - enum hdmi_audio_sample_frequency - aud_hdmi_fs); + u8 *channel_status); void mtk_hdmi_hw_aud_src_enable(struct mtk_hdmi *hdmi, bool enable); void mtk_hdmi_hw_aud_set_mclk(struct mtk_hdmi *hdmi, enum hdmi_aud_mclk mclk); void mtk_hdmi_hw_aud_src_off(struct mtk_hdmi *hdmi);
This should be more robust to future changes than adressing array entries by index number.
Signed-off-by: Philipp Zabel p.zabel@pengutronix.de --- sound/soc/mediatek/mt8173-rt5650-rt5676.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-)
diff --git a/sound/soc/mediatek/mt8173-rt5650-rt5676.c b/sound/soc/mediatek/mt8173-rt5650-rt5676.c index 50ba538..5c4c58c 100644 --- a/sound/soc/mediatek/mt8173-rt5650-rt5676.c +++ b/sound/soc/mediatek/mt8173-rt5650-rt5676.c @@ -131,10 +131,17 @@ static struct snd_soc_dai_link_component mt8173_rt5650_rt5676_codecs[] = { }, };
+enum { + DAI_LINK_PLAYBACK, + DAI_LINK_CAPTURE, + DAI_LINK_CODEC_I2S, + DAI_LINK_INTERCODEC +}; + /* Digital audio interface glue - connects codec <---> CPU */ static struct snd_soc_dai_link mt8173_rt5650_rt5676_dais[] = { /* Front End DAI links */ - { + [DAI_LINK_PLAYBACK] = { .name = "rt5650_rt5676 Playback", .stream_name = "rt5650_rt5676 Playback", .cpu_dai_name = "DL1", @@ -144,7 +151,7 @@ static struct snd_soc_dai_link mt8173_rt5650_rt5676_dais[] = { .dynamic = 1, .dpcm_playback = 1, }, - { + [DAI_LINK_CAPTURE] = { .name = "rt5650_rt5676 Capture", .stream_name = "rt5650_rt5676 Capture", .cpu_dai_name = "VUL", @@ -156,7 +163,7 @@ static struct snd_soc_dai_link mt8173_rt5650_rt5676_dais[] = { },
/* Back End DAI links */ - { + [DAI_LINK_CODEC_I2S] = { .name = "Codec", .cpu_dai_name = "I2S", .no_pcm = 1, @@ -170,7 +177,8 @@ static struct snd_soc_dai_link mt8173_rt5650_rt5676_dais[] = { .dpcm_playback = 1, .dpcm_capture = 1, }, - { /* rt5676 <-> rt5650 intercodec link: Sets rt5676 I2S2 as master */ + /* rt5676 <-> rt5650 intercodec link: Sets rt5676 I2S2 as master */ + [DAI_LINK_INTERCODEC] = { .name = "rt5650_rt5676 intercodec", .stream_name = "rt5650_rt5676 intercodec", .cpu_dai_name = "snd-soc-dummy-dai", @@ -240,7 +248,7 @@ static int mt8173_rt5650_rt5676_dev_probe(struct platform_device *pdev) mt8173_rt5650_rt5676_codec_conf[0].of_node = mt8173_rt5650_rt5676_codecs[1].of_node;
- mt8173_rt5650_rt5676_dais[3].codec_of_node = + mt8173_rt5650_rt5676_dais[DAI_LINK_INTERCODEC].codec_of_node = mt8173_rt5650_rt5676_codecs[1].of_node;
card->dev = &pdev->dev;
From: Koro Chen koro.chen@mediatek.com
This creates pcmC0D2p for the HDMI playback in the same card.
Signed-off-by: Koro Chen koro.chen@mediatek.com Signed-off-by: Philipp Zabel p.zabel@pengutronix.de --- .../bindings/sound/mt8173-rt5650-rt5676.txt | 5 ++- sound/soc/mediatek/mt8173-rt5650-rt5676.c | 48 ++++++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-)
diff --git a/Documentation/devicetree/bindings/sound/mt8173-rt5650-rt5676.txt b/Documentation/devicetree/bindings/sound/mt8173-rt5650-rt5676.txt index f205ce9..ac28cdb 100644 --- a/Documentation/devicetree/bindings/sound/mt8173-rt5650-rt5676.txt +++ b/Documentation/devicetree/bindings/sound/mt8173-rt5650-rt5676.txt @@ -1,15 +1,16 @@ -MT8173 with RT5650 RT5676 CODECS +MT8173 with RT5650 RT5676 CODECS and HDMI via I2S
Required properties: - compatible : "mediatek,mt8173-rt5650-rt5676" - mediatek,audio-codec: the phandles of rt5650 and rt5676 codecs + and of the hdmi encoder node - mediatek,platform: the phandle of MT8173 ASoC platform
Example:
sound { compatible = "mediatek,mt8173-rt5650-rt5676"; - mediatek,audio-codec = <&rt5650 &rt5676>; + mediatek,audio-codec = <&rt5650 &rt5676 &hdmi0>; mediatek,platform = <&afe>; };
diff --git a/sound/soc/mediatek/mt8173-rt5650-rt5676.c b/sound/soc/mediatek/mt8173-rt5650-rt5676.c index 5c4c58c..def9d95 100644 --- a/sound/soc/mediatek/mt8173-rt5650-rt5676.c +++ b/sound/soc/mediatek/mt8173-rt5650-rt5676.c @@ -18,6 +18,7 @@ #include <linux/gpio.h> #include <linux/of_gpio.h> #include <sound/soc.h> +#include <sound/hdmi-codec.h> #include <sound/jack.h> #include "../codecs/rt5645.h" #include "../codecs/rt5677.h" @@ -131,10 +132,31 @@ static struct snd_soc_dai_link_component mt8173_rt5650_rt5676_codecs[] = { }, };
+static struct snd_soc_jack mt8173_hdmi_card_jack; + +static int mt8173_hdmi_init(struct snd_soc_pcm_runtime *runtime) +{ + struct snd_soc_card *card = runtime->card; + struct snd_soc_codec *codec = runtime->codec; + int ret; + + /* enable jack detection */ + ret = snd_soc_card_jack_new(card, "HDMI Jack", SND_JACK_LINEOUT, + &mt8173_hdmi_card_jack, NULL, 0); + if (ret) { + dev_err(card->dev, "Can't new HDMI Jack %d\n", ret); + return ret; + } + + return hdmi_codec_set_jack_detect(codec, &mt8173_hdmi_card_jack); +} + enum { DAI_LINK_PLAYBACK, DAI_LINK_CAPTURE, + DAI_LINK_HDMI, DAI_LINK_CODEC_I2S, + DAI_LINK_HDMI_I2S, DAI_LINK_INTERCODEC };
@@ -161,6 +183,16 @@ static struct snd_soc_dai_link mt8173_rt5650_rt5676_dais[] = { .dynamic = 1, .dpcm_capture = 1, }, + [DAI_LINK_HDMI] = { + .name = "HDMI", + .stream_name = "HDMI PCM", + .cpu_dai_name = "HDMI", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dynamic = 1, + .dpcm_playback = 1, + },
/* Back End DAI links */ [DAI_LINK_CODEC_I2S] = { @@ -177,6 +209,14 @@ static struct snd_soc_dai_link mt8173_rt5650_rt5676_dais[] = { .dpcm_playback = 1, .dpcm_capture = 1, }, + [DAI_LINK_HDMI_I2S] = { + .name = "HDMI BE", + .cpu_dai_name = "HDMIO", + .no_pcm = 1, + .codec_dai_name = "i2s-hifi", + .dpcm_playback = 1, + .init = mt8173_hdmi_init, + }, /* rt5676 <-> rt5650 intercodec link: Sets rt5676 I2S2 as master */ [DAI_LINK_INTERCODEC] = { .name = "rt5650_rt5676 intercodec", @@ -251,6 +291,14 @@ static int mt8173_rt5650_rt5676_dev_probe(struct platform_device *pdev) mt8173_rt5650_rt5676_dais[DAI_LINK_INTERCODEC].codec_of_node = mt8173_rt5650_rt5676_codecs[1].of_node;
+ mt8173_rt5650_rt5676_dais[DAI_LINK_HDMI_I2S].codec_of_node = + of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 2); + if (!mt8173_rt5650_rt5676_dais[DAI_LINK_HDMI_I2S].codec_of_node) { + dev_err(&pdev->dev, + "Property 'audio-codec' missing or invalid\n"); + return -EINVAL; + } + card->dev = &pdev->dev; platform_set_drvdata(pdev, card);
On 02/03/16 15:49, Philipp Zabel wrote:
From: Koro Chen koro.chen@mediatek.com
This creates pcmC0D2p for the HDMI playback in the same card.
Signed-off-by: Koro Chen koro.chen@mediatek.com Signed-off-by: Philipp Zabel p.zabel@pengutronix.de
.../bindings/sound/mt8173-rt5650-rt5676.txt | 5 ++- sound/soc/mediatek/mt8173-rt5650-rt5676.c | 48 ++++++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-)
diff --git a/Documentation/devicetree/bindings/sound/mt8173-rt5650-rt5676.txt b/Documentation/devicetree/bindings/sound/mt8173-rt5650-rt5676.txt index f205ce9..ac28cdb 100644 --- a/Documentation/devicetree/bindings/sound/mt8173-rt5650-rt5676.txt +++ b/Documentation/devicetree/bindings/sound/mt8173-rt5650-rt5676.txt @@ -1,15 +1,16 @@ -MT8173 with RT5650 RT5676 CODECS +MT8173 with RT5650 RT5676 CODECS and HDMI via I2S
Required properties:
- compatible : "mediatek,mt8173-rt5650-rt5676"
- mediatek,audio-codec: the phandles of rt5650 and rt5676 codecs
and of the hdmi encoder node
- mediatek,platform: the phandle of MT8173 ASoC platform
Example:
sound { compatible = "mediatek,mt8173-rt5650-rt5676";
mediatek,audio-codec = <&rt5650 &rt5676>;
mediatek,platform = <&afe>; };mediatek,audio-codec = <&rt5650 &rt5676 &hdmi0>;
diff --git a/sound/soc/mediatek/mt8173-rt5650-rt5676.c b/sound/soc/mediatek/mt8173-rt5650-rt5676.c index 5c4c58c..def9d95 100644 --- a/sound/soc/mediatek/mt8173-rt5650-rt5676.c +++ b/sound/soc/mediatek/mt8173-rt5650-rt5676.c @@ -18,6 +18,7 @@ #include <linux/gpio.h> #include <linux/of_gpio.h> #include <sound/soc.h> +#include <sound/hdmi-codec.h> #include <sound/jack.h> #include "../codecs/rt5645.h" #include "../codecs/rt5677.h" @@ -131,10 +132,31 @@ static struct snd_soc_dai_link_component mt8173_rt5650_rt5676_codecs[] = { }, };
+static struct snd_soc_jack mt8173_hdmi_card_jack;
+static int mt8173_hdmi_init(struct snd_soc_pcm_runtime *runtime) +{
- struct snd_soc_card *card = runtime->card;
- struct snd_soc_codec *codec = runtime->codec;
- int ret;
- /* enable jack detection */
- ret = snd_soc_card_jack_new(card, "HDMI Jack", SND_JACK_LINEOUT,
&mt8173_hdmi_card_jack, NULL, 0);
- if (ret) {
dev_err(card->dev, "Can't new HDMI Jack %d\n", ret);
return ret;
- }
- return hdmi_codec_set_jack_detect(codec, &mt8173_hdmi_card_jack);
+}
- enum { DAI_LINK_PLAYBACK, DAI_LINK_CAPTURE,
- DAI_LINK_HDMI, DAI_LINK_CODEC_I2S,
- DAI_LINK_HDMI_I2S, DAI_LINK_INTERCODEC };
@@ -161,6 +183,16 @@ static struct snd_soc_dai_link mt8173_rt5650_rt5676_dais[] = { .dynamic = 1, .dpcm_capture = 1, },
[DAI_LINK_HDMI] = {
.name = "HDMI",
.stream_name = "HDMI PCM",
.cpu_dai_name = "HDMI",
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
.dynamic = 1,
.dpcm_playback = 1,
},
/* Back End DAI links */ [DAI_LINK_CODEC_I2S] = {
@@ -177,6 +209,14 @@ static struct snd_soc_dai_link mt8173_rt5650_rt5676_dais[] = { .dpcm_playback = 1, .dpcm_capture = 1, },
- [DAI_LINK_HDMI_I2S] = {
.name = "HDMI BE",
.cpu_dai_name = "HDMIO",
.no_pcm = 1,
.codec_dai_name = "i2s-hifi",
.dpcm_playback = 1,
.init = mt8173_hdmi_init,
- }, /* rt5676 <-> rt5650 intercodec link: Sets rt5676 I2S2 as master */ [DAI_LINK_INTERCODEC] = { .name = "rt5650_rt5676 intercodec",
@@ -251,6 +291,14 @@ static int mt8173_rt5650_rt5676_dev_probe(struct platform_device *pdev) mt8173_rt5650_rt5676_dais[DAI_LINK_INTERCODEC].codec_of_node = mt8173_rt5650_rt5676_codecs[1].of_node;
- mt8173_rt5650_rt5676_dais[DAI_LINK_HDMI_I2S].codec_of_node =
of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 2);
- if (!mt8173_rt5650_rt5676_dais[DAI_LINK_HDMI_I2S].codec_of_node) {
dev_err(&pdev->dev,
"Property 'audio-codec' missing or invalid\n");
return -EINVAL;
Do we really need to break the old dts binding? Can't we just drop the return and keep add the audio-codec as optional?
Regards, Matthias
- }
- card->dev = &pdev->dev; platform_set_drvdata(pdev, card);
Hi Matthias,
On Tue, 2016-03-15 at 07:36 +0800, Matthias Brugger wrote:
On 02/03/16 15:49, Philipp Zabel wrote:
From: Koro Chen koro.chen@mediatek.com
This creates pcmC0D2p for the HDMI playback in the same card.
Signed-off-by: Koro Chen koro.chen@mediatek.com Signed-off-by: Philipp Zabel p.zabel@pengutronix.de
.../bindings/sound/mt8173-rt5650-rt5676.txt | 5 ++- sound/soc/mediatek/mt8173-rt5650-rt5676.c | 48 ++++++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-)
diff --git a/Documentation/devicetree/bindings/sound/mt8173-rt5650-rt5676.txt b/Documentation/devicetree/bindings/sound/mt8173-rt5650-rt5676.txt index f205ce9..ac28cdb 100644 --- a/Documentation/devicetree/bindings/sound/mt8173-rt5650-rt5676.txt +++ b/Documentation/devicetree/bindings/sound/mt8173-rt5650-rt5676.txt @@ -1,15 +1,16 @@ -MT8173 with RT5650 RT5676 CODECS +MT8173 with RT5650 RT5676 CODECS and HDMI via I2S
Required properties:
- compatible : "mediatek,mt8173-rt5650-rt5676"
- mediatek,audio-codec: the phandles of rt5650 and rt5676 codecs
and of the hdmi encoder node
- mediatek,platform: the phandle of MT8173 ASoC platform
Example:
sound { compatible = "mediatek,mt8173-rt5650-rt5676";
mediatek,audio-codec = <&rt5650 &rt5676>;
mediatek,platform = <&afe>; };mediatek,audio-codec = <&rt5650 &rt5676 &hdmi0>;
diff --git a/sound/soc/mediatek/mt8173-rt5650-rt5676.c b/sound/soc/mediatek/mt8173-rt5650-rt5676.c index 5c4c58c..def9d95 100644 --- a/sound/soc/mediatek/mt8173-rt5650-rt5676.c +++ b/sound/soc/mediatek/mt8173-rt5650-rt5676.c @@ -18,6 +18,7 @@ #include <linux/gpio.h> #include <linux/of_gpio.h> #include <sound/soc.h> +#include <sound/hdmi-codec.h> #include <sound/jack.h> #include "../codecs/rt5645.h" #include "../codecs/rt5677.h" @@ -131,10 +132,31 @@ static struct snd_soc_dai_link_component mt8173_rt5650_rt5676_codecs[] = { }, };
+static struct snd_soc_jack mt8173_hdmi_card_jack;
+static int mt8173_hdmi_init(struct snd_soc_pcm_runtime *runtime) +{
- struct snd_soc_card *card = runtime->card;
- struct snd_soc_codec *codec = runtime->codec;
- int ret;
- /* enable jack detection */
- ret = snd_soc_card_jack_new(card, "HDMI Jack", SND_JACK_LINEOUT,
&mt8173_hdmi_card_jack, NULL, 0);
- if (ret) {
dev_err(card->dev, "Can't new HDMI Jack %d\n", ret);
return ret;
- }
- return hdmi_codec_set_jack_detect(codec, &mt8173_hdmi_card_jack);
+}
- enum { DAI_LINK_PLAYBACK, DAI_LINK_CAPTURE,
- DAI_LINK_HDMI, DAI_LINK_CODEC_I2S,
- DAI_LINK_HDMI_I2S, DAI_LINK_INTERCODEC };
@@ -161,6 +183,16 @@ static struct snd_soc_dai_link mt8173_rt5650_rt5676_dais[] = { .dynamic = 1, .dpcm_capture = 1, },
[DAI_LINK_HDMI] = {
.name = "HDMI",
.stream_name = "HDMI PCM",
.cpu_dai_name = "HDMI",
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
.dynamic = 1,
.dpcm_playback = 1,
},
/* Back End DAI links */ [DAI_LINK_CODEC_I2S] = {
@@ -177,6 +209,14 @@ static struct snd_soc_dai_link mt8173_rt5650_rt5676_dais[] = { .dpcm_playback = 1, .dpcm_capture = 1, },
- [DAI_LINK_HDMI_I2S] = {
.name = "HDMI BE",
.cpu_dai_name = "HDMIO",
.no_pcm = 1,
.codec_dai_name = "i2s-hifi",
.dpcm_playback = 1,
.init = mt8173_hdmi_init,
- }, /* rt5676 <-> rt5650 intercodec link: Sets rt5676 I2S2 as master */ [DAI_LINK_INTERCODEC] = { .name = "rt5650_rt5676 intercodec",
@@ -251,6 +291,14 @@ static int mt8173_rt5650_rt5676_dev_probe(struct platform_device *pdev) mt8173_rt5650_rt5676_dais[DAI_LINK_INTERCODEC].codec_of_node = mt8173_rt5650_rt5676_codecs[1].of_node;
- mt8173_rt5650_rt5676_dais[DAI_LINK_HDMI_I2S].codec_of_node =
of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 2);
- if (!mt8173_rt5650_rt5676_dais[DAI_LINK_HDMI_I2S].codec_of_node) {
dev_err(&pdev->dev,
"Property 'audio-codec' missing or invalid\n");
return -EINVAL;
Do we really need to break the old dts binding? Can't we just drop the return and keep add the audio-codec as optional?
Regards, Matthias
I think that it really needs to break dts binding, because it has declared DAI_LINK_HDMI_I2S in static struct snd_soc_dai_link mt8173_rt5650_rt5676_dais[].
If I just remove this line: return -EINVAL; it will probe failed like: [ 2.498403] mtk-rt5650-rt5676 sound: Property 'audio-codec' missing or invalid [ 2.505031] mtk-rt5650-rt5676 sound: ASoC: Neither/both codec name/of_node are set for HDMI BE [ 2.513090] mtk-rt5650-rt5676 sound: mt8173_rt5650_rt5676_dev_probe snd_soc_register_card fail -22 [ 2.520896] mtk-rt5650-rt5676: probe of sound failed with error -22
Thanks!
- }
- card->dev = &pdev->dev; platform_set_drvdata(pdev, card);
Alsa-devel mailing list Alsa-devel@alsa-project.org http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
This is Russell's HDMI notification prototype [1], currently waiting for the HDMI CEC situation to resolve.
The use case for the notifications on MediaTek MT8173 is to let the (dis)connection notifications control an ALSA jack object.
No Signed-off-by since this is not my code, and still up for discussion.
[1] https://patchwork.kernel.org/patch/8351501/ --- Changes since v4: - Updated to latest version --- drivers/video/Kconfig | 3 +++ drivers/video/Makefile | 1 + drivers/video/hdmi-notifier.c | 61 +++++++++++++++++++++++++++++++++++++++++++ include/linux/hdmi-notifier.h | 44 +++++++++++++++++++++++++++++++ 4 files changed, 109 insertions(+) create mode 100644 drivers/video/hdmi-notifier.c create mode 100644 include/linux/hdmi-notifier.h
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index e0606c0..193649c 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -40,6 +40,9 @@ config VIDEOMODE_HELPERS config HDMI bool
+config HDMI_NOTIFIERS + bool + if VT source "drivers/video/console/Kconfig" endif diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 9ad3c17..65f5649 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_VGASTATE) += vgastate.o obj-$(CONFIG_HDMI) += hdmi.o +obj-$(CONFIG_HDMI_NOTIFIERS) += hdmi-notifier.o
obj-$(CONFIG_VT) += console/ obj-$(CONFIG_LOGO) += logo/ diff --git a/drivers/video/hdmi-notifier.c b/drivers/video/hdmi-notifier.c new file mode 100644 index 0000000..a233359 --- /dev/null +++ b/drivers/video/hdmi-notifier.c @@ -0,0 +1,61 @@ +#include <linux/export.h> +#include <linux/hdmi-notifier.h> +#include <linux/notifier.h> +#include <linux/string.h> + +static BLOCKING_NOTIFIER_HEAD(hdmi_notifier); + +int hdmi_register_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&hdmi_notifier, nb); +} +EXPORT_SYMBOL_GPL(hdmi_register_notifier); + +int hdmi_unregister_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&hdmi_notifier, nb); +} +EXPORT_SYMBOL_GPL(hdmi_unregister_notifier); + +void hdmi_event_connect(struct device *dev) +{ + struct hdmi_event_base base; + + base.source = dev; + + blocking_notifier_call_chain(&hdmi_notifier, HDMI_CONNECTED, &base); +} +EXPORT_SYMBOL_GPL(hdmi_event_connect); + +void hdmi_event_disconnect(struct device *dev) +{ + struct hdmi_event_base base; + + base.source = dev; + + blocking_notifier_call_chain(&hdmi_notifier, HDMI_DISCONNECTED, &base); +} +EXPORT_SYMBOL_GPL(hdmi_event_disconnect); + +void hdmi_event_new_edid(struct device *dev, const void *edid, size_t size) +{ + struct hdmi_event_new_edid new_edid; + + new_edid.base.source = dev; + new_edid.edid = edid; + new_edid.size = size; + + blocking_notifier_call_chain(&hdmi_notifier, HDMI_NEW_EDID, &new_edid); +} +EXPORT_SYMBOL_GPL(hdmi_event_new_edid); + +void hdmi_event_new_eld(struct device *dev, const void *eld) +{ + struct hdmi_event_new_eld new_eld; + + new_eld.base.source = dev; + memcpy(new_eld.eld, eld, sizeof(new_eld.eld)); + + blocking_notifier_call_chain(&hdmi_notifier, HDMI_NEW_ELD, &new_eld); +} +EXPORT_SYMBOL_GPL(hdmi_event_new_eld); diff --git a/include/linux/hdmi-notifier.h b/include/linux/hdmi-notifier.h new file mode 100644 index 0000000..9c5ad49 --- /dev/null +++ b/include/linux/hdmi-notifier.h @@ -0,0 +1,44 @@ +#ifndef LINUX_HDMI_NOTIFIER_H +#define LINUX_HDMI_NOTIFIER_H + +#include <linux/types.h> + +enum { + HDMI_CONNECTED, + HDMI_DISCONNECTED, + HDMI_NEW_EDID, + HDMI_NEW_ELD, +}; + +struct hdmi_event_base { + struct device *source; +}; + +struct hdmi_event_new_edid { + struct hdmi_event_base base; + const void *edid; + size_t size; +}; + +struct hdmi_event_new_eld { + struct hdmi_event_base base; + unsigned char eld[128]; +}; + +union hdmi_event { + struct hdmi_event_base base; + struct hdmi_event_new_edid edid; + struct hdmi_event_new_eld eld; +}; + +struct notifier_block; + +int hdmi_register_notifier(struct notifier_block *nb); +int hdmi_unregister_notifier(struct notifier_block *nb); + +void hdmi_event_connect(struct device *dev); +void hdmi_event_disconnect(struct device *dev); +void hdmi_event_new_edid(struct device *dev, const void *edid, size_t size); +void hdmi_event_new_eld(struct device *dev, const void *eld); + +#endif
Issue hot-plug detection, EDID update, and ELD update notifications from the CEC and HDMI drivers.
Signed-off-by: Philipp Zabel p.zabel@pengutronix.de --- drivers/gpu/drm/mediatek/mtk_cec.c | 11 +++++++++++ drivers/gpu/drm/mediatek/mtk_drm_hdmi_drv.c | 3 +++ 2 files changed, 14 insertions(+)
diff --git a/drivers/gpu/drm/mediatek/mtk_cec.c b/drivers/gpu/drm/mediatek/mtk_cec.c index cba3647..11e3449 100644 --- a/drivers/gpu/drm/mediatek/mtk_cec.c +++ b/drivers/gpu/drm/mediatek/mtk_cec.c @@ -13,6 +13,7 @@ */ #include <linux/clk.h> #include <linux/delay.h> +#include <linux/hdmi-notifier.h> #include <linux/io.h> #include <linux/interrupt.h> #include <linux/platform_device.h> @@ -79,6 +80,12 @@ void mtk_cec_set_hpd_event(struct device *dev,
cec->hdmi_dev = hdmi_dev; cec->hpd_event = hpd_event; + + /* Initial notification event to set jack state */ + if (mtk_cec_hpd_high(dev)) + hdmi_event_connect(hdmi_dev); + else + hdmi_event_disconnect(hdmi_dev); }
bool mtk_cec_hpd_high(struct device *dev) @@ -160,6 +167,10 @@ static irqreturn_t mtk_cec_htplg_isr_thread(int irq, void *arg) if (cec->hpd != hpd) { dev_info(dev, "hotplug event!,cur hpd = %d, hpd = %d\n", cec->hpd, hpd); + if (hpd) + hdmi_event_connect(cec->hdmi_dev); + else + hdmi_event_disconnect(cec->hdmi_dev); cec->hpd = hpd; if (cec->hpd_event) cec->hpd_event(hpd, cec->hdmi_dev); diff --git a/drivers/gpu/drm/mediatek/mtk_drm_hdmi_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_hdmi_drv.c index 96ad53a..0775a17 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_hdmi_drv.c +++ b/drivers/gpu/drm/mediatek/mtk_drm_hdmi_drv.c @@ -17,6 +17,7 @@ #include <drm/drm_crtc_helper.h> #include <drm/drm_edid.h> #include <linux/clk.h> +#include <linux/hdmi-notifier.h> #include <linux/i2c.h> #include <linux/kernel.h> #include <linux/mfd/syscon.h> @@ -122,9 +123,11 @@ static int mtk_hdmi_conn_get_modes(struct drm_connector *conn) hdmi->dvi_mode = !drm_detect_monitor_audio(edid);
drm_mode_connector_update_edid_property(conn, edid); + hdmi_event_new_edid(hdmi->dev, edid, sizeof(*edid));
ret = drm_add_edid_modes(conn, edid); drm_edid_to_eld(conn, edid); + hdmi_event_new_eld(hdmi->dev, conn->eld); kfree(edid); return ret; }
Use HDMI connection / disconnection notifications to update an ALSA jack object. Also make a copy of the ELD block after every change.
Signed-off-by: Philipp Zabel p.zabel@pengutronix.de --- include/sound/hdmi-codec.h | 6 +++ sound/soc/codecs/hdmi-codec.c | 85 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 83 insertions(+), 8 deletions(-)
diff --git a/include/sound/hdmi-codec.h b/include/sound/hdmi-codec.h index 15fe70f..8b6219d 100644 --- a/include/sound/hdmi-codec.h +++ b/include/sound/hdmi-codec.h @@ -99,6 +99,12 @@ struct hdmi_codec_pdata { int max_i2s_channels; };
+struct snd_soc_codec; +struct snd_soc_jack; + +int hdmi_codec_set_jack_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *jack); + #define HDMI_CODEC_DRV_NAME "hdmi-audio-codec"
#endif /* __HDMI_CODEC_H__ */ diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c index 687332d..7d5db11 100644 --- a/sound/soc/codecs/hdmi-codec.c +++ b/sound/soc/codecs/hdmi-codec.c @@ -12,9 +12,12 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. */ +#include <linux/hdmi-not.h> #include <linux/module.h> +#include <linux/notifier.h> #include <linux/string.h> #include <sound/core.h> +#include <sound/jack.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> @@ -27,11 +30,16 @@ struct hdmi_codec_priv { struct hdmi_codec_pdata hcd; struct snd_soc_dai_driver *daidrv; + struct snd_soc_jack *jack; struct hdmi_codec_daifmt daifmt[2]; struct mutex current_stream_lock; struct snd_pcm_substream *current_stream; struct snd_pcm_hw_constraint_list ratec; + struct mutex eld_lock; uint8_t eld[MAX_ELD_BYTES]; + struct device *dev; + struct notifier_block nb; + unsigned int jack_status; };
static const struct snd_soc_dapm_widget hdmi_widgets[] = { @@ -86,7 +94,7 @@ static int hdmi_codec_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); - int ret = 0; + int ret;
dev_dbg(dai->dev, "%s()\n", __func__);
@@ -106,17 +114,15 @@ static int hdmi_codec_startup(struct snd_pcm_substream *substream, }
if (hcp->hcd.ops->get_eld) { + mutex_lock(&hcp->eld_lock); ret = hcp->hcd.ops->get_eld(dai->dev->parent, hcp->eld, sizeof(hcp->eld)); - - if (!ret) { + if (!ret) ret = snd_pcm_hw_constraint_eld(substream->runtime, hcp->eld); - if (ret) - return ret; - } + mutex_unlock(&hcp->eld_lock); } - return 0; + return ret; }
static void hdmi_codec_shutdown(struct snd_pcm_substream *substream, @@ -326,6 +332,62 @@ static struct snd_soc_codec_driver hdmi_codec = { .num_dapm_routes = ARRAY_SIZE(hdmi_routes), };
+static void hdmi_codec_jack_report(struct hdmi_codec_priv *hcp, + unsigned int jack_status) +{ + if (!hcp->jack) + return; + + if (jack_status != hcp->jack_status) { + snd_soc_jack_report(hcp->jack, jack_status, SND_JACK_LINEOUT); + hcp->jack_status = jack_status; + } +} + +static int hdmi_codec_notify(struct notifier_block *nb, unsigned long event, + void *data) +{ + struct hdmi_codec_priv *hcp = container_of(nb, struct hdmi_codec_priv, + nb); + union hdmi_event *event_block = data; + + if (hcp->dev->parent != event_block->base.source) + return NOTIFY_OK; + + if (!hcp->jack) + return NOTIFY_OK; + + switch (event) { + case HDMI_CONNECTED: + hdmi_codec_jack_report(hcp, SND_JACK_LINEOUT); + break; + case HDMI_DISCONNECTED: + hdmi_codec_jack_report(hcp, 0); + break; + case HDMI_NEW_ELD: + mutex_lock(&hcp->eld_lock); + memcpy(hcp->eld, event_block->eld.eld, sizeof(hcp->eld)); + mutex_unlock(&hcp->eld_lock); + break; + } + + return NOTIFY_OK; +} + +int hdmi_codec_set_jack_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *jack) +{ + struct hdmi_codec_priv *hcp = snd_soc_codec_get_drvdata(codec); + + hcp->jack = jack; + hcp->nb.notifier_call = hdmi_codec_notify; + + hdmi_register_notifier(&hcp->nb); + + return 0; +} +EXPORT_SYMBOL_GPL(hdmi_codec_set_jack_detect); + static int hdmi_codec_probe(struct platform_device *pdev) { struct hdmi_codec_pdata *hcd = pdev->dev.platform_data; @@ -354,6 +416,7 @@ static int hdmi_codec_probe(struct platform_device *pdev)
hcp->hcd = *hcd; mutex_init(&hcp->current_stream_lock); + mutex_init(&hcp->eld_lock);
hcp->daidrv = devm_kzalloc(dev, dai_count * sizeof(*hcp->daidrv), GFP_KERNEL); @@ -370,6 +433,8 @@ static int hdmi_codec_probe(struct platform_device *pdev) if (hcd->spdif) hcp->daidrv[i] = hdmi_spdif_dai;
+ dev_set_drvdata(dev, hcp); + ret = snd_soc_register_codec(dev, &hdmi_codec, hcp->daidrv, dai_count); if (ret) { @@ -378,12 +443,16 @@ static int hdmi_codec_probe(struct platform_device *pdev) return ret; }
- dev_set_drvdata(dev, hcp); + hcp->dev = dev; + return 0; }
static int hdmi_codec_remove(struct platform_device *pdev) { + struct hdmi_codec_priv *hcp = platform_get_drvdata(pdev); + + hdmi_unregister_notifier(&hcp->nb); snd_soc_unregister_codec(&pdev->dev); return 0; }
I don't think this patch was updated according to updated "video: rmk's HDMI notification prototype":
On 03/02/16 16:49, Philipp Zabel wrote:
Use HDMI connection / disconnection notifications to update an ALSA jack object. Also make a copy of the ELD block after every change.
Signed-off-by: Philipp Zabel p.zabel@pengutronix.de
include/sound/hdmi-codec.h | 6 +++ sound/soc/codecs/hdmi-codec.c | 85 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 83 insertions(+), 8 deletions(-)
diff --git a/include/sound/hdmi-codec.h b/include/sound/hdmi-codec.h index 15fe70f..8b6219d 100644 --- a/include/sound/hdmi-codec.h +++ b/include/sound/hdmi-codec.h @@ -99,6 +99,12 @@ struct hdmi_codec_pdata { int max_i2s_channels; };
+struct snd_soc_codec; +struct snd_soc_jack;
+int hdmi_codec_set_jack_detect(struct snd_soc_codec *codec,
struct snd_soc_jack *jack);
#define HDMI_CODEC_DRV_NAME "hdmi-audio-codec"
#endif /* __HDMI_CODEC_H__ */
diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c index 687332d..7d5db11 100644 --- a/sound/soc/codecs/hdmi-codec.c +++ b/sound/soc/codecs/hdmi-codec.c @@ -12,9 +12,12 @@
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
*/ +#include <linux/hdmi-not.h>
This should be hdmi-notifier.h. "select HDMI_NOTIFIERS" also needs to be added to SND_SOC_HDMI_CODEC config option for compilation to succeed.
#include <linux/module.h> +#include <linux/notifier.h> #include <linux/string.h> #include <sound/core.h> +#include <sound/jack.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> @@ -27,11 +30,16 @@ struct hdmi_codec_priv { struct hdmi_codec_pdata hcd; struct snd_soc_dai_driver *daidrv;
struct snd_soc_jack *jack; struct hdmi_codec_daifmt daifmt[2]; struct mutex current_stream_lock; struct snd_pcm_substream *current_stream; struct snd_pcm_hw_constraint_list ratec;
struct mutex eld_lock; uint8_t eld[MAX_ELD_BYTES];
struct device *dev;
struct notifier_block nb;
unsigned int jack_status; };
static const struct snd_soc_dapm_widget hdmi_widgets[] = {
@@ -86,7 +94,7 @@ static int hdmi_codec_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
- int ret = 0;
int ret;
dev_dbg(dai->dev, "%s()\n", __func__);
@@ -106,17 +114,15 @@ static int hdmi_codec_startup(struct snd_pcm_substream *substream, }
if (hcp->hcd.ops->get_eld) {
ret = hcp->hcd.ops->get_eld(dai->dev->parent, hcp->eld, sizeof(hcp->eld));mutex_lock(&hcp->eld_lock);
if (!ret) {
if (!ret) ret = snd_pcm_hw_constraint_eld(substream->runtime, hcp->eld);
if (ret)
return ret;
}
}mutex_unlock(&hcp->eld_lock);
- return 0;
return ret; }
static void hdmi_codec_shutdown(struct snd_pcm_substream *substream,
@@ -326,6 +332,62 @@ static struct snd_soc_codec_driver hdmi_codec = { .num_dapm_routes = ARRAY_SIZE(hdmi_routes), };
+static void hdmi_codec_jack_report(struct hdmi_codec_priv *hcp,
unsigned int jack_status)
+{
- if (!hcp->jack)
return;
- if (jack_status != hcp->jack_status) {
snd_soc_jack_report(hcp->jack, jack_status, SND_JACK_LINEOUT);
hcp->jack_status = jack_status;
- }
+}
+static int hdmi_codec_notify(struct notifier_block *nb, unsigned long event,
void *data)
+{
- struct hdmi_codec_priv *hcp = container_of(nb, struct hdmi_codec_priv,
nb);
- union hdmi_event *event_block = data;
- if (hcp->dev->parent != event_block->base.source)
return NOTIFY_OK;
- if (!hcp->jack)
return NOTIFY_OK;
- switch (event) {
- case HDMI_CONNECTED:
hdmi_codec_jack_report(hcp, SND_JACK_LINEOUT);
break;
- case HDMI_DISCONNECTED:
hdmi_codec_jack_report(hcp, 0);
break;
- case HDMI_NEW_ELD:
mutex_lock(&hcp->eld_lock);
memcpy(hcp->eld, event_block->eld.eld, sizeof(hcp->eld));
mutex_unlock(&hcp->eld_lock);
break;
- }
- return NOTIFY_OK;
+}
+int hdmi_codec_set_jack_detect(struct snd_soc_codec *codec,
struct snd_soc_jack *jack)
+{
- struct hdmi_codec_priv *hcp = snd_soc_codec_get_drvdata(codec);
- hcp->jack = jack;
- hcp->nb.notifier_call = hdmi_codec_notify;
- hdmi_register_notifier(&hcp->nb);
- return 0;
+} +EXPORT_SYMBOL_GPL(hdmi_codec_set_jack_detect);
- static int hdmi_codec_probe(struct platform_device *pdev) { struct hdmi_codec_pdata *hcd = pdev->dev.platform_data;
@@ -354,6 +416,7 @@ static int hdmi_codec_probe(struct platform_device *pdev)
hcp->hcd = *hcd; mutex_init(&hcp->current_stream_lock);
mutex_init(&hcp->eld_lock);
hcp->daidrv = devm_kzalloc(dev, dai_count * sizeof(*hcp->daidrv), GFP_KERNEL);
@@ -370,6 +433,8 @@ static int hdmi_codec_probe(struct platform_device *pdev) if (hcd->spdif) hcp->daidrv[i] = hdmi_spdif_dai;
- dev_set_drvdata(dev, hcp);
- ret = snd_soc_register_codec(dev, &hdmi_codec, hcp->daidrv, dai_count); if (ret) {
@@ -378,12 +443,16 @@ static int hdmi_codec_probe(struct platform_device *pdev) return ret; }
- dev_set_drvdata(dev, hcp);
hcp->dev = dev;
return 0; }
static int hdmi_codec_remove(struct platform_device *pdev) {
struct hdmi_codec_priv *hcp = platform_get_drvdata(pdev);
hdmi_unregister_notifier(&hcp->nb); snd_soc_unregister_codec(&pdev->dev); return 0; }
Hi Jyri,
Am Dienstag, den 08.03.2016, 00:31 +0200 schrieb Jyri Sarha:
I don't think this patch was updated according to updated "video: rmk's HDMI notification prototype":
On 03/02/16 16:49, Philipp Zabel wrote:
Use HDMI connection / disconnection notifications to update an ALSA jack object. Also make a copy of the ELD block after every change.
Signed-off-by: Philipp Zabel p.zabel@pengutronix.de
include/sound/hdmi-codec.h | 6 +++ sound/soc/codecs/hdmi-codec.c | 85 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 83 insertions(+), 8 deletions(-)
diff --git a/include/sound/hdmi-codec.h b/include/sound/hdmi-codec.h index 15fe70f..8b6219d 100644 --- a/include/sound/hdmi-codec.h +++ b/include/sound/hdmi-codec.h @@ -99,6 +99,12 @@ struct hdmi_codec_pdata { int max_i2s_channels; };
+struct snd_soc_codec; +struct snd_soc_jack;
+int hdmi_codec_set_jack_detect(struct snd_soc_codec *codec,
struct snd_soc_jack *jack);
#define HDMI_CODEC_DRV_NAME "hdmi-audio-codec"
#endif /* __HDMI_CODEC_H__ */
diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c index 687332d..7d5db11 100644 --- a/sound/soc/codecs/hdmi-codec.c +++ b/sound/soc/codecs/hdmi-codec.c @@ -12,9 +12,12 @@
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
*/ +#include <linux/hdmi-not.h>
This should be hdmi-notifier.h. "select HDMI_NOTIFIERS" also needs to be added to SND_SOC_HDMI_CODEC config option for compilation to succeed.
Yes, thank you for pointing that out. Will be fixed in v6.
regards Philipp
ALSA doesn't know about all the different compressed audio formats, so there is no interface to let userspace enumerate the formats that are supported by the connected sink. Exporting the raw ELD bytes to userspace allows an application to select the appropriate audio format depending on the current capabilities of the connected HDMI sink device. Usually userspace then just pretends to ALSA that the data is in one of the raw 16-bit PCM audio formats and relies on the IEC controls to tell the sink how to interpret the data.
Signed-off-by: Philipp Zabel p.zabel@pengutronix.de Reviewed-by: Jyri Sarha jsarha@ti.com Tested-by: Jyri Sarha jsarha@ti.com --- sound/soc/codecs/hdmi-codec.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+)
diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c index 7d5db11..9c4e65c 100644 --- a/sound/soc/codecs/hdmi-codec.c +++ b/sound/soc/codecs/hdmi-codec.c @@ -55,6 +55,42 @@ enum { DAI_ID_SPDIF, };
+static int hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = sizeof(hcp->eld); + + return 0; +} + +static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component); + + mutex_lock(&hcp->eld_lock); + memcpy(ucontrol->value.bytes.data, hcp->eld, sizeof(hcp->eld)); + mutex_unlock(&hcp->eld_lock); + + return 0; +} + +static const struct snd_kcontrol_new hdmi_controls[] = { + { + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "ELD", + .info = hdmi_eld_ctl_info, + .get = hdmi_eld_ctl_get, + }, +}; + static void hdmi_codec_abort(struct device *dev) { struct hdmi_codec_priv *hcp = dev_get_drvdata(dev); @@ -326,6 +362,8 @@ static const struct snd_soc_dai_driver hdmi_spdif_dai = { };
static struct snd_soc_codec_driver hdmi_codec = { + .controls = hdmi_controls, + .num_controls = ARRAY_SIZE(hdmi_controls), .dapm_widgets = hdmi_widgets, .num_dapm_widgets = ARRAY_SIZE(hdmi_widgets), .dapm_routes = hdmi_routes,
Use the hdmi helper function [1] to calculate N and CTS.
[1] https://patchwork.kernel.org/patch/8091531/ ("video: hdmi: add helper function for N and CTS")
Signed-off-by: Philipp Zabel p.zabel@pengutronix.de --- drivers/gpu/drm/mediatek/mtk_hdmi_hw.c | 85 +++++++++------------------------- 1 file changed, 21 insertions(+), 64 deletions(-)
diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_hw.c b/drivers/gpu/drm/mediatek/mtk_hdmi_hw.c index 6acbe77..9be50f5 100644 --- a/drivers/gpu/drm/mediatek/mtk_hdmi_hw.c +++ b/drivers/gpu/drm/mediatek/mtk_hdmi_hw.c @@ -492,65 +492,6 @@ static const struct hdmi_acr_n hdmi_rec_n_table[] = { { 0, { 4096, 6272, 6144 } }, /* all other TMDS clocks */ };
-/** - * hdmi_recommended_n() - Return N value recommended by HDMI specification - * @freq: audio sample rate in Hz - * @clock: rounded TMDS clock in kHz - */ -static unsigned int hdmi_recommended_n(unsigned int freq, unsigned int clock) -{ - const struct hdmi_acr_n *recommended; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(hdmi_rec_n_table) - 1; i++) { - if (clock == hdmi_rec_n_table[i].clock) - break; - } - recommended = hdmi_rec_n_table + i; - - switch (freq) { - case 32000: - return recommended->n[0]; - case 44100: - return recommended->n[1]; - case 48000: - return recommended->n[2]; - case 88200: - return recommended->n[1] * 2; - case 96000: - return recommended->n[2] * 2; - case 176400: - return recommended->n[1] * 4; - case 192000: - return recommended->n[2] * 4; - default: - return (128 * freq) / 1000; - } -} - -static unsigned int hdmi_mode_clock_to_hz(unsigned int clock) -{ - switch (clock) { - case 25175: - return 25174825; /* 25.2/1.001 MHz */ - case 74176: - return 74175824; /* 74.25/1.001 MHz */ - case 148352: - return 148351648; /* 148.5/1.001 MHz */ - case 296703: - return 296703297; /* 297/1.001 MHz */ - default: - return clock * 1000; - } -} - -static unsigned int hdmi_expected_cts(unsigned int audio_sample_rate, - unsigned int tmds_clock, unsigned int n) -{ - return DIV_ROUND_CLOSEST_ULL((u64)hdmi_mode_clock_to_hz(tmds_clock) * n, - 128 * audio_sample_rate); -} - static void do_hdmi_hw_aud_set_ncts(struct mtk_hdmi *hdmi, unsigned int n, unsigned int cts) { @@ -575,18 +516,34 @@ static void do_hdmi_hw_aud_set_ncts(struct mtk_hdmi *hdmi, unsigned int n, mtk_hdmi_write(hdmi, GRL_NCTS, val[i]); }
+static unsigned int hdmi_mode_clock_to_hz(unsigned int clock) +{ + switch (clock) { + case 25175: + return 25174825; /* 25.2/1.001 MHz */ + case 74176: + return 74175824; /* 74.25/1.001 MHz */ + case 148352: + return 148351648; /* 148.5/1.001 MHz */ + case 296703: + return 296703296; /* 297/1.001 MHz */ + default: + return clock * 1000; + } +} + void mtk_hdmi_hw_aud_set_ncts(struct mtk_hdmi *hdmi, unsigned int sample_rate, unsigned int clock) { - unsigned int n, cts; + struct hdmi_audio_n_cts n_cts;
- n = hdmi_recommended_n(sample_rate, clock); - cts = hdmi_expected_cts(sample_rate, clock, n); + hdmi_audio_get_coherent_n_cts(sample_rate, hdmi_mode_clock_to_hz(clock), + &n_cts);
dev_dbg(hdmi->dev, "%s: sample_rate=%u, clock=%d, cts=%u, n=%u\n", - __func__, sample_rate, clock, n, cts); + __func__, sample_rate, clock, n_cts.cts, n_cts.n);
mtk_hdmi_mask(hdmi, DUMMY_304, AUDIO_I2S_NCTS_SEL_64, AUDIO_I2S_NCTS_SEL); - do_hdmi_hw_aud_set_ncts(hdmi, n, cts); + do_hdmi_hw_aud_set_ncts(hdmi, n_cts.n, n_cts.cts); }
From: PC Liao pc.liao@mediatek.com
This patch adds HDMI audio output support to the MT8173 RT5650 machine driver.
Signed-off-by: PC Liao pc.liao@mediatek.com Signed-off-by: Philipp Zabel p.zabel@pengutronix.de --- .../devicetree/bindings/sound/mt8173-rt5650.txt | 5 ++- sound/soc/mediatek/Kconfig | 1 + sound/soc/mediatek/mt8173-rt5650.c | 47 ++++++++++++++++++++++ 3 files changed, 51 insertions(+), 2 deletions(-)
diff --git a/Documentation/devicetree/bindings/sound/mt8173-rt5650.txt b/Documentation/devicetree/bindings/sound/mt8173-rt5650.txt index fe5a5ef..6b182c4 100644 --- a/Documentation/devicetree/bindings/sound/mt8173-rt5650.txt +++ b/Documentation/devicetree/bindings/sound/mt8173-rt5650.txt @@ -1,15 +1,16 @@ -MT8173 with RT5650 CODECS +MT8173 with RT5650 CODECS and HDMI via I2S
Required properties: - compatible : "mediatek,mt8173-rt5650" - mediatek,audio-codec: the phandles of rt5650 codecs + and of the hdmi encoder node - mediatek,platform: the phandle of MT8173 ASoC platform
Example:
sound { compatible = "mediatek,mt8173-rt5650"; - mediatek,audio-codec = <&rt5650>; + mediatek,audio-codec = <&rt5650 &hdmi0>; mediatek,platform = <&afe>; };
diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig index 6c88087..1a8c844 100644 --- a/sound/soc/mediatek/Kconfig +++ b/sound/soc/mediatek/Kconfig @@ -21,6 +21,7 @@ config SND_SOC_MT8173_RT5650 tristate "ASoC Audio driver for MT8173 with RT5650 codec" depends on SND_SOC_MEDIATEK && I2C select SND_SOC_RT5645 + select SND_SOC_HDMI_CODEC help This adds ASoC driver for Mediatek MT8173 boards with the RT5650 audio codec. diff --git a/sound/soc/mediatek/mt8173-rt5650.c b/sound/soc/mediatek/mt8173-rt5650.c index bb09bb1..55eeca5 100644 --- a/sound/soc/mediatek/mt8173-rt5650.c +++ b/sound/soc/mediatek/mt8173-rt5650.c @@ -18,6 +18,7 @@ #include <linux/gpio.h> #include <linux/of_gpio.h> #include <sound/soc.h> +#include <sound/hdmi-codec.h> #include <sound/jack.h> #include "../codecs/rt5645.h"
@@ -114,10 +115,31 @@ static struct snd_soc_dai_link_component mt8173_rt5650_codecs[] = { }, };
+static struct snd_soc_jack mt8173_hdmi_card_jack; + +static int mt8173_hdmi_init(struct snd_soc_pcm_runtime *runtime) +{ + struct snd_soc_card *card = runtime->card; + struct snd_soc_codec *codec = runtime->codec; + int ret; + + /* enable jack detection */ + ret = snd_soc_card_jack_new(card, "HDMI Jack", SND_JACK_LINEOUT, + &mt8173_hdmi_card_jack, NULL, 0); + if (ret) { + dev_err(card->dev, "Can't new HDMI Jack %d\n", ret); + return ret; + } + + return hdmi_codec_set_jack_detect(codec, &mt8173_hdmi_card_jack); +} + enum { DAI_LINK_PLAYBACK, DAI_LINK_CAPTURE, + DAI_LINK_HDMI, DAI_LINK_CODEC_I2S, + DAI_LINK_HDMI_I2S, };
/* Digital audio interface glue - connects codec <---> CPU */ @@ -143,6 +165,16 @@ static struct snd_soc_dai_link mt8173_rt5650_dais[] = { .dynamic = 1, .dpcm_capture = 1, }, + [DAI_LINK_HDMI] = { + .name = "HDMI", + .stream_name = "HDMI PCM", + .cpu_dai_name = "HDMI", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dynamic = 1, + .dpcm_playback = 1, + }, /* Back End DAI links */ [DAI_LINK_CODEC_I2S] = { .name = "Codec", @@ -158,6 +190,14 @@ static struct snd_soc_dai_link mt8173_rt5650_dais[] = { .dpcm_playback = 1, .dpcm_capture = 1, }, + [DAI_LINK_HDMI_I2S] = { + .name = "HDMI BE", + .cpu_dai_name = "HDMIO", + .no_pcm = 1, + .codec_dai_name = "i2s-hifi", + .dpcm_playback = 1, + .init = mt8173_hdmi_init, + }, };
static struct snd_soc_card mt8173_rt5650_card = { @@ -199,6 +239,13 @@ static int mt8173_rt5650_dev_probe(struct platform_device *pdev) "Property 'audio-codec' missing or invalid\n"); return -EINVAL; } + mt8173_rt5650_dais[DAI_LINK_HDMI_I2S].codec_of_node = + of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 1); + if (!mt8173_rt5650_dais[DAI_LINK_HDMI_I2S].codec_of_node) { + dev_err(&pdev->dev, + "Property 'audio-codec' missing or invalid\n"); + return -EINVAL; + } card->dev = &pdev->dev; platform_set_drvdata(pdev, card);
participants (4)
-
Jyri Sarha
-
Matthias Brugger
-
PC Liao
-
Philipp Zabel