aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/layerscape/patches-5.4/805-display-0007-drm-bridge-cadence-Add-mhdp-audio-driver.patch
diff options
context:
space:
mode:
authorYangbo Lu <yangbo.lu@nxp.com>2020-04-10 10:47:05 +0800
committerPetr Štetiar <ynezz@true.cz>2020-05-07 12:53:06 +0200
commitcddd4591404fb4c53dc0b3c0b15b942cdbed4356 (patch)
tree392c1179de46b0f804e3789edca19069b64e6b44 /target/linux/layerscape/patches-5.4/805-display-0007-drm-bridge-cadence-Add-mhdp-audio-driver.patch
parentd1d2c0b5579ea4f69a42246c9318539d61ba1999 (diff)
downloadupstream-cddd4591404fb4c53dc0b3c0b15b942cdbed4356.tar.gz
upstream-cddd4591404fb4c53dc0b3c0b15b942cdbed4356.tar.bz2
upstream-cddd4591404fb4c53dc0b3c0b15b942cdbed4356.zip
layerscape: add patches-5.4
Add patches for linux-5.4. The patches are from NXP LSDK-20.04 release which was tagged LSDK-20.04-V5.4. https://source.codeaurora.org/external/qoriq/qoriq-components/linux/ For boards LS1021A-IOT, and Traverse-LS1043 which are not involved in LSDK, port the dts patches from 4.14. The patches are sorted into the following categories: 301-arch-xxxx 302-dts-xxxx 303-core-xxxx 701-net-xxxx 801-audio-xxxx 802-can-xxxx 803-clock-xxxx 804-crypto-xxxx 805-display-xxxx 806-dma-xxxx 807-gpio-xxxx 808-i2c-xxxx 809-jailhouse-xxxx 810-keys-xxxx 811-kvm-xxxx 812-pcie-xxxx 813-pm-xxxx 814-qe-xxxx 815-sata-xxxx 816-sdhc-xxxx 817-spi-xxxx 818-thermal-xxxx 819-uart-xxxx 820-usb-xxxx 821-vfio-xxxx Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
Diffstat (limited to 'target/linux/layerscape/patches-5.4/805-display-0007-drm-bridge-cadence-Add-mhdp-audio-driver.patch')
-rw-r--r--target/linux/layerscape/patches-5.4/805-display-0007-drm-bridge-cadence-Add-mhdp-audio-driver.patch715
1 files changed, 715 insertions, 0 deletions
diff --git a/target/linux/layerscape/patches-5.4/805-display-0007-drm-bridge-cadence-Add-mhdp-audio-driver.patch b/target/linux/layerscape/patches-5.4/805-display-0007-drm-bridge-cadence-Add-mhdp-audio-driver.patch
new file mode 100644
index 0000000000..f0a1414b03
--- /dev/null
+++ b/target/linux/layerscape/patches-5.4/805-display-0007-drm-bridge-cadence-Add-mhdp-audio-driver.patch
@@ -0,0 +1,715 @@
+From 3cd4b3cfc651c4d54897c72fbbaa9cd583ee6208 Mon Sep 17 00:00:00 2001
+From: Sandor Yu <Sandor.yu@nxp.com>
+Date: Fri, 30 Aug 2019 17:51:43 +0800
+Subject: [PATCH] drm: bridge: cadence: Add mhdp audio driver
+
+Move mhdp audio driver to cadence folder.
+Add audio info-frame set function for hdmi tx audio.
+The driver suppoer both HDMI and DP audio.
+
+Signed-off-by: Sandor Yu <Sandor.yu@nxp.com>
+---
+ drivers/gpu/drm/bridge/cadence/Kconfig | 3 +
+ drivers/gpu/drm/bridge/cadence/Makefile | 3 +-
+ drivers/gpu/drm/bridge/cadence/cdns-dp-core.c | 4 +
+ drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c | 5 +-
+ drivers/gpu/drm/bridge/cadence/cdns-mhdp-audio.c | 395 ++++++++++++++++++++++
+ drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c | 183 ----------
+ drivers/gpu/drm/imx/Kconfig | 1 +
+ include/drm/bridge/cdns-mhdp-common.h | 6 +
+ 8 files changed, 414 insertions(+), 186 deletions(-)
+ create mode 100644 drivers/gpu/drm/bridge/cadence/cdns-mhdp-audio.c
+
+--- a/drivers/gpu/drm/bridge/cadence/Kconfig
++++ b/drivers/gpu/drm/bridge/cadence/Kconfig
+@@ -11,3 +11,6 @@ config DRM_CDNS_HDMI
+
+ config DRM_CDNS_DP
+ tristate "Cadence DP DRM driver"
++
++config DRM_CDNS_AUDIO
++ tristate "Cadence MHDP Audio driver"
+--- a/drivers/gpu/drm/bridge/cadence/Makefile
++++ b/drivers/gpu/drm/bridge/cadence/Makefile
+@@ -1,5 +1,4 @@
+-#ccflags-y := -Iinclude/drm
+-
+ obj-$(CONFIG_DRM_CDNS_MHDP) += cdns-mhdp-common.o cdns-mhdp-hdmi.o
+ obj-$(CONFIG_DRM_CDNS_HDMI) += cdns-hdmi-core.o
+ obj-$(CONFIG_DRM_CDNS_DP) += cdns-dp-core.o
++obj-$(CONFIG_DRM_CDNS_AUDIO) += cdns-mhdp-audio.o
+--- a/drivers/gpu/drm/bridge/cadence/cdns-dp-core.c
++++ b/drivers/gpu/drm/bridge/cadence/cdns-dp-core.c
+@@ -526,6 +526,9 @@ __cdns_dp_probe(struct platform_device *
+
+ dev_set_drvdata(dev, &dp->mhdp);
+
++ /* register audio driver */
++ cdns_mhdp_register_audio_driver(dev);
++
+ dp_aux_init(&dp->mhdp, dev);
+
+ return dp;
+@@ -537,6 +540,7 @@ err_out:
+ static void __cdns_dp_remove(struct cdns_mhdp_device *mhdp)
+ {
+ dp_aux_destroy(mhdp);
++ cdns_mhdp_unregister_audio_driver(mhdp->dev);
+ }
+
+ /* -----------------------------------------------------------------------------
+--- a/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c
++++ b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c
+@@ -9,7 +9,6 @@
+ * (at your option) any later version.
+ *
+ */
+-
+ #include <drm/bridge/cdns-mhdp-imx.h>
+ #include <drm/drm_atomic_helper.h>
+ #include <drm/drm_crtc_helper.h>
+@@ -513,6 +512,9 @@ __cdns_hdmi_probe(struct platform_device
+
+ dev_set_drvdata(dev, &hdmi->mhdp);
+
++ /* register audio driver */
++ cdns_mhdp_register_audio_driver(dev);
++
+ return hdmi;
+
+ err_out:
+@@ -522,6 +524,7 @@ err_out:
+
+ static void __cdns_hdmi_remove(struct cdns_mhdp_device *mhdp)
+ {
++ cdns_mhdp_unregister_audio_driver(mhdp->dev);
+ }
+
+ /* -----------------------------------------------------------------------------
+--- /dev/null
++++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-audio.c
+@@ -0,0 +1,395 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
++ * Author: Chris Zhong <zyw@rock-chips.com>
++ *
++ * This software is licensed under the terms of the GNU General Public
++ * License version 2, as published by the Free Software Foundation, and
++ * may be copied, distributed, and modified under those terms.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ */
++#include <linux/clk.h>
++#include <linux/reset.h>
++#include <drm/bridge/cdns-mhdp-common.h>
++#include <sound/hdmi-codec.h>
++#include <drm/bridge/cdns-mhdp-imx.h>
++#include <drm/drm_of.h>
++#include <drm/drmP.h>
++
++#define CDNS_DP_SPDIF_CLK 200000000
++
++static u32 TMDS_rate_table[7] = {
++ 25200, 27000, 54000, 74250, 148500, 297000, 594000,
++};
++
++static u32 N_table_32k[7] = {
++/* 25200/27000/54000/74250/148500/297000/594000 */
++ 4096, 4096, 4096, 4096, 4096, 3072, 3072,
++};
++
++static u32 N_table_44k[7] = {
++ 6272, 6272, 6272, 6272, 6272, 4704, 9408,
++};
++
++static u32 N_table_48k[7] = {
++ 6144, 6144, 6144, 6144, 6144, 5120, 6144,
++};
++
++static int select_N_index(u32 pclk)
++{
++ int num = sizeof(TMDS_rate_table)/sizeof(int);
++ int i = 0;
++
++ for (i = 0; i < num ; i++)
++ if (pclk == TMDS_rate_table[i])
++ break;
++
++ if (i == num) {
++ DRM_WARN("pclkc %d is not supported!\n", pclk);
++ return num-1;
++ }
++
++ return i;
++}
++
++static void hdmi_audio_avi_set(struct cdns_mhdp_device *mhdp,
++ u32 channels)
++{
++ struct hdmi_audio_infoframe frame;
++ u8 buf[32];
++ int ret;
++
++ hdmi_audio_infoframe_init(&frame);
++
++ frame.channels = channels;
++ frame.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM;
++
++ if (channels == 2)
++ frame.channel_allocation = 0;
++ else if (channels == 4)
++ frame.channel_allocation = 0x3;
++ else if (channels == 8)
++ frame.channel_allocation = 0x13;
++
++ ret = hdmi_audio_infoframe_pack(&frame, buf + 1, sizeof(buf) - 1);
++ if (ret < 0) {
++ DRM_ERROR("failed to pack audio infoframe: %d\n", ret);
++ return;
++ }
++
++ buf[0] = 0;
++
++ cdns_mhdp_infoframe_set(mhdp, 1, sizeof(buf), buf, HDMI_INFOFRAME_TYPE_AUDIO);
++}
++
++int cdns_mhdp_audio_stop(struct cdns_mhdp_device *mhdp,
++ struct audio_info *audio)
++{
++ int ret;
++
++ if (audio->connector_type == DRM_MODE_CONNECTOR_DisplayPort) {
++ ret = cdns_mhdp_reg_write(mhdp, AUDIO_PACK_CONTROL, 0);
++ if (ret) {
++ DRM_DEV_ERROR(mhdp->dev, "audio stop failed: %d\n", ret);
++ return ret;
++ }
++ }
++
++ cdns_mhdp_bus_write(0, mhdp, SPDIF_CTRL_ADDR);
++
++ /* clearn the audio config and reset */
++ cdns_mhdp_bus_write(0, mhdp, AUDIO_SRC_CNTL);
++ cdns_mhdp_bus_write(0, mhdp, AUDIO_SRC_CNFG);
++ cdns_mhdp_bus_write(AUDIO_SW_RST, mhdp, AUDIO_SRC_CNTL);
++ cdns_mhdp_bus_write(0, mhdp, AUDIO_SRC_CNTL);
++
++ /* reset smpl2pckt component */
++ cdns_mhdp_bus_write(0, mhdp, SMPL2PKT_CNTL);
++ cdns_mhdp_bus_write(AUDIO_SW_RST, mhdp, SMPL2PKT_CNTL);
++ cdns_mhdp_bus_write(0, mhdp, SMPL2PKT_CNTL);
++
++ /* reset FIFO */
++ cdns_mhdp_bus_write(AUDIO_SW_RST, mhdp, FIFO_CNTL);
++ cdns_mhdp_bus_write(0, mhdp, FIFO_CNTL);
++
++ if (audio->format == AFMT_SPDIF_INT)
++ clk_disable_unprepare(mhdp->spdif_clk);
++
++ return 0;
++}
++EXPORT_SYMBOL(cdns_mhdp_audio_stop);
++
++int cdns_mhdp_audio_mute(struct cdns_mhdp_device *mhdp, bool enable)
++{
++ struct audio_info *audio = &mhdp->audio_info;
++ int ret = true;
++
++ if (audio->connector_type == DRM_MODE_CONNECTOR_DisplayPort) {
++ ret = cdns_mhdp_reg_write_bit(mhdp, DP_VB_ID, 4, 1, enable);
++ if (ret)
++ DRM_DEV_ERROR(mhdp->dev, "audio mute failed: %d\n", ret);
++ }
++
++ return ret;
++}
++EXPORT_SYMBOL(cdns_mhdp_audio_mute);
++
++static void cdns_mhdp_audio_config_i2s(struct cdns_mhdp_device *mhdp,
++ struct audio_info *audio)
++{
++ int sub_pckt_num = 1, i2s_port_en_val = 0xf, i;
++ int idx = select_N_index(mhdp->mode.clock);
++ u32 val, ncts;
++
++ if (audio->channels == 2) {
++ if (mhdp->dp.link.num_lanes == 1)
++ sub_pckt_num = 2;
++ else
++ sub_pckt_num = 4;
++
++ i2s_port_en_val = 1;
++ } else if (audio->channels == 4) {
++ i2s_port_en_val = 3;
++ }
++
++ cdns_mhdp_bus_write(0x0, mhdp, SPDIF_CTRL_ADDR);
++
++ cdns_mhdp_bus_write(SYNC_WR_TO_CH_ZERO, mhdp, FIFO_CNTL);
++
++ val = MAX_NUM_CH(audio->channels);
++ val |= NUM_OF_I2S_PORTS(audio->channels);
++ val |= AUDIO_TYPE_LPCM;
++ val |= CFG_SUB_PCKT_NUM(sub_pckt_num);
++ cdns_mhdp_bus_write(val, mhdp, SMPL2PKT_CNFG);
++
++ if (audio->sample_width == 16)
++ val = 0;
++ else if (audio->sample_width == 24)
++ val = 1 << 9;
++ else
++ val = 2 << 9;
++
++ val |= AUDIO_CH_NUM(audio->channels);
++ val |= I2S_DEC_PORT_EN(i2s_port_en_val);
++ val |= TRANS_SMPL_WIDTH_32;
++ cdns_mhdp_bus_write(val, mhdp, AUDIO_SRC_CNFG);
++
++ for (i = 0; i < (audio->channels + 1) / 2; i++) {
++ if (audio->sample_width == 16)
++ val = (0x02 << 8) | (0x02 << 20);
++ else if (audio->sample_width == 24)
++ val = (0x0b << 8) | (0x0b << 20);
++
++ val |= ((2 * i) << 4) | ((2 * i + 1) << 16);
++ cdns_mhdp_bus_write(val, mhdp, STTS_BIT_CH(i));
++ }
++
++ switch (audio->sample_rate) {
++ case 32000:
++ val = SAMPLING_FREQ(3) |
++ ORIGINAL_SAMP_FREQ(0xc);
++ ncts = N_table_32k[idx];
++ break;
++ case 44100:
++ val = SAMPLING_FREQ(0) |
++ ORIGINAL_SAMP_FREQ(0xf);
++ ncts = N_table_44k[idx];
++ break;
++ case 48000:
++ val = SAMPLING_FREQ(2) |
++ ORIGINAL_SAMP_FREQ(0xd);
++ ncts = N_table_48k[idx];
++ break;
++ case 88200:
++ val = SAMPLING_FREQ(8) |
++ ORIGINAL_SAMP_FREQ(0x7);
++ ncts = N_table_44k[idx] * 2;
++ break;
++ case 96000:
++ val = SAMPLING_FREQ(0xa) |
++ ORIGINAL_SAMP_FREQ(5);
++ ncts = N_table_48k[idx] * 2;
++ break;
++ case 176400:
++ val = SAMPLING_FREQ(0xc) |
++ ORIGINAL_SAMP_FREQ(3);
++ ncts = N_table_44k[idx] * 4;
++ break;
++ case 192000:
++ default:
++ val = SAMPLING_FREQ(0xe) |
++ ORIGINAL_SAMP_FREQ(1);
++ ncts = N_table_48k[idx] * 4;
++ break;
++ }
++ val |= 4;
++ cdns_mhdp_bus_write(val, mhdp, COM_CH_STTS_BITS);
++
++ if (audio->connector_type == DRM_MODE_CONNECTOR_HDMIA)
++ cdns_mhdp_reg_write(mhdp, CM_I2S_CTRL, ncts | 0x4000000);
++
++ cdns_mhdp_bus_write(SMPL2PKT_EN, mhdp, SMPL2PKT_CNTL);
++ cdns_mhdp_bus_write(I2S_DEC_START, mhdp, AUDIO_SRC_CNTL);
++}
++
++static void cdns_mhdp_audio_config_spdif(struct cdns_mhdp_device *mhdp)
++{
++ u32 val;
++
++ cdns_mhdp_bus_write(SYNC_WR_TO_CH_ZERO, mhdp, FIFO_CNTL);
++
++ val = MAX_NUM_CH(2) | AUDIO_TYPE_LPCM | CFG_SUB_PCKT_NUM(4);
++ cdns_mhdp_bus_write(val, mhdp, SMPL2PKT_CNFG);
++ cdns_mhdp_bus_write(SMPL2PKT_EN, mhdp, SMPL2PKT_CNTL);
++
++ val = SPDIF_ENABLE | SPDIF_AVG_SEL | SPDIF_JITTER_BYPASS;
++ cdns_mhdp_bus_write(val, mhdp, SPDIF_CTRL_ADDR);
++
++ clk_prepare_enable(mhdp->spdif_clk);
++ clk_set_rate(mhdp->spdif_clk, CDNS_DP_SPDIF_CLK);
++}
++
++int cdns_mhdp_audio_config(struct cdns_mhdp_device *mhdp,
++ struct audio_info *audio)
++{
++ int ret;
++
++ /* reset the spdif clk before config */
++ if (audio->format == AFMT_SPDIF_INT) {
++ reset_control_assert(mhdp->spdif_rst);
++ reset_control_deassert(mhdp->spdif_rst);
++ }
++
++ if (audio->connector_type == DRM_MODE_CONNECTOR_DisplayPort) {
++ ret = cdns_mhdp_reg_write(mhdp, CM_LANE_CTRL, LANE_REF_CYC);
++ if (ret)
++ goto err_audio_config;
++
++ ret = cdns_mhdp_reg_write(mhdp, CM_CTRL, 0);
++ if (ret)
++ goto err_audio_config;
++ } else {
++ /* HDMI Mode */
++ ret = cdns_mhdp_reg_write(mhdp, CM_CTRL, 8);
++ if (ret)
++ goto err_audio_config;
++ }
++
++ if (audio->format == AFMT_I2S)
++ cdns_mhdp_audio_config_i2s(mhdp, audio);
++ else if (audio->format == AFMT_SPDIF_INT)
++ cdns_mhdp_audio_config_spdif(mhdp);
++
++ if (audio->connector_type == DRM_MODE_CONNECTOR_DisplayPort)
++ ret = cdns_mhdp_reg_write(mhdp, AUDIO_PACK_CONTROL, AUDIO_PACK_EN);
++
++ if (audio->connector_type == DRM_MODE_CONNECTOR_HDMIA)
++ hdmi_audio_avi_set(mhdp, audio->channels);
++
++err_audio_config:
++ if (ret)
++ DRM_DEV_ERROR(mhdp->dev, "audio config failed: %d\n", ret);
++ return ret;
++}
++EXPORT_SYMBOL(cdns_mhdp_audio_config);
++
++static int audio_hw_params(struct device *dev, void *data,
++ struct hdmi_codec_daifmt *daifmt,
++ struct hdmi_codec_params *params)
++{
++ struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev);
++ struct audio_info audio = {
++ .sample_width = params->sample_width,
++ .sample_rate = params->sample_rate,
++ .channels = params->channels,
++ .connector_type = mhdp->connector.base.connector_type,
++ };
++ int ret;
++
++ switch (daifmt->fmt) {
++ case HDMI_I2S:
++ audio.format = AFMT_I2S;
++ break;
++ case HDMI_SPDIF:
++ audio.format = AFMT_SPDIF_EXT;
++ break;
++ default:
++ DRM_DEV_ERROR(dev, "Invalid format %d\n", daifmt->fmt);
++ ret = -EINVAL;
++ goto out;
++ }
++
++ ret = cdns_mhdp_audio_config(mhdp, &audio);
++ if (!ret)
++ mhdp->audio_info = audio;
++
++out:
++ return ret;
++}
++
++static void audio_shutdown(struct device *dev, void *data)
++{
++ struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev);
++ int ret;
++
++ ret = cdns_mhdp_audio_stop(mhdp, &mhdp->audio_info);
++ if (!ret)
++ mhdp->audio_info.format = AFMT_UNUSED;
++}
++
++static int audio_digital_mute(struct device *dev, void *data,
++ bool enable)
++{
++ struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev);
++ int ret;
++
++ ret = cdns_mhdp_audio_mute(mhdp, enable);
++
++ return ret;
++}
++
++static int audio_get_eld(struct device *dev, void *data,
++ u8 *buf, size_t len)
++{
++ struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev);
++
++ memcpy(buf, mhdp->connector.base.eld,
++ min(sizeof(mhdp->connector.base.eld), len));
++
++ return 0;
++}
++
++static const struct hdmi_codec_ops audio_codec_ops = {
++ .hw_params = audio_hw_params,
++ .audio_shutdown = audio_shutdown,
++ .digital_mute = audio_digital_mute,
++ .get_eld = audio_get_eld,
++};
++
++int cdns_mhdp_register_audio_driver(struct device *dev)
++{
++ struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev);
++ struct hdmi_codec_pdata codec_data = {
++ .i2s = 1,
++ .spdif = 1,
++ .ops = &audio_codec_ops,
++ .max_i2s_channels = 8,
++ };
++
++ mhdp->audio_pdev = platform_device_register_data(
++ dev, HDMI_CODEC_DRV_NAME, 1,
++ &codec_data, sizeof(codec_data));
++
++ return PTR_ERR_OR_ZERO(mhdp->audio_pdev);
++}
++
++void cdns_mhdp_unregister_audio_driver(struct device *dev)
++{
++ struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev);
++
++ platform_device_unregister(mhdp->audio_pdev);
++}
+--- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c
++++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c
+@@ -937,189 +937,6 @@ err_config_video:
+ }
+ EXPORT_SYMBOL(cdns_mhdp_config_video);
+
+-int cdns_mhdp_audio_stop(struct cdns_mhdp_device *mhdp,
+- struct audio_info *audio)
+-{
+- int ret;
+-
+- ret = cdns_mhdp_reg_write(mhdp, AUDIO_PACK_CONTROL, 0);
+- if (ret) {
+- DRM_DEV_ERROR(mhdp->dev, "audio stop failed: %d\n", ret);
+- return ret;
+- }
+-
+- cdns_mhdp_bus_write(0, mhdp, SPDIF_CTRL_ADDR);
+-
+- /* clearn the audio config and reset */
+- cdns_mhdp_bus_write(0, mhdp, AUDIO_SRC_CNTL);
+- cdns_mhdp_bus_write(0, mhdp, AUDIO_SRC_CNFG);
+- cdns_mhdp_bus_write(AUDIO_SW_RST, mhdp, AUDIO_SRC_CNTL);
+- cdns_mhdp_bus_write(0, mhdp, AUDIO_SRC_CNTL);
+-
+- /* reset smpl2pckt component */
+- cdns_mhdp_bus_write(0, mhdp, SMPL2PKT_CNTL);
+- cdns_mhdp_bus_write(AUDIO_SW_RST, mhdp, SMPL2PKT_CNTL);
+- cdns_mhdp_bus_write(0, mhdp, SMPL2PKT_CNTL);
+-
+- /* reset FIFO */
+- cdns_mhdp_bus_write(AUDIO_SW_RST, mhdp, FIFO_CNTL);
+- cdns_mhdp_bus_write(0, mhdp, FIFO_CNTL);
+-
+- if (audio->format == AFMT_SPDIF_INT)
+- clk_disable_unprepare(mhdp->spdif_clk);
+-
+- return 0;
+-}
+-EXPORT_SYMBOL(cdns_mhdp_audio_stop);
+-
+-int cdns_mhdp_audio_mute(struct cdns_mhdp_device *mhdp, bool enable)
+-{
+- int ret;
+-
+- ret = cdns_mhdp_reg_write_bit(mhdp, DP_VB_ID, 4, 1, enable);
+- if (ret)
+- DRM_DEV_ERROR(mhdp->dev, "audio mute failed: %d\n", ret);
+-
+- return ret;
+-}
+-EXPORT_SYMBOL(cdns_mhdp_audio_mute);
+-
+-static void cdns_mhdp_audio_config_i2s(struct cdns_mhdp_device *mhdp,
+- struct audio_info *audio)
+-{
+- int sub_pckt_num = 1, i2s_port_en_val = 0xf, i;
+- u32 val;
+-
+- if (audio->channels == 2) {
+- if (mhdp->dp.link.num_lanes == 1)
+- sub_pckt_num = 2;
+- else
+- sub_pckt_num = 4;
+-
+- i2s_port_en_val = 1;
+- } else if (audio->channels == 4) {
+- i2s_port_en_val = 3;
+- }
+-
+- cdns_mhdp_bus_write(0x0, mhdp, SPDIF_CTRL_ADDR);
+-
+- cdns_mhdp_bus_write(SYNC_WR_TO_CH_ZERO, mhdp, FIFO_CNTL);
+-
+- val = MAX_NUM_CH(audio->channels);
+- val |= NUM_OF_I2S_PORTS(audio->channels);
+- val |= AUDIO_TYPE_LPCM;
+- val |= CFG_SUB_PCKT_NUM(sub_pckt_num);
+- cdns_mhdp_bus_write(val, mhdp, SMPL2PKT_CNFG);
+-
+- if (audio->sample_width == 16)
+- val = 0;
+- else if (audio->sample_width == 24)
+- val = 1 << 9;
+- else
+- val = 2 << 9;
+-
+- val |= AUDIO_CH_NUM(audio->channels);
+- val |= I2S_DEC_PORT_EN(i2s_port_en_val);
+- val |= TRANS_SMPL_WIDTH_32;
+- cdns_mhdp_bus_write(val, mhdp, AUDIO_SRC_CNFG);
+-
+- for (i = 0; i < (audio->channels + 1) / 2; i++) {
+- if (audio->sample_width == 16)
+- val = (0x02 << 8) | (0x02 << 20);
+- else if (audio->sample_width == 24)
+- val = (0x0b << 8) | (0x0b << 20);
+-
+- val |= ((2 * i) << 4) | ((2 * i + 1) << 16);
+- cdns_mhdp_bus_write(val, mhdp, STTS_BIT_CH(i));
+- }
+-
+- switch (audio->sample_rate) {
+- case 32000:
+- val = SAMPLING_FREQ(3) |
+- ORIGINAL_SAMP_FREQ(0xc);
+- break;
+- case 44100:
+- val = SAMPLING_FREQ(0) |
+- ORIGINAL_SAMP_FREQ(0xf);
+- break;
+- case 48000:
+- val = SAMPLING_FREQ(2) |
+- ORIGINAL_SAMP_FREQ(0xd);
+- break;
+- case 88200:
+- val = SAMPLING_FREQ(8) |
+- ORIGINAL_SAMP_FREQ(0x7);
+- break;
+- case 96000:
+- val = SAMPLING_FREQ(0xa) |
+- ORIGINAL_SAMP_FREQ(5);
+- break;
+- case 176400:
+- val = SAMPLING_FREQ(0xc) |
+- ORIGINAL_SAMP_FREQ(3);
+- break;
+- case 192000:
+- val = SAMPLING_FREQ(0xe) |
+- ORIGINAL_SAMP_FREQ(1);
+- break;
+- }
+- val |= 4;
+- cdns_mhdp_bus_write(val, mhdp, COM_CH_STTS_BITS);
+-
+- cdns_mhdp_bus_write(SMPL2PKT_EN, mhdp, SMPL2PKT_CNTL);
+- cdns_mhdp_bus_write(I2S_DEC_START, mhdp, AUDIO_SRC_CNTL);
+-}
+-
+-static void cdns_mhdp_audio_config_spdif(struct cdns_mhdp_device *mhdp)
+-{
+- u32 val;
+-
+- cdns_mhdp_bus_write(SYNC_WR_TO_CH_ZERO, mhdp, FIFO_CNTL);
+-
+- val = MAX_NUM_CH(2) | AUDIO_TYPE_LPCM | CFG_SUB_PCKT_NUM(4);
+- cdns_mhdp_bus_write(val, mhdp, SMPL2PKT_CNFG);
+- cdns_mhdp_bus_write(SMPL2PKT_EN, mhdp, SMPL2PKT_CNTL);
+-
+- val = SPDIF_ENABLE | SPDIF_AVG_SEL | SPDIF_JITTER_BYPASS;
+- cdns_mhdp_bus_write(val, mhdp, SPDIF_CTRL_ADDR);
+-
+- clk_prepare_enable(mhdp->spdif_clk);
+- clk_set_rate(mhdp->spdif_clk, CDNS_DP_SPDIF_CLK);
+-}
+-
+-int cdns_mhdp_audio_config(struct cdns_mhdp_device *mhdp,
+- struct audio_info *audio)
+-{
+- int ret;
+-
+- /* reset the spdif clk before config */
+- if (audio->format == AFMT_SPDIF_INT) {
+- reset_control_assert(mhdp->spdif_rst);
+- reset_control_deassert(mhdp->spdif_rst);
+- }
+-
+- ret = cdns_mhdp_reg_write(mhdp, CM_LANE_CTRL, LANE_REF_CYC);
+- if (ret)
+- goto err_audio_config;
+-
+- ret = cdns_mhdp_reg_write(mhdp, CM_CTRL, 0);
+- if (ret)
+- goto err_audio_config;
+-
+- if (audio->format == AFMT_I2S)
+- cdns_mhdp_audio_config_i2s(mhdp, audio);
+- else if (audio->format == AFMT_SPDIF_INT)
+- cdns_mhdp_audio_config_spdif(mhdp);
+-
+- ret = cdns_mhdp_reg_write(mhdp, AUDIO_PACK_CONTROL, AUDIO_PACK_EN);
+-
+-err_audio_config:
+- if (ret)
+- DRM_DEV_ERROR(mhdp->dev, "audio config failed: %d\n", ret);
+- return ret;
+-}
+-EXPORT_SYMBOL(cdns_mhdp_audio_config);
+-
+ int cdns_mhdp_adjust_lt(struct cdns_mhdp_device *mhdp,
+ u8 nlanes, u16 udelay, u8 *lanes_data, u8 *dpcd)
+ {
+--- a/drivers/gpu/drm/imx/Kconfig
++++ b/drivers/gpu/drm/imx/Kconfig
+@@ -45,6 +45,7 @@ config DRM_IMX_CDNS_MHDP
+ select DRM_CDNS_MHDP
+ select DRM_CDNS_DP
+ select DRM_CDNS_HDMI
++ select DRM_CDNS_AUDIO
+ depends on DRM_IMX
+ help
+ Choose this if you want to use HDMI on i.MX8.
+--- a/include/drm/bridge/cdns-mhdp-common.h
++++ b/include/drm/bridge/cdns-mhdp-common.h
+@@ -548,6 +548,7 @@ struct audio_info {
+ int sample_rate;
+ int channels;
+ int sample_width;
++ int connector_type;
+ };
+
+ enum vic_pxl_encoding_format {
+@@ -670,11 +671,16 @@ int cdns_mhdp_get_edid_block(void *mhdp,
+ int cdns_mhdp_train_link(struct cdns_mhdp_device *mhdp);
+ int cdns_mhdp_set_video_status(struct cdns_mhdp_device *mhdp, int active);
+ int cdns_mhdp_config_video(struct cdns_mhdp_device *mhdp);
++
++/* Audio */
+ int cdns_mhdp_audio_stop(struct cdns_mhdp_device *mhdp,
+ struct audio_info *audio);
+ int cdns_mhdp_audio_mute(struct cdns_mhdp_device *mhdp, bool enable);
+ int cdns_mhdp_audio_config(struct cdns_mhdp_device *mhdp,
+ struct audio_info *audio);
++int cdns_mhdp_register_audio_driver(struct device *dev);
++void cdns_mhdp_unregister_audio_driver(struct device *dev);
++
+ int cdns_mhdp_reg_read(struct cdns_mhdp_device *mhdp, u32 addr);
+ int cdns_mhdp_reg_write(struct cdns_mhdp_device *mhdp, u32 addr, u32 val);
+ int cdns_mhdp_reg_write_bit(struct cdns_mhdp_device *mhdp, u16 addr,