ASR_BASE

Change-Id: Icf3719cc0afe3eeb3edc7fa80a2eb5199ca9dda1
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 0000000..f0a1414
--- /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,