diff options
author | Yangbo Lu <yangbo.lu@nxp.com> | 2020-04-10 10:47:05 +0800 |
---|---|---|
committer | Petr Štetiar <ynezz@true.cz> | 2020-05-07 12:53:06 +0200 |
commit | cddd4591404fb4c53dc0b3c0b15b942cdbed4356 (patch) | |
tree | 392c1179de46b0f804e3789edca19069b64e6b44 /target/linux/layerscape/patches-5.4/805-display-0007-drm-bridge-cadence-Add-mhdp-audio-driver.patch | |
parent | d1d2c0b5579ea4f69a42246c9318539d61ba1999 (diff) | |
download | upstream-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.patch | 715 |
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, |