| b.liu | e958203 | 2025-04-17 19:18:16 +0800 | [diff] [blame] | 1 | From 3cd4b3cfc651c4d54897c72fbbaa9cd583ee6208 Mon Sep 17 00:00:00 2001 |
| 2 | From: Sandor Yu <Sandor.yu@nxp.com> |
| 3 | Date: Fri, 30 Aug 2019 17:51:43 +0800 |
| 4 | Subject: [PATCH] drm: bridge: cadence: Add mhdp audio driver |
| 5 | |
| 6 | Move mhdp audio driver to cadence folder. |
| 7 | Add audio info-frame set function for hdmi tx audio. |
| 8 | The driver suppoer both HDMI and DP audio. |
| 9 | |
| 10 | Signed-off-by: Sandor Yu <Sandor.yu@nxp.com> |
| 11 | --- |
| 12 | drivers/gpu/drm/bridge/cadence/Kconfig | 3 + |
| 13 | drivers/gpu/drm/bridge/cadence/Makefile | 3 +- |
| 14 | drivers/gpu/drm/bridge/cadence/cdns-dp-core.c | 4 + |
| 15 | drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c | 5 +- |
| 16 | drivers/gpu/drm/bridge/cadence/cdns-mhdp-audio.c | 395 ++++++++++++++++++++++ |
| 17 | drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c | 183 ---------- |
| 18 | drivers/gpu/drm/imx/Kconfig | 1 + |
| 19 | include/drm/bridge/cdns-mhdp-common.h | 6 + |
| 20 | 8 files changed, 414 insertions(+), 186 deletions(-) |
| 21 | create mode 100644 drivers/gpu/drm/bridge/cadence/cdns-mhdp-audio.c |
| 22 | |
| 23 | --- a/drivers/gpu/drm/bridge/cadence/Kconfig |
| 24 | +++ b/drivers/gpu/drm/bridge/cadence/Kconfig |
| 25 | @@ -11,3 +11,6 @@ config DRM_CDNS_HDMI |
| 26 | |
| 27 | config DRM_CDNS_DP |
| 28 | tristate "Cadence DP DRM driver" |
| 29 | + |
| 30 | +config DRM_CDNS_AUDIO |
| 31 | + tristate "Cadence MHDP Audio driver" |
| 32 | --- a/drivers/gpu/drm/bridge/cadence/Makefile |
| 33 | +++ b/drivers/gpu/drm/bridge/cadence/Makefile |
| 34 | @@ -1,5 +1,4 @@ |
| 35 | -#ccflags-y := -Iinclude/drm |
| 36 | - |
| 37 | obj-$(CONFIG_DRM_CDNS_MHDP) += cdns-mhdp-common.o cdns-mhdp-hdmi.o |
| 38 | obj-$(CONFIG_DRM_CDNS_HDMI) += cdns-hdmi-core.o |
| 39 | obj-$(CONFIG_DRM_CDNS_DP) += cdns-dp-core.o |
| 40 | +obj-$(CONFIG_DRM_CDNS_AUDIO) += cdns-mhdp-audio.o |
| 41 | --- a/drivers/gpu/drm/bridge/cadence/cdns-dp-core.c |
| 42 | +++ b/drivers/gpu/drm/bridge/cadence/cdns-dp-core.c |
| 43 | @@ -526,6 +526,9 @@ __cdns_dp_probe(struct platform_device * |
| 44 | |
| 45 | dev_set_drvdata(dev, &dp->mhdp); |
| 46 | |
| 47 | + /* register audio driver */ |
| 48 | + cdns_mhdp_register_audio_driver(dev); |
| 49 | + |
| 50 | dp_aux_init(&dp->mhdp, dev); |
| 51 | |
| 52 | return dp; |
| 53 | @@ -537,6 +540,7 @@ err_out: |
| 54 | static void __cdns_dp_remove(struct cdns_mhdp_device *mhdp) |
| 55 | { |
| 56 | dp_aux_destroy(mhdp); |
| 57 | + cdns_mhdp_unregister_audio_driver(mhdp->dev); |
| 58 | } |
| 59 | |
| 60 | /* ----------------------------------------------------------------------------- |
| 61 | --- a/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c |
| 62 | +++ b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c |
| 63 | @@ -9,7 +9,6 @@ |
| 64 | * (at your option) any later version. |
| 65 | * |
| 66 | */ |
| 67 | - |
| 68 | #include <drm/bridge/cdns-mhdp-imx.h> |
| 69 | #include <drm/drm_atomic_helper.h> |
| 70 | #include <drm/drm_crtc_helper.h> |
| 71 | @@ -513,6 +512,9 @@ __cdns_hdmi_probe(struct platform_device |
| 72 | |
| 73 | dev_set_drvdata(dev, &hdmi->mhdp); |
| 74 | |
| 75 | + /* register audio driver */ |
| 76 | + cdns_mhdp_register_audio_driver(dev); |
| 77 | + |
| 78 | return hdmi; |
| 79 | |
| 80 | err_out: |
| 81 | @@ -522,6 +524,7 @@ err_out: |
| 82 | |
| 83 | static void __cdns_hdmi_remove(struct cdns_mhdp_device *mhdp) |
| 84 | { |
| 85 | + cdns_mhdp_unregister_audio_driver(mhdp->dev); |
| 86 | } |
| 87 | |
| 88 | /* ----------------------------------------------------------------------------- |
| 89 | --- /dev/null |
| 90 | +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-audio.c |
| 91 | @@ -0,0 +1,395 @@ |
| 92 | +// SPDX-License-Identifier: GPL-2.0 |
| 93 | +/* |
| 94 | + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd |
| 95 | + * Author: Chris Zhong <zyw@rock-chips.com> |
| 96 | + * |
| 97 | + * This software is licensed under the terms of the GNU General Public |
| 98 | + * License version 2, as published by the Free Software Foundation, and |
| 99 | + * may be copied, distributed, and modified under those terms. |
| 100 | + * |
| 101 | + * This program is distributed in the hope that it will be useful, |
| 102 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 103 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 104 | + * GNU General Public License for more details. |
| 105 | + */ |
| 106 | +#include <linux/clk.h> |
| 107 | +#include <linux/reset.h> |
| 108 | +#include <drm/bridge/cdns-mhdp-common.h> |
| 109 | +#include <sound/hdmi-codec.h> |
| 110 | +#include <drm/bridge/cdns-mhdp-imx.h> |
| 111 | +#include <drm/drm_of.h> |
| 112 | +#include <drm/drmP.h> |
| 113 | + |
| 114 | +#define CDNS_DP_SPDIF_CLK 200000000 |
| 115 | + |
| 116 | +static u32 TMDS_rate_table[7] = { |
| 117 | + 25200, 27000, 54000, 74250, 148500, 297000, 594000, |
| 118 | +}; |
| 119 | + |
| 120 | +static u32 N_table_32k[7] = { |
| 121 | +/* 25200/27000/54000/74250/148500/297000/594000 */ |
| 122 | + 4096, 4096, 4096, 4096, 4096, 3072, 3072, |
| 123 | +}; |
| 124 | + |
| 125 | +static u32 N_table_44k[7] = { |
| 126 | + 6272, 6272, 6272, 6272, 6272, 4704, 9408, |
| 127 | +}; |
| 128 | + |
| 129 | +static u32 N_table_48k[7] = { |
| 130 | + 6144, 6144, 6144, 6144, 6144, 5120, 6144, |
| 131 | +}; |
| 132 | + |
| 133 | +static int select_N_index(u32 pclk) |
| 134 | +{ |
| 135 | + int num = sizeof(TMDS_rate_table)/sizeof(int); |
| 136 | + int i = 0; |
| 137 | + |
| 138 | + for (i = 0; i < num ; i++) |
| 139 | + if (pclk == TMDS_rate_table[i]) |
| 140 | + break; |
| 141 | + |
| 142 | + if (i == num) { |
| 143 | + DRM_WARN("pclkc %d is not supported!\n", pclk); |
| 144 | + return num-1; |
| 145 | + } |
| 146 | + |
| 147 | + return i; |
| 148 | +} |
| 149 | + |
| 150 | +static void hdmi_audio_avi_set(struct cdns_mhdp_device *mhdp, |
| 151 | + u32 channels) |
| 152 | +{ |
| 153 | + struct hdmi_audio_infoframe frame; |
| 154 | + u8 buf[32]; |
| 155 | + int ret; |
| 156 | + |
| 157 | + hdmi_audio_infoframe_init(&frame); |
| 158 | + |
| 159 | + frame.channels = channels; |
| 160 | + frame.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM; |
| 161 | + |
| 162 | + if (channels == 2) |
| 163 | + frame.channel_allocation = 0; |
| 164 | + else if (channels == 4) |
| 165 | + frame.channel_allocation = 0x3; |
| 166 | + else if (channels == 8) |
| 167 | + frame.channel_allocation = 0x13; |
| 168 | + |
| 169 | + ret = hdmi_audio_infoframe_pack(&frame, buf + 1, sizeof(buf) - 1); |
| 170 | + if (ret < 0) { |
| 171 | + DRM_ERROR("failed to pack audio infoframe: %d\n", ret); |
| 172 | + return; |
| 173 | + } |
| 174 | + |
| 175 | + buf[0] = 0; |
| 176 | + |
| 177 | + cdns_mhdp_infoframe_set(mhdp, 1, sizeof(buf), buf, HDMI_INFOFRAME_TYPE_AUDIO); |
| 178 | +} |
| 179 | + |
| 180 | +int cdns_mhdp_audio_stop(struct cdns_mhdp_device *mhdp, |
| 181 | + struct audio_info *audio) |
| 182 | +{ |
| 183 | + int ret; |
| 184 | + |
| 185 | + if (audio->connector_type == DRM_MODE_CONNECTOR_DisplayPort) { |
| 186 | + ret = cdns_mhdp_reg_write(mhdp, AUDIO_PACK_CONTROL, 0); |
| 187 | + if (ret) { |
| 188 | + DRM_DEV_ERROR(mhdp->dev, "audio stop failed: %d\n", ret); |
| 189 | + return ret; |
| 190 | + } |
| 191 | + } |
| 192 | + |
| 193 | + cdns_mhdp_bus_write(0, mhdp, SPDIF_CTRL_ADDR); |
| 194 | + |
| 195 | + /* clearn the audio config and reset */ |
| 196 | + cdns_mhdp_bus_write(0, mhdp, AUDIO_SRC_CNTL); |
| 197 | + cdns_mhdp_bus_write(0, mhdp, AUDIO_SRC_CNFG); |
| 198 | + cdns_mhdp_bus_write(AUDIO_SW_RST, mhdp, AUDIO_SRC_CNTL); |
| 199 | + cdns_mhdp_bus_write(0, mhdp, AUDIO_SRC_CNTL); |
| 200 | + |
| 201 | + /* reset smpl2pckt component */ |
| 202 | + cdns_mhdp_bus_write(0, mhdp, SMPL2PKT_CNTL); |
| 203 | + cdns_mhdp_bus_write(AUDIO_SW_RST, mhdp, SMPL2PKT_CNTL); |
| 204 | + cdns_mhdp_bus_write(0, mhdp, SMPL2PKT_CNTL); |
| 205 | + |
| 206 | + /* reset FIFO */ |
| 207 | + cdns_mhdp_bus_write(AUDIO_SW_RST, mhdp, FIFO_CNTL); |
| 208 | + cdns_mhdp_bus_write(0, mhdp, FIFO_CNTL); |
| 209 | + |
| 210 | + if (audio->format == AFMT_SPDIF_INT) |
| 211 | + clk_disable_unprepare(mhdp->spdif_clk); |
| 212 | + |
| 213 | + return 0; |
| 214 | +} |
| 215 | +EXPORT_SYMBOL(cdns_mhdp_audio_stop); |
| 216 | + |
| 217 | +int cdns_mhdp_audio_mute(struct cdns_mhdp_device *mhdp, bool enable) |
| 218 | +{ |
| 219 | + struct audio_info *audio = &mhdp->audio_info; |
| 220 | + int ret = true; |
| 221 | + |
| 222 | + if (audio->connector_type == DRM_MODE_CONNECTOR_DisplayPort) { |
| 223 | + ret = cdns_mhdp_reg_write_bit(mhdp, DP_VB_ID, 4, 1, enable); |
| 224 | + if (ret) |
| 225 | + DRM_DEV_ERROR(mhdp->dev, "audio mute failed: %d\n", ret); |
| 226 | + } |
| 227 | + |
| 228 | + return ret; |
| 229 | +} |
| 230 | +EXPORT_SYMBOL(cdns_mhdp_audio_mute); |
| 231 | + |
| 232 | +static void cdns_mhdp_audio_config_i2s(struct cdns_mhdp_device *mhdp, |
| 233 | + struct audio_info *audio) |
| 234 | +{ |
| 235 | + int sub_pckt_num = 1, i2s_port_en_val = 0xf, i; |
| 236 | + int idx = select_N_index(mhdp->mode.clock); |
| 237 | + u32 val, ncts; |
| 238 | + |
| 239 | + if (audio->channels == 2) { |
| 240 | + if (mhdp->dp.link.num_lanes == 1) |
| 241 | + sub_pckt_num = 2; |
| 242 | + else |
| 243 | + sub_pckt_num = 4; |
| 244 | + |
| 245 | + i2s_port_en_val = 1; |
| 246 | + } else if (audio->channels == 4) { |
| 247 | + i2s_port_en_val = 3; |
| 248 | + } |
| 249 | + |
| 250 | + cdns_mhdp_bus_write(0x0, mhdp, SPDIF_CTRL_ADDR); |
| 251 | + |
| 252 | + cdns_mhdp_bus_write(SYNC_WR_TO_CH_ZERO, mhdp, FIFO_CNTL); |
| 253 | + |
| 254 | + val = MAX_NUM_CH(audio->channels); |
| 255 | + val |= NUM_OF_I2S_PORTS(audio->channels); |
| 256 | + val |= AUDIO_TYPE_LPCM; |
| 257 | + val |= CFG_SUB_PCKT_NUM(sub_pckt_num); |
| 258 | + cdns_mhdp_bus_write(val, mhdp, SMPL2PKT_CNFG); |
| 259 | + |
| 260 | + if (audio->sample_width == 16) |
| 261 | + val = 0; |
| 262 | + else if (audio->sample_width == 24) |
| 263 | + val = 1 << 9; |
| 264 | + else |
| 265 | + val = 2 << 9; |
| 266 | + |
| 267 | + val |= AUDIO_CH_NUM(audio->channels); |
| 268 | + val |= I2S_DEC_PORT_EN(i2s_port_en_val); |
| 269 | + val |= TRANS_SMPL_WIDTH_32; |
| 270 | + cdns_mhdp_bus_write(val, mhdp, AUDIO_SRC_CNFG); |
| 271 | + |
| 272 | + for (i = 0; i < (audio->channels + 1) / 2; i++) { |
| 273 | + if (audio->sample_width == 16) |
| 274 | + val = (0x02 << 8) | (0x02 << 20); |
| 275 | + else if (audio->sample_width == 24) |
| 276 | + val = (0x0b << 8) | (0x0b << 20); |
| 277 | + |
| 278 | + val |= ((2 * i) << 4) | ((2 * i + 1) << 16); |
| 279 | + cdns_mhdp_bus_write(val, mhdp, STTS_BIT_CH(i)); |
| 280 | + } |
| 281 | + |
| 282 | + switch (audio->sample_rate) { |
| 283 | + case 32000: |
| 284 | + val = SAMPLING_FREQ(3) | |
| 285 | + ORIGINAL_SAMP_FREQ(0xc); |
| 286 | + ncts = N_table_32k[idx]; |
| 287 | + break; |
| 288 | + case 44100: |
| 289 | + val = SAMPLING_FREQ(0) | |
| 290 | + ORIGINAL_SAMP_FREQ(0xf); |
| 291 | + ncts = N_table_44k[idx]; |
| 292 | + break; |
| 293 | + case 48000: |
| 294 | + val = SAMPLING_FREQ(2) | |
| 295 | + ORIGINAL_SAMP_FREQ(0xd); |
| 296 | + ncts = N_table_48k[idx]; |
| 297 | + break; |
| 298 | + case 88200: |
| 299 | + val = SAMPLING_FREQ(8) | |
| 300 | + ORIGINAL_SAMP_FREQ(0x7); |
| 301 | + ncts = N_table_44k[idx] * 2; |
| 302 | + break; |
| 303 | + case 96000: |
| 304 | + val = SAMPLING_FREQ(0xa) | |
| 305 | + ORIGINAL_SAMP_FREQ(5); |
| 306 | + ncts = N_table_48k[idx] * 2; |
| 307 | + break; |
| 308 | + case 176400: |
| 309 | + val = SAMPLING_FREQ(0xc) | |
| 310 | + ORIGINAL_SAMP_FREQ(3); |
| 311 | + ncts = N_table_44k[idx] * 4; |
| 312 | + break; |
| 313 | + case 192000: |
| 314 | + default: |
| 315 | + val = SAMPLING_FREQ(0xe) | |
| 316 | + ORIGINAL_SAMP_FREQ(1); |
| 317 | + ncts = N_table_48k[idx] * 4; |
| 318 | + break; |
| 319 | + } |
| 320 | + val |= 4; |
| 321 | + cdns_mhdp_bus_write(val, mhdp, COM_CH_STTS_BITS); |
| 322 | + |
| 323 | + if (audio->connector_type == DRM_MODE_CONNECTOR_HDMIA) |
| 324 | + cdns_mhdp_reg_write(mhdp, CM_I2S_CTRL, ncts | 0x4000000); |
| 325 | + |
| 326 | + cdns_mhdp_bus_write(SMPL2PKT_EN, mhdp, SMPL2PKT_CNTL); |
| 327 | + cdns_mhdp_bus_write(I2S_DEC_START, mhdp, AUDIO_SRC_CNTL); |
| 328 | +} |
| 329 | + |
| 330 | +static void cdns_mhdp_audio_config_spdif(struct cdns_mhdp_device *mhdp) |
| 331 | +{ |
| 332 | + u32 val; |
| 333 | + |
| 334 | + cdns_mhdp_bus_write(SYNC_WR_TO_CH_ZERO, mhdp, FIFO_CNTL); |
| 335 | + |
| 336 | + val = MAX_NUM_CH(2) | AUDIO_TYPE_LPCM | CFG_SUB_PCKT_NUM(4); |
| 337 | + cdns_mhdp_bus_write(val, mhdp, SMPL2PKT_CNFG); |
| 338 | + cdns_mhdp_bus_write(SMPL2PKT_EN, mhdp, SMPL2PKT_CNTL); |
| 339 | + |
| 340 | + val = SPDIF_ENABLE | SPDIF_AVG_SEL | SPDIF_JITTER_BYPASS; |
| 341 | + cdns_mhdp_bus_write(val, mhdp, SPDIF_CTRL_ADDR); |
| 342 | + |
| 343 | + clk_prepare_enable(mhdp->spdif_clk); |
| 344 | + clk_set_rate(mhdp->spdif_clk, CDNS_DP_SPDIF_CLK); |
| 345 | +} |
| 346 | + |
| 347 | +int cdns_mhdp_audio_config(struct cdns_mhdp_device *mhdp, |
| 348 | + struct audio_info *audio) |
| 349 | +{ |
| 350 | + int ret; |
| 351 | + |
| 352 | + /* reset the spdif clk before config */ |
| 353 | + if (audio->format == AFMT_SPDIF_INT) { |
| 354 | + reset_control_assert(mhdp->spdif_rst); |
| 355 | + reset_control_deassert(mhdp->spdif_rst); |
| 356 | + } |
| 357 | + |
| 358 | + if (audio->connector_type == DRM_MODE_CONNECTOR_DisplayPort) { |
| 359 | + ret = cdns_mhdp_reg_write(mhdp, CM_LANE_CTRL, LANE_REF_CYC); |
| 360 | + if (ret) |
| 361 | + goto err_audio_config; |
| 362 | + |
| 363 | + ret = cdns_mhdp_reg_write(mhdp, CM_CTRL, 0); |
| 364 | + if (ret) |
| 365 | + goto err_audio_config; |
| 366 | + } else { |
| 367 | + /* HDMI Mode */ |
| 368 | + ret = cdns_mhdp_reg_write(mhdp, CM_CTRL, 8); |
| 369 | + if (ret) |
| 370 | + goto err_audio_config; |
| 371 | + } |
| 372 | + |
| 373 | + if (audio->format == AFMT_I2S) |
| 374 | + cdns_mhdp_audio_config_i2s(mhdp, audio); |
| 375 | + else if (audio->format == AFMT_SPDIF_INT) |
| 376 | + cdns_mhdp_audio_config_spdif(mhdp); |
| 377 | + |
| 378 | + if (audio->connector_type == DRM_MODE_CONNECTOR_DisplayPort) |
| 379 | + ret = cdns_mhdp_reg_write(mhdp, AUDIO_PACK_CONTROL, AUDIO_PACK_EN); |
| 380 | + |
| 381 | + if (audio->connector_type == DRM_MODE_CONNECTOR_HDMIA) |
| 382 | + hdmi_audio_avi_set(mhdp, audio->channels); |
| 383 | + |
| 384 | +err_audio_config: |
| 385 | + if (ret) |
| 386 | + DRM_DEV_ERROR(mhdp->dev, "audio config failed: %d\n", ret); |
| 387 | + return ret; |
| 388 | +} |
| 389 | +EXPORT_SYMBOL(cdns_mhdp_audio_config); |
| 390 | + |
| 391 | +static int audio_hw_params(struct device *dev, void *data, |
| 392 | + struct hdmi_codec_daifmt *daifmt, |
| 393 | + struct hdmi_codec_params *params) |
| 394 | +{ |
| 395 | + struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev); |
| 396 | + struct audio_info audio = { |
| 397 | + .sample_width = params->sample_width, |
| 398 | + .sample_rate = params->sample_rate, |
| 399 | + .channels = params->channels, |
| 400 | + .connector_type = mhdp->connector.base.connector_type, |
| 401 | + }; |
| 402 | + int ret; |
| 403 | + |
| 404 | + switch (daifmt->fmt) { |
| 405 | + case HDMI_I2S: |
| 406 | + audio.format = AFMT_I2S; |
| 407 | + break; |
| 408 | + case HDMI_SPDIF: |
| 409 | + audio.format = AFMT_SPDIF_EXT; |
| 410 | + break; |
| 411 | + default: |
| 412 | + DRM_DEV_ERROR(dev, "Invalid format %d\n", daifmt->fmt); |
| 413 | + ret = -EINVAL; |
| 414 | + goto out; |
| 415 | + } |
| 416 | + |
| 417 | + ret = cdns_mhdp_audio_config(mhdp, &audio); |
| 418 | + if (!ret) |
| 419 | + mhdp->audio_info = audio; |
| 420 | + |
| 421 | +out: |
| 422 | + return ret; |
| 423 | +} |
| 424 | + |
| 425 | +static void audio_shutdown(struct device *dev, void *data) |
| 426 | +{ |
| 427 | + struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev); |
| 428 | + int ret; |
| 429 | + |
| 430 | + ret = cdns_mhdp_audio_stop(mhdp, &mhdp->audio_info); |
| 431 | + if (!ret) |
| 432 | + mhdp->audio_info.format = AFMT_UNUSED; |
| 433 | +} |
| 434 | + |
| 435 | +static int audio_digital_mute(struct device *dev, void *data, |
| 436 | + bool enable) |
| 437 | +{ |
| 438 | + struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev); |
| 439 | + int ret; |
| 440 | + |
| 441 | + ret = cdns_mhdp_audio_mute(mhdp, enable); |
| 442 | + |
| 443 | + return ret; |
| 444 | +} |
| 445 | + |
| 446 | +static int audio_get_eld(struct device *dev, void *data, |
| 447 | + u8 *buf, size_t len) |
| 448 | +{ |
| 449 | + struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev); |
| 450 | + |
| 451 | + memcpy(buf, mhdp->connector.base.eld, |
| 452 | + min(sizeof(mhdp->connector.base.eld), len)); |
| 453 | + |
| 454 | + return 0; |
| 455 | +} |
| 456 | + |
| 457 | +static const struct hdmi_codec_ops audio_codec_ops = { |
| 458 | + .hw_params = audio_hw_params, |
| 459 | + .audio_shutdown = audio_shutdown, |
| 460 | + .digital_mute = audio_digital_mute, |
| 461 | + .get_eld = audio_get_eld, |
| 462 | +}; |
| 463 | + |
| 464 | +int cdns_mhdp_register_audio_driver(struct device *dev) |
| 465 | +{ |
| 466 | + struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev); |
| 467 | + struct hdmi_codec_pdata codec_data = { |
| 468 | + .i2s = 1, |
| 469 | + .spdif = 1, |
| 470 | + .ops = &audio_codec_ops, |
| 471 | + .max_i2s_channels = 8, |
| 472 | + }; |
| 473 | + |
| 474 | + mhdp->audio_pdev = platform_device_register_data( |
| 475 | + dev, HDMI_CODEC_DRV_NAME, 1, |
| 476 | + &codec_data, sizeof(codec_data)); |
| 477 | + |
| 478 | + return PTR_ERR_OR_ZERO(mhdp->audio_pdev); |
| 479 | +} |
| 480 | + |
| 481 | +void cdns_mhdp_unregister_audio_driver(struct device *dev) |
| 482 | +{ |
| 483 | + struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev); |
| 484 | + |
| 485 | + platform_device_unregister(mhdp->audio_pdev); |
| 486 | +} |
| 487 | --- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c |
| 488 | +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c |
| 489 | @@ -937,189 +937,6 @@ err_config_video: |
| 490 | } |
| 491 | EXPORT_SYMBOL(cdns_mhdp_config_video); |
| 492 | |
| 493 | -int cdns_mhdp_audio_stop(struct cdns_mhdp_device *mhdp, |
| 494 | - struct audio_info *audio) |
| 495 | -{ |
| 496 | - int ret; |
| 497 | - |
| 498 | - ret = cdns_mhdp_reg_write(mhdp, AUDIO_PACK_CONTROL, 0); |
| 499 | - if (ret) { |
| 500 | - DRM_DEV_ERROR(mhdp->dev, "audio stop failed: %d\n", ret); |
| 501 | - return ret; |
| 502 | - } |
| 503 | - |
| 504 | - cdns_mhdp_bus_write(0, mhdp, SPDIF_CTRL_ADDR); |
| 505 | - |
| 506 | - /* clearn the audio config and reset */ |
| 507 | - cdns_mhdp_bus_write(0, mhdp, AUDIO_SRC_CNTL); |
| 508 | - cdns_mhdp_bus_write(0, mhdp, AUDIO_SRC_CNFG); |
| 509 | - cdns_mhdp_bus_write(AUDIO_SW_RST, mhdp, AUDIO_SRC_CNTL); |
| 510 | - cdns_mhdp_bus_write(0, mhdp, AUDIO_SRC_CNTL); |
| 511 | - |
| 512 | - /* reset smpl2pckt component */ |
| 513 | - cdns_mhdp_bus_write(0, mhdp, SMPL2PKT_CNTL); |
| 514 | - cdns_mhdp_bus_write(AUDIO_SW_RST, mhdp, SMPL2PKT_CNTL); |
| 515 | - cdns_mhdp_bus_write(0, mhdp, SMPL2PKT_CNTL); |
| 516 | - |
| 517 | - /* reset FIFO */ |
| 518 | - cdns_mhdp_bus_write(AUDIO_SW_RST, mhdp, FIFO_CNTL); |
| 519 | - cdns_mhdp_bus_write(0, mhdp, FIFO_CNTL); |
| 520 | - |
| 521 | - if (audio->format == AFMT_SPDIF_INT) |
| 522 | - clk_disable_unprepare(mhdp->spdif_clk); |
| 523 | - |
| 524 | - return 0; |
| 525 | -} |
| 526 | -EXPORT_SYMBOL(cdns_mhdp_audio_stop); |
| 527 | - |
| 528 | -int cdns_mhdp_audio_mute(struct cdns_mhdp_device *mhdp, bool enable) |
| 529 | -{ |
| 530 | - int ret; |
| 531 | - |
| 532 | - ret = cdns_mhdp_reg_write_bit(mhdp, DP_VB_ID, 4, 1, enable); |
| 533 | - if (ret) |
| 534 | - DRM_DEV_ERROR(mhdp->dev, "audio mute failed: %d\n", ret); |
| 535 | - |
| 536 | - return ret; |
| 537 | -} |
| 538 | -EXPORT_SYMBOL(cdns_mhdp_audio_mute); |
| 539 | - |
| 540 | -static void cdns_mhdp_audio_config_i2s(struct cdns_mhdp_device *mhdp, |
| 541 | - struct audio_info *audio) |
| 542 | -{ |
| 543 | - int sub_pckt_num = 1, i2s_port_en_val = 0xf, i; |
| 544 | - u32 val; |
| 545 | - |
| 546 | - if (audio->channels == 2) { |
| 547 | - if (mhdp->dp.link.num_lanes == 1) |
| 548 | - sub_pckt_num = 2; |
| 549 | - else |
| 550 | - sub_pckt_num = 4; |
| 551 | - |
| 552 | - i2s_port_en_val = 1; |
| 553 | - } else if (audio->channels == 4) { |
| 554 | - i2s_port_en_val = 3; |
| 555 | - } |
| 556 | - |
| 557 | - cdns_mhdp_bus_write(0x0, mhdp, SPDIF_CTRL_ADDR); |
| 558 | - |
| 559 | - cdns_mhdp_bus_write(SYNC_WR_TO_CH_ZERO, mhdp, FIFO_CNTL); |
| 560 | - |
| 561 | - val = MAX_NUM_CH(audio->channels); |
| 562 | - val |= NUM_OF_I2S_PORTS(audio->channels); |
| 563 | - val |= AUDIO_TYPE_LPCM; |
| 564 | - val |= CFG_SUB_PCKT_NUM(sub_pckt_num); |
| 565 | - cdns_mhdp_bus_write(val, mhdp, SMPL2PKT_CNFG); |
| 566 | - |
| 567 | - if (audio->sample_width == 16) |
| 568 | - val = 0; |
| 569 | - else if (audio->sample_width == 24) |
| 570 | - val = 1 << 9; |
| 571 | - else |
| 572 | - val = 2 << 9; |
| 573 | - |
| 574 | - val |= AUDIO_CH_NUM(audio->channels); |
| 575 | - val |= I2S_DEC_PORT_EN(i2s_port_en_val); |
| 576 | - val |= TRANS_SMPL_WIDTH_32; |
| 577 | - cdns_mhdp_bus_write(val, mhdp, AUDIO_SRC_CNFG); |
| 578 | - |
| 579 | - for (i = 0; i < (audio->channels + 1) / 2; i++) { |
| 580 | - if (audio->sample_width == 16) |
| 581 | - val = (0x02 << 8) | (0x02 << 20); |
| 582 | - else if (audio->sample_width == 24) |
| 583 | - val = (0x0b << 8) | (0x0b << 20); |
| 584 | - |
| 585 | - val |= ((2 * i) << 4) | ((2 * i + 1) << 16); |
| 586 | - cdns_mhdp_bus_write(val, mhdp, STTS_BIT_CH(i)); |
| 587 | - } |
| 588 | - |
| 589 | - switch (audio->sample_rate) { |
| 590 | - case 32000: |
| 591 | - val = SAMPLING_FREQ(3) | |
| 592 | - ORIGINAL_SAMP_FREQ(0xc); |
| 593 | - break; |
| 594 | - case 44100: |
| 595 | - val = SAMPLING_FREQ(0) | |
| 596 | - ORIGINAL_SAMP_FREQ(0xf); |
| 597 | - break; |
| 598 | - case 48000: |
| 599 | - val = SAMPLING_FREQ(2) | |
| 600 | - ORIGINAL_SAMP_FREQ(0xd); |
| 601 | - break; |
| 602 | - case 88200: |
| 603 | - val = SAMPLING_FREQ(8) | |
| 604 | - ORIGINAL_SAMP_FREQ(0x7); |
| 605 | - break; |
| 606 | - case 96000: |
| 607 | - val = SAMPLING_FREQ(0xa) | |
| 608 | - ORIGINAL_SAMP_FREQ(5); |
| 609 | - break; |
| 610 | - case 176400: |
| 611 | - val = SAMPLING_FREQ(0xc) | |
| 612 | - ORIGINAL_SAMP_FREQ(3); |
| 613 | - break; |
| 614 | - case 192000: |
| 615 | - val = SAMPLING_FREQ(0xe) | |
| 616 | - ORIGINAL_SAMP_FREQ(1); |
| 617 | - break; |
| 618 | - } |
| 619 | - val |= 4; |
| 620 | - cdns_mhdp_bus_write(val, mhdp, COM_CH_STTS_BITS); |
| 621 | - |
| 622 | - cdns_mhdp_bus_write(SMPL2PKT_EN, mhdp, SMPL2PKT_CNTL); |
| 623 | - cdns_mhdp_bus_write(I2S_DEC_START, mhdp, AUDIO_SRC_CNTL); |
| 624 | -} |
| 625 | - |
| 626 | -static void cdns_mhdp_audio_config_spdif(struct cdns_mhdp_device *mhdp) |
| 627 | -{ |
| 628 | - u32 val; |
| 629 | - |
| 630 | - cdns_mhdp_bus_write(SYNC_WR_TO_CH_ZERO, mhdp, FIFO_CNTL); |
| 631 | - |
| 632 | - val = MAX_NUM_CH(2) | AUDIO_TYPE_LPCM | CFG_SUB_PCKT_NUM(4); |
| 633 | - cdns_mhdp_bus_write(val, mhdp, SMPL2PKT_CNFG); |
| 634 | - cdns_mhdp_bus_write(SMPL2PKT_EN, mhdp, SMPL2PKT_CNTL); |
| 635 | - |
| 636 | - val = SPDIF_ENABLE | SPDIF_AVG_SEL | SPDIF_JITTER_BYPASS; |
| 637 | - cdns_mhdp_bus_write(val, mhdp, SPDIF_CTRL_ADDR); |
| 638 | - |
| 639 | - clk_prepare_enable(mhdp->spdif_clk); |
| 640 | - clk_set_rate(mhdp->spdif_clk, CDNS_DP_SPDIF_CLK); |
| 641 | -} |
| 642 | - |
| 643 | -int cdns_mhdp_audio_config(struct cdns_mhdp_device *mhdp, |
| 644 | - struct audio_info *audio) |
| 645 | -{ |
| 646 | - int ret; |
| 647 | - |
| 648 | - /* reset the spdif clk before config */ |
| 649 | - if (audio->format == AFMT_SPDIF_INT) { |
| 650 | - reset_control_assert(mhdp->spdif_rst); |
| 651 | - reset_control_deassert(mhdp->spdif_rst); |
| 652 | - } |
| 653 | - |
| 654 | - ret = cdns_mhdp_reg_write(mhdp, CM_LANE_CTRL, LANE_REF_CYC); |
| 655 | - if (ret) |
| 656 | - goto err_audio_config; |
| 657 | - |
| 658 | - ret = cdns_mhdp_reg_write(mhdp, CM_CTRL, 0); |
| 659 | - if (ret) |
| 660 | - goto err_audio_config; |
| 661 | - |
| 662 | - if (audio->format == AFMT_I2S) |
| 663 | - cdns_mhdp_audio_config_i2s(mhdp, audio); |
| 664 | - else if (audio->format == AFMT_SPDIF_INT) |
| 665 | - cdns_mhdp_audio_config_spdif(mhdp); |
| 666 | - |
| 667 | - ret = cdns_mhdp_reg_write(mhdp, AUDIO_PACK_CONTROL, AUDIO_PACK_EN); |
| 668 | - |
| 669 | -err_audio_config: |
| 670 | - if (ret) |
| 671 | - DRM_DEV_ERROR(mhdp->dev, "audio config failed: %d\n", ret); |
| 672 | - return ret; |
| 673 | -} |
| 674 | -EXPORT_SYMBOL(cdns_mhdp_audio_config); |
| 675 | - |
| 676 | int cdns_mhdp_adjust_lt(struct cdns_mhdp_device *mhdp, |
| 677 | u8 nlanes, u16 udelay, u8 *lanes_data, u8 *dpcd) |
| 678 | { |
| 679 | --- a/drivers/gpu/drm/imx/Kconfig |
| 680 | +++ b/drivers/gpu/drm/imx/Kconfig |
| 681 | @@ -45,6 +45,7 @@ config DRM_IMX_CDNS_MHDP |
| 682 | select DRM_CDNS_MHDP |
| 683 | select DRM_CDNS_DP |
| 684 | select DRM_CDNS_HDMI |
| 685 | + select DRM_CDNS_AUDIO |
| 686 | depends on DRM_IMX |
| 687 | help |
| 688 | Choose this if you want to use HDMI on i.MX8. |
| 689 | --- a/include/drm/bridge/cdns-mhdp-common.h |
| 690 | +++ b/include/drm/bridge/cdns-mhdp-common.h |
| 691 | @@ -548,6 +548,7 @@ struct audio_info { |
| 692 | int sample_rate; |
| 693 | int channels; |
| 694 | int sample_width; |
| 695 | + int connector_type; |
| 696 | }; |
| 697 | |
| 698 | enum vic_pxl_encoding_format { |
| 699 | @@ -670,11 +671,16 @@ int cdns_mhdp_get_edid_block(void *mhdp, |
| 700 | int cdns_mhdp_train_link(struct cdns_mhdp_device *mhdp); |
| 701 | int cdns_mhdp_set_video_status(struct cdns_mhdp_device *mhdp, int active); |
| 702 | int cdns_mhdp_config_video(struct cdns_mhdp_device *mhdp); |
| 703 | + |
| 704 | +/* Audio */ |
| 705 | int cdns_mhdp_audio_stop(struct cdns_mhdp_device *mhdp, |
| 706 | struct audio_info *audio); |
| 707 | int cdns_mhdp_audio_mute(struct cdns_mhdp_device *mhdp, bool enable); |
| 708 | int cdns_mhdp_audio_config(struct cdns_mhdp_device *mhdp, |
| 709 | struct audio_info *audio); |
| 710 | +int cdns_mhdp_register_audio_driver(struct device *dev); |
| 711 | +void cdns_mhdp_unregister_audio_driver(struct device *dev); |
| 712 | + |
| 713 | int cdns_mhdp_reg_read(struct cdns_mhdp_device *mhdp, u32 addr); |
| 714 | int cdns_mhdp_reg_write(struct cdns_mhdp_device *mhdp, u32 addr, u32 val); |
| 715 | int cdns_mhdp_reg_write_bit(struct cdns_mhdp_device *mhdp, u16 addr, |