diff --git a/projects/NXP/devices/iMX8/linux/linux.aarch64.conf b/projects/NXP/devices/iMX8/linux/linux.aarch64.conf index 7bca5f4f11..77f82ec07a 100644 --- a/projects/NXP/devices/iMX8/linux/linux.aarch64.conf +++ b/projects/NXP/devices/iMX8/linux/linux.aarch64.conf @@ -585,7 +585,7 @@ CONFIG_CRYPTO_AES_ARM64_CE_CCM=y CONFIG_CRYPTO_AES_ARM64_CE_BLK=y CONFIG_CRYPTO_AES_ARM64_NEON_BLK=y CONFIG_CRYPTO_CHACHA20_NEON=y -# CONFIG_CRYPTO_POLY1305_NEON is not set +CONFIG_CRYPTO_POLY1305_NEON=m # CONFIG_CRYPTO_NHPOLY1305_NEON is not set CONFIG_CRYPTO_AES_ARM64_BS=y @@ -1483,14 +1483,12 @@ CONFIG_MTD_CFI_I2=y # Mapping drivers for chip access # # CONFIG_MTD_COMPLEX_MAPPINGS is not set -# CONFIG_MTD_INTEL_VR_NOR is not set # CONFIG_MTD_PLATRAM is not set # end of Mapping drivers for chip access # # Self-contained MTD device drivers # -# CONFIG_MTD_PMC551 is not set # CONFIG_MTD_DATAFLASH is not set # CONFIG_MTD_MCHP23K256 is not set # CONFIG_MTD_SST25L is not set @@ -1663,7 +1661,8 @@ CONFIG_MII=y CONFIG_NET_CORE=y # CONFIG_BONDING is not set # CONFIG_DUMMY is not set -# CONFIG_WIREGUARD is not set +CONFIG_WIREGUARD=m +# CONFIG_WIREGUARD_DEBUG is not set # CONFIG_EQUALIZER is not set # CONFIG_NET_TEAM is not set CONFIG_MACVLAN=m @@ -2978,6 +2977,8 @@ CONFIG_IR_PWM_TX=m # CONFIG_IR_SIR is not set CONFIG_RC_XBOX_DVD=m # CONFIG_IR_TOY is not set +CONFIG_CEC_CORE=y +CONFIG_CEC_NOTIFIER=y CONFIG_MEDIA_CEC_SUPPORT=y # CONFIG_CEC_CH7322 is not set # CONFIG_CEC_CROS_EC is not set @@ -3701,9 +3702,16 @@ CONFIG_DRM_PANEL_BRIDGE=y # CONFIG_DRM_ANALOGIX_ANX78XX is not set # CONFIG_DRM_I2C_ADV7511 is not set # CONFIG_DRM_CDNS_MHDP8546 is not set +CONFIG_DRM_CDNS_MHDP=y +CONFIG_DRM_CDNS_HDMI=y +CONFIG_DRM_CDNS_DP=y +CONFIG_DRM_CDNS_AUDIO=y +CONFIG_DRM_CDNS_HDMI_HDCP=y +CONFIG_DRM_CDNS_HDMI_CEC=y # end of Display Interface Bridges CONFIG_DRM_IMX_DCSS=y +CONFIG_DRM_IMX_CDNS_MHDP=y CONFIG_DRM_ETNAVIV=y CONFIG_DRM_ETNAVIV_THERMAL=y # CONFIG_DRM_ARCPGU is not set @@ -6000,15 +6008,18 @@ CONFIG_CRYPTO_HASH_INFO=y # CONFIG_CRYPTO_LIB_AES=y CONFIG_CRYPTO_LIB_ARC4=m -# CONFIG_CRYPTO_LIB_BLAKE2S is not set +CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=m +CONFIG_CRYPTO_LIB_BLAKE2S=m CONFIG_CRYPTO_ARCH_HAVE_LIB_CHACHA=y CONFIG_CRYPTO_LIB_CHACHA_GENERIC=y -# CONFIG_CRYPTO_LIB_CHACHA is not set -# CONFIG_CRYPTO_LIB_CURVE25519 is not set +CONFIG_CRYPTO_LIB_CHACHA=m +CONFIG_CRYPTO_LIB_CURVE25519_GENERIC=m +CONFIG_CRYPTO_LIB_CURVE25519=m CONFIG_CRYPTO_LIB_DES=y CONFIG_CRYPTO_LIB_POLY1305_RSIZE=9 -# CONFIG_CRYPTO_LIB_POLY1305 is not set -# CONFIG_CRYPTO_LIB_CHACHA20POLY1305 is not set +CONFIG_CRYPTO_ARCH_HAVE_LIB_POLY1305=m +CONFIG_CRYPTO_LIB_POLY1305=m +CONFIG_CRYPTO_LIB_CHACHA20POLY1305=m CONFIG_CRYPTO_LIB_SHA256=y CONFIG_CRYPTO_HW=y # CONFIG_CRYPTO_DEV_FSL_CAAM is not set @@ -6024,7 +6035,7 @@ CONFIG_CRYPTO_HW=y CONFIG_ASYMMETRIC_KEY_TYPE=y CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y CONFIG_X509_CERTIFICATE_PARSER=y -CONFIG_PKCS8_PRIVATE_KEY_PARSER=y +CONFIG_PKCS8_PRIVATE_KEY_PARSER=m CONFIG_PKCS7_MESSAGE_PARSER=y # CONFIG_PKCS7_TEST_KEY is not set # CONFIG_SIGNED_PE_FILE_VERIFICATION is not set diff --git a/projects/NXP/devices/iMX8/patches/linux/0001-drm-bridge-mhdp-Add-cdns-mhdp-driver-bridge-driver.patch b/projects/NXP/devices/iMX8/patches/linux/0001-drm-bridge-mhdp-Add-cdns-mhdp-driver-bridge-driver.patch new file mode 100644 index 0000000000..c3ab38d336 --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0001-drm-bridge-mhdp-Add-cdns-mhdp-driver-bridge-driver.patch @@ -0,0 +1,5931 @@ +From c50c4f565797ac47544e1f0a2669d9cf1cb9d1c7 Mon Sep 17 00:00:00 2001 +From: Sandor Yu +Date: Mon, 20 Apr 2020 23:01:50 +0800 +Subject: [PATCH 01/49] drm: bridge: mhdp: Add cdns mhdp driver bridge driver + +Base on rockchip cdn-dp-reg.c code, +create cdns mhdp DP API functions driver. +Move the driver to a separate directory. +Added HDMI/Audio/CEC API functions. + +Signed-off-by: Sandor Yu +[ Aisheng: fix conflict due to below commit +611e22b1d9f6 ("drm/rockchip: Remove unneeded semicolon") ] +Signed-off-by: Dong Aisheng +--- + drivers/gpu/drm/bridge/cadence/Kconfig | 26 + + drivers/gpu/drm/bridge/cadence/Makefile | 9 + + drivers/gpu/drm/bridge/cadence/cdns-dp-core.c | 574 +++++++++++ + .../gpu/drm/bridge/cadence/cdns-hdmi-core.c | 690 +++++++++++++ + .../gpu/drm/bridge/cadence/cdns-mhdp-audio.c | 395 +++++++ + .../gpu/drm/bridge/cadence/cdns-mhdp-cec.c | 341 +++++++ + .../gpu/drm/bridge/cadence/cdns-mhdp-common.c | 795 +++++++++++++++ + drivers/gpu/drm/bridge/cadence/cdns-mhdp-dp.c | 172 ++++ + .../gpu/drm/bridge/cadence/cdns-mhdp-hdmi.c | 332 ++++++ + drivers/gpu/drm/bridge/cadence/cdns-mhdp.h | 209 ++++ + drivers/gpu/drm/rockchip/Makefile | 2 +- + drivers/gpu/drm/rockchip/cdn-dp-core.c | 241 ++--- + drivers/gpu/drm/rockchip/cdn-dp-core.h | 44 +- + drivers/gpu/drm/rockchip/cdn-dp-reg.c | 960 ------------------ + .../drm/bridge/cdns-mhdp.h | 389 ++++++- + 15 files changed, 4034 insertions(+), 1145 deletions(-) + create mode 100644 drivers/gpu/drm/bridge/cadence/cdns-dp-core.c + create mode 100644 drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c + create mode 100644 drivers/gpu/drm/bridge/cadence/cdns-mhdp-audio.c + create mode 100644 drivers/gpu/drm/bridge/cadence/cdns-mhdp-cec.c + create mode 100644 drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c + create mode 100644 drivers/gpu/drm/bridge/cadence/cdns-mhdp-dp.c + create mode 100644 drivers/gpu/drm/bridge/cadence/cdns-mhdp-hdmi.c + create mode 100644 drivers/gpu/drm/bridge/cadence/cdns-mhdp.h + delete mode 100644 drivers/gpu/drm/rockchip/cdn-dp-reg.c + rename drivers/gpu/drm/rockchip/cdn-dp-reg.h => include/drm/bridge/cdns-mhdp.h (53%) + +diff --git a/drivers/gpu/drm/bridge/cadence/Kconfig b/drivers/gpu/drm/bridge/cadence/Kconfig +index ef8c230e0f62..bb1865b15aca 100644 +--- a/drivers/gpu/drm/bridge/cadence/Kconfig ++++ b/drivers/gpu/drm/bridge/cadence/Kconfig +@@ -22,3 +22,29 @@ config DRM_CDNS_MHDP8546_J721E + initializes the J721E Display Port and sets up the + clock and data muxes. + endif ++ ++config DRM_CDNS_MHDP ++ tristate "Cadence MHDP COMMON API driver" ++ select DRM_KMS_HELPER ++ select DRM_PANEL_BRIDGE ++ depends on OF ++ help ++ Support Cadence MHDP API library. ++ ++config DRM_CDNS_HDMI ++ tristate "Cadence HDMI DRM driver" ++ depends on DRM_CDNS_MHDP ++ ++config DRM_CDNS_DP ++ tristate "Cadence DP DRM driver" ++ depends on DRM_CDNS_MHDP ++ ++config DRM_CDNS_AUDIO ++ tristate "Cadence MHDP Audio driver" ++ depends on DRM_CDNS_MHDP ++ ++config DRM_CDNS_HDMI_CEC ++ tristate "Cadence MHDP HDMI CEC driver" ++ depends on DRM_CDNS_HDMI ++ select CEC_CORE ++ select CEC_NOTIFIER +diff --git a/drivers/gpu/drm/bridge/cadence/Makefile b/drivers/gpu/drm/bridge/cadence/Makefile +index 8f647991b374..618290870ba5 100644 +--- a/drivers/gpu/drm/bridge/cadence/Makefile ++++ b/drivers/gpu/drm/bridge/cadence/Makefile +@@ -2,3 +2,12 @@ + obj-$(CONFIG_DRM_CDNS_MHDP8546) += cdns-mhdp8546.o + cdns-mhdp8546-y := cdns-mhdp8546-core.o + cdns-mhdp8546-$(CONFIG_DRM_CDNS_MHDP8546_J721E) += cdns-mhdp8546-j721e.o ++ ++cdns_mhdp_drmcore-y := cdns-mhdp-common.o cdns-mhdp-dp.o cdns-mhdp-hdmi.o ++ ++cdns_mhdp_drmcore-$(CONFIG_DRM_CDNS_HDMI) += cdns-hdmi-core.o ++cdns_mhdp_drmcore-$(CONFIG_DRM_CDNS_DP) += cdns-dp-core.o ++cdns_mhdp_drmcore-$(CONFIG_DRM_CDNS_AUDIO) += cdns-mhdp-audio.o ++cdns_mhdp_drmcore-$(CONFIG_DRM_CDNS_HDMI_CEC) += cdns-mhdp-cec.o ++ ++obj-$(CONFIG_DRM_CDNS_MHDP) += cdns_mhdp_drmcore.o +diff --git a/drivers/gpu/drm/bridge/cadence/cdns-dp-core.c b/drivers/gpu/drm/bridge/cadence/cdns-dp-core.c +new file mode 100644 +index 000000000000..acb5c860da73 +--- /dev/null ++++ b/drivers/gpu/drm/bridge/cadence/cdns-dp-core.c +@@ -0,0 +1,574 @@ ++/* ++ * Cadence Display Port Interface (DP) driver ++ * ++ * Copyright (C) 2019 NXP Semiconductor, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * This function only implements native DPDC reads and writes ++ */ ++static ssize_t dp_aux_transfer(struct drm_dp_aux *aux, ++ struct drm_dp_aux_msg *msg) ++{ ++ struct cdns_mhdp_device *mhdp = dev_get_drvdata(aux->dev); ++ bool native = msg->request & (DP_AUX_NATIVE_WRITE & DP_AUX_NATIVE_READ); ++ int ret; ++ ++ /* Ignore address only message */ ++ if ((msg->size == 0) || (msg->buffer == NULL)) { ++ msg->reply = native ? ++ DP_AUX_NATIVE_REPLY_ACK : DP_AUX_I2C_REPLY_ACK; ++ return msg->size; ++ } ++ ++ if (!native) { ++ dev_err(mhdp->dev, "%s: only native messages supported\n", __func__); ++ return -EINVAL; ++ } ++ ++ /* msg sanity check */ ++ if (msg->size > DP_AUX_MAX_PAYLOAD_BYTES) { ++ dev_err(mhdp->dev, "%s: invalid msg: size(%zu), request(%x)\n", ++ __func__, msg->size, (unsigned int)msg->request); ++ return -EINVAL; ++ } ++ ++ if (msg->request == DP_AUX_NATIVE_WRITE) { ++ const u8 *buf = msg->buffer; ++ int i; ++ for (i = 0; i < msg->size; ++i) { ++ ret = cdns_mhdp_dpcd_write(mhdp, ++ msg->address + i, buf[i]); ++ if (!ret) ++ continue; ++ ++ DRM_DEV_ERROR(mhdp->dev, "Failed to write DPCD\n"); ++ ++ return ret; ++ } ++ } ++ ++ if (msg->request == DP_AUX_NATIVE_READ) { ++ ret = cdns_mhdp_dpcd_read(mhdp, msg->address, msg->buffer, msg->size); ++ if (ret < 0) ++ return -EIO; ++ msg->reply = DP_AUX_NATIVE_REPLY_ACK; ++ return msg->size; ++ } ++ return 0; ++} ++ ++static int dp_aux_init(struct cdns_mhdp_device *mhdp, ++ struct device *dev) ++{ ++ int ret; ++ ++ mhdp->dp.aux.name = "imx_dp_aux"; ++ mhdp->dp.aux.dev = dev; ++ mhdp->dp.aux.transfer = dp_aux_transfer; ++ ++ ret = drm_dp_aux_register(&mhdp->dp.aux); ++ ++ return ret; ++} ++ ++static int dp_aux_destroy(struct cdns_mhdp_device *mhdp) ++{ ++ drm_dp_aux_unregister(&mhdp->dp.aux); ++ return 0; ++} ++ ++static void dp_pixel_clk_reset(struct cdns_mhdp_device *mhdp) ++{ ++ u32 val; ++ ++ /* reset pixel clk */ ++ val = cdns_mhdp_reg_read(mhdp, SOURCE_HDTX_CAR); ++ cdns_mhdp_reg_write(mhdp, SOURCE_HDTX_CAR, val & 0xFD); ++ cdns_mhdp_reg_write(mhdp, SOURCE_HDTX_CAR, val); ++} ++ ++static void cdns_dp_mode_set(struct cdns_mhdp_device *mhdp) ++{ ++ u32 lane_mapping = mhdp->lane_mapping; ++ int ret; ++ ++ cdns_mhdp_plat_call(mhdp, pclk_rate); ++ ++ /* delay for DP FW stable after pixel clock relock */ ++ msleep(50); ++ ++ dp_pixel_clk_reset(mhdp); ++ ++ /* Get DP Caps */ ++ ret = drm_dp_dpcd_read(&mhdp->dp.aux, DP_DPCD_REV, mhdp->dp.dpcd, ++ DP_RECEIVER_CAP_SIZE); ++ if (ret < 0) { ++ DRM_ERROR("Failed to get caps %d\n", ret); ++ return; ++ } ++ ++ mhdp->dp.rate = drm_dp_max_link_rate(mhdp->dp.dpcd); ++ mhdp->dp.num_lanes = drm_dp_max_lane_count(mhdp->dp.dpcd); ++ ++ /* check the max link rate */ ++ if (mhdp->dp.rate > CDNS_DP_MAX_LINK_RATE) ++ mhdp->dp.rate = CDNS_DP_MAX_LINK_RATE; ++ ++ /* Initialize link rate/num_lanes as panel max link rate/max_num_lanes */ ++ cdns_mhdp_plat_call(mhdp, phy_set); ++ ++ /* Video off */ ++ ret = cdns_mhdp_set_video_status(mhdp, CONTROL_VIDEO_IDLE); ++ if (ret) { ++ DRM_DEV_ERROR(mhdp->dev, "Failed to valid video %d\n", ret); ++ return; ++ } ++ ++ /* Line swaping */ ++ cdns_mhdp_reg_write(mhdp, LANES_CONFIG, 0x00400000 | lane_mapping); ++ ++ /* Set DP host capability */ ++ ret = cdns_mhdp_set_host_cap(mhdp, false); ++ if (ret) { ++ DRM_DEV_ERROR(mhdp->dev, "Failed to set host cap %d\n", ret); ++ return; ++ } ++ ++ ret = cdns_mhdp_config_video(mhdp); ++ if (ret) { ++ DRM_DEV_ERROR(mhdp->dev, "Failed to config video %d\n", ret); ++ return; ++ } ++ ++ return; ++} ++ ++/* ----------------------------------------------------------------------------- ++ * dp TX Setup ++ */ ++static enum drm_connector_status ++cdns_dp_connector_detect(struct drm_connector *connector, bool force) ++{ ++ struct cdns_mhdp_device *mhdp = container_of(connector, ++ struct cdns_mhdp_device, connector.base); ++ u8 hpd = 0xf; ++ ++ hpd = cdns_mhdp_read_hpd(mhdp); ++ if (hpd == 1) ++ /* Cable Connected */ ++ return connector_status_connected; ++ else if (hpd == 0) ++ /* Cable Disconnedted */ ++ return connector_status_disconnected; ++ else { ++ /* Cable status unknown */ ++ DRM_INFO("Unknow cable status, hdp=%u\n", hpd); ++ return connector_status_unknown; ++ } ++} ++ ++static int cdns_dp_connector_get_modes(struct drm_connector *connector) ++{ ++ struct cdns_mhdp_device *mhdp = container_of(connector, ++ struct cdns_mhdp_device, connector.base); ++ int num_modes = 0; ++ struct edid *edid; ++ ++ edid = drm_do_get_edid(&mhdp->connector.base, ++ cdns_mhdp_get_edid_block, mhdp); ++ if (edid) { ++ dev_info(mhdp->dev, "%x,%x,%x,%x,%x,%x,%x,%x\n", ++ edid->header[0], edid->header[1], ++ edid->header[2], edid->header[3], ++ edid->header[4], edid->header[5], ++ edid->header[6], edid->header[7]); ++ drm_connector_update_edid_property(connector, edid); ++ num_modes = drm_add_edid_modes(connector, edid); ++ kfree(edid); ++ } ++ ++ if (num_modes == 0) ++ DRM_ERROR("Invalid edid\n"); ++ return num_modes; ++} ++ ++static const struct drm_connector_funcs cdns_dp_connector_funcs = { ++ .fill_modes = drm_helper_probe_single_connector_modes, ++ .detect = cdns_dp_connector_detect, ++ .destroy = drm_connector_cleanup, ++ .reset = drm_atomic_helper_connector_reset, ++ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, ++ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, ++}; ++ ++static const struct drm_connector_helper_funcs cdns_dp_connector_helper_funcs = { ++ .get_modes = cdns_dp_connector_get_modes, ++}; ++ ++static int cdns_dp_bridge_attach(struct drm_bridge *bridge, ++ enum drm_bridge_attach_flags flags) ++{ ++ struct cdns_mhdp_device *mhdp = bridge->driver_private; ++ struct drm_encoder *encoder = bridge->encoder; ++ struct drm_connector *connector = &mhdp->connector.base; ++ ++ connector->interlace_allowed = 1; ++ ++ if (mhdp->is_hpd) ++ connector->polled = DRM_CONNECTOR_POLL_HPD; ++ else ++ connector->polled = DRM_CONNECTOR_POLL_CONNECT | ++ DRM_CONNECTOR_POLL_DISCONNECT; ++ ++ drm_connector_helper_add(connector, &cdns_dp_connector_helper_funcs); ++ ++ drm_connector_init(bridge->dev, connector, &cdns_dp_connector_funcs, ++ DRM_MODE_CONNECTOR_DisplayPort); ++ ++ drm_connector_attach_encoder(connector, encoder); ++ ++ return 0; ++} ++ ++static enum drm_mode_status ++cdns_dp_bridge_mode_valid(struct drm_bridge *bridge, ++ const struct drm_display_mode *mode) ++{ ++ enum drm_mode_status mode_status = MODE_OK; ++ ++ /* We don't support double-clocked modes */ ++ if (mode->flags & DRM_MODE_FLAG_DBLCLK || ++ mode->flags & DRM_MODE_FLAG_INTERLACE) ++ return MODE_BAD; ++ ++ /* MAX support pixel clock rate 594MHz */ ++ if (mode->clock > 594000) ++ return MODE_CLOCK_HIGH; ++ ++ /* 4096x2160 is not supported now */ ++ if (mode->hdisplay > 3840) ++ return MODE_BAD_HVALUE; ++ ++ if (mode->vdisplay > 2160) ++ return MODE_BAD_VVALUE; ++ ++ return mode_status; ++} ++ ++static void cdns_dp_bridge_mode_set(struct drm_bridge *bridge, ++ const struct drm_display_mode *orig_mode, ++ const struct drm_display_mode *mode) ++{ ++ struct cdns_mhdp_device *mhdp = bridge->driver_private; ++ struct drm_display_info *display_info = &mhdp->connector.base.display_info; ++ struct video_info *video = &mhdp->video_info; ++ ++ switch (display_info->bpc) { ++ case 10: ++ video->color_depth = 10; ++ break; ++ case 6: ++ video->color_depth = 6; ++ break; ++ default: ++ video->color_depth = 8; ++ break; ++ } ++ ++ video->color_fmt = PXL_RGB; ++ video->v_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NVSYNC); ++ video->h_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NHSYNC); ++ ++ DRM_INFO("Mode: %dx%dp%d\n", mode->hdisplay, mode->vdisplay, mode->clock); ++ memcpy(&mhdp->mode, mode, sizeof(struct drm_display_mode)); ++ ++ mutex_lock(&mhdp->lock); ++ cdns_dp_mode_set(mhdp); ++ mutex_unlock(&mhdp->lock); ++ ++ /* reset force mode set flag */ ++ mhdp->force_mode_set = false; ++} ++ ++static void cdn_dp_bridge_enable(struct drm_bridge *bridge) ++{ ++ struct cdns_mhdp_device *mhdp = bridge->driver_private; ++ int ret; ++ ++ /* Link trainning */ ++ ret = cdns_mhdp_train_link(mhdp); ++ if (ret) { ++ DRM_DEV_ERROR(mhdp->dev, "Failed link train %d\n", ret); ++ return; ++ } ++ ++ ret = cdns_mhdp_set_video_status(mhdp, CONTROL_VIDEO_VALID); ++ if (ret) { ++ DRM_DEV_ERROR(mhdp->dev, "Failed to valid video %d\n", ret); ++ return; ++ } ++} ++ ++static void cdn_dp_bridge_disable(struct drm_bridge *bridge) ++{ ++ struct cdns_mhdp_device *mhdp = bridge->driver_private; ++ ++ cdns_mhdp_set_video_status(mhdp, CONTROL_VIDEO_IDLE); ++} ++ ++static const struct drm_bridge_funcs cdns_dp_bridge_funcs = { ++ .attach = cdns_dp_bridge_attach, ++ .enable = cdn_dp_bridge_enable, ++ .disable = cdn_dp_bridge_disable, ++ .mode_set = cdns_dp_bridge_mode_set, ++ .mode_valid = cdns_dp_bridge_mode_valid, ++}; ++ ++static void hotplug_work_func(struct work_struct *work) ++{ ++ struct cdns_mhdp_device *mhdp = container_of(work, ++ struct cdns_mhdp_device, hotplug_work.work); ++ struct drm_connector *connector = &mhdp->connector.base; ++ ++ drm_helper_hpd_irq_event(connector->dev); ++ ++ if (connector->status == connector_status_connected) { ++ /* Cable connedted */ ++ DRM_INFO("HDMI/DP Cable Plug In\n"); ++ enable_irq(mhdp->irq[IRQ_OUT]); ++ } else if (connector->status == connector_status_disconnected) { ++ /* Cable Disconnedted */ ++ DRM_INFO("HDMI/DP Cable Plug Out\n"); ++ /* force mode set for cable replugin to recovery DP video modes */ ++ mhdp->force_mode_set = true; ++ enable_irq(mhdp->irq[IRQ_IN]); ++ } ++} ++ ++static irqreturn_t cdns_dp_irq_thread(int irq, void *data) ++{ ++ struct cdns_mhdp_device *mhdp = data; ++ ++ disable_irq_nosync(irq); ++ ++ mod_delayed_work(system_wq, &mhdp->hotplug_work, ++ msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS)); ++ ++ return IRQ_HANDLED; ++} ++ ++static void cdns_dp_parse_dt(struct cdns_mhdp_device *mhdp) ++{ ++ struct device_node *of_node = mhdp->dev->of_node; ++ int ret; ++ ++ ret = of_property_read_u32(of_node, "lane-mapping", ++ &mhdp->lane_mapping); ++ if (ret) { ++ mhdp->lane_mapping = 0xc6; ++ dev_warn(mhdp->dev, "Failed to get lane_mapping - using default 0xc6\n"); ++ } ++ dev_info(mhdp->dev, "lane-mapping 0x%02x\n", mhdp->lane_mapping); ++} ++ ++static int __cdns_dp_probe(struct platform_device *pdev, ++ struct cdns_mhdp_device *mhdp) ++{ ++ struct device *dev = &pdev->dev; ++ struct resource *iores = NULL; ++ int ret; ++ ++ mutex_init(&mhdp->lock); ++ mutex_init(&mhdp->iolock); ++ ++ INIT_DELAYED_WORK(&mhdp->hotplug_work, hotplug_work_func); ++ ++ iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (iores) { ++ mhdp->regs_base = devm_ioremap(dev, iores->start, ++ resource_size(iores)); ++ if (IS_ERR(mhdp->regs_base)) ++ return -ENOMEM; ++ } ++ ++ iores = platform_get_resource(pdev, IORESOURCE_MEM, 1); ++ if (iores) { ++ mhdp->regs_sec = devm_ioremap(dev, iores->start, ++ resource_size(iores)); ++ if (IS_ERR(mhdp->regs_sec)) ++ return -ENOMEM; ++ } ++ ++ mhdp->is_hpd = true; ++ mhdp->is_ls1028a = false; ++ ++ mhdp->irq[IRQ_IN] = platform_get_irq_byname(pdev, "plug_in"); ++ if (mhdp->irq[IRQ_IN] < 0) { ++ mhdp->is_hpd = false; ++ dev_info(dev, "No plug_in irq number\n"); ++ } ++ ++ mhdp->irq[IRQ_OUT] = platform_get_irq_byname(pdev, "plug_out"); ++ if (mhdp->irq[IRQ_OUT] < 0) { ++ mhdp->is_hpd = false; ++ dev_info(dev, "No plug_out irq number\n"); ++ } ++ ++ cdns_dp_parse_dt(mhdp); ++ ++ if (of_device_is_compatible(dev->of_node, "cdn,ls1028a-dp")) ++ mhdp->is_ls1028a = true; ++ ++ cdns_mhdp_plat_call(mhdp, power_on); ++ ++ cdns_mhdp_plat_call(mhdp, firmware_init); ++ ++ /* DP FW alive check */ ++ ret = cdns_mhdp_check_alive(mhdp); ++ if (ret == false) { ++ DRM_ERROR("NO dp FW running\n"); ++ return -ENXIO; ++ } ++ ++ /* DP PHY init before AUX init */ ++ cdns_mhdp_plat_call(mhdp, phy_set); ++ ++ /* Enable Hotplug Detect IRQ thread */ ++ if (mhdp->is_hpd) { ++ irq_set_status_flags(mhdp->irq[IRQ_IN], IRQ_NOAUTOEN); ++ ret = devm_request_threaded_irq(dev, mhdp->irq[IRQ_IN], ++ NULL, cdns_dp_irq_thread, ++ IRQF_ONESHOT, dev_name(dev), ++ mhdp); ++ ++ if (ret) { ++ dev_err(dev, "can't claim irq %d\n", ++ mhdp->irq[IRQ_IN]); ++ return -EINVAL; ++ } ++ ++ irq_set_status_flags(mhdp->irq[IRQ_OUT], IRQ_NOAUTOEN); ++ ret = devm_request_threaded_irq(dev, mhdp->irq[IRQ_OUT], ++ NULL, cdns_dp_irq_thread, ++ IRQF_ONESHOT, dev_name(dev), ++ mhdp); ++ ++ if (ret) { ++ dev_err(dev, "can't claim irq %d\n", ++ mhdp->irq[IRQ_OUT]); ++ return -EINVAL; ++ } ++ ++ if (cdns_mhdp_read_hpd(mhdp)) ++ enable_irq(mhdp->irq[IRQ_OUT]); ++ else ++ enable_irq(mhdp->irq[IRQ_IN]); ++ } ++ ++ mhdp->bridge.base.driver_private = mhdp; ++ mhdp->bridge.base.funcs = &cdns_dp_bridge_funcs; ++#ifdef CONFIG_OF ++ mhdp->bridge.base.of_node = dev->of_node; ++#endif ++ ++ dev_set_drvdata(dev, mhdp); ++ ++ /* register audio driver */ ++ cdns_mhdp_register_audio_driver(dev); ++ ++ dp_aux_init(mhdp, dev); ++ ++ return 0; ++} ++ ++static void __cdns_dp_remove(struct cdns_mhdp_device *mhdp) ++{ ++ dp_aux_destroy(mhdp); ++ cdns_mhdp_unregister_audio_driver(mhdp->dev); ++} ++ ++/* ----------------------------------------------------------------------------- ++ * Probe/remove API, used from platforms based on the DRM bridge API. ++ */ ++int cdns_dp_probe(struct platform_device *pdev, ++ struct cdns_mhdp_device *mhdp) ++{ ++ int ret; ++ ++ ret = __cdns_dp_probe(pdev, mhdp); ++ if (ret) ++ return ret; ++ ++ drm_bridge_add(&mhdp->bridge.base); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(cdns_dp_probe); ++ ++void cdns_dp_remove(struct platform_device *pdev) ++{ ++ struct cdns_mhdp_device *mhdp = platform_get_drvdata(pdev); ++ ++ drm_bridge_remove(&mhdp->bridge.base); ++ ++ __cdns_dp_remove(mhdp); ++} ++EXPORT_SYMBOL_GPL(cdns_dp_remove); ++ ++/* ----------------------------------------------------------------------------- ++ * Bind/unbind API, used from platforms based on the component framework. ++ */ ++int cdns_dp_bind(struct platform_device *pdev, struct drm_encoder *encoder, ++ struct cdns_mhdp_device *mhdp) ++{ ++ int ret; ++ ++ ret = __cdns_dp_probe(pdev, mhdp); ++ if (ret < 0) ++ return ret; ++ ++ ret = drm_bridge_attach(encoder, &mhdp->bridge.base, NULL, 0); ++ if (ret) { ++ cdns_dp_remove(pdev); ++ DRM_ERROR("Failed to initialize bridge with drm\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(cdns_dp_bind); ++ ++void cdns_dp_unbind(struct device *dev) ++{ ++ struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev); ++ ++ __cdns_dp_remove(mhdp); ++} ++EXPORT_SYMBOL_GPL(cdns_dp_unbind); ++ ++MODULE_AUTHOR("Sandor Yu "); ++MODULE_DESCRIPTION("Cadence Display Port transmitter driver"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform:cdn-dp"); +diff --git a/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c +new file mode 100644 +index 000000000000..da40f62617ef +--- /dev/null ++++ b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c +@@ -0,0 +1,690 @@ ++/* ++ * Cadence High-Definition Multimedia Interface (HDMI) driver ++ * ++ * Copyright (C) 2019 NXP Semiconductor, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static void hdmi_sink_config(struct cdns_mhdp_device *mhdp) ++{ ++ struct drm_scdc *scdc = &mhdp->connector.base.display_info.hdmi.scdc; ++ u8 buff = 0; ++ ++ /* Default work in HDMI1.4 */ ++ mhdp->hdmi.hdmi_type = MODE_HDMI_1_4; ++ ++ /* check sink support SCDC or not */ ++ if (scdc->supported != true) { ++ DRM_INFO("Sink Not Support SCDC\n"); ++ return; ++ } ++ ++ if (mhdp->hdmi.char_rate > 340000) { ++ /* ++ * TMDS Character Rate above 340MHz should working in HDMI2.0 ++ * Enable scrambling and TMDS_Bit_Clock_Ratio ++ */ ++ buff = SCDC_TMDS_BIT_CLOCK_RATIO_BY_40 | SCDC_SCRAMBLING_ENABLE; ++ mhdp->hdmi.hdmi_type = MODE_HDMI_2_0; ++ } else if (scdc->scrambling.low_rates) { ++ /* ++ * Enable scrambling and HDMI2.0 when scrambling capability of sink ++ * be indicated in the HF-VSDB LTE_340Mcsc_scramble bit ++ */ ++ buff = SCDC_SCRAMBLING_ENABLE; ++ mhdp->hdmi.hdmi_type = MODE_HDMI_2_0; ++ } ++ ++ /* TMDS config */ ++ cdns_hdmi_scdc_write(mhdp, 0x20, buff); ++} ++ ++static void hdmi_lanes_config(struct cdns_mhdp_device *mhdp) ++{ ++ /* Line swaping */ ++ cdns_mhdp_reg_write(mhdp, LANES_CONFIG, 0x00400000 | mhdp->lane_mapping); ++} ++ ++static int hdmi_avi_info_set(struct cdns_mhdp_device *mhdp, ++ struct drm_display_mode *mode) ++{ ++ struct hdmi_avi_infoframe frame; ++ int format = mhdp->video_info.color_fmt; ++ struct drm_connector_state *conn_state = mhdp->connector.base.state; ++ struct drm_display_mode *adj_mode; ++ enum hdmi_quantization_range qr; ++ u8 buf[32]; ++ int ret; ++ ++ /* Initialise info frame from DRM mode */ ++ drm_hdmi_avi_infoframe_from_display_mode(&frame, &mhdp->connector.base, ++ mode); ++ ++ switch (format) { ++ case YCBCR_4_4_4: ++ frame.colorspace = HDMI_COLORSPACE_YUV444; ++ break; ++ case YCBCR_4_2_2: ++ frame.colorspace = HDMI_COLORSPACE_YUV422; ++ break; ++ case YCBCR_4_2_0: ++ frame.colorspace = HDMI_COLORSPACE_YUV420; ++ break; ++ default: ++ frame.colorspace = HDMI_COLORSPACE_RGB; ++ break; ++ } ++ ++ drm_hdmi_avi_infoframe_colorspace(&frame, conn_state); ++ ++ adj_mode = &mhdp->bridge.base.encoder->crtc->state->adjusted_mode; ++ ++ qr = drm_default_rgb_quant_range(adj_mode); ++ ++ drm_hdmi_avi_infoframe_quant_range(&frame, &mhdp->connector.base, ++ adj_mode, qr); ++ ++ ret = hdmi_avi_infoframe_check(&frame); ++ if (WARN_ON(ret)) ++ return false; ++ ++ ret = hdmi_avi_infoframe_pack(&frame, buf + 1, sizeof(buf) - 1); ++ if (ret < 0) { ++ DRM_ERROR("failed to pack AVI infoframe: %d\n", ret); ++ return -1; ++ } ++ ++ buf[0] = 0; ++ cdns_mhdp_infoframe_set(mhdp, 0, sizeof(buf), buf, HDMI_INFOFRAME_TYPE_AVI); ++ return 0; ++} ++ ++static void hdmi_vendor_info_set(struct cdns_mhdp_device *mhdp, ++ struct drm_display_mode *mode) ++{ ++ struct hdmi_vendor_infoframe frame; ++ u8 buf[32]; ++ int ret; ++ ++ /* Initialise vendor frame from DRM mode */ ++ ret = drm_hdmi_vendor_infoframe_from_display_mode(&frame, &mhdp->connector.base, mode); ++ if (ret < 0) { ++ DRM_INFO("No vendor infoframe\n"); ++ return; ++ } ++ ++ ret = hdmi_vendor_infoframe_pack(&frame, buf + 1, sizeof(buf) - 1); ++ if (ret < 0) { ++ DRM_WARN("Unable to pack vendor infoframe: %d\n", ret); ++ return; ++ } ++ ++ buf[0] = 0; ++ cdns_mhdp_infoframe_set(mhdp, 3, sizeof(buf), buf, HDMI_INFOFRAME_TYPE_VENDOR); ++} ++ ++static void hdmi_drm_info_set(struct cdns_mhdp_device *mhdp) ++{ ++ struct drm_connector_state *conn_state; ++ struct hdmi_drm_infoframe frame; ++ u8 buf[32]; ++ int ret; ++ ++ conn_state = mhdp->connector.base.state; ++ ++ if (!conn_state->hdr_output_metadata) ++ return; ++ ++ ret = drm_hdmi_infoframe_set_hdr_metadata(&frame, conn_state); ++ if (ret < 0) { ++ DRM_DEBUG_KMS("couldn't set HDR metadata in infoframe\n"); ++ return; ++ } ++ ++ ret = hdmi_drm_infoframe_pack(&frame, buf + 1, sizeof(buf) - 1); ++ if (ret < 0) { ++ DRM_DEBUG_KMS("couldn't pack HDR infoframe\n"); ++ return; ++ } ++ ++ buf[0] = 0; ++ cdns_mhdp_infoframe_set(mhdp, 3, sizeof(buf), ++ buf, HDMI_INFOFRAME_TYPE_DRM); ++} ++ ++void cdns_hdmi_mode_set(struct cdns_mhdp_device *mhdp) ++{ ++ struct drm_display_mode *mode = &mhdp->mode; ++ int ret; ++ ++ /* video mode valid check */ ++ if (mode->clock == 0 || mode->hdisplay == 0 || mode->vdisplay == 0) ++ return; ++ ++ hdmi_lanes_config(mhdp); ++ ++ cdns_mhdp_plat_call(mhdp, pclk_rate); ++ ++ /* delay for HDMI FW stable after pixel clock relock */ ++ msleep(20); ++ ++ cdns_mhdp_plat_call(mhdp, phy_set); ++ ++ hdmi_sink_config(mhdp); ++ ++ ret = cdns_hdmi_ctrl_init(mhdp, mhdp->hdmi.hdmi_type, mhdp->hdmi.char_rate); ++ if (ret < 0) { ++ DRM_ERROR("%s, ret = %d\n", __func__, ret); ++ return; ++ } ++ ++ /* Config GCP */ ++ if (mhdp->video_info.color_depth == 8) ++ cdns_hdmi_disable_gcp(mhdp); ++ else ++ cdns_hdmi_enable_gcp(mhdp); ++ ++ ret = hdmi_avi_info_set(mhdp, mode); ++ if (ret < 0) { ++ DRM_ERROR("%s ret = %d\n", __func__, ret); ++ return; ++ } ++ ++ /* vendor info frame is enable only when HDMI1.4 4K mode */ ++ hdmi_vendor_info_set(mhdp, mode); ++ ++ hdmi_drm_info_set(mhdp); ++ ++ ret = cdns_hdmi_mode_config(mhdp, mode, &mhdp->video_info); ++ if (ret < 0) { ++ DRM_ERROR("CDN_API_HDMITX_SetVic_blocking ret = %d\n", ret); ++ return; ++ } ++} ++ ++static enum drm_connector_status ++cdns_hdmi_connector_detect(struct drm_connector *connector, bool force) ++{ ++ struct cdns_mhdp_device *mhdp = ++ container_of(connector, struct cdns_mhdp_device, connector.base); ++ ++ u8 hpd = 0xf; ++ ++ hpd = cdns_mhdp_read_hpd(mhdp); ++ ++ if (hpd == 1) ++ /* Cable Connected */ ++ return connector_status_connected; ++ else if (hpd == 0) ++ /* Cable Disconnedted */ ++ return connector_status_disconnected; ++ else { ++ /* Cable status unknown */ ++ DRM_INFO("Unknow cable status, hdp=%u\n", hpd); ++ return connector_status_unknown; ++ } ++} ++ ++static int cdns_hdmi_connector_get_modes(struct drm_connector *connector) ++{ ++ struct cdns_mhdp_device *mhdp = ++ container_of(connector, struct cdns_mhdp_device, connector.base); ++ int num_modes = 0; ++ struct edid *edid; ++ ++ edid = drm_do_get_edid(&mhdp->connector.base, ++ cdns_hdmi_get_edid_block, mhdp); ++ if (edid) { ++ dev_info(mhdp->dev, "%x,%x,%x,%x,%x,%x,%x,%x\n", ++ edid->header[0], edid->header[1], ++ edid->header[2], edid->header[3], ++ edid->header[4], edid->header[5], ++ edid->header[6], edid->header[7]); ++ drm_connector_update_edid_property(connector, edid); ++ num_modes = drm_add_edid_modes(connector, edid); ++ kfree(edid); ++ } ++ ++ if (num_modes == 0) ++ DRM_ERROR("Invalid edid\n"); ++ return num_modes; ++} ++ ++static bool blob_equal(const struct drm_property_blob *a, ++ const struct drm_property_blob *b) ++{ ++ if (a && b) ++ return a->length == b->length && ++ !memcmp(a->data, b->data, a->length); ++ ++ return !a == !b; ++} ++ ++static int cdns_hdmi_connector_atomic_check(struct drm_connector *connector, ++ struct drm_atomic_state *state) ++{ ++ struct drm_connector_state *new_con_state = ++ drm_atomic_get_new_connector_state(state, connector); ++ struct drm_connector_state *old_con_state = ++ drm_atomic_get_old_connector_state(state, connector); ++ struct drm_crtc *crtc = new_con_state->crtc; ++ struct drm_crtc_state *new_crtc_state; ++ ++ if (!blob_equal(new_con_state->hdr_output_metadata, ++ old_con_state->hdr_output_metadata) || ++ new_con_state->colorspace != old_con_state->colorspace) { ++ new_crtc_state = drm_atomic_get_crtc_state(state, crtc); ++ if (IS_ERR(new_crtc_state)) ++ return PTR_ERR(new_crtc_state); ++ ++ new_crtc_state->mode_changed = ++ !new_con_state->hdr_output_metadata || ++ !old_con_state->hdr_output_metadata || ++ new_con_state->colorspace != old_con_state->colorspace; ++ } ++ ++ return 0; ++} ++ ++static const struct drm_connector_funcs cdns_hdmi_connector_funcs = { ++ .fill_modes = drm_helper_probe_single_connector_modes, ++ .detect = cdns_hdmi_connector_detect, ++ .destroy = drm_connector_cleanup, ++ .reset = drm_atomic_helper_connector_reset, ++ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, ++ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, ++}; ++ ++static const struct drm_connector_helper_funcs cdns_hdmi_connector_helper_funcs = { ++ .get_modes = cdns_hdmi_connector_get_modes, ++ .atomic_check = cdns_hdmi_connector_atomic_check, ++}; ++ ++static int cdns_hdmi_bridge_attach(struct drm_bridge *bridge, ++ enum drm_bridge_attach_flags flags) ++{ ++ struct cdns_mhdp_device *mhdp = bridge->driver_private; ++ struct drm_mode_config *config = &bridge->dev->mode_config; ++ struct drm_encoder *encoder = bridge->encoder; ++ struct drm_connector *connector = &mhdp->connector.base; ++ ++ connector->interlace_allowed = 1; ++ connector->polled = DRM_CONNECTOR_POLL_HPD; ++ ++ drm_connector_helper_add(connector, &cdns_hdmi_connector_helper_funcs); ++ ++ drm_connector_init(bridge->dev, connector, &cdns_hdmi_connector_funcs, ++ DRM_MODE_CONNECTOR_HDMIA); ++ ++ if (!strncmp("imx8mq-hdmi", mhdp->plat_data->plat_name, 11)) { ++ drm_object_attach_property(&connector->base, ++ config->hdr_output_metadata_property, ++ 0); ++ ++ if (!drm_mode_create_hdmi_colorspace_property(connector)) ++ drm_object_attach_property(&connector->base, ++ connector->colorspace_property, ++ 0); ++ } ++ ++ drm_connector_attach_encoder(connector, encoder); ++ ++ return 0; ++} ++ ++static enum drm_mode_status ++cdns_hdmi_bridge_mode_valid(struct drm_bridge *bridge, ++ const struct drm_display_mode *mode) ++{ ++ struct cdns_mhdp_device *mhdp = bridge->driver_private; ++ enum drm_mode_status mode_status = MODE_OK; ++ int ret; ++ ++ /* We don't support double-clocked and Interlaced modes */ ++ if (mode->flags & DRM_MODE_FLAG_DBLCLK || ++ mode->flags & DRM_MODE_FLAG_INTERLACE) ++ return MODE_BAD; ++ ++ /* MAX support pixel clock rate 594MHz */ ++ if (mode->clock > 594000) ++ return MODE_CLOCK_HIGH; ++ ++ /* 4096x2160 is not supported */ ++ if (mode->hdisplay > 3840 || mode->vdisplay > 2160) ++ return MODE_BAD_HVALUE; ++ ++ mhdp->valid_mode = mode; ++ ret = cdns_mhdp_plat_call(mhdp, phy_video_valid); ++ if (ret == false) ++ return MODE_CLOCK_RANGE; ++ ++ return mode_status; ++} ++ ++static void cdns_hdmi_bridge_mode_set(struct drm_bridge *bridge, ++ const struct drm_display_mode *orig_mode, ++ const struct drm_display_mode *mode) ++{ ++ struct cdns_mhdp_device *mhdp = bridge->driver_private; ++ struct video_info *video = &mhdp->video_info; ++ ++ video->v_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NVSYNC); ++ video->h_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NHSYNC); ++ ++ DRM_INFO("Mode: %dx%dp%d\n", mode->hdisplay, mode->vdisplay, mode->clock); ++ memcpy(&mhdp->mode, mode, sizeof(struct drm_display_mode)); ++ ++ mutex_lock(&mhdp->lock); ++ cdns_hdmi_mode_set(mhdp); ++ mutex_unlock(&mhdp->lock); ++ /* reset force mode set flag */ ++ mhdp->force_mode_set = false; ++} ++ ++bool cdns_hdmi_bridge_mode_fixup(struct drm_bridge *bridge, ++ const struct drm_display_mode *mode, ++ struct drm_display_mode *adjusted_mode) ++{ ++ struct cdns_mhdp_device *mhdp = bridge->driver_private; ++ struct drm_display_info *di = &mhdp->connector.base.display_info; ++ struct video_info *video = &mhdp->video_info; ++ int vic = drm_match_cea_mode(mode); ++ ++ video->color_depth = 8; ++ video->color_fmt = PXL_RGB; ++ ++ /* for all other platforms, other than imx8mq */ ++ if (strncmp("imx8mq-hdmi", mhdp->plat_data->plat_name, 11)) { ++ if (di->bpc == 10 || di->bpc == 6) ++ video->color_depth = di->bpc; ++ ++ return true; ++ } ++ ++ /* imx8mq */ ++ if (vic == 97 || vic == 96) { ++ if (di->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_36) ++ video->color_depth = 12; ++ else if (di->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30) ++ video->color_depth = 10; ++ ++ if (drm_mode_is_420_only(di, mode) || ++ (drm_mode_is_420_also(di, mode) && ++ video->color_depth > 8)) { ++ video->color_fmt = YCBCR_4_2_0; ++ ++ adjusted_mode->private_flags = 1; ++ return true; ++ } ++ ++ video->color_depth = 8; ++ return true; ++ } ++ ++ /* Any defined maximum tmds clock limit we must not exceed*/ ++ if ((di->edid_hdmi_dc_modes & DRM_EDID_HDMI_DC_36) && ++ (mode->clock * 3 / 2 <= di->max_tmds_clock)) ++ video->color_depth = 12; ++ else if ((di->edid_hdmi_dc_modes & DRM_EDID_HDMI_DC_30) && ++ (mode->clock * 5 / 4 <= di->max_tmds_clock)) ++ video->color_depth = 10; ++ ++ /* 10-bit color depth for the following modes is not supported */ ++ if ((vic == 95 || vic == 94 || vic == 93) && video->color_depth == 10) ++ video->color_depth = 8; ++ ++ return true; ++} ++ ++static const struct drm_bridge_funcs cdns_hdmi_bridge_funcs = { ++ .attach = cdns_hdmi_bridge_attach, ++ .mode_set = cdns_hdmi_bridge_mode_set, ++ .mode_valid = cdns_hdmi_bridge_mode_valid, ++ .mode_fixup = cdns_hdmi_bridge_mode_fixup, ++}; ++ ++static void hotplug_work_func(struct work_struct *work) ++{ ++ struct cdns_mhdp_device *mhdp = container_of(work, ++ struct cdns_mhdp_device, hotplug_work.work); ++ struct drm_connector *connector = &mhdp->connector.base; ++ ++ drm_helper_hpd_irq_event(connector->dev); ++ ++ if (connector->status == connector_status_connected) { ++ DRM_INFO("HDMI Cable Plug In\n"); ++ mhdp->force_mode_set = true; ++ enable_irq(mhdp->irq[IRQ_OUT]); ++ } else if (connector->status == connector_status_disconnected) { ++ /* Cable Disconnedted */ ++ DRM_INFO("HDMI Cable Plug Out\n"); ++ /* force mode set for cable replugin to recovery HDMI2.0 video modes */ ++ mhdp->force_mode_set = true; ++ enable_irq(mhdp->irq[IRQ_IN]); ++ } ++} ++ ++static irqreturn_t cdns_hdmi_irq_thread(int irq, void *data) ++{ ++ struct cdns_mhdp_device *mhdp = data; ++ ++ disable_irq_nosync(irq); ++ ++ mod_delayed_work(system_wq, &mhdp->hotplug_work, ++ msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS)); ++ ++ return IRQ_HANDLED; ++} ++ ++static void cdns_hdmi_parse_dt(struct cdns_mhdp_device *mhdp) ++{ ++ struct device_node *of_node = mhdp->dev->of_node; ++ int ret; ++ ++ ret = of_property_read_u32(of_node, "lane-mapping", &mhdp->lane_mapping); ++ if (ret) { ++ mhdp->lane_mapping = 0xc6; ++ dev_warn(mhdp->dev, "Failed to get lane_mapping - using default 0xc6\n"); ++ } ++ dev_info(mhdp->dev, "lane-mapping 0x%02x\n", mhdp->lane_mapping); ++} ++ ++static int __cdns_hdmi_probe(struct platform_device *pdev, ++ struct cdns_mhdp_device *mhdp) ++{ ++ struct device *dev = &pdev->dev; ++ struct platform_device_info pdevinfo; ++ struct resource *iores = NULL; ++ int ret; ++ ++ mutex_init(&mhdp->lock); ++ mutex_init(&mhdp->iolock); ++ ++ INIT_DELAYED_WORK(&mhdp->hotplug_work, hotplug_work_func); ++ ++ iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ mhdp->regs_base = devm_ioremap(dev, iores->start, resource_size(iores)); ++ if (IS_ERR(mhdp->regs_base)) { ++ dev_err(dev, "No regs_base memory\n"); ++ return -ENOMEM; ++ } ++ ++ /* sec register base */ ++ iores = platform_get_resource(pdev, IORESOURCE_MEM, 1); ++ mhdp->regs_sec = devm_ioremap(dev, iores->start, resource_size(iores)); ++ if (IS_ERR(mhdp->regs_sec)) { ++ dev_err(dev, "No regs_sec memory\n"); ++ return -ENOMEM; ++ } ++ ++ mhdp->irq[IRQ_IN] = platform_get_irq_byname(pdev, "plug_in"); ++ if (mhdp->irq[IRQ_IN] < 0) { ++ dev_info(dev, "No plug_in irq number\n"); ++ return -EPROBE_DEFER; ++ } ++ ++ mhdp->irq[IRQ_OUT] = platform_get_irq_byname(pdev, "plug_out"); ++ if (mhdp->irq[IRQ_OUT] < 0) { ++ dev_info(dev, "No plug_out irq number\n"); ++ return -EPROBE_DEFER; ++ } ++ ++ cdns_mhdp_plat_call(mhdp, power_on); ++ ++ /* Initialize FW */ ++ cdns_mhdp_plat_call(mhdp, firmware_init); ++ ++ /* HDMI FW alive check */ ++ ret = cdns_mhdp_check_alive(mhdp); ++ if (ret == false) { ++ dev_err(dev, "NO HDMI FW running\n"); ++ return -ENXIO; ++ } ++ ++ /* Enable Hotplug Detect thread */ ++ irq_set_status_flags(mhdp->irq[IRQ_IN], IRQ_NOAUTOEN); ++ ret = devm_request_threaded_irq(dev, mhdp->irq[IRQ_IN], ++ NULL, cdns_hdmi_irq_thread, ++ IRQF_ONESHOT, dev_name(dev), ++ mhdp); ++ if (ret < 0) { ++ dev_err(dev, "can't claim irq %d\n", ++ mhdp->irq[IRQ_IN]); ++ return -EINVAL; ++ } ++ ++ irq_set_status_flags(mhdp->irq[IRQ_OUT], IRQ_NOAUTOEN); ++ ret = devm_request_threaded_irq(dev, mhdp->irq[IRQ_OUT], ++ NULL, cdns_hdmi_irq_thread, ++ IRQF_ONESHOT, dev_name(dev), ++ mhdp); ++ if (ret < 0) { ++ dev_err(dev, "can't claim irq %d\n", ++ mhdp->irq[IRQ_OUT]); ++ return -EINVAL; ++ } ++ ++ cdns_hdmi_parse_dt(mhdp); ++ ++ if (cdns_mhdp_read_hpd(mhdp)) ++ enable_irq(mhdp->irq[IRQ_OUT]); ++ else ++ enable_irq(mhdp->irq[IRQ_IN]); ++ ++ mhdp->bridge.base.driver_private = mhdp; ++ mhdp->bridge.base.funcs = &cdns_hdmi_bridge_funcs; ++#ifdef CONFIG_OF ++ mhdp->bridge.base.of_node = dev->of_node; ++#endif ++ ++ memset(&pdevinfo, 0, sizeof(pdevinfo)); ++ pdevinfo.parent = dev; ++ pdevinfo.id = PLATFORM_DEVID_AUTO; ++ ++ dev_set_drvdata(dev, mhdp); ++ ++ /* register audio driver */ ++ cdns_mhdp_register_audio_driver(dev); ++ ++ /* register cec driver */ ++#ifdef CONFIG_DRM_CDNS_HDMI_CEC ++ cdns_mhdp_register_cec_driver(dev); ++#endif ++ ++ return 0; ++} ++ ++static void __cdns_hdmi_remove(struct cdns_mhdp_device *mhdp) ++{ ++ /* unregister cec driver */ ++#ifdef CONFIG_DRM_CDNS_HDMI_CEC ++ cdns_mhdp_unregister_cec_driver(mhdp->dev); ++#endif ++ cdns_mhdp_unregister_audio_driver(mhdp->dev); ++} ++ ++/* ----------------------------------------------------------------------------- ++ * Probe/remove API, used from platforms based on the DRM bridge API. ++ */ ++int cdns_hdmi_probe(struct platform_device *pdev, ++ struct cdns_mhdp_device *mhdp) ++{ ++ int ret; ++ ++ ret = __cdns_hdmi_probe(pdev, mhdp); ++ if (ret < 0) ++ return ret; ++ ++ drm_bridge_add(&mhdp->bridge.base); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(cdns_hdmi_probe); ++ ++void cdns_hdmi_remove(struct platform_device *pdev) ++{ ++ struct cdns_mhdp_device *mhdp = platform_get_drvdata(pdev); ++ ++ drm_bridge_remove(&mhdp->bridge.base); ++ ++ __cdns_hdmi_remove(mhdp); ++} ++EXPORT_SYMBOL_GPL(cdns_hdmi_remove); ++ ++/* ----------------------------------------------------------------------------- ++ * Bind/unbind API, used from platforms based on the component framework. ++ */ ++int cdns_hdmi_bind(struct platform_device *pdev, struct drm_encoder *encoder, ++ struct cdns_mhdp_device *mhdp) ++{ ++ int ret; ++ ++ ret = __cdns_hdmi_probe(pdev, mhdp); ++ if (ret) ++ return ret; ++ ++ ret = drm_bridge_attach(encoder, &mhdp->bridge.base, NULL, 0); ++ if (ret) { ++ cdns_hdmi_remove(pdev); ++ DRM_ERROR("Failed to initialize bridge with drm\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(cdns_hdmi_bind); ++ ++void cdns_hdmi_unbind(struct device *dev) ++{ ++ struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev); ++ ++ __cdns_hdmi_remove(mhdp); ++} ++EXPORT_SYMBOL_GPL(cdns_hdmi_unbind); ++ ++MODULE_AUTHOR("Sandor Yu "); ++MODULE_DESCRIPTION("Cadence HDMI transmitter driver"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform:cdn-hdmi"); +diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-audio.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-audio.c +new file mode 100644 +index 000000000000..86174fb633bc +--- /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 ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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.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); ++} +diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-cec.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-cec.c +new file mode 100644 +index 000000000000..5717bb0bcb75 +--- /dev/null ++++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-cec.c +@@ -0,0 +1,341 @@ ++/* ++ * Copyright 2019 NXP ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * 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 ++#include ++#include ++#include ++#include ++ ++#define CEC_NAME "cdns-mhdp-cec" ++ ++#define REG_ADDR_OFF 4 ++ ++/* regsiter define */ ++#define TX_MSG_HEADER 0x33800 ++#define TX_MSG_LENGTH 0x33840 ++#define TX_MSG_CMD 0x33844 ++#define RX_MSG_CMD 0x33850 ++#define RX_CLEAR_BUF 0x33854 ++#define LOGICAL_ADDRESS_LA0 0x33858 ++ ++#define CLK_DIV_MSB 0x3386c ++#define CLK_DIV_LSB 0x33870 ++#define RX_MSG_DATA1 0x33900 ++#define RX_MSG_LENGTH 0x33940 ++#define RX_MSG_STATUS 0x33944 ++#define NUM_OF_MSG_RX_BUF 0x33948 ++#define TX_MSG_STATUS 0x3394c ++#define DB_L_TIMER 0x33980 ++ ++/** ++ * CEC Transceiver operation. ++ */ ++enum { ++ CEC_TX_STOP, ++ CEC_TX_TRANSMIT, ++ CEC_TX_ABORT, ++ CEC_TX_ABORT_AND_TRANSMIT ++}; ++ ++/** ++ * CEC Transceiver status. ++ */ ++enum { ++ CEC_STS_IDLE, ++ CEC_STS_BUSY, ++ CEC_STS_SUCCESS, ++ CEC_STS_ERROR ++}; ++ ++/** ++ * CEC Receiver operation. ++ */ ++enum { ++ CEC_RX_STOP, ++ CEC_RX_READ, ++ CEC_RX_DISABLE, ++ CEC_RX_ABORT_AND_CLR_FIFO ++}; ++/** ++ * Maximum number of Messages in the RX Buffers. ++ */ ++#define CEC_MAX_RX_MSGS 2 ++ ++static u32 mhdp_cec_read(struct cdns_mhdp_cec *cec, u32 offset) ++{ ++ struct cdns_mhdp_device *mhdp = ++ container_of(cec, struct cdns_mhdp_device, hdmi.cec); ++ return cdns_mhdp_bus_read(mhdp, offset); ++} ++ ++static void mhdp_cec_write(struct cdns_mhdp_cec *cec, u32 offset, u32 val) ++{ ++ struct cdns_mhdp_device *mhdp = ++ container_of(cec, struct cdns_mhdp_device, hdmi.cec); ++ cdns_mhdp_bus_write(val, mhdp, offset); ++} ++ ++static void mhdp_cec_clear_rx_buffer(struct cdns_mhdp_cec *cec) ++{ ++ mhdp_cec_write(cec, RX_CLEAR_BUF, 1); ++ mhdp_cec_write(cec, RX_CLEAR_BUF, 0); ++} ++ ++static void mhdp_cec_set_divider(struct cdns_mhdp_cec *cec) ++{ ++ struct cdns_mhdp_device *mhdp = ++ container_of(cec, struct cdns_mhdp_device, hdmi.cec); ++ u32 clk_div; ++ ++ /* Set clock divider */ ++ clk_div = cdns_mhdp_get_fw_clk(mhdp) * 10; ++ ++ mhdp_cec_write(cec, CLK_DIV_MSB, ++ (clk_div >> 8) & 0xFF); ++ mhdp_cec_write(cec, CLK_DIV_LSB, clk_div & 0xFF); ++} ++ ++static u32 mhdp_cec_read_message(struct cdns_mhdp_cec *cec) ++{ ++ struct cec_msg *msg = &cec->msg; ++ int len; ++ int i; ++ ++ mhdp_cec_write(cec, RX_MSG_CMD, CEC_RX_READ); ++ ++ len = mhdp_cec_read(cec, RX_MSG_LENGTH); ++ msg->len = len + 1; ++ dev_dbg(cec->dev, "RX MSG len =%d\n", len); ++ ++ /* Read RX MSG bytes */ ++ for (i = 0; i < msg->len; ++i) { ++ msg->msg[i] = (u8) mhdp_cec_read(cec, RX_MSG_DATA1 + (i * REG_ADDR_OFF)); ++ dev_dbg(cec->dev, "RX MSG[%d]=0x%x\n", i, msg->msg[i]); ++ } ++ ++ mhdp_cec_write(cec, RX_MSG_CMD, CEC_RX_STOP); ++ ++ return true; ++} ++ ++static u32 mhdp_cec_write_message(struct cdns_mhdp_cec *cec, struct cec_msg *msg) ++{ ++ u8 i; ++ ++ mhdp_cec_write(cec, TX_MSG_CMD, CEC_TX_STOP); ++ ++ if (msg->len > CEC_MAX_MSG_SIZE) { ++ dev_err(cec->dev, "Invalid MSG size!\n"); ++ return -EINVAL; ++ } ++ ++ for (i = 0; i < msg->len; ++i) ++ printk("msg[%d]=0x%x\n",i, msg->msg[i]); ++ ++ /* Write Message to register */ ++ for (i = 0; i < msg->len; ++i) { ++ mhdp_cec_write(cec, TX_MSG_HEADER + (i * REG_ADDR_OFF), ++ msg->msg[i]); ++ } ++ /* Write Message Length (payload + opcode) */ ++ mhdp_cec_write(cec, TX_MSG_LENGTH, msg->len - 1); ++ ++ mhdp_cec_write(cec, TX_MSG_CMD, CEC_TX_TRANSMIT); ++ ++ return true; ++} ++ ++static int mhdp_cec_set_logical_addr(struct cdns_mhdp_cec *cec, u32 la) ++{ ++ u8 la_reg; ++ u8 i; ++ ++ if (la == CEC_LOG_ADDR_INVALID) ++ /* invalid all LA address */ ++ for (i = 0; i < CEC_MAX_LOG_ADDRS; ++i) { ++ mhdp_cec_write(cec, LOGICAL_ADDRESS_LA0 + (i * REG_ADDR_OFF), 0); ++ return 0; ++ } ++ ++ /* In fact cdns mhdp cec could support max 5 La address */ ++ for (i = 0; i < CEC_MAX_LOG_ADDRS; ++i) { ++ la_reg = mhdp_cec_read(cec, LOGICAL_ADDRESS_LA0 + (i * REG_ADDR_OFF)); ++ /* Check LA already used */ ++ if (la_reg & 0x10) ++ continue; ++ ++ if ((la_reg & 0xF) == la) { ++ dev_warn(cec->dev, "Warning. LA already in use.\n"); ++ return 0; ++ } ++ ++ la = (la & 0xF) | (1 << 4); ++ ++ mhdp_cec_write(cec, LOGICAL_ADDRESS_LA0 + (i * REG_ADDR_OFF), la); ++ return 0; ++ } ++ ++ dev_warn(cec->dev, "All LA in use\n"); ++ ++ return -ENXIO; ++} ++ ++static int mhdp_cec_poll_worker(void *_cec) ++{ ++ struct cdns_mhdp_cec *cec = (struct cdns_mhdp_cec *)_cec; ++ int num_rx_msgs, i; ++ int sts; ++ ++ set_freezable(); ++ ++ for (;;) { ++ if (kthread_freezable_should_stop(NULL)) ++ break; ++ ++ /* Check TX State */ ++ sts = mhdp_cec_read(cec, TX_MSG_STATUS); ++ switch (sts) { ++ case CEC_STS_SUCCESS: ++ cec_transmit_done(cec->adap, CEC_TX_STATUS_OK, 0, 0, 0, ++ 0); ++ mhdp_cec_write(cec, TX_MSG_CMD, CEC_TX_STOP); ++ break; ++ case CEC_STS_ERROR: ++ mhdp_cec_write(cec, TX_MSG_CMD, CEC_TX_STOP); ++ cec_transmit_done(cec->adap, ++ CEC_TX_STATUS_MAX_RETRIES | ++ CEC_TX_STATUS_NACK, 0, 1, 0, 0); ++ break; ++ case CEC_STS_BUSY: ++ default: ++ break; ++ } ++ ++ /* Check RX State */ ++ sts = mhdp_cec_read(cec, RX_MSG_STATUS); ++ num_rx_msgs = mhdp_cec_read(cec, NUM_OF_MSG_RX_BUF); ++ switch (sts) { ++ case CEC_STS_SUCCESS: ++ if (num_rx_msgs == 0xf) ++ num_rx_msgs = CEC_MAX_RX_MSGS; ++ ++ if (num_rx_msgs > CEC_MAX_RX_MSGS) { ++ dev_err(cec->dev, "Error rx msg num %d\n", ++ num_rx_msgs); ++ mhdp_cec_clear_rx_buffer(cec); ++ break; ++ } ++ ++ /* Rx FIFO Depth 2 RX MSG */ ++ for (i = 0; i < num_rx_msgs; i++) { ++ mhdp_cec_read_message(cec); ++ cec->msg.rx_status = CEC_RX_STATUS_OK; ++ cec_received_msg(cec->adap, &cec->msg); ++ } ++ break; ++ default: ++ break; ++ } ++ ++ if (!kthread_should_stop()) ++ schedule_timeout_idle(20); ++ } ++ ++ return 0; ++} ++ ++static int mhdp_cec_adap_enable(struct cec_adapter *adap, bool enable) ++{ ++ struct cdns_mhdp_cec *cec = cec_get_drvdata(adap); ++ ++ if (enable) { ++ mhdp_cec_write(cec, DB_L_TIMER, 0x10); ++ mhdp_cec_set_divider(cec); ++ } else ++ mhdp_cec_set_divider(cec); ++ ++ return 0; ++} ++ ++static int mhdp_cec_adap_log_addr(struct cec_adapter *adap, u8 addr) ++{ ++ struct cdns_mhdp_cec *cec = cec_get_drvdata(adap); ++ ++ return mhdp_cec_set_logical_addr(cec, addr); ++} ++ ++static int mhdp_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, ++ u32 signal_free_time, struct cec_msg *msg) ++{ ++ struct cdns_mhdp_cec *cec = cec_get_drvdata(adap); ++ ++ mhdp_cec_write_message(cec, msg); ++ ++ return 0; ++} ++ ++static const struct cec_adap_ops cdns_mhdp_cec_adap_ops = { ++ .adap_enable = mhdp_cec_adap_enable, ++ .adap_log_addr = mhdp_cec_adap_log_addr, ++ .adap_transmit = mhdp_cec_adap_transmit, ++}; ++ ++int cdns_mhdp_register_cec_driver(struct device *dev) ++{ ++ struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev); ++ struct cdns_mhdp_cec *cec = &mhdp->hdmi.cec; ++ int ret; ++ ++ cec->adap = cec_allocate_adapter(&cdns_mhdp_cec_adap_ops, cec, ++ CEC_NAME, ++ CEC_CAP_PHYS_ADDR | CEC_CAP_LOG_ADDRS | ++ CEC_CAP_TRANSMIT | CEC_CAP_PASSTHROUGH ++ | CEC_CAP_RC, CEC_MAX_LOG_ADDRS); ++ ret = PTR_ERR_OR_ZERO(cec->adap); ++ if (ret) ++ return ret; ++ ret = cec_register_adapter(cec->adap, dev); ++ if (ret) { ++ cec_delete_adapter(cec->adap); ++ return ret; ++ } ++ ++ cec->dev = dev; ++ ++ cec->cec_worker = kthread_create(mhdp_cec_poll_worker, cec, "cdns-mhdp-cec"); ++ if (IS_ERR(cec->cec_worker)) ++ dev_err(cec->dev, "failed create hdp cec thread\n"); ++ ++ wake_up_process(cec->cec_worker); ++ ++ dev_dbg(dev, "CEC successfuly probed\n"); ++ return 0; ++} ++ ++int cdns_mhdp_unregister_cec_driver(struct device *dev) ++{ ++ struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev); ++ struct cdns_mhdp_cec *cec = &mhdp->hdmi.cec; ++ ++ if (cec->cec_worker) { ++ kthread_stop(cec->cec_worker); ++ cec->cec_worker = NULL; ++ } ++ cec_unregister_adapter(cec->adap); ++ return 0; ++} ++ ++MODULE_AUTHOR("Sandor.Yu@NXP.com"); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("NXP CDNS MHDP HDMI CEC driver"); +diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c +new file mode 100644 +index 000000000000..91d1cfd4b2af +--- /dev/null ++++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c +@@ -0,0 +1,795 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd ++ * Author: Chris Zhong ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++#include ++#include ++ ++#define CDNS_DP_SPDIF_CLK 200000000 ++#define FW_ALIVE_TIMEOUT_US 1000000 ++#define MAILBOX_RETRY_US 1000 ++#define MAILBOX_TIMEOUT_US 5000000 ++ ++#define mhdp_readx_poll_timeout(op, addr, offset, val, cond, sleep_us, timeout_us) \ ++({ \ ++ u64 __timeout_us = (timeout_us); \ ++ unsigned long __sleep_us = (sleep_us); \ ++ ktime_t __timeout = ktime_add_us(ktime_get(), __timeout_us); \ ++ might_sleep_if((__sleep_us) != 0); \ ++ for (;;) { \ ++ (val) = op(addr, offset); \ ++ if (cond) \ ++ break; \ ++ if (__timeout_us && \ ++ ktime_compare(ktime_get(), __timeout) > 0) { \ ++ (val) = op(addr, offset); \ ++ break; \ ++ } \ ++ if (__sleep_us) \ ++ usleep_range((__sleep_us >> 2) + 1, __sleep_us); \ ++ } \ ++ (cond) ? 0 : -ETIMEDOUT; \ ++}) ++ ++u32 cdns_mhdp_bus_read(struct cdns_mhdp_device *mhdp, u32 offset) ++{ ++ u32 val; ++ ++ mutex_lock(&mhdp->iolock); ++ ++ if (mhdp->bus_type == BUS_TYPE_LOW4K_SAPB) { ++ /* Remap address to low 4K SAPB bus */ ++ writel(offset >> 12, mhdp->regs_sec + 0xc); ++ val = readl((offset & 0xfff) + mhdp->regs_base); ++ } else if (mhdp->bus_type == BUS_TYPE_LOW4K_APB) { ++ /* Remap address to low 4K memory */ ++ writel(offset >> 12, mhdp->regs_sec + 8); ++ val = readl((offset & 0xfff) + mhdp->regs_base); ++ } else if (mhdp->bus_type == BUS_TYPE_NORMAL_SAPB) ++ val = readl(mhdp->regs_sec + offset); ++ else ++ val = readl(mhdp->regs_base + offset); ++ ++ mutex_unlock(&mhdp->iolock); ++ ++ return val; ++} ++EXPORT_SYMBOL(cdns_mhdp_bus_read); ++ ++void cdns_mhdp_bus_write(u32 val, struct cdns_mhdp_device *mhdp, u32 offset) ++{ ++ mutex_lock(&mhdp->iolock); ++ ++ if (mhdp->bus_type == BUS_TYPE_LOW4K_SAPB) { ++ /* Remap address to low 4K SAPB bus */ ++ writel(offset >> 12, mhdp->regs_sec + 0xc); ++ writel(val, (offset & 0xfff) + mhdp->regs_base); ++ } else if (mhdp->bus_type == BUS_TYPE_LOW4K_APB) { ++ /* Remap address to low 4K memory */ ++ writel(offset >> 12, mhdp->regs_sec + 8); ++ writel(val, (offset & 0xfff) + mhdp->regs_base); ++ } else if (mhdp->bus_type == BUS_TYPE_NORMAL_SAPB) ++ writel(val, mhdp->regs_sec + offset); ++ else ++ writel(val, mhdp->regs_base + offset); ++ ++ mutex_unlock(&mhdp->iolock); ++} ++EXPORT_SYMBOL(cdns_mhdp_bus_write); ++ ++u32 cdns_mhdp_get_fw_clk(struct cdns_mhdp_device *mhdp) ++{ ++ return cdns_mhdp_bus_read(mhdp, SW_CLK_H); ++} ++EXPORT_SYMBOL(cdns_mhdp_get_fw_clk); ++ ++void cdns_mhdp_set_fw_clk(struct cdns_mhdp_device *mhdp, unsigned long clk) ++{ ++ cdns_mhdp_bus_write(clk / 1000000, mhdp, SW_CLK_H); ++} ++EXPORT_SYMBOL(cdns_mhdp_set_fw_clk); ++ ++void cdns_mhdp_clock_reset(struct cdns_mhdp_device *mhdp) ++{ ++ u32 val; ++ ++ val = DPTX_FRMR_DATA_CLK_RSTN_EN | ++ DPTX_FRMR_DATA_CLK_EN | ++ DPTX_PHY_DATA_RSTN_EN | ++ DPTX_PHY_DATA_CLK_EN | ++ DPTX_PHY_CHAR_RSTN_EN | ++ DPTX_PHY_CHAR_CLK_EN | ++ SOURCE_AUX_SYS_CLK_RSTN_EN | ++ SOURCE_AUX_SYS_CLK_EN | ++ DPTX_SYS_CLK_RSTN_EN | ++ DPTX_SYS_CLK_EN | ++ CFG_DPTX_VIF_CLK_RSTN_EN | ++ CFG_DPTX_VIF_CLK_EN; ++ cdns_mhdp_bus_write(val, mhdp, SOURCE_DPTX_CAR); ++ ++ val = SOURCE_PHY_RSTN_EN | SOURCE_PHY_CLK_EN; ++ cdns_mhdp_bus_write(val, mhdp, SOURCE_PHY_CAR); ++ ++ val = SOURCE_PKT_SYS_RSTN_EN | ++ SOURCE_PKT_SYS_CLK_EN | ++ SOURCE_PKT_DATA_RSTN_EN | ++ SOURCE_PKT_DATA_CLK_EN; ++ cdns_mhdp_bus_write(val, mhdp, SOURCE_PKT_CAR); ++ ++ val = SPDIF_CDR_CLK_RSTN_EN | ++ SPDIF_CDR_CLK_EN | ++ SOURCE_AIF_SYS_RSTN_EN | ++ SOURCE_AIF_SYS_CLK_EN | ++ SOURCE_AIF_CLK_RSTN_EN | ++ SOURCE_AIF_CLK_EN; ++ cdns_mhdp_bus_write(val, mhdp, SOURCE_AIF_CAR); ++ ++ val = SOURCE_CIPHER_SYSTEM_CLK_RSTN_EN | ++ SOURCE_CIPHER_SYS_CLK_EN | ++ SOURCE_CIPHER_CHAR_CLK_RSTN_EN | ++ SOURCE_CIPHER_CHAR_CLK_EN; ++ cdns_mhdp_bus_write(val, mhdp, SOURCE_CIPHER_CAR); ++ ++ val = SOURCE_CRYPTO_SYS_CLK_RSTN_EN | ++ SOURCE_CRYPTO_SYS_CLK_EN; ++ cdns_mhdp_bus_write(val, mhdp, SOURCE_CRYPTO_CAR); ++ ++ /* enable Mailbox and PIF interrupt */ ++ cdns_mhdp_bus_write(0, mhdp, APB_INT_MASK); ++} ++EXPORT_SYMBOL(cdns_mhdp_clock_reset); ++ ++bool cdns_mhdp_check_alive(struct cdns_mhdp_device *mhdp) ++{ ++ u32 alive, newalive; ++ u8 retries_left = 50; ++ ++ alive = cdns_mhdp_bus_read(mhdp, KEEP_ALIVE); ++ ++ while (retries_left--) { ++ udelay(2); ++ ++ newalive = cdns_mhdp_bus_read(mhdp, KEEP_ALIVE); ++ if (alive == newalive) ++ continue; ++ return true; ++ } ++ return false; ++} ++EXPORT_SYMBOL(cdns_mhdp_check_alive); ++ ++static int mhdp_mailbox_read(struct cdns_mhdp_device *mhdp) ++{ ++ int val, ret; ++ ++ ret = mhdp_readx_poll_timeout(cdns_mhdp_bus_read, mhdp, MAILBOX_EMPTY_ADDR, ++ val, !val, MAILBOX_RETRY_US, ++ MAILBOX_TIMEOUT_US); ++ if (ret < 0) ++ return ret; ++ ++ return cdns_mhdp_bus_read(mhdp, MAILBOX0_RD_DATA) & 0xff; ++} ++ ++static int mhdp_mailbox_write(struct cdns_mhdp_device *mhdp, u8 val) ++{ ++ int ret, full; ++ ++ ret = mhdp_readx_poll_timeout(cdns_mhdp_bus_read, mhdp, MAILBOX_FULL_ADDR, ++ full, !full, MAILBOX_RETRY_US, ++ MAILBOX_TIMEOUT_US); ++ if (ret < 0) ++ return ret; ++ ++ cdns_mhdp_bus_write(val, mhdp, MAILBOX0_WR_DATA); ++ ++ return 0; ++} ++ ++int cdns_mhdp_mailbox_validate_receive(struct cdns_mhdp_device *mhdp, ++ u8 module_id, u8 opcode, ++ u16 req_size) ++{ ++ u32 mbox_size, i; ++ u8 header[4]; ++ int ret; ++ ++ /* read the header of the message */ ++ for (i = 0; i < 4; i++) { ++ ret = mhdp_mailbox_read(mhdp); ++ if (ret < 0) ++ return ret; ++ ++ header[i] = ret; ++ } ++ ++ mbox_size = get_unaligned_be16(header + 2); ++ ++ if (opcode != header[0] || module_id != header[1] || ++ req_size != mbox_size) { ++ /* ++ * If the message in mailbox is not what we want, we need to ++ * clear the mailbox by reading its contents. ++ */ ++ for (i = 0; i < mbox_size; i++) ++ if (mhdp_mailbox_read(mhdp) < 0) ++ break; ++ ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(cdns_mhdp_mailbox_validate_receive); ++ ++int cdns_mhdp_mailbox_read_receive(struct cdns_mhdp_device *mhdp, ++ u8 *buff, u16 buff_size) ++{ ++ u32 i; ++ int ret; ++ ++ for (i = 0; i < buff_size; i++) { ++ ret = mhdp_mailbox_read(mhdp); ++ if (ret < 0) ++ return ret; ++ ++ buff[i] = ret; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(cdns_mhdp_mailbox_read_receive); ++ ++int cdns_mhdp_mailbox_send(struct cdns_mhdp_device *mhdp, u8 module_id, ++ u8 opcode, u16 size, u8 *message) ++{ ++ u8 header[4]; ++ int ret, i; ++ ++ header[0] = opcode; ++ header[1] = module_id; ++ put_unaligned_be16(size, header + 2); ++ ++ for (i = 0; i < 4; i++) { ++ ret = mhdp_mailbox_write(mhdp, header[i]); ++ if (ret) ++ return ret; ++ } ++ ++ for (i = 0; i < size; i++) { ++ ret = mhdp_mailbox_write(mhdp, message[i]); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(cdns_mhdp_mailbox_send); ++ ++int cdns_mhdp_reg_read(struct cdns_mhdp_device *mhdp, u32 addr) ++{ ++ u8 msg[4], resp[8]; ++ u32 val; ++ int ret; ++ ++ if (addr == 0) { ++ ret = -EINVAL; ++ goto err_reg_read; ++ } ++ ++ put_unaligned_be32(addr, msg); ++ ++ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_GENERAL, ++ GENERAL_READ_REGISTER, ++ sizeof(msg), msg); ++ if (ret) ++ goto err_reg_read; ++ ++ ret = cdns_mhdp_mailbox_validate_receive(mhdp, MB_MODULE_ID_GENERAL, ++ GENERAL_READ_REGISTER, ++ sizeof(resp)); ++ if (ret) ++ goto err_reg_read; ++ ++ ret = cdns_mhdp_mailbox_read_receive(mhdp, resp, sizeof(resp)); ++ if (ret) ++ goto err_reg_read; ++ ++ /* Returned address value should be the same as requested */ ++ if (memcmp(msg, resp, sizeof(msg))) { ++ ret = -EINVAL; ++ goto err_reg_read; ++ } ++ ++ val = get_unaligned_be32(resp + 4); ++ ++ return val; ++err_reg_read: ++ DRM_DEV_ERROR(mhdp->dev, "Failed to read register.\n"); ++ ++ return ret; ++} ++EXPORT_SYMBOL(cdns_mhdp_reg_read); ++ ++int cdns_mhdp_reg_write(struct cdns_mhdp_device *mhdp, u32 addr, u32 val) ++{ ++ u8 msg[8]; ++ ++ put_unaligned_be32(addr, msg); ++ put_unaligned_be32(val, msg + 4); ++ ++ return cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_GENERAL, ++ GENERAL_WRITE_REGISTER, sizeof(msg), msg); ++} ++EXPORT_SYMBOL(cdns_mhdp_reg_write); ++ ++int cdns_mhdp_reg_write_bit(struct cdns_mhdp_device *mhdp, u16 addr, ++ u8 start_bit, u8 bits_no, u32 val) ++{ ++ u8 field[8]; ++ ++ put_unaligned_be16(addr, field); ++ field[2] = start_bit; ++ field[3] = bits_no; ++ put_unaligned_be32(val, field + 4); ++ ++ return cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, ++ DPTX_WRITE_FIELD, sizeof(field), field); ++} ++EXPORT_SYMBOL(cdns_mhdp_reg_write_bit); ++ ++int cdns_mhdp_load_firmware(struct cdns_mhdp_device *mhdp, const u32 *i_mem, ++ u32 i_size, const u32 *d_mem, u32 d_size) ++{ ++ u32 reg; ++ int i, ret; ++ ++ /* reset ucpu before load firmware*/ ++ cdns_mhdp_bus_write(APB_IRAM_PATH | APB_DRAM_PATH | APB_XT_RESET, ++ mhdp, APB_CTRL); ++ ++ for (i = 0; i < i_size; i += 4) ++ cdns_mhdp_bus_write(*i_mem++, mhdp, ADDR_IMEM + i); ++ ++ for (i = 0; i < d_size; i += 4) ++ cdns_mhdp_bus_write(*d_mem++, mhdp, ADDR_DMEM + i); ++ ++ /* un-reset ucpu */ ++ cdns_mhdp_bus_write(0, mhdp, APB_CTRL); ++ ++ /* check the keep alive register to make sure fw working */ ++ ret = mhdp_readx_poll_timeout(cdns_mhdp_bus_read, mhdp, KEEP_ALIVE, ++ reg, reg, 2000, FW_ALIVE_TIMEOUT_US); ++ if (ret < 0) { ++ DRM_DEV_ERROR(mhdp->dev, "failed to loaded the FW reg = %x\n", ++ reg); ++ return -EINVAL; ++ } ++ ++ reg = cdns_mhdp_bus_read(mhdp, VER_L) & 0xff; ++ mhdp->fw_version = reg; ++ reg = cdns_mhdp_bus_read(mhdp, VER_H) & 0xff; ++ mhdp->fw_version |= reg << 8; ++ reg = cdns_mhdp_bus_read(mhdp, VER_LIB_L_ADDR) & 0xff; ++ mhdp->fw_version |= reg << 16; ++ reg = cdns_mhdp_bus_read(mhdp, VER_LIB_H_ADDR) & 0xff; ++ mhdp->fw_version |= reg << 24; ++ ++ DRM_DEV_DEBUG(mhdp->dev, "firmware version: %x\n", mhdp->fw_version); ++ ++ return 0; ++} ++EXPORT_SYMBOL(cdns_mhdp_load_firmware); ++ ++int cdns_mhdp_set_firmware_active(struct cdns_mhdp_device *mhdp, bool enable) ++{ ++ u8 msg[5]; ++ int ret, i; ++ ++ msg[0] = GENERAL_MAIN_CONTROL; ++ msg[1] = MB_MODULE_ID_GENERAL; ++ msg[2] = 0; ++ msg[3] = 1; ++ msg[4] = enable ? FW_ACTIVE : FW_STANDBY; ++ ++ for (i = 0; i < sizeof(msg); i++) { ++ ret = mhdp_mailbox_write(mhdp, msg[i]); ++ if (ret) ++ goto err_set_firmware_active; ++ } ++ ++ /* read the firmware state */ ++ for (i = 0; i < sizeof(msg); i++) { ++ ret = mhdp_mailbox_read(mhdp); ++ if (ret < 0) ++ goto err_set_firmware_active; ++ ++ msg[i] = ret; ++ } ++ ++ ret = 0; ++ ++err_set_firmware_active: ++ if (ret < 0) ++ DRM_DEV_ERROR(mhdp->dev, "set firmware active failed\n"); ++ return ret; ++} ++EXPORT_SYMBOL(cdns_mhdp_set_firmware_active); ++ ++int cdns_mhdp_set_host_cap(struct cdns_mhdp_device *mhdp, bool flip) ++{ ++ u8 msg[8]; ++ int ret; ++ ++ msg[0] = drm_dp_link_rate_to_bw_code(mhdp->dp.rate); ++ msg[1] = mhdp->dp.num_lanes | SCRAMBLER_EN; ++ msg[2] = VOLTAGE_LEVEL_2; ++ msg[3] = PRE_EMPHASIS_LEVEL_3; ++ msg[4] = PTS1 | PTS2 | PTS3 | PTS4; ++ msg[5] = FAST_LT_NOT_SUPPORT; ++ msg[6] = flip ? LANE_MAPPING_FLIPPED : LANE_MAPPING_NORMAL; ++ msg[7] = ENHANCED; ++ ++ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, ++ DPTX_SET_HOST_CAPABILITIES, ++ sizeof(msg), msg); ++ if (ret) ++ goto err_set_host_cap; ++ ++/* TODO Sandor */ ++// ret = cdns_mhdp_reg_write(mhdp, DP_AUX_SWAP_INVERSION_CONTROL, ++// AUX_HOST_INVERT); ++ ++err_set_host_cap: ++ if (ret) ++ DRM_DEV_ERROR(mhdp->dev, "set host cap failed: %d\n", ret); ++ return ret; ++} ++EXPORT_SYMBOL(cdns_mhdp_set_host_cap); ++ ++int cdns_mhdp_event_config(struct cdns_mhdp_device *mhdp) ++{ ++ u8 msg[5]; ++ int ret; ++ ++ memset(msg, 0, sizeof(msg)); ++ ++ msg[0] = MHDP_EVENT_ENABLE_HPD | MHDP_EVENT_ENABLE_TRAINING; ++ ++ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, ++ DPTX_ENABLE_EVENT, sizeof(msg), msg); ++ if (ret) ++ DRM_DEV_ERROR(mhdp->dev, "set event config failed: %d\n", ret); ++ ++ return ret; ++} ++EXPORT_SYMBOL(cdns_mhdp_event_config); ++ ++u32 cdns_mhdp_get_event(struct cdns_mhdp_device *mhdp) ++{ ++ return cdns_mhdp_bus_read(mhdp, SW_EVENTS0); ++} ++EXPORT_SYMBOL(cdns_mhdp_get_event); ++ ++int cdns_mhdp_read_hpd(struct cdns_mhdp_device *mhdp) ++{ ++ u8 status; ++ int ret; ++ ++ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_GENERAL, GENERAL_GET_HPD_STATE, ++ 0, NULL); ++ if (ret) ++ goto err_get_hpd; ++ ++ ret = cdns_mhdp_mailbox_validate_receive(mhdp, MB_MODULE_ID_GENERAL, ++ GENERAL_GET_HPD_STATE, sizeof(status)); ++ if (ret) ++ goto err_get_hpd; ++ ++ ret = cdns_mhdp_mailbox_read_receive(mhdp, &status, sizeof(status)); ++ if (ret) ++ goto err_get_hpd; ++ ++ return status; ++ ++err_get_hpd: ++ DRM_ERROR("read hpd failed: %d\n", ret); ++ return ret; ++} ++EXPORT_SYMBOL(cdns_mhdp_read_hpd); ++ ++int cdns_mhdp_get_edid_block(void *data, u8 *edid, ++ unsigned int block, size_t length) ++{ ++ struct cdns_mhdp_device *mhdp = data; ++ u8 msg[2], reg[2], i; ++ int ret; ++ ++ for (i = 0; i < 4; i++) { ++ msg[0] = block / 2; ++ msg[1] = block % 2; ++ ++ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, ++ DPTX_GET_EDID, sizeof(msg), msg); ++ if (ret) ++ continue; ++ ++ ret = cdns_mhdp_mailbox_validate_receive(mhdp, ++ MB_MODULE_ID_DP_TX, ++ DPTX_GET_EDID, ++ sizeof(reg) + length); ++ if (ret) ++ continue; ++ ++ ret = cdns_mhdp_mailbox_read_receive(mhdp, reg, sizeof(reg)); ++ if (ret) ++ continue; ++ ++ ret = cdns_mhdp_mailbox_read_receive(mhdp, edid, length); ++ if (ret) ++ continue; ++ ++ if (reg[0] == length && reg[1] == block / 2) ++ break; ++ } ++ ++ if (ret) ++ DRM_DEV_ERROR(mhdp->dev, "get block[%d] edid failed: %d\n", ++ block, ret); ++ ++ return ret; ++} ++EXPORT_SYMBOL(cdns_mhdp_get_edid_block); ++ ++int cdns_mhdp_set_video_status(struct cdns_mhdp_device *mhdp, int active) ++{ ++ u8 msg; ++ int ret; ++ ++ msg = !!active; ++ ++ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, ++ DPTX_SET_VIDEO, sizeof(msg), &msg); ++ if (ret) ++ DRM_DEV_ERROR(mhdp->dev, "set video status failed: %d\n", ret); ++ ++ return ret; ++} ++EXPORT_SYMBOL(cdns_mhdp_set_video_status); ++ ++static int mhdp_get_msa_misc(struct video_info *video, ++ struct drm_display_mode *mode) ++{ ++ u32 msa_misc; ++ u8 val[2] = {0}; ++ ++ switch (video->color_fmt) { ++ case PXL_RGB: ++ case Y_ONLY: ++ val[0] = 0; ++ break; ++ /* set YUV default color space conversion to BT601 */ ++ case YCBCR_4_4_4: ++ val[0] = 6 + BT_601 * 8; ++ break; ++ case YCBCR_4_2_2: ++ val[0] = 5 + BT_601 * 8; ++ break; ++ case YCBCR_4_2_0: ++ val[0] = 5; ++ break; ++ } ++ ++ switch (video->color_depth) { ++ case 6: ++ val[1] = 0; ++ break; ++ case 8: ++ val[1] = 1; ++ break; ++ case 10: ++ val[1] = 2; ++ break; ++ case 12: ++ val[1] = 3; ++ break; ++ case 16: ++ val[1] = 4; ++ break; ++ } ++ ++ msa_misc = 2 * val[0] + 32 * val[1] + ++ ((video->color_fmt == Y_ONLY) ? (1 << 14) : 0); ++ ++ return msa_misc; ++} ++ ++int cdns_mhdp_config_video(struct cdns_mhdp_device *mhdp) ++{ ++ struct video_info *video = &mhdp->video_info; ++ struct drm_display_mode *mode = &mhdp->mode; ++ u64 symbol; ++ u32 val, link_rate, rem; ++ u8 bit_per_pix, tu_size_reg = TU_SIZE; ++ int ret; ++ ++ bit_per_pix = (video->color_fmt == YCBCR_4_2_2) ? ++ (video->color_depth * 2) : (video->color_depth * 3); ++ ++ link_rate = mhdp->dp.rate / 1000; ++ ++ ret = cdns_mhdp_reg_write(mhdp, BND_HSYNC2VSYNC, VIF_BYPASS_INTERLACE); ++ if (ret) ++ goto err_config_video; ++ ++ ret = cdns_mhdp_reg_write(mhdp, HSYNC2VSYNC_POL_CTRL, 0); ++ if (ret) ++ goto err_config_video; ++ ++ /* ++ * get a best tu_size and valid symbol: ++ * 1. chose Lclk freq(162Mhz, 270Mhz, 540Mhz), set TU to 32 ++ * 2. calculate VS(valid symbol) = TU * Pclk * Bpp / (Lclk * Lanes) ++ * 3. if VS > *.85 or VS < *.1 or VS < 2 or TU < VS + 4, then set ++ * TU += 2 and repeat 2nd step. ++ */ ++ do { ++ tu_size_reg += 2; ++ symbol = tu_size_reg * mode->clock * bit_per_pix; ++ do_div(symbol, mhdp->dp.num_lanes * link_rate * 8); ++ rem = do_div(symbol, 1000); ++ if (tu_size_reg > 64) { ++ ret = -EINVAL; ++ DRM_DEV_ERROR(mhdp->dev, ++ "tu error, clk:%d, lanes:%d, rate:%d\n", ++ mode->clock, mhdp->dp.num_lanes, ++ link_rate); ++ goto err_config_video; ++ } ++ } while ((symbol <= 1) || (tu_size_reg - symbol < 4) || ++ (rem > 850) || (rem < 100)); ++ ++ val = symbol + (tu_size_reg << 8); ++ val |= TU_CNT_RST_EN; ++ ret = cdns_mhdp_reg_write(mhdp, DP_FRAMER_TU, val); ++ if (ret) ++ goto err_config_video; ++ ++ /* set the FIFO Buffer size */ ++ val = div_u64(mode->clock * (symbol + 1), 1000) + link_rate; ++ val /= (mhdp->dp.num_lanes * link_rate); ++ val = div_u64(8 * (symbol + 1), bit_per_pix) - val; ++ val += 2; ++ ret = cdns_mhdp_reg_write(mhdp, DP_VC_TABLE(15), val); ++ ++ switch (video->color_depth) { ++ case 6: ++ val = BCS_6; ++ break; ++ case 8: ++ val = BCS_8; ++ break; ++ case 10: ++ val = BCS_10; ++ break; ++ case 12: ++ val = BCS_12; ++ break; ++ case 16: ++ val = BCS_16; ++ break; ++ } ++ ++ val += video->color_fmt << 8; ++ ret = cdns_mhdp_reg_write(mhdp, DP_FRAMER_PXL_REPR, val); ++ if (ret) ++ goto err_config_video; ++ ++ val = video->h_sync_polarity ? DP_FRAMER_SP_HSP : 0; ++ val |= video->v_sync_polarity ? DP_FRAMER_SP_VSP : 0; ++ ret = cdns_mhdp_reg_write(mhdp, DP_FRAMER_SP, val); ++ if (ret) ++ goto err_config_video; ++ ++ val = (mode->hsync_start - mode->hdisplay) << 16; ++ val |= mode->htotal - mode->hsync_end; ++ ret = cdns_mhdp_reg_write(mhdp, DP_FRONT_BACK_PORCH, val); ++ if (ret) ++ goto err_config_video; ++ ++ val = mode->hdisplay * bit_per_pix / 8; ++ ret = cdns_mhdp_reg_write(mhdp, DP_BYTE_COUNT, val); ++ if (ret) ++ goto err_config_video; ++ ++ val = mode->htotal | ((mode->htotal - mode->hsync_start) << 16); ++ ret = cdns_mhdp_reg_write(mhdp, MSA_HORIZONTAL_0, val); ++ if (ret) ++ goto err_config_video; ++ ++ val = mode->hsync_end - mode->hsync_start; ++ val |= (mode->hdisplay << 16) | (video->h_sync_polarity << 15); ++ ret = cdns_mhdp_reg_write(mhdp, MSA_HORIZONTAL_1, val); ++ if (ret) ++ goto err_config_video; ++ ++ val = mode->vtotal; ++ val |= (mode->vtotal - mode->vsync_start) << 16; ++ ret = cdns_mhdp_reg_write(mhdp, MSA_VERTICAL_0, val); ++ if (ret) ++ goto err_config_video; ++ ++ val = mode->vsync_end - mode->vsync_start; ++ val |= (mode->vdisplay << 16) | (video->v_sync_polarity << 15); ++ ret = cdns_mhdp_reg_write(mhdp, MSA_VERTICAL_1, val); ++ if (ret) ++ goto err_config_video; ++ ++ val = mhdp_get_msa_misc(video, mode); ++ ret = cdns_mhdp_reg_write(mhdp, MSA_MISC, val); ++ if (ret) ++ goto err_config_video; ++ ++ ret = cdns_mhdp_reg_write(mhdp, STREAM_CONFIG, 1); ++ if (ret) ++ goto err_config_video; ++ ++ val = mode->hsync_end - mode->hsync_start; ++ val |= mode->hdisplay << 16; ++ ret = cdns_mhdp_reg_write(mhdp, DP_HORIZONTAL, val); ++ if (ret) ++ goto err_config_video; ++ ++ val = mode->vdisplay; ++ val |= (mode->vtotal - mode->vsync_start) << 16; ++ ret = cdns_mhdp_reg_write(mhdp, DP_VERTICAL_0, val); ++ if (ret) ++ goto err_config_video; ++ ++ val = mode->vtotal; ++ ret = cdns_mhdp_reg_write(mhdp, DP_VERTICAL_1, val); ++ if (ret) ++ goto err_config_video; ++ ++ ret = cdns_mhdp_reg_write_bit(mhdp, DP_VB_ID, 2, 1, 0); ++ ++err_config_video: ++ if (ret) ++ DRM_DEV_ERROR(mhdp->dev, "config video failed: %d\n", ret); ++ return ret; ++} ++EXPORT_SYMBOL(cdns_mhdp_config_video); ++ ++int cdns_phy_reg_write(struct cdns_mhdp_device *mhdp, u32 addr, u32 val) ++{ ++ return cdns_mhdp_reg_write(mhdp, ADDR_PHY_AFE + (addr << 2), val); ++} ++EXPORT_SYMBOL(cdns_phy_reg_write); ++ ++u32 cdns_phy_reg_read(struct cdns_mhdp_device *mhdp, u32 addr) ++{ ++ return cdns_mhdp_reg_read(mhdp, ADDR_PHY_AFE + (addr << 2)); ++} ++EXPORT_SYMBOL(cdns_phy_reg_read); +diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-dp.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-dp.c +new file mode 100644 +index 000000000000..f025c39d12ea +--- /dev/null ++++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-dp.c +@@ -0,0 +1,172 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++ ++#include ++#include ++#include ++#include ++ ++#define LINK_TRAINING_TIMEOUT_MS 500 ++#define LINK_TRAINING_RETRY_MS 20 ++ ++int cdns_mhdp_dpcd_read(struct cdns_mhdp_device *mhdp, ++ u32 addr, u8 *data, u16 len) ++{ ++ u8 msg[5], reg[5]; ++ int ret; ++ ++ put_unaligned_be16(len, msg); ++ put_unaligned_be24(addr, msg + 2); ++ ++ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, ++ DPTX_READ_DPCD, sizeof(msg), msg); ++ if (ret) ++ goto err_dpcd_read; ++ ++ ret = cdns_mhdp_mailbox_validate_receive(mhdp, MB_MODULE_ID_DP_TX, ++ DPTX_READ_DPCD, ++ sizeof(reg) + len); ++ if (ret) ++ goto err_dpcd_read; ++ ++ ret = cdns_mhdp_mailbox_read_receive(mhdp, reg, sizeof(reg)); ++ if (ret) ++ goto err_dpcd_read; ++ ++ ret = cdns_mhdp_mailbox_read_receive(mhdp, data, len); ++ ++err_dpcd_read: ++ return ret; ++} ++EXPORT_SYMBOL(cdns_mhdp_dpcd_read); ++ ++int cdns_mhdp_dpcd_write(struct cdns_mhdp_device *mhdp, u32 addr, u8 value) ++{ ++ u8 msg[6], reg[5]; ++ int ret; ++ ++ put_unaligned_be16(1, msg); ++ put_unaligned_be24(addr, msg + 2); ++ msg[5] = value; ++ ++ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, ++ DPTX_WRITE_DPCD, sizeof(msg), msg); ++ if (ret) ++ goto err_dpcd_write; ++ ++ ret = cdns_mhdp_mailbox_validate_receive(mhdp, MB_MODULE_ID_DP_TX, ++ DPTX_WRITE_DPCD, sizeof(reg)); ++ if (ret) ++ goto err_dpcd_write; ++ ++ ret = cdns_mhdp_mailbox_read_receive(mhdp, reg, sizeof(reg)); ++ if (ret) ++ goto err_dpcd_write; ++ ++ if (addr != get_unaligned_be24(reg + 2)) ++ ret = -EINVAL; ++ ++err_dpcd_write: ++ if (ret) ++ DRM_DEV_ERROR(mhdp->dev, "dpcd write failed: %d\n", ret); ++ return ret; ++} ++EXPORT_SYMBOL(cdns_mhdp_dpcd_write); ++ ++static int cdns_mhdp_training_start(struct cdns_mhdp_device *mhdp) ++{ ++ unsigned long timeout; ++ u8 msg, event[2]; ++ int ret; ++ ++ msg = LINK_TRAINING_RUN; ++ ++ /* start training */ ++ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, ++ DPTX_TRAINING_CONTROL, sizeof(msg), &msg); ++ if (ret) ++ goto err_training_start; ++ ++ timeout = jiffies + msecs_to_jiffies(LINK_TRAINING_TIMEOUT_MS); ++ while (time_before(jiffies, timeout)) { ++ msleep(LINK_TRAINING_RETRY_MS); ++ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, ++ DPTX_READ_EVENT, 0, NULL); ++ if (ret) ++ goto err_training_start; ++ ++ ret = cdns_mhdp_mailbox_validate_receive(mhdp, ++ MB_MODULE_ID_DP_TX, ++ DPTX_READ_EVENT, ++ sizeof(event)); ++ if (ret) ++ goto err_training_start; ++ ++ ret = cdns_mhdp_mailbox_read_receive(mhdp, event, ++ sizeof(event)); ++ if (ret) ++ goto err_training_start; ++ ++ if (event[1] & EQ_PHASE_FINISHED) ++ return 0; ++ } ++ ++ ret = -ETIMEDOUT; ++ ++err_training_start: ++ DRM_DEV_ERROR(mhdp->dev, "training failed: %d\n", ret); ++ return ret; ++} ++ ++static int cdns_mhdp_get_training_status(struct cdns_mhdp_device *mhdp) ++{ ++ u8 status[10]; ++ int ret; ++ ++ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, ++ DPTX_READ_LINK_STAT, 0, NULL); ++ if (ret) ++ goto err_get_training_status; ++ ++ ret = cdns_mhdp_mailbox_validate_receive(mhdp, MB_MODULE_ID_DP_TX, ++ DPTX_READ_LINK_STAT, ++ sizeof(status)); ++ if (ret) ++ goto err_get_training_status; ++ ++ ret = cdns_mhdp_mailbox_read_receive(mhdp, status, sizeof(status)); ++ if (ret) ++ goto err_get_training_status; ++ ++ mhdp->dp.rate = drm_dp_bw_code_to_link_rate(status[0]); ++ mhdp->dp.num_lanes = status[1]; ++ ++err_get_training_status: ++ if (ret) ++ DRM_DEV_ERROR(mhdp->dev, "get training status failed: %d\n", ++ ret); ++ return ret; ++} ++ ++int cdns_mhdp_train_link(struct cdns_mhdp_device *mhdp) ++{ ++ int ret; ++ ++ ret = cdns_mhdp_training_start(mhdp); ++ if (ret) { ++ DRM_DEV_ERROR(mhdp->dev, "Failed to start training %d\n", ++ ret); ++ return ret; ++ } ++ ++ ret = cdns_mhdp_get_training_status(mhdp); ++ if (ret) { ++ DRM_DEV_ERROR(mhdp->dev, "Failed to get training stat %d\n", ++ ret); ++ return ret; ++ } ++ ++ DRM_DEV_DEBUG_KMS(mhdp->dev, "rate:0x%x, lanes:%d\n", mhdp->dp.rate, ++ mhdp->dp.num_lanes); ++ return ret; ++} ++EXPORT_SYMBOL(cdns_mhdp_train_link); +diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-hdmi.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-hdmi.c +new file mode 100644 +index 000000000000..c37a7ac6af9b +--- /dev/null ++++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-hdmi.c +@@ -0,0 +1,332 @@ ++/* ++ * Copyright (C) 2019 NXP Semiconductor, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++void cdns_mhdp_infoframe_set(struct cdns_mhdp_device *mhdp, ++ u8 entry_id, u8 packet_len, u8 *packet, u8 packet_type) ++{ ++ u32 *packet32, len32; ++ u32 val, i; ++ ++ /* invalidate entry */ ++ val = F_ACTIVE_IDLE_TYPE(1) | F_PKT_ALLOC_ADDRESS(entry_id); ++ cdns_mhdp_bus_write(val, mhdp, SOURCE_PIF_PKT_ALLOC_REG); ++ cdns_mhdp_bus_write(F_PKT_ALLOC_WR_EN(1), mhdp, SOURCE_PIF_PKT_ALLOC_WR_EN); ++ ++ /* flush fifo 1 */ ++ cdns_mhdp_bus_write(F_FIFO1_FLUSH(1), mhdp, SOURCE_PIF_FIFO1_FLUSH); ++ ++ /* write packet into memory */ ++ packet32 = (u32 *)packet; ++ len32 = packet_len / 4; ++ for (i = 0; i < len32; i++) ++ cdns_mhdp_bus_write(F_DATA_WR(packet32[i]), mhdp, SOURCE_PIF_DATA_WR); ++ ++ /* write entry id */ ++ cdns_mhdp_bus_write(F_WR_ADDR(entry_id), mhdp, SOURCE_PIF_WR_ADDR); ++ ++ /* write request */ ++ cdns_mhdp_bus_write(F_HOST_WR(1), mhdp, SOURCE_PIF_WR_REQ); ++ ++ /* update entry */ ++ val = F_ACTIVE_IDLE_TYPE(1) | F_TYPE_VALID(1) | ++ F_PACKET_TYPE(packet_type) | F_PKT_ALLOC_ADDRESS(entry_id); ++ cdns_mhdp_bus_write(val, mhdp, SOURCE_PIF_PKT_ALLOC_REG); ++ ++ cdns_mhdp_bus_write(F_PKT_ALLOC_WR_EN(1), mhdp, SOURCE_PIF_PKT_ALLOC_WR_EN); ++} ++ ++int cdns_hdmi_get_edid_block(void *data, u8 *edid, ++ u32 block, size_t length) ++{ ++ struct cdns_mhdp_device *mhdp = data; ++ u8 msg[2], reg[5], i; ++ int ret; ++ ++ for (i = 0; i < 4; i++) { ++ msg[0] = block / 2; ++ msg[1] = block % 2; ++ ++ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_HDMI_TX, HDMI_TX_EDID, ++ sizeof(msg), msg); ++ if (ret) ++ continue; ++ ++ ret = cdns_mhdp_mailbox_validate_receive(mhdp, MB_MODULE_ID_HDMI_TX, ++ HDMI_TX_EDID, sizeof(reg) + length); ++ if (ret) ++ continue; ++ ++ ret = cdns_mhdp_mailbox_read_receive(mhdp, reg, sizeof(reg)); ++ if (ret) ++ continue; ++ ++ ret = cdns_mhdp_mailbox_read_receive(mhdp, edid, length); ++ if (ret) ++ continue; ++ ++ if ((reg[3] << 8 | reg[4]) == length) ++ break; ++ } ++ ++ if (ret) ++ DRM_ERROR("get block[%d] edid failed: %d\n", block, ret); ++ return ret; ++} ++ ++int cdns_hdmi_scdc_read(struct cdns_mhdp_device *mhdp, u8 addr, u8 *data) ++{ ++ u8 msg[4], reg[6]; ++ int ret; ++ ++ msg[0] = 0x54; ++ msg[1] = addr; ++ msg[2] = 0; ++ msg[3] = 1; ++ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_HDMI_TX, HDMI_TX_READ, ++ sizeof(msg), msg); ++ if (ret) ++ goto err_scdc_read; ++ ++ ret = cdns_mhdp_mailbox_validate_receive(mhdp, MB_MODULE_ID_HDMI_TX, ++ HDMI_TX_READ, sizeof(reg)); ++ if (ret) ++ goto err_scdc_read; ++ ++ ret = cdns_mhdp_mailbox_read_receive(mhdp, reg, sizeof(reg)); ++ if (ret) ++ goto err_scdc_read; ++ ++ *data = reg[5]; ++ ++err_scdc_read: ++ if (ret) ++ DRM_ERROR("scdc read failed: %d\n", ret); ++ return ret; ++} ++ ++int cdns_hdmi_scdc_write(struct cdns_mhdp_device *mhdp, u8 addr, u8 value) ++{ ++ u8 msg[5], reg[5]; ++ int ret; ++ ++ msg[0] = 0x54; ++ msg[1] = addr; ++ msg[2] = 0; ++ msg[3] = 1; ++ msg[4] = value; ++ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_HDMI_TX, HDMI_TX_WRITE, ++ sizeof(msg), msg); ++ if (ret) ++ goto err_scdc_write; ++ ++ ret = cdns_mhdp_mailbox_validate_receive(mhdp, MB_MODULE_ID_HDMI_TX, ++ HDMI_TX_WRITE, sizeof(reg)); ++ if (ret) ++ goto err_scdc_write; ++ ++ ret = cdns_mhdp_mailbox_read_receive(mhdp, reg, sizeof(reg)); ++ if (ret) ++ goto err_scdc_write; ++ ++ if (reg[0] != 0) ++ ret = -EINVAL; ++ ++err_scdc_write: ++ if (ret) ++ DRM_ERROR("scdc write failed: %d\n", ret); ++ return ret; ++} ++ ++int cdns_hdmi_ctrl_init(struct cdns_mhdp_device *mhdp, ++ int protocol, ++ u32 char_rate) ++{ ++ u32 reg0; ++ u32 reg1; ++ u32 val; ++ int ret; ++ ++ /* Set PHY to HDMI data */ ++ ret = cdns_mhdp_reg_write(mhdp, PHY_DATA_SEL, F_SOURCE_PHY_MHDP_SEL(1)); ++ if (ret < 0) ++ return ret; ++ ++ ret = cdns_mhdp_reg_write(mhdp, HDTX_HPD, ++ F_HPD_VALID_WIDTH(4) | F_HPD_GLITCH_WIDTH(0)); ++ if (ret < 0) ++ return ret; ++ ++ /* open CARS */ ++ ret = cdns_mhdp_reg_write(mhdp, SOURCE_PHY_CAR, 0xF); ++ if (ret < 0) ++ return ret; ++ ret = cdns_mhdp_reg_write(mhdp, SOURCE_HDTX_CAR, 0xFF); ++ if (ret < 0) ++ return ret; ++ ret = cdns_mhdp_reg_write(mhdp, SOURCE_PKT_CAR, 0xF); ++ if (ret < 0) ++ return ret; ++ ret = cdns_mhdp_reg_write(mhdp, SOURCE_AIF_CAR, 0xF); ++ if (ret < 0) ++ return ret; ++ ret = cdns_mhdp_reg_write(mhdp, SOURCE_CIPHER_CAR, 0xF); ++ if (ret < 0) ++ return ret; ++ ret = cdns_mhdp_reg_write(mhdp, SOURCE_CRYPTO_CAR, 0xF); ++ if (ret < 0) ++ return ret; ++ ret = cdns_mhdp_reg_write(mhdp, SOURCE_CEC_CAR, 3); ++ if (ret < 0) ++ return ret; ++ ++ reg0 = reg1 = 0x7c1f; ++ if (protocol == MODE_HDMI_2_0 && char_rate >= 340000) { ++ reg0 = 0; ++ reg1 = 0xFFFFF; ++ } ++ ret = cdns_mhdp_reg_write(mhdp, HDTX_CLOCK_REG_0, reg0); ++ if (ret < 0) ++ return ret; ++ ret = cdns_mhdp_reg_write(mhdp, HDTX_CLOCK_REG_1, reg1); ++ if (ret < 0) ++ return ret; ++ ++ /* set hdmi mode and preemble mode data enable */ ++ val = F_HDMI_MODE(protocol) | F_HDMI2_PREAMBLE_EN(1) | F_DATA_EN(1) | ++ F_HDMI2_CTRL_IL_MODE(1) | F_BCH_EN(1) | F_PIC_3D(0XF); ++ ret = cdns_mhdp_reg_write(mhdp, HDTX_CONTROLLER, val); ++ ++ return ret; ++} ++ ++int cdns_hdmi_mode_config(struct cdns_mhdp_device *mhdp, ++ struct drm_display_mode *mode, ++ struct video_info *video_info) ++{ ++ int ret; ++ u32 val; ++ u32 vsync_lines = mode->vsync_end - mode->vsync_start; ++ u32 eof_lines = mode->vsync_start - mode->vdisplay; ++ u32 sof_lines = mode->vtotal - mode->vsync_end; ++ u32 hblank = mode->htotal - mode->hdisplay; ++ u32 hactive = mode->hdisplay; ++ u32 vblank = mode->vtotal - mode->vdisplay; ++ u32 vactive = mode->vdisplay; ++ u32 hfront = mode->hsync_start - mode->hdisplay; ++ u32 hback = mode->htotal - mode->hsync_end; ++ u32 vfront = eof_lines; ++ u32 hsync = hblank - hfront - hback; ++ u32 vsync = vsync_lines; ++ u32 vback = sof_lines; ++ u32 v_h_polarity = ((mode->flags & DRM_MODE_FLAG_NHSYNC) ? 0 : 1) + ++ ((mode->flags & DRM_MODE_FLAG_NVSYNC) ? 0 : 2); ++ ++ ret = cdns_mhdp_reg_write(mhdp, SCHEDULER_H_SIZE, (hactive << 16) + hblank); ++ if (ret < 0) ++ return ret; ++ ++ ret = cdns_mhdp_reg_write(mhdp, SCHEDULER_V_SIZE, (vactive << 16) + vblank); ++ if (ret < 0) ++ return ret; ++ ++ ret = cdns_mhdp_reg_write(mhdp, HDTX_SIGNAL_FRONT_WIDTH, (vfront << 16) + hfront); ++ if (ret < 0) ++ return ret; ++ ++ ret = cdns_mhdp_reg_write(mhdp, HDTX_SIGNAL_SYNC_WIDTH, (vsync << 16) + hsync); ++ if (ret < 0) ++ return ret; ++ ++ ret = cdns_mhdp_reg_write(mhdp, HDTX_SIGNAL_BACK_WIDTH, (vback << 16) + hback); ++ if (ret < 0) ++ return ret; ++ ++ ret = cdns_mhdp_reg_write(mhdp, HSYNC2VSYNC_POL_CTRL, v_h_polarity); ++ if (ret < 0) ++ return ret; ++ ++ /* Reset Data Enable */ ++ val = cdns_mhdp_reg_read(mhdp, HDTX_CONTROLLER); ++ val &= ~F_DATA_EN(1); ++ ret = cdns_mhdp_reg_write(mhdp, HDTX_CONTROLLER, val); ++ if (ret < 0) ++ return ret; ++ ++ /* Set bpc */ ++ val &= ~F_VIF_DATA_WIDTH(3); ++ switch (video_info->color_depth) { ++ case 10: ++ val |= F_VIF_DATA_WIDTH(1); ++ break; ++ case 12: ++ val |= F_VIF_DATA_WIDTH(2); ++ break; ++ case 16: ++ val |= F_VIF_DATA_WIDTH(3); ++ break; ++ case 8: ++ default: ++ val |= F_VIF_DATA_WIDTH(0); ++ break; ++ } ++ ++ /* select color encoding */ ++ val &= ~F_HDMI_ENCODING(3); ++ switch (video_info->color_fmt) { ++ case YCBCR_4_4_4: ++ val |= F_HDMI_ENCODING(2); ++ break; ++ case YCBCR_4_2_2: ++ val |= F_HDMI_ENCODING(1); ++ break; ++ case YCBCR_4_2_0: ++ val |= F_HDMI_ENCODING(3); ++ break; ++ case PXL_RGB: ++ default: ++ val |= F_HDMI_ENCODING(0); ++ break; ++ } ++ ++ ret = cdns_mhdp_reg_write(mhdp, HDTX_CONTROLLER, val); ++ if (ret < 0) ++ return ret; ++ ++ /* set data enable */ ++ val |= F_DATA_EN(1); ++ ret = cdns_mhdp_reg_write(mhdp, HDTX_CONTROLLER, val); ++ ++ return ret; ++} ++ ++int cdns_hdmi_disable_gcp(struct cdns_mhdp_device *mhdp) ++{ ++ u32 val; ++ ++ val = cdns_mhdp_reg_read(mhdp, HDTX_CONTROLLER); ++ val &= ~F_GCP_EN(1); ++ ++ return cdns_mhdp_reg_write(mhdp, HDTX_CONTROLLER, val); ++} ++ ++int cdns_hdmi_enable_gcp(struct cdns_mhdp_device *mhdp) ++{ ++ u32 val; ++ ++ val = cdns_mhdp_reg_read(mhdp, HDTX_CONTROLLER); ++ val |= F_GCP_EN(1); ++ ++ return cdns_mhdp_reg_write(mhdp, HDTX_CONTROLLER, val); ++} +diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp.h b/drivers/gpu/drm/bridge/cadence/cdns-mhdp.h +new file mode 100644 +index 000000000000..399c3f6f86ad +--- /dev/null ++++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp.h +@@ -0,0 +1,209 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Cadence MHDP DP MST bridge driver. ++ * ++ * Copyright: 2018 Cadence Design Systems, Inc. ++ * ++ * Author: Quentin Schulz ++ */ ++ ++ ++#ifndef CDNS_MHDP_H ++#define CDNS_MHDP_H ++ ++#include ++ ++#define CDNS_APB_CFG 0x00000 ++#define CDNS_APB_CTRL (CDNS_APB_CFG + 0x00) ++#define CDNS_MAILBOX_FULL (CDNS_APB_CFG + 0x08) ++#define CDNS_MAILBOX_EMPTY (CDNS_APB_CFG + 0x0c) ++#define CDNS_MAILBOX_TX_DATA (CDNS_APB_CFG + 0x10) ++#define CDNS_MAILBOX_RX_DATA (CDNS_APB_CFG + 0x14) ++#define CDNS_KEEP_ALIVE (CDNS_APB_CFG + 0x18) ++#define CDNS_KEEP_ALIVE_MASK GENMASK(7, 0) ++ ++#define CDNS_MB_INT_MASK (CDNS_APB_CFG + 0x34) ++ ++#define CDNS_SW_CLK_L (CDNS_APB_CFG + 0x3c) ++#define CDNS_SW_CLK_H (CDNS_APB_CFG + 0x40) ++#define CDNS_SW_EVENT0 (CDNS_APB_CFG + 0x44) ++#define CDNS_DPTX_HPD BIT(0) ++ ++#define CDNS_SW_EVENT1 (CDNS_APB_CFG + 0x48) ++#define CDNS_SW_EVENT2 (CDNS_APB_CFG + 0x4c) ++#define CDNS_SW_EVENT3 (CDNS_APB_CFG + 0x50) ++ ++#define CDNS_APB_INT_MASK (CDNS_APB_CFG + 0x6C) ++#define CDNS_APB_INT_MASK_MAILBOX_INT BIT(0) ++#define CDNS_APB_INT_MASK_SW_EVENT_INT BIT(1) ++ ++#define CDNS_DPTX_CAR (CDNS_APB_CFG + 0x904) ++#define CDNS_VIF_CLK_EN BIT(0) ++#define CDNS_VIF_CLK_RSTN BIT(1) ++ ++#define CDNS_SOURCE_VIDEO_IF(s) (0x00b00 + (s * 0x20)) ++#define CDNS_BND_HSYNC2VSYNC(s) (CDNS_SOURCE_VIDEO_IF(s) + \ ++ 0x00) ++#define CDNS_IP_DTCT_WIN GENMASK(11, 0) ++#define CDNS_IP_DET_INTERLACE_FORMAT BIT(12) ++#define CDNS_IP_BYPASS_V_INTERFACE BIT(13) ++ ++#define CDNS_HSYNC2VSYNC_POL_CTRL(s) (CDNS_SOURCE_VIDEO_IF(s) + \ ++ 0x10) ++#define CDNS_H2V_HSYNC_POL_ACTIVE_LOW BIT(1) ++#define CDNS_H2V_VSYNC_POL_ACTIVE_LOW BIT(2) ++ ++#define CDNS_DPTX_PHY_CONFIG 0x02000 ++#define CDNS_PHY_TRAINING_EN BIT(0) ++#define CDNS_PHY_TRAINING_TYPE(x) (((x) & GENMASK(3, 0)) << 1) ++#define CDNS_PHY_SCRAMBLER_BYPASS BIT(5) ++#define CDNS_PHY_ENCODER_BYPASS BIT(6) ++#define CDNS_PHY_SKEW_BYPASS BIT(7) ++#define CDNS_PHY_TRAINING_AUTO BIT(8) ++#define CDNS_PHY_LANE0_SKEW(x) (((x) & GENMASK(2, 0)) << 9) ++#define CDNS_PHY_LANE1_SKEW(x) (((x) & GENMASK(2, 0)) << 12) ++#define CDNS_PHY_LANE2_SKEW(x) (((x) & GENMASK(2, 0)) << 15) ++#define CDNS_PHY_LANE3_SKEW(x) (((x) & GENMASK(2, 0)) << 18) ++#define CDNS_PHY_COMMON_CONFIG (CDNS_PHY_LANE1_SKEW(1) | \ ++ CDNS_PHY_LANE2_SKEW(2) | \ ++ CDNS_PHY_LANE3_SKEW(3)) ++#define CDNS_PHY_10BIT_EN BIT(21) ++ ++#define CDNS_DPTX_FRAMER 0x02200 ++#define CDNS_DP_FRAMER_GLOBAL_CONFIG (CDNS_DPTX_FRAMER + 0x00) ++#define CDNS_DP_NUM_LANES(x) (x - 1) ++#define CDNS_DP_MST_EN BIT(2) ++#define CDNS_DP_FRAMER_EN BIT(3) ++#define CDNS_DP_RATE_GOVERNOR_EN BIT(4) ++#define CDNS_DP_NO_VIDEO_MODE BIT(5) ++#define CDNS_DP_DISABLE_PHY_RST BIT(6) ++#define CDNS_DP_WR_FAILING_EDGE_VSYNC BIT(7) ++ ++#define CDNS_DP_SW_RESET (CDNS_DPTX_FRAMER + 0x04) ++#define CDNS_DP_FRAMER_TU (CDNS_DPTX_FRAMER + 0x08) ++#define CDNS_DP_FRAMER_TU_SIZE(x) (((x) & GENMASK(6, 0)) << 8) ++#define CDNS_DP_FRAMER_TU_VS(x) ((x) & GENMASK(5, 0)) ++#define CDNS_DP_FRAMER_TU_CNT_RST_EN BIT(15) ++ ++#define CDNS_DPTX_STREAM(s) (0x03000 + s * 0x80) ++#define CDNS_DP_MSA_HORIZONTAL_0(s) (CDNS_DPTX_STREAM(s) + 0x00) ++#define CDNS_DP_MSAH0_H_TOTAL(x) (x) ++#define CDNS_DP_MSAH0_HSYNC_START(x) ((x) << 16) ++ ++#define CDNS_DP_MSA_HORIZONTAL_1(s) (CDNS_DPTX_STREAM(s) + 0x04) ++#define CDNS_DP_MSAH1_HSYNC_WIDTH(x) (x) ++#define CDNS_DP_MSAH1_HSYNC_POL_LOW BIT(15) ++#define CDNS_DP_MSAH1_HDISP_WIDTH(x) ((x) << 16) ++ ++#define CDNS_DP_MSA_VERTICAL_0(s) (CDNS_DPTX_STREAM(s) + 0x08) ++#define CDNS_DP_MSAV0_V_TOTAL(x) (x) ++#define CDNS_DP_MSAV0_VSYNC_START(x) ((x) << 16) ++ ++#define CDNS_DP_MSA_VERTICAL_1(s) (CDNS_DPTX_STREAM(s) + 0x0c) ++#define CDNS_DP_MSAV1_VSYNC_WIDTH(x) (x) ++#define CDNS_DP_MSAV1_VSYNC_POL_LOW BIT(15) ++#define CDNS_DP_MSAV1_VDISP_WIDTH(x) ((x) << 16) ++ ++#define CDNS_DP_MSA_MISC(s) (CDNS_DPTX_STREAM(s) + 0x10) ++#define CDNS_DP_STREAM_CONFIGs(s) (CDNS_DPTX_STREAM(s) + 0x14) ++#define CDNS_DP_STREAM_CONFIG_2(s) (CDNS_DPTX_STREAM(s) + 0x2c) ++#define CDNS_DP_SC2_TU_VS_DIFF(x) ((x) << 8) ++ ++#define CDNS_DP_HORIZONTAL(s) (CDNS_DPTX_STREAM(s) + 0x30) ++#define CDNS_DP_H_HSYNC_WIDTH(x) (x) ++#define CDNS_DP_H_H_TOTAL(x) ((x) << 16) ++ ++#define CDNS_DP_VERTICAL_0(s) (CDNS_DPTX_STREAM(s) + 0x34) ++#define CDNS_DP_V0_VHEIGHT(x) (x) ++#define CDNS_DP_V0_VSTART(x) ((x) << 16) ++ ++#define CDNS_DP_VERTICAL_1(s) (CDNS_DPTX_STREAM(s) + 0x38) ++#define CDNS_DP_V1_VTOTAL(x) (x) ++#define CDNS_DP_V1_VTOTAL_EVEN BIT(16) ++ ++#define CDNS_DP_FRAMER_PXL_REPR(s) (CDNS_DPTX_STREAM(s) + 0x4c) ++#define CDNS_DP_FRAMER_6_BPC BIT(0) ++#define CDNS_DP_FRAMER_8_BPC BIT(1) ++#define CDNS_DP_FRAMER_10_BPC BIT(2) ++#define CDNS_DP_FRAMER_12_BPC BIT(3) ++#define CDNS_DP_FRAMER_16_BPC BIT(4) ++#define CDNS_DP_FRAMER_PXL_FORMAT 0x8 ++#define CDNS_DP_FRAMER_RGB BIT(0) ++#define CDNS_DP_FRAMER_YCBCR444 BIT(1) ++#define CDNS_DP_FRAMER_YCBCR422 BIT(2) ++#define CDNS_DP_FRAMER_YCBCR420 BIT(3) ++#define CDNS_DP_FRAMER_Y_ONLY BIT(4) ++ ++#define CDNS_DP_FRAMER_SP(s) (CDNS_DPTX_STREAM(s) + 0x10) ++#define CDNS_DP_FRAMER_VSYNC_POL_LOW BIT(0) ++#define CDNS_DP_FRAMER_HSYNC_POL_LOW BIT(1) ++#define CDNS_DP_FRAMER_INTERLACE BIT(2) ++ ++#define CDNS_DP_LINE_THRESH(s) (CDNS_DPTX_STREAM(s) + 0x64) ++#define CDNS_DP_ACTIVE_LINE_THRESH(x) (x) ++ ++#define CDNS_DP_VB_ID(s) (CDNS_DPTX_STREAM(s) + 0x68) ++#define CDNS_DP_VB_ID_INTERLACED BIT(2) ++#define CDNS_DP_VB_ID_COMPRESSED BIT(6) ++ ++#define CDNS_DP_FRONT_BACK_PORCH(s) (CDNS_DPTX_STREAM(s) + 0x78) ++#define CDNS_DP_BACK_PORCH(x) (x) ++#define CDNS_DP_FRONT_PORCH(x) ((x) << 16) ++ ++#define CDNS_DP_BYTE_COUNT(s) (CDNS_DPTX_STREAM(s) + 0x7c) ++#define CDNS_DP_BYTE_COUNT_BYTES_IN_CHUNK_SHIFT 16 ++ ++#define CDNS_DP_MST_STREAM_CONFIG(s) (CDNS_DPTX_STREAM(s) + 0x14) ++#define CDNS_DP_MST_STRM_CFG_STREAM_EN BIT(0) ++#define CDNS_DP_MST_STRM_CFG_NO_VIDEO BIT(1) ++ ++#define CDNS_DP_MST_SLOT_ALLOCATE(s) (CDNS_DPTX_STREAM(s) + 0x44) ++#define CDNS_DP_S_ALLOC_START_SLOT(x) (x) ++#define CDNS_DP_S_ALLOC_END_SLOT(x) ((x) << 8) ++ ++#define CDNS_DP_RATE_GOVERNING(s) (CDNS_DPTX_STREAM(s) + 0x48) ++#define CDNS_DP_RG_TARG_AV_SLOTS_Y(x) (x) ++#define CDNS_DP_RG_TARG_AV_SLOTS_X(x) (x << 4) ++#define CDNS_DP_RG_ENABLE BIT(10) ++ ++#define CDNS_DP_MTPH_CONTROL 0x2264 ++#define CDNS_DP_MTPH_ECF_EN BIT(0) ++#define CDNS_DP_MTPH_ACT_EN BIT(1) ++#define CDNS_DP_MTPH_LVP_EN BIT(2) ++ ++#define CDNS_DP_MTPH_STATUS 0x226C ++#define CDNS_DP_MTPH_ACT_STATUS BIT(0) ++ ++ ++#define CDNS_DPTX_GLOBAL 0x02300 ++#define CDNS_DP_LANE_EN (CDNS_DPTX_GLOBAL + 0x00) ++#define CDNS_DP_LANE_EN_LANES(x) GENMASK(x - 1, 0) ++#define CDNS_DP_ENHNCD (CDNS_DPTX_GLOBAL + 0x04) ++ ++ ++#define to_mhdp_connector(x) container_of(x, struct cdns_mhdp_connector, base) ++#define to_mhdp_bridge(x) container_of(x, struct cdns_mhdp_bridge, base) ++#define mgr_to_mhdp(x) container_of(x, struct cdns_mhdp_device, mst_mgr) ++ ++#define CDNS_MHDP_MAX_STREAMS 4 ++ ++enum pixel_format { ++ PIXEL_FORMAT_RGB = 1, ++ PIXEL_FORMAT_YCBCR_444 = 2, ++ PIXEL_FORMAT_YCBCR_422 = 4, ++ PIXEL_FORMAT_YCBCR_420 = 8, ++ PIXEL_FORMAT_Y_ONLY = 16, ++}; ++ ++ ++int cdns_mhdp_mst_init(struct cdns_mhdp_device *mhdp); ++void cdns_mhdp_mst_deinit(struct cdns_mhdp_device *mhdp); ++bool cdns_mhdp_mst_probe(struct cdns_mhdp_device *mhdp); ++enum pixel_format cdns_mhdp_get_pxlfmt(u32 color_formats); ++u32 cdns_mhdp_get_bpp(u32 bpc, u32 color_formats); ++void cdns_mhdp_configure_video(struct drm_bridge *bridge); ++void cdns_mhdp_mst_enable(struct drm_bridge *bridge); ++void cdns_mhdp_mst_disable(struct drm_bridge *bridge); ++void cdns_mhdp_enable(struct drm_bridge *bridge); ++ ++#endif +diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile +index 17a9e7eb2130..bd013659404f 100644 +--- a/drivers/gpu/drm/rockchip/Makefile ++++ b/drivers/gpu/drm/rockchip/Makefile +@@ -8,7 +8,7 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \ + rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o + + rockchipdrm-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o +-rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o ++rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o + rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o + rockchipdrm-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi-rockchip.o + rockchipdrm-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o +diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c +index a4a45daf93f2..058bc372f02b 100644 +--- a/drivers/gpu/drm/rockchip/cdn-dp-core.c ++++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c +@@ -23,11 +23,10 @@ + #include + + #include "cdn-dp-core.h" +-#include "cdn-dp-reg.h" + #include "rockchip_drm_vop.h" + + #define connector_to_dp(c) \ +- container_of(c, struct cdn_dp_device, connector) ++ container_of(c, struct cdn_dp_device, mhdp.connector.base) + + #define encoder_to_dp(c) \ + container_of(c, struct cdn_dp_device, encoder) +@@ -62,17 +61,18 @@ MODULE_DEVICE_TABLE(of, cdn_dp_dt_ids); + static int cdn_dp_grf_write(struct cdn_dp_device *dp, + unsigned int reg, unsigned int val) + { ++ struct device *dev = dp->mhdp.dev; + int ret; + + ret = clk_prepare_enable(dp->grf_clk); + if (ret) { +- DRM_DEV_ERROR(dp->dev, "Failed to prepare_enable grf clock\n"); ++ DRM_DEV_ERROR(dev, "Failed to prepare_enable grf clock\n"); + return ret; + } + + ret = regmap_write(dp->grf, reg, val); + if (ret) { +- DRM_DEV_ERROR(dp->dev, "Could not write to GRF: %d\n", ret); ++ DRM_DEV_ERROR(dev, "Could not write to GRF: %d\n", ret); + return ret; + } + +@@ -83,24 +83,25 @@ static int cdn_dp_grf_write(struct cdn_dp_device *dp, + + static int cdn_dp_clk_enable(struct cdn_dp_device *dp) + { ++ struct device *dev = dp->mhdp.dev; + int ret; + unsigned long rate; + + ret = clk_prepare_enable(dp->pclk); + if (ret < 0) { +- DRM_DEV_ERROR(dp->dev, "cannot enable dp pclk %d\n", ret); ++ DRM_DEV_ERROR(dev, "cannot enable dp pclk %d\n", ret); + goto err_pclk; + } + + ret = clk_prepare_enable(dp->core_clk); + if (ret < 0) { +- DRM_DEV_ERROR(dp->dev, "cannot enable core_clk %d\n", ret); ++ DRM_DEV_ERROR(dev, "cannot enable core_clk %d\n", ret); + goto err_core_clk; + } + +- ret = pm_runtime_get_sync(dp->dev); ++ ret = pm_runtime_get_sync(dev); + if (ret < 0) { +- DRM_DEV_ERROR(dp->dev, "cannot get pm runtime %d\n", ret); ++ DRM_DEV_ERROR(dev, "cannot get pm runtime %d\n", ret); + goto err_pm_runtime_get; + } + +@@ -113,18 +114,18 @@ static int cdn_dp_clk_enable(struct cdn_dp_device *dp) + + rate = clk_get_rate(dp->core_clk); + if (!rate) { +- DRM_DEV_ERROR(dp->dev, "get clk rate failed\n"); ++ DRM_DEV_ERROR(dev, "get clk rate failed\n"); + ret = -EINVAL; + goto err_set_rate; + } + +- cdn_dp_set_fw_clk(dp, rate); +- cdn_dp_clock_reset(dp); ++ cdns_mhdp_set_fw_clk(&dp->mhdp, rate); ++ cdns_mhdp_clock_reset(&dp->mhdp); + + return 0; + + err_set_rate: +- pm_runtime_put(dp->dev); ++ pm_runtime_put(dev); + err_pm_runtime_get: + clk_disable_unprepare(dp->core_clk); + err_core_clk: +@@ -135,7 +136,7 @@ static int cdn_dp_clk_enable(struct cdn_dp_device *dp) + + static void cdn_dp_clk_disable(struct cdn_dp_device *dp) + { +- pm_runtime_put_sync(dp->dev); ++ pm_runtime_put_sync(dp->mhdp.dev); + clk_disable_unprepare(dp->pclk); + clk_disable_unprepare(dp->core_clk); + } +@@ -168,7 +169,7 @@ static int cdn_dp_get_sink_count(struct cdn_dp_device *dp, u8 *sink_count) + u8 value; + + *sink_count = 0; +- ret = cdn_dp_dpcd_read(dp, DP_SINK_COUNT, &value, 1); ++ ret = drm_dp_dpcd_read(&dp->mhdp.dp.aux, DP_SINK_COUNT, &value, 1); + if (ret) + return ret; + +@@ -192,12 +193,13 @@ static struct cdn_dp_port *cdn_dp_connected_port(struct cdn_dp_device *dp) + + static bool cdn_dp_check_sink_connection(struct cdn_dp_device *dp) + { ++ struct device *dev = dp->mhdp.dev; + unsigned long timeout = jiffies + msecs_to_jiffies(CDN_DPCD_TIMEOUT_MS); + struct cdn_dp_port *port; + u8 sink_count = 0; + + if (dp->active_port < 0 || dp->active_port >= dp->ports) { +- DRM_DEV_ERROR(dp->dev, "active_port is wrong!\n"); ++ DRM_DEV_ERROR(dev, "active_port is wrong!\n"); + return false; + } + +@@ -219,7 +221,7 @@ static bool cdn_dp_check_sink_connection(struct cdn_dp_device *dp) + usleep_range(5000, 10000); + } + +- DRM_DEV_ERROR(dp->dev, "Get sink capability timed out\n"); ++ DRM_DEV_ERROR(dev, "Get sink capability timed out\n"); + return false; + } + +@@ -261,7 +263,8 @@ static int cdn_dp_connector_get_modes(struct drm_connector *connector) + mutex_lock(&dp->lock); + edid = dp->edid; + if (edid) { +- DRM_DEV_DEBUG_KMS(dp->dev, "got edid: width[%d] x height[%d]\n", ++ DRM_DEV_DEBUG_KMS(dp->mhdp.dev, ++ "got edid: width[%d] x height[%d]\n", + edid->width_cm, edid->height_cm); + + dp->sink_has_audio = drm_detect_monitor_audio(edid); +@@ -279,7 +282,8 @@ static int cdn_dp_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) + { + struct cdn_dp_device *dp = connector_to_dp(connector); +- struct drm_display_info *display_info = &dp->connector.display_info; ++ struct drm_display_info *display_info = ++ &dp->mhdp.connector.base.display_info; + u32 requested, actual, rate, sink_max, source_max = 0; + u8 lanes, bpc; + +@@ -302,11 +306,11 @@ static int cdn_dp_connector_mode_valid(struct drm_connector *connector, + requested = mode->clock * bpc * 3 / 1000; + + source_max = dp->lanes; +- sink_max = drm_dp_max_lane_count(dp->dpcd); ++ sink_max = drm_dp_max_lane_count(dp->mhdp.dp.dpcd); + lanes = min(source_max, sink_max); + +- source_max = drm_dp_bw_code_to_link_rate(CDN_DP_MAX_LINK_RATE); +- sink_max = drm_dp_max_link_rate(dp->dpcd); ++ source_max = CDNS_DP_MAX_LINK_RATE; ++ sink_max = drm_dp_max_link_rate(dp->mhdp.dp.dpcd); + rate = min(source_max, sink_max); + + actual = rate * lanes / 100; +@@ -315,7 +319,7 @@ static int cdn_dp_connector_mode_valid(struct drm_connector *connector, + actual = actual * 8 / 10; + + if (requested > actual) { +- DRM_DEV_DEBUG_KMS(dp->dev, ++ DRM_DEV_DEBUG_KMS(dp->mhdp.dev, + "requested=%d, actual=%d, clock=%d\n", + requested, actual, mode->clock); + return MODE_CLOCK_HIGH; +@@ -335,59 +339,62 @@ static int cdn_dp_firmware_init(struct cdn_dp_device *dp) + const u32 *iram_data, *dram_data; + const struct firmware *fw = dp->fw; + const struct cdn_firmware_header *hdr; ++ struct device *dev = dp->mhdp.dev; + + hdr = (struct cdn_firmware_header *)fw->data; + if (fw->size != le32_to_cpu(hdr->size_bytes)) { +- DRM_DEV_ERROR(dp->dev, "firmware is invalid\n"); ++ DRM_DEV_ERROR(dev, "firmware is invalid\n"); + return -EINVAL; + } + + iram_data = (const u32 *)(fw->data + hdr->header_size); + dram_data = (const u32 *)(fw->data + hdr->header_size + hdr->iram_size); + +- ret = cdn_dp_load_firmware(dp, iram_data, hdr->iram_size, +- dram_data, hdr->dram_size); ++ ret = cdns_mhdp_load_firmware(&dp->mhdp, iram_data, hdr->iram_size, ++ dram_data, hdr->dram_size); + if (ret) + return ret; + +- ret = cdn_dp_set_firmware_active(dp, true); ++ ret = cdns_mhdp_set_firmware_active(&dp->mhdp, true); + if (ret) { +- DRM_DEV_ERROR(dp->dev, "active ucpu failed: %d\n", ret); ++ DRM_DEV_ERROR(dev, "active ucpu failed: %d\n", ret); + return ret; + } + +- return cdn_dp_event_config(dp); ++ return cdns_mhdp_event_config(&dp->mhdp); + } + + static int cdn_dp_get_sink_capability(struct cdn_dp_device *dp) + { ++ struct cdns_mhdp_device *mhdp = &dp->mhdp; + int ret; + + if (!cdn_dp_check_sink_connection(dp)) + return -ENODEV; + +- ret = cdn_dp_dpcd_read(dp, DP_DPCD_REV, dp->dpcd, ++ ret = drm_dp_dpcd_read(&mhdp->dp.aux, DP_DPCD_REV, mhdp->dp.dpcd, + DP_RECEIVER_CAP_SIZE); + if (ret) { +- DRM_DEV_ERROR(dp->dev, "Failed to get caps %d\n", ret); ++ DRM_DEV_ERROR(mhdp->dev, "Failed to get caps %d\n", ret); + return ret; + } + + kfree(dp->edid); +- dp->edid = drm_do_get_edid(&dp->connector, +- cdn_dp_get_edid_block, dp); ++ dp->edid = drm_do_get_edid(&mhdp->connector.base, ++ cdns_mhdp_get_edid_block, mhdp); + return 0; + } + + static int cdn_dp_enable_phy(struct cdn_dp_device *dp, struct cdn_dp_port *port) + { ++ struct device *dev = dp->mhdp.dev; + union extcon_property_value property; + int ret; + + if (!port->phy_enabled) { + ret = phy_power_on(port->phy); + if (ret) { +- DRM_DEV_ERROR(dp->dev, "phy power on failed: %d\n", ++ DRM_DEV_ERROR(dev, "phy power on failed: %d\n", + ret); + goto err_phy; + } +@@ -397,28 +404,28 @@ static int cdn_dp_enable_phy(struct cdn_dp_device *dp, struct cdn_dp_port *port) + ret = cdn_dp_grf_write(dp, GRF_SOC_CON26, + DPTX_HPD_SEL_MASK | DPTX_HPD_SEL); + if (ret) { +- DRM_DEV_ERROR(dp->dev, "Failed to write HPD_SEL %d\n", ret); ++ DRM_DEV_ERROR(dev, "Failed to write HPD_SEL %d\n", ret); + goto err_power_on; + } + +- ret = cdn_dp_get_hpd_status(dp); ++ ret = cdns_mhdp_read_hpd(&dp->mhdp); + if (ret <= 0) { + if (!ret) +- DRM_DEV_ERROR(dp->dev, "hpd does not exist\n"); ++ DRM_DEV_ERROR(dev, "hpd does not exist\n"); + goto err_power_on; + } + + ret = extcon_get_property(port->extcon, EXTCON_DISP_DP, + EXTCON_PROP_USB_TYPEC_POLARITY, &property); + if (ret) { +- DRM_DEV_ERROR(dp->dev, "get property failed\n"); ++ DRM_DEV_ERROR(dev, "get property failed\n"); + goto err_power_on; + } + + port->lanes = cdn_dp_get_port_lanes(port); +- ret = cdn_dp_set_host_cap(dp, port->lanes, property.intval); ++ ret = cdns_mhdp_set_host_cap(&dp->mhdp, property.intval); + if (ret) { +- DRM_DEV_ERROR(dp->dev, "set host capabilities failed: %d\n", ++ DRM_DEV_ERROR(dev, "set host capabilities failed: %d\n", + ret); + goto err_power_on; + } +@@ -428,7 +435,7 @@ static int cdn_dp_enable_phy(struct cdn_dp_device *dp, struct cdn_dp_port *port) + + err_power_on: + if (phy_power_off(port->phy)) +- DRM_DEV_ERROR(dp->dev, "phy power off failed: %d", ret); ++ DRM_DEV_ERROR(dev, "phy power off failed: %d", ret); + else + port->phy_enabled = false; + +@@ -446,7 +453,8 @@ static int cdn_dp_disable_phy(struct cdn_dp_device *dp, + if (port->phy_enabled) { + ret = phy_power_off(port->phy); + if (ret) { +- DRM_DEV_ERROR(dp->dev, "phy power off failed: %d", ret); ++ DRM_DEV_ERROR(dp->mhdp.dev, ++ "phy power off failed: %d", ret); + return ret; + } + } +@@ -470,16 +478,16 @@ static int cdn_dp_disable(struct cdn_dp_device *dp) + ret = cdn_dp_grf_write(dp, GRF_SOC_CON26, + DPTX_HPD_SEL_MASK | DPTX_HPD_DEL); + if (ret) { +- DRM_DEV_ERROR(dp->dev, "Failed to clear hpd sel %d\n", ++ DRM_DEV_ERROR(dp->mhdp.dev, "Failed to clear hpd sel %d\n", + ret); + return ret; + } + +- cdn_dp_set_firmware_active(dp, false); ++ cdns_mhdp_set_firmware_active(&dp->mhdp, false); + cdn_dp_clk_disable(dp); + dp->active = false; +- dp->max_lanes = 0; +- dp->max_rate = 0; ++ dp->mhdp.dp.rate = 0; ++ dp->mhdp.dp.num_lanes = 0; + if (!dp->connected) { + kfree(dp->edid); + dp->edid = NULL; +@@ -492,11 +500,11 @@ static int cdn_dp_enable(struct cdn_dp_device *dp) + { + int ret, i, lanes; + struct cdn_dp_port *port; ++ struct device *dev = dp->mhdp.dev; + + port = cdn_dp_connected_port(dp); + if (!port) { +- DRM_DEV_ERROR(dp->dev, +- "Can't enable without connection\n"); ++ DRM_DEV_ERROR(dev, "Can't enable without connection\n"); + return -ENODEV; + } + +@@ -509,7 +517,7 @@ static int cdn_dp_enable(struct cdn_dp_device *dp) + + ret = cdn_dp_firmware_init(dp); + if (ret) { +- DRM_DEV_ERROR(dp->dev, "firmware init failed: %d", ret); ++ DRM_DEV_ERROR(dp->mhdp.dev, "firmware init failed: %d", ret); + goto err_clk_disable; + } + +@@ -543,8 +551,9 @@ static void cdn_dp_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *adjusted) + { + struct cdn_dp_device *dp = encoder_to_dp(encoder); +- struct drm_display_info *display_info = &dp->connector.display_info; +- struct video_info *video = &dp->video_info; ++ struct drm_display_info *display_info = ++ &dp->mhdp.connector.base.display_info; ++ struct video_info *video = &dp->mhdp.video_info; + + switch (display_info->bpc) { + case 10: +@@ -562,20 +571,20 @@ static void cdn_dp_encoder_mode_set(struct drm_encoder *encoder, + video->v_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NVSYNC); + video->h_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NHSYNC); + +- memcpy(&dp->mode, adjusted, sizeof(*mode)); ++ memcpy(&dp->mhdp.mode, adjusted, sizeof(*mode)); + } + + static bool cdn_dp_check_link_status(struct cdn_dp_device *dp) + { + u8 link_status[DP_LINK_STATUS_SIZE]; + struct cdn_dp_port *port = cdn_dp_connected_port(dp); +- u8 sink_lanes = drm_dp_max_lane_count(dp->dpcd); ++ u8 sink_lanes = drm_dp_max_lane_count(dp->mhdp.dp.dpcd); + +- if (!port || !dp->max_rate || !dp->max_lanes) ++ if (!port || !dp->mhdp.dp.rate || !dp->mhdp.dp.num_lanes) + return false; + +- if (cdn_dp_dpcd_read(dp, DP_LANE0_1_STATUS, link_status, +- DP_LINK_STATUS_SIZE)) { ++ if (drm_dp_dpcd_read(&dp->mhdp.dp.aux, DP_LANE0_1_STATUS, link_status, ++ DP_LINK_STATUS_SIZE)) { + DRM_ERROR("Failed to get link status\n"); + return false; + } +@@ -587,15 +596,16 @@ static bool cdn_dp_check_link_status(struct cdn_dp_device *dp) + static void cdn_dp_encoder_enable(struct drm_encoder *encoder) + { + struct cdn_dp_device *dp = encoder_to_dp(encoder); ++ struct device *dev = dp->mhdp.dev; + int ret, val; + +- ret = drm_of_encoder_active_endpoint_id(dp->dev->of_node, encoder); ++ ret = drm_of_encoder_active_endpoint_id(dev->of_node, encoder); + if (ret < 0) { +- DRM_DEV_ERROR(dp->dev, "Could not get vop id, %d", ret); ++ DRM_DEV_ERROR(dev, "Could not get vop id, %d", ret); + return; + } + +- DRM_DEV_DEBUG_KMS(dp->dev, "vop %s output to cdn-dp\n", ++ DRM_DEV_DEBUG_KMS(dev, "vop %s output to cdn-dp\n", + (ret) ? "LIT" : "BIG"); + if (ret) + val = DP_SEL_VOP_LIT | (DP_SEL_VOP_LIT << 16); +@@ -610,33 +620,33 @@ static void cdn_dp_encoder_enable(struct drm_encoder *encoder) + + ret = cdn_dp_enable(dp); + if (ret) { +- DRM_DEV_ERROR(dp->dev, "Failed to enable encoder %d\n", ++ DRM_DEV_ERROR(dev, "Failed to enable encoder %d\n", + ret); + goto out; + } + if (!cdn_dp_check_link_status(dp)) { +- ret = cdn_dp_train_link(dp); ++ ret = cdns_mhdp_train_link(&dp->mhdp); + if (ret) { +- DRM_DEV_ERROR(dp->dev, "Failed link train %d\n", ret); ++ DRM_DEV_ERROR(dev, "Failed link train %d\n", ret); + goto out; + } + } + +- ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE); ++ ret = cdns_mhdp_set_video_status(&dp->mhdp, CONTROL_VIDEO_IDLE); + if (ret) { +- DRM_DEV_ERROR(dp->dev, "Failed to idle video %d\n", ret); ++ DRM_DEV_ERROR(dev, "Failed to idle video %d\n", ret); + goto out; + } + +- ret = cdn_dp_config_video(dp); ++ ret = cdns_mhdp_config_video(&dp->mhdp); + if (ret) { +- DRM_DEV_ERROR(dp->dev, "Failed to config video %d\n", ret); ++ DRM_DEV_ERROR(dev, "Failed to config video %d\n", ret); + goto out; + } + +- ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID); ++ ret = cdns_mhdp_set_video_status(&dp->mhdp, CONTROL_VIDEO_VALID); + if (ret) { +- DRM_DEV_ERROR(dp->dev, "Failed to valid video %d\n", ret); ++ DRM_DEV_ERROR(dev, "Failed to valid video %d\n", ret); + goto out; + } + out: +@@ -652,7 +662,8 @@ static void cdn_dp_encoder_disable(struct drm_encoder *encoder) + if (dp->active) { + ret = cdn_dp_disable(dp); + if (ret) { +- DRM_DEV_ERROR(dp->dev, "Failed to disable encoder %d\n", ++ DRM_DEV_ERROR(dp->mhdp.dev, ++ "Failed to disable encoder %d\n", + ret); + } + } +@@ -692,7 +703,7 @@ static const struct drm_encoder_helper_funcs cdn_dp_encoder_helper_funcs = { + + static int cdn_dp_parse_dt(struct cdn_dp_device *dp) + { +- struct device *dev = dp->dev; ++ struct device *dev = dp->mhdp.dev; + struct device_node *np = dev->of_node; + struct platform_device *pdev = to_platform_device(dev); + struct resource *res; +@@ -704,10 +715,10 @@ static int cdn_dp_parse_dt(struct cdn_dp_device *dp) + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- dp->regs = devm_ioremap_resource(dev, res); +- if (IS_ERR(dp->regs)) { ++ dp->mhdp.regs_base = devm_ioremap_resource(dev, res); ++ if (IS_ERR(dp->mhdp.regs_base)) { + DRM_DEV_ERROR(dev, "ioremap reg failed\n"); +- return PTR_ERR(dp->regs); ++ return PTR_ERR(dp->mhdp.regs_base); + } + + dp->core_clk = devm_clk_get(dev, "core-clk"); +@@ -722,10 +733,10 @@ static int cdn_dp_parse_dt(struct cdn_dp_device *dp) + return PTR_ERR(dp->pclk); + } + +- dp->spdif_clk = devm_clk_get(dev, "spdif"); +- if (IS_ERR(dp->spdif_clk)) { ++ dp->mhdp.spdif_clk = devm_clk_get(dev, "spdif"); ++ if (IS_ERR(dp->mhdp.spdif_clk)) { + DRM_DEV_ERROR(dev, "cannot get spdif_clk\n"); +- return PTR_ERR(dp->spdif_clk); ++ return PTR_ERR(dp->mhdp.spdif_clk); + } + + dp->grf_clk = devm_clk_get(dev, "grf"); +@@ -734,10 +745,10 @@ static int cdn_dp_parse_dt(struct cdn_dp_device *dp) + return PTR_ERR(dp->grf_clk); + } + +- dp->spdif_rst = devm_reset_control_get(dev, "spdif"); +- if (IS_ERR(dp->spdif_rst)) { ++ dp->mhdp.spdif_rst = devm_reset_control_get(dev, "spdif"); ++ if (IS_ERR(dp->mhdp.spdif_rst)) { + DRM_DEV_ERROR(dev, "no spdif reset control found\n"); +- return PTR_ERR(dp->spdif_rst); ++ return PTR_ERR(dp->mhdp.spdif_rst); + } + + dp->dptx_rst = devm_reset_control_get(dev, "dptx"); +@@ -784,7 +795,7 @@ static int cdn_dp_audio_hw_params(struct device *dev, void *data, + audio.format = AFMT_I2S; + break; + case HDMI_SPDIF: +- audio.format = AFMT_SPDIF; ++ audio.format = AFMT_SPDIF_INT; + break; + default: + DRM_DEV_ERROR(dev, "Invalid format %d\n", daifmt->fmt); +@@ -792,9 +803,9 @@ static int cdn_dp_audio_hw_params(struct device *dev, void *data, + goto out; + } + +- ret = cdn_dp_audio_config(dp, &audio); ++ ret = cdns_mhdp_audio_config(&dp->mhdp, &audio); + if (!ret) +- dp->audio_info = audio; ++ dp->mhdp.audio_info = audio; + + out: + mutex_unlock(&dp->lock); +@@ -810,9 +821,9 @@ static void cdn_dp_audio_shutdown(struct device *dev, void *data) + if (!dp->active) + goto out; + +- ret = cdn_dp_audio_stop(dp, &dp->audio_info); ++ ret = cdns_mhdp_audio_stop(&dp->mhdp, &dp->mhdp.audio_info); + if (!ret) +- dp->audio_info.format = AFMT_UNUSED; ++ dp->mhdp.audio_info.format = AFMT_UNUSED; + out: + mutex_unlock(&dp->lock); + } +@@ -829,7 +840,7 @@ static int cdn_dp_audio_mute_stream(struct device *dev, void *data, + goto out; + } + +- ret = cdn_dp_audio_mute(dp, enable); ++ ret = cdns_mhdp_audio_mute(&dp->mhdp, enable); + + out: + mutex_unlock(&dp->lock); +@@ -841,7 +852,8 @@ static int cdn_dp_audio_get_eld(struct device *dev, void *data, + { + struct cdn_dp_device *dp = dev_get_drvdata(dev); + +- memcpy(buf, dp->connector.eld, min(sizeof(dp->connector.eld), len)); ++ memcpy(buf, dp->mhdp.connector.base.eld, ++ min(sizeof(dp->mhdp.connector.base.eld), len)); + + return 0; + } +@@ -864,11 +876,11 @@ static int cdn_dp_audio_codec_init(struct cdn_dp_device *dp, + .max_i2s_channels = 8, + }; + +- dp->audio_pdev = platform_device_register_data( +- dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_AUTO, +- &codec_data, sizeof(codec_data)); ++ dp->mhdp.audio_pdev = platform_device_register_data( ++ dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_AUTO, ++ &codec_data, sizeof(codec_data)); + +- return PTR_ERR_OR_ZERO(dp->audio_pdev); ++ return PTR_ERR_OR_ZERO(dp->mhdp.audio_pdev); + } + + static int cdn_dp_request_firmware(struct cdn_dp_device *dp) +@@ -876,6 +888,7 @@ static int cdn_dp_request_firmware(struct cdn_dp_device *dp) + int ret; + unsigned long timeout = jiffies + msecs_to_jiffies(CDN_FW_TIMEOUT_MS); + unsigned long sleep = 1000; ++ struct device *dev = dp->mhdp.dev; + + WARN_ON(!mutex_is_locked(&dp->lock)); + +@@ -886,13 +899,13 @@ static int cdn_dp_request_firmware(struct cdn_dp_device *dp) + mutex_unlock(&dp->lock); + + while (time_before(jiffies, timeout)) { +- ret = request_firmware(&dp->fw, CDN_DP_FIRMWARE, dp->dev); ++ ret = request_firmware(&dp->fw, CDN_DP_FIRMWARE, dev); + if (ret == -ENOENT) { + msleep(sleep); + sleep *= 2; + continue; + } else if (ret) { +- DRM_DEV_ERROR(dp->dev, ++ DRM_DEV_ERROR(dev, + "failed to request firmware: %d\n", ret); + goto out; + } +@@ -902,7 +915,7 @@ static int cdn_dp_request_firmware(struct cdn_dp_device *dp) + goto out; + } + +- DRM_DEV_ERROR(dp->dev, "Timed out trying to load firmware\n"); ++ DRM_DEV_ERROR(dev, "Timed out trying to load firmware\n"); + ret = -ETIMEDOUT; + out: + mutex_lock(&dp->lock); +@@ -913,8 +926,9 @@ static void cdn_dp_pd_event_work(struct work_struct *work) + { + struct cdn_dp_device *dp = container_of(work, struct cdn_dp_device, + event_work); +- struct drm_connector *connector = &dp->connector; ++ struct drm_connector *connector = &dp->mhdp.connector.base; + enum drm_connector_status old_status; ++ struct device *dev = dp->mhdp.dev; + + int ret; + +@@ -931,44 +945,45 @@ static void cdn_dp_pd_event_work(struct work_struct *work) + + /* Not connected, notify userspace to disable the block */ + if (!cdn_dp_connected_port(dp)) { +- DRM_DEV_INFO(dp->dev, "Not connected. Disabling cdn\n"); ++ DRM_DEV_INFO(dev, "Not connected. Disabling cdn\n"); + dp->connected = false; + + /* Connected but not enabled, enable the block */ + } else if (!dp->active) { +- DRM_DEV_INFO(dp->dev, "Connected, not enabled. Enabling cdn\n"); ++ DRM_DEV_INFO(dev, "Connected, not enabled. Enabling cdn\n"); + ret = cdn_dp_enable(dp); + if (ret) { +- DRM_DEV_ERROR(dp->dev, "Enable dp failed %d\n", ret); ++ DRM_DEV_ERROR(dev, "Enable dp failed %d\n", ret); + dp->connected = false; + } + + /* Enabled and connected to a dongle without a sink, notify userspace */ + } else if (!cdn_dp_check_sink_connection(dp)) { +- DRM_DEV_INFO(dp->dev, "Connected without sink. Assert hpd\n"); ++ DRM_DEV_INFO(dev, "Connected without sink. Assert hpd\n"); + dp->connected = false; + + /* Enabled and connected with a sink, re-train if requested */ + } else if (!cdn_dp_check_link_status(dp)) { +- unsigned int rate = dp->max_rate; +- unsigned int lanes = dp->max_lanes; +- struct drm_display_mode *mode = &dp->mode; ++ unsigned int rate = dp->mhdp.dp.rate; ++ unsigned int lanes = dp->mhdp.dp.num_lanes; ++ struct drm_display_mode *mode = &dp->mhdp.mode; + +- DRM_DEV_INFO(dp->dev, "Connected with sink. Re-train link\n"); +- ret = cdn_dp_train_link(dp); ++ DRM_DEV_INFO(dev, "Connected with sink. Re-train link\n"); ++ ret = cdns_mhdp_train_link(&dp->mhdp); + if (ret) { + dp->connected = false; +- DRM_DEV_ERROR(dp->dev, "Train link failed %d\n", ret); ++ DRM_DEV_ERROR(dev, "Train link failed %d\n", ret); + goto out; + } + + /* If training result is changed, update the video config */ + if (mode->clock && +- (rate != dp->max_rate || lanes != dp->max_lanes)) { +- ret = cdn_dp_config_video(dp); ++ (rate != dp->mhdp.dp.rate || ++ lanes != dp->mhdp.dp.num_lanes)) { ++ ret = cdns_mhdp_config_video(&dp->mhdp); + if (ret) { + dp->connected = false; +- DRM_DEV_ERROR(dp->dev, ++ DRM_DEV_ERROR(dev, + "Failed to config video %d\n", + ret); + } +@@ -1037,7 +1052,7 @@ static int cdn_dp_bind(struct device *dev, struct device *master, void *data) + + drm_encoder_helper_add(encoder, &cdn_dp_encoder_helper_funcs); + +- connector = &dp->connector; ++ connector = &dp->mhdp.connector.base; + connector->polled = DRM_CONNECTOR_POLL_HPD; + connector->dpms = DRM_MODE_DPMS_OFF; + +@@ -1061,7 +1076,7 @@ static int cdn_dp_bind(struct device *dev, struct device *master, void *data) + port = dp->port[i]; + + port->event_nb.notifier_call = cdn_dp_pd_event; +- ret = devm_extcon_register_notifier(dp->dev, port->extcon, ++ ret = devm_extcon_register_notifier(dp->mhdp.dev, port->extcon, + EXTCON_DISP_DP, + &port->event_nb); + if (ret) { +@@ -1088,7 +1103,7 @@ static void cdn_dp_unbind(struct device *dev, struct device *master, void *data) + { + struct cdn_dp_device *dp = dev_get_drvdata(dev); + struct drm_encoder *encoder = &dp->encoder; +- struct drm_connector *connector = &dp->connector; ++ struct drm_connector *connector = &dp->mhdp.connector.base; + + cancel_work_sync(&dp->event_work); + cdn_dp_encoder_disable(encoder); +@@ -1148,7 +1163,7 @@ static int cdn_dp_probe(struct platform_device *pdev) + dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL); + if (!dp) + return -ENOMEM; +- dp->dev = dev; ++ dp->mhdp.dev = dev; + + match = of_match_node(cdn_dp_dt_ids, pdev->dev.of_node); + dp_data = (struct cdn_dp_data *)match->data; +@@ -1192,8 +1207,8 @@ static int cdn_dp_remove(struct platform_device *pdev) + { + struct cdn_dp_device *dp = platform_get_drvdata(pdev); + +- platform_device_unregister(dp->audio_pdev); +- cdn_dp_suspend(dp->dev); ++ platform_device_unregister(dp->mhdp.audio_pdev); ++ cdn_dp_suspend(dp->mhdp.dev); + component_del(&pdev->dev, &cdn_dp_component_ops); + + return 0; +@@ -1203,7 +1218,7 @@ static void cdn_dp_shutdown(struct platform_device *pdev) + { + struct cdn_dp_device *dp = platform_get_drvdata(pdev); + +- cdn_dp_suspend(dp->dev); ++ cdn_dp_suspend(dp->mhdp.dev); + } + + static const struct dev_pm_ops cdn_dp_pm_ops = { +diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h +index 81ac9b658a70..8b1b15b92503 100644 +--- a/drivers/gpu/drm/rockchip/cdn-dp-core.h ++++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h +@@ -7,6 +7,7 @@ + #ifndef _CDN_DP_CORE_H + #define _CDN_DP_CORE_H + ++#include + #include + #include + #include +@@ -15,35 +16,6 @@ + + #define MAX_PHY 2 + +-enum audio_format { +- AFMT_I2S = 0, +- AFMT_SPDIF = 1, +- AFMT_UNUSED, +-}; +- +-struct audio_info { +- enum audio_format format; +- int sample_rate; +- int channels; +- int sample_width; +-}; +- +-enum vic_pxl_encoding_format { +- PXL_RGB = 0x1, +- YCBCR_4_4_4 = 0x2, +- YCBCR_4_2_2 = 0x4, +- YCBCR_4_2_0 = 0x8, +- Y_ONLY = 0x10, +-}; +- +-struct video_info { +- bool h_sync_polarity; +- bool v_sync_polarity; +- bool interlaced; +- int color_depth; +- enum vic_pxl_encoding_format color_fmt; +-}; +- + struct cdn_firmware_header { + u32 size_bytes; /* size of the entire header+image(s) in bytes */ + u32 header_size; /* size of just the header in bytes */ +@@ -62,12 +34,9 @@ struct cdn_dp_port { + }; + + struct cdn_dp_device { +- struct device *dev; ++ struct cdns_mhdp_device mhdp; + struct drm_device *drm_dev; +- struct drm_connector connector; + struct drm_encoder encoder; +- struct drm_display_mode mode; +- struct platform_device *audio_pdev; + struct work_struct event_work; + struct edid *edid; + +@@ -77,29 +46,20 @@ struct cdn_dp_device { + bool suspended; + + const struct firmware *fw; /* cdn dp firmware */ +- unsigned int fw_version; /* cdn fw version */ + bool fw_loaded; + +- void __iomem *regs; + struct regmap *grf; + struct clk *core_clk; + struct clk *pclk; +- struct clk *spdif_clk; + struct clk *grf_clk; +- struct reset_control *spdif_rst; + struct reset_control *dptx_rst; + struct reset_control *apb_rst; + struct reset_control *core_rst; +- struct audio_info audio_info; +- struct video_info video_info; + struct cdn_dp_port *port[MAX_PHY]; + u8 ports; +- u8 max_lanes; +- unsigned int max_rate; + u8 lanes; + int active_port; + +- u8 dpcd[DP_RECEIVER_CAP_SIZE]; + bool sink_has_audio; + }; + #endif /* _CDN_DP_CORE_H */ +diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c +deleted file mode 100644 +index 9d2163ef4d6e..000000000000 +--- a/drivers/gpu/drm/rockchip/cdn-dp-reg.c ++++ /dev/null +@@ -1,960 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0-only +-/* +- * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd +- * Author: Chris Zhong +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "cdn-dp-core.h" +-#include "cdn-dp-reg.h" +- +-#define CDN_DP_SPDIF_CLK 200000000 +-#define FW_ALIVE_TIMEOUT_US 1000000 +-#define MAILBOX_RETRY_US 1000 +-#define MAILBOX_TIMEOUT_US 5000000 +-#define LINK_TRAINING_RETRY_MS 20 +-#define LINK_TRAINING_TIMEOUT_MS 500 +- +-void cdn_dp_set_fw_clk(struct cdn_dp_device *dp, unsigned long clk) +-{ +- writel(clk / 1000000, dp->regs + SW_CLK_H); +-} +- +-void cdn_dp_clock_reset(struct cdn_dp_device *dp) +-{ +- u32 val; +- +- val = DPTX_FRMR_DATA_CLK_RSTN_EN | +- DPTX_FRMR_DATA_CLK_EN | +- DPTX_PHY_DATA_RSTN_EN | +- DPTX_PHY_DATA_CLK_EN | +- DPTX_PHY_CHAR_RSTN_EN | +- DPTX_PHY_CHAR_CLK_EN | +- SOURCE_AUX_SYS_CLK_RSTN_EN | +- SOURCE_AUX_SYS_CLK_EN | +- DPTX_SYS_CLK_RSTN_EN | +- DPTX_SYS_CLK_EN | +- CFG_DPTX_VIF_CLK_RSTN_EN | +- CFG_DPTX_VIF_CLK_EN; +- writel(val, dp->regs + SOURCE_DPTX_CAR); +- +- val = SOURCE_PHY_RSTN_EN | SOURCE_PHY_CLK_EN; +- writel(val, dp->regs + SOURCE_PHY_CAR); +- +- val = SOURCE_PKT_SYS_RSTN_EN | +- SOURCE_PKT_SYS_CLK_EN | +- SOURCE_PKT_DATA_RSTN_EN | +- SOURCE_PKT_DATA_CLK_EN; +- writel(val, dp->regs + SOURCE_PKT_CAR); +- +- val = SPDIF_CDR_CLK_RSTN_EN | +- SPDIF_CDR_CLK_EN | +- SOURCE_AIF_SYS_RSTN_EN | +- SOURCE_AIF_SYS_CLK_EN | +- SOURCE_AIF_CLK_RSTN_EN | +- SOURCE_AIF_CLK_EN; +- writel(val, dp->regs + SOURCE_AIF_CAR); +- +- val = SOURCE_CIPHER_SYSTEM_CLK_RSTN_EN | +- SOURCE_CIPHER_SYS_CLK_EN | +- SOURCE_CIPHER_CHAR_CLK_RSTN_EN | +- SOURCE_CIPHER_CHAR_CLK_EN; +- writel(val, dp->regs + SOURCE_CIPHER_CAR); +- +- val = SOURCE_CRYPTO_SYS_CLK_RSTN_EN | +- SOURCE_CRYPTO_SYS_CLK_EN; +- writel(val, dp->regs + SOURCE_CRYPTO_CAR); +- +- /* enable Mailbox and PIF interrupt */ +- writel(0, dp->regs + APB_INT_MASK); +-} +- +-static int cdn_dp_mailbox_read(struct cdn_dp_device *dp) +-{ +- int val, ret; +- +- ret = readx_poll_timeout(readl, dp->regs + MAILBOX_EMPTY_ADDR, +- val, !val, MAILBOX_RETRY_US, +- MAILBOX_TIMEOUT_US); +- if (ret < 0) +- return ret; +- +- return readl(dp->regs + MAILBOX0_RD_DATA) & 0xff; +-} +- +-static int cdp_dp_mailbox_write(struct cdn_dp_device *dp, u8 val) +-{ +- int ret, full; +- +- ret = readx_poll_timeout(readl, dp->regs + MAILBOX_FULL_ADDR, +- full, !full, MAILBOX_RETRY_US, +- MAILBOX_TIMEOUT_US); +- if (ret < 0) +- return ret; +- +- writel(val, dp->regs + MAILBOX0_WR_DATA); +- +- return 0; +-} +- +-static int cdn_dp_mailbox_validate_receive(struct cdn_dp_device *dp, +- u8 module_id, u8 opcode, +- u16 req_size) +-{ +- u32 mbox_size, i; +- u8 header[4]; +- int ret; +- +- /* read the header of the message */ +- for (i = 0; i < 4; i++) { +- ret = cdn_dp_mailbox_read(dp); +- if (ret < 0) +- return ret; +- +- header[i] = ret; +- } +- +- mbox_size = (header[2] << 8) | header[3]; +- +- if (opcode != header[0] || module_id != header[1] || +- req_size != mbox_size) { +- /* +- * If the message in mailbox is not what we want, we need to +- * clear the mailbox by reading its contents. +- */ +- for (i = 0; i < mbox_size; i++) +- if (cdn_dp_mailbox_read(dp) < 0) +- break; +- +- return -EINVAL; +- } +- +- return 0; +-} +- +-static int cdn_dp_mailbox_read_receive(struct cdn_dp_device *dp, +- u8 *buff, u16 buff_size) +-{ +- u32 i; +- int ret; +- +- for (i = 0; i < buff_size; i++) { +- ret = cdn_dp_mailbox_read(dp); +- if (ret < 0) +- return ret; +- +- buff[i] = ret; +- } +- +- return 0; +-} +- +-static int cdn_dp_mailbox_send(struct cdn_dp_device *dp, u8 module_id, +- u8 opcode, u16 size, u8 *message) +-{ +- u8 header[4]; +- int ret, i; +- +- header[0] = opcode; +- header[1] = module_id; +- header[2] = (size >> 8) & 0xff; +- header[3] = size & 0xff; +- +- for (i = 0; i < 4; i++) { +- ret = cdp_dp_mailbox_write(dp, header[i]); +- if (ret) +- return ret; +- } +- +- for (i = 0; i < size; i++) { +- ret = cdp_dp_mailbox_write(dp, message[i]); +- if (ret) +- return ret; +- } +- +- return 0; +-} +- +-static int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val) +-{ +- u8 msg[6]; +- +- msg[0] = (addr >> 8) & 0xff; +- msg[1] = addr & 0xff; +- msg[2] = (val >> 24) & 0xff; +- msg[3] = (val >> 16) & 0xff; +- msg[4] = (val >> 8) & 0xff; +- msg[5] = val & 0xff; +- return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_REGISTER, +- sizeof(msg), msg); +-} +- +-static int cdn_dp_reg_write_bit(struct cdn_dp_device *dp, u16 addr, +- u8 start_bit, u8 bits_no, u32 val) +-{ +- u8 field[8]; +- +- field[0] = (addr >> 8) & 0xff; +- field[1] = addr & 0xff; +- field[2] = start_bit; +- field[3] = bits_no; +- field[4] = (val >> 24) & 0xff; +- field[5] = (val >> 16) & 0xff; +- field[6] = (val >> 8) & 0xff; +- field[7] = val & 0xff; +- +- return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_FIELD, +- sizeof(field), field); +-} +- +-int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len) +-{ +- u8 msg[5], reg[5]; +- int ret; +- +- msg[0] = (len >> 8) & 0xff; +- msg[1] = len & 0xff; +- msg[2] = (addr >> 16) & 0xff; +- msg[3] = (addr >> 8) & 0xff; +- msg[4] = addr & 0xff; +- ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_READ_DPCD, +- sizeof(msg), msg); +- if (ret) +- goto err_dpcd_read; +- +- ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX, +- DPTX_READ_DPCD, +- sizeof(reg) + len); +- if (ret) +- goto err_dpcd_read; +- +- ret = cdn_dp_mailbox_read_receive(dp, reg, sizeof(reg)); +- if (ret) +- goto err_dpcd_read; +- +- ret = cdn_dp_mailbox_read_receive(dp, data, len); +- +-err_dpcd_read: +- return ret; +-} +- +-int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value) +-{ +- u8 msg[6], reg[5]; +- int ret; +- +- msg[0] = 0; +- msg[1] = 1; +- msg[2] = (addr >> 16) & 0xff; +- msg[3] = (addr >> 8) & 0xff; +- msg[4] = addr & 0xff; +- msg[5] = value; +- ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_DPCD, +- sizeof(msg), msg); +- if (ret) +- goto err_dpcd_write; +- +- ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX, +- DPTX_WRITE_DPCD, sizeof(reg)); +- if (ret) +- goto err_dpcd_write; +- +- ret = cdn_dp_mailbox_read_receive(dp, reg, sizeof(reg)); +- if (ret) +- goto err_dpcd_write; +- +- if (addr != (reg[2] << 16 | reg[3] << 8 | reg[4])) +- ret = -EINVAL; +- +-err_dpcd_write: +- if (ret) +- DRM_DEV_ERROR(dp->dev, "dpcd write failed: %d\n", ret); +- return ret; +-} +- +-int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem, +- u32 i_size, const u32 *d_mem, u32 d_size) +-{ +- u32 reg; +- int i, ret; +- +- /* reset ucpu before load firmware*/ +- writel(APB_IRAM_PATH | APB_DRAM_PATH | APB_XT_RESET, +- dp->regs + APB_CTRL); +- +- for (i = 0; i < i_size; i += 4) +- writel(*i_mem++, dp->regs + ADDR_IMEM + i); +- +- for (i = 0; i < d_size; i += 4) +- writel(*d_mem++, dp->regs + ADDR_DMEM + i); +- +- /* un-reset ucpu */ +- writel(0, dp->regs + APB_CTRL); +- +- /* check the keep alive register to make sure fw working */ +- ret = readx_poll_timeout(readl, dp->regs + KEEP_ALIVE, +- reg, reg, 2000, FW_ALIVE_TIMEOUT_US); +- if (ret < 0) { +- DRM_DEV_ERROR(dp->dev, "failed to loaded the FW reg = %x\n", +- reg); +- return -EINVAL; +- } +- +- reg = readl(dp->regs + VER_L) & 0xff; +- dp->fw_version = reg; +- reg = readl(dp->regs + VER_H) & 0xff; +- dp->fw_version |= reg << 8; +- reg = readl(dp->regs + VER_LIB_L_ADDR) & 0xff; +- dp->fw_version |= reg << 16; +- reg = readl(dp->regs + VER_LIB_H_ADDR) & 0xff; +- dp->fw_version |= reg << 24; +- +- DRM_DEV_DEBUG(dp->dev, "firmware version: %x\n", dp->fw_version); +- +- return 0; +-} +- +-int cdn_dp_set_firmware_active(struct cdn_dp_device *dp, bool enable) +-{ +- u8 msg[5]; +- int ret, i; +- +- msg[0] = GENERAL_MAIN_CONTROL; +- msg[1] = MB_MODULE_ID_GENERAL; +- msg[2] = 0; +- msg[3] = 1; +- msg[4] = enable ? FW_ACTIVE : FW_STANDBY; +- +- for (i = 0; i < sizeof(msg); i++) { +- ret = cdp_dp_mailbox_write(dp, msg[i]); +- if (ret) +- goto err_set_firmware_active; +- } +- +- /* read the firmware state */ +- for (i = 0; i < sizeof(msg); i++) { +- ret = cdn_dp_mailbox_read(dp); +- if (ret < 0) +- goto err_set_firmware_active; +- +- msg[i] = ret; +- } +- +- ret = 0; +- +-err_set_firmware_active: +- if (ret < 0) +- DRM_DEV_ERROR(dp->dev, "set firmware active failed\n"); +- return ret; +-} +- +-int cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8 lanes, bool flip) +-{ +- u8 msg[8]; +- int ret; +- +- msg[0] = CDN_DP_MAX_LINK_RATE; +- msg[1] = lanes | SCRAMBLER_EN; +- msg[2] = VOLTAGE_LEVEL_2; +- msg[3] = PRE_EMPHASIS_LEVEL_3; +- msg[4] = PTS1 | PTS2 | PTS3 | PTS4; +- msg[5] = FAST_LT_NOT_SUPPORT; +- msg[6] = flip ? LANE_MAPPING_FLIPPED : LANE_MAPPING_NORMAL; +- msg[7] = ENHANCED; +- +- ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, +- DPTX_SET_HOST_CAPABILITIES, +- sizeof(msg), msg); +- if (ret) +- goto err_set_host_cap; +- +- ret = cdn_dp_reg_write(dp, DP_AUX_SWAP_INVERSION_CONTROL, +- AUX_HOST_INVERT); +- +-err_set_host_cap: +- if (ret) +- DRM_DEV_ERROR(dp->dev, "set host cap failed: %d\n", ret); +- return ret; +-} +- +-int cdn_dp_event_config(struct cdn_dp_device *dp) +-{ +- u8 msg[5]; +- int ret; +- +- memset(msg, 0, sizeof(msg)); +- +- msg[0] = DPTX_EVENT_ENABLE_HPD | DPTX_EVENT_ENABLE_TRAINING; +- +- ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_ENABLE_EVENT, +- sizeof(msg), msg); +- if (ret) +- DRM_DEV_ERROR(dp->dev, "set event config failed: %d\n", ret); +- +- return ret; +-} +- +-u32 cdn_dp_get_event(struct cdn_dp_device *dp) +-{ +- return readl(dp->regs + SW_EVENTS0); +-} +- +-int cdn_dp_get_hpd_status(struct cdn_dp_device *dp) +-{ +- u8 status; +- int ret; +- +- ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_HPD_STATE, +- 0, NULL); +- if (ret) +- goto err_get_hpd; +- +- ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX, +- DPTX_HPD_STATE, sizeof(status)); +- if (ret) +- goto err_get_hpd; +- +- ret = cdn_dp_mailbox_read_receive(dp, &status, sizeof(status)); +- if (ret) +- goto err_get_hpd; +- +- return status; +- +-err_get_hpd: +- DRM_DEV_ERROR(dp->dev, "get hpd status failed: %d\n", ret); +- return ret; +-} +- +-int cdn_dp_get_edid_block(void *data, u8 *edid, +- unsigned int block, size_t length) +-{ +- struct cdn_dp_device *dp = data; +- u8 msg[2], reg[2], i; +- int ret; +- +- for (i = 0; i < 4; i++) { +- msg[0] = block / 2; +- msg[1] = block % 2; +- +- ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_GET_EDID, +- sizeof(msg), msg); +- if (ret) +- continue; +- +- ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX, +- DPTX_GET_EDID, +- sizeof(reg) + length); +- if (ret) +- continue; +- +- ret = cdn_dp_mailbox_read_receive(dp, reg, sizeof(reg)); +- if (ret) +- continue; +- +- ret = cdn_dp_mailbox_read_receive(dp, edid, length); +- if (ret) +- continue; +- +- if (reg[0] == length && reg[1] == block / 2) +- break; +- } +- +- if (ret) +- DRM_DEV_ERROR(dp->dev, "get block[%d] edid failed: %d\n", block, +- ret); +- +- return ret; +-} +- +-static int cdn_dp_training_start(struct cdn_dp_device *dp) +-{ +- unsigned long timeout; +- u8 msg, event[2]; +- int ret; +- +- msg = LINK_TRAINING_RUN; +- +- /* start training */ +- ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_TRAINING_CONTROL, +- sizeof(msg), &msg); +- if (ret) +- goto err_training_start; +- +- timeout = jiffies + msecs_to_jiffies(LINK_TRAINING_TIMEOUT_MS); +- while (time_before(jiffies, timeout)) { +- msleep(LINK_TRAINING_RETRY_MS); +- ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, +- DPTX_READ_EVENT, 0, NULL); +- if (ret) +- goto err_training_start; +- +- ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX, +- DPTX_READ_EVENT, +- sizeof(event)); +- if (ret) +- goto err_training_start; +- +- ret = cdn_dp_mailbox_read_receive(dp, event, sizeof(event)); +- if (ret) +- goto err_training_start; +- +- if (event[1] & EQ_PHASE_FINISHED) +- return 0; +- } +- +- ret = -ETIMEDOUT; +- +-err_training_start: +- DRM_DEV_ERROR(dp->dev, "training failed: %d\n", ret); +- return ret; +-} +- +-static int cdn_dp_get_training_status(struct cdn_dp_device *dp) +-{ +- u8 status[10]; +- int ret; +- +- ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_READ_LINK_STAT, +- 0, NULL); +- if (ret) +- goto err_get_training_status; +- +- ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX, +- DPTX_READ_LINK_STAT, +- sizeof(status)); +- if (ret) +- goto err_get_training_status; +- +- ret = cdn_dp_mailbox_read_receive(dp, status, sizeof(status)); +- if (ret) +- goto err_get_training_status; +- +- dp->max_rate = drm_dp_bw_code_to_link_rate(status[0]); +- dp->max_lanes = status[1]; +- +-err_get_training_status: +- if (ret) +- DRM_DEV_ERROR(dp->dev, "get training status failed: %d\n", ret); +- return ret; +-} +- +-int cdn_dp_train_link(struct cdn_dp_device *dp) +-{ +- int ret; +- +- ret = cdn_dp_training_start(dp); +- if (ret) { +- DRM_DEV_ERROR(dp->dev, "Failed to start training %d\n", ret); +- return ret; +- } +- +- ret = cdn_dp_get_training_status(dp); +- if (ret) { +- DRM_DEV_ERROR(dp->dev, "Failed to get training stat %d\n", ret); +- return ret; +- } +- +- DRM_DEV_DEBUG_KMS(dp->dev, "rate:0x%x, lanes:%d\n", dp->max_rate, +- dp->max_lanes); +- return ret; +-} +- +-int cdn_dp_set_video_status(struct cdn_dp_device *dp, int active) +-{ +- u8 msg; +- int ret; +- +- msg = !!active; +- +- ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_SET_VIDEO, +- sizeof(msg), &msg); +- if (ret) +- DRM_DEV_ERROR(dp->dev, "set video status failed: %d\n", ret); +- +- return ret; +-} +- +-static int cdn_dp_get_msa_misc(struct video_info *video, +- struct drm_display_mode *mode) +-{ +- u32 msa_misc; +- u8 val[2] = {0}; +- +- switch (video->color_fmt) { +- case PXL_RGB: +- case Y_ONLY: +- val[0] = 0; +- break; +- /* set YUV default color space conversion to BT601 */ +- case YCBCR_4_4_4: +- val[0] = 6 + BT_601 * 8; +- break; +- case YCBCR_4_2_2: +- val[0] = 5 + BT_601 * 8; +- break; +- case YCBCR_4_2_0: +- val[0] = 5; +- break; +- } +- +- switch (video->color_depth) { +- case 6: +- val[1] = 0; +- break; +- case 8: +- val[1] = 1; +- break; +- case 10: +- val[1] = 2; +- break; +- case 12: +- val[1] = 3; +- break; +- case 16: +- val[1] = 4; +- break; +- } +- +- msa_misc = 2 * val[0] + 32 * val[1] + +- ((video->color_fmt == Y_ONLY) ? (1 << 14) : 0); +- +- return msa_misc; +-} +- +-int cdn_dp_config_video(struct cdn_dp_device *dp) +-{ +- struct video_info *video = &dp->video_info; +- struct drm_display_mode *mode = &dp->mode; +- u64 symbol; +- u32 val, link_rate, rem; +- u8 bit_per_pix, tu_size_reg = TU_SIZE; +- int ret; +- +- bit_per_pix = (video->color_fmt == YCBCR_4_2_2) ? +- (video->color_depth * 2) : (video->color_depth * 3); +- +- link_rate = dp->max_rate / 1000; +- +- ret = cdn_dp_reg_write(dp, BND_HSYNC2VSYNC, VIF_BYPASS_INTERLACE); +- if (ret) +- goto err_config_video; +- +- ret = cdn_dp_reg_write(dp, HSYNC2VSYNC_POL_CTRL, 0); +- if (ret) +- goto err_config_video; +- +- /* +- * get a best tu_size and valid symbol: +- * 1. chose Lclk freq(162Mhz, 270Mhz, 540Mhz), set TU to 32 +- * 2. calculate VS(valid symbol) = TU * Pclk * Bpp / (Lclk * Lanes) +- * 3. if VS > *.85 or VS < *.1 or VS < 2 or TU < VS + 4, then set +- * TU += 2 and repeat 2nd step. +- */ +- do { +- tu_size_reg += 2; +- symbol = tu_size_reg * mode->clock * bit_per_pix; +- do_div(symbol, dp->max_lanes * link_rate * 8); +- rem = do_div(symbol, 1000); +- if (tu_size_reg > 64) { +- ret = -EINVAL; +- DRM_DEV_ERROR(dp->dev, +- "tu error, clk:%d, lanes:%d, rate:%d\n", +- mode->clock, dp->max_lanes, link_rate); +- goto err_config_video; +- } +- } while ((symbol <= 1) || (tu_size_reg - symbol < 4) || +- (rem > 850) || (rem < 100)); +- +- val = symbol + (tu_size_reg << 8); +- val |= TU_CNT_RST_EN; +- ret = cdn_dp_reg_write(dp, DP_FRAMER_TU, val); +- if (ret) +- goto err_config_video; +- +- /* set the FIFO Buffer size */ +- val = div_u64(mode->clock * (symbol + 1), 1000) + link_rate; +- val /= (dp->max_lanes * link_rate); +- val = div_u64(8 * (symbol + 1), bit_per_pix) - val; +- val += 2; +- ret = cdn_dp_reg_write(dp, DP_VC_TABLE(15), val); +- +- switch (video->color_depth) { +- case 6: +- val = BCS_6; +- break; +- case 8: +- val = BCS_8; +- break; +- case 10: +- val = BCS_10; +- break; +- case 12: +- val = BCS_12; +- break; +- case 16: +- val = BCS_16; +- break; +- } +- +- val += video->color_fmt << 8; +- ret = cdn_dp_reg_write(dp, DP_FRAMER_PXL_REPR, val); +- if (ret) +- goto err_config_video; +- +- val = video->h_sync_polarity ? DP_FRAMER_SP_HSP : 0; +- val |= video->v_sync_polarity ? DP_FRAMER_SP_VSP : 0; +- ret = cdn_dp_reg_write(dp, DP_FRAMER_SP, val); +- if (ret) +- goto err_config_video; +- +- val = (mode->hsync_start - mode->hdisplay) << 16; +- val |= mode->htotal - mode->hsync_end; +- ret = cdn_dp_reg_write(dp, DP_FRONT_BACK_PORCH, val); +- if (ret) +- goto err_config_video; +- +- val = mode->hdisplay * bit_per_pix / 8; +- ret = cdn_dp_reg_write(dp, DP_BYTE_COUNT, val); +- if (ret) +- goto err_config_video; +- +- val = mode->htotal | ((mode->htotal - mode->hsync_start) << 16); +- ret = cdn_dp_reg_write(dp, MSA_HORIZONTAL_0, val); +- if (ret) +- goto err_config_video; +- +- val = mode->hsync_end - mode->hsync_start; +- val |= (mode->hdisplay << 16) | (video->h_sync_polarity << 15); +- ret = cdn_dp_reg_write(dp, MSA_HORIZONTAL_1, val); +- if (ret) +- goto err_config_video; +- +- val = mode->vtotal; +- val |= (mode->vtotal - mode->vsync_start) << 16; +- ret = cdn_dp_reg_write(dp, MSA_VERTICAL_0, val); +- if (ret) +- goto err_config_video; +- +- val = mode->vsync_end - mode->vsync_start; +- val |= (mode->vdisplay << 16) | (video->v_sync_polarity << 15); +- ret = cdn_dp_reg_write(dp, MSA_VERTICAL_1, val); +- if (ret) +- goto err_config_video; +- +- val = cdn_dp_get_msa_misc(video, mode); +- ret = cdn_dp_reg_write(dp, MSA_MISC, val); +- if (ret) +- goto err_config_video; +- +- ret = cdn_dp_reg_write(dp, STREAM_CONFIG, 1); +- if (ret) +- goto err_config_video; +- +- val = mode->hsync_end - mode->hsync_start; +- val |= mode->hdisplay << 16; +- ret = cdn_dp_reg_write(dp, DP_HORIZONTAL, val); +- if (ret) +- goto err_config_video; +- +- val = mode->vdisplay; +- val |= (mode->vtotal - mode->vsync_start) << 16; +- ret = cdn_dp_reg_write(dp, DP_VERTICAL_0, val); +- if (ret) +- goto err_config_video; +- +- val = mode->vtotal; +- ret = cdn_dp_reg_write(dp, DP_VERTICAL_1, val); +- if (ret) +- goto err_config_video; +- +- ret = cdn_dp_reg_write_bit(dp, DP_VB_ID, 2, 1, 0); +- +-err_config_video: +- if (ret) +- DRM_DEV_ERROR(dp->dev, "config video failed: %d\n", ret); +- return ret; +-} +- +-int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info *audio) +-{ +- int ret; +- +- ret = cdn_dp_reg_write(dp, AUDIO_PACK_CONTROL, 0); +- if (ret) { +- DRM_DEV_ERROR(dp->dev, "audio stop failed: %d\n", ret); +- return ret; +- } +- +- writel(0, dp->regs + SPDIF_CTRL_ADDR); +- +- /* clearn the audio config and reset */ +- writel(0, dp->regs + AUDIO_SRC_CNTL); +- writel(0, dp->regs + AUDIO_SRC_CNFG); +- writel(AUDIO_SW_RST, dp->regs + AUDIO_SRC_CNTL); +- writel(0, dp->regs + AUDIO_SRC_CNTL); +- +- /* reset smpl2pckt component */ +- writel(0, dp->regs + SMPL2PKT_CNTL); +- writel(AUDIO_SW_RST, dp->regs + SMPL2PKT_CNTL); +- writel(0, dp->regs + SMPL2PKT_CNTL); +- +- /* reset FIFO */ +- writel(AUDIO_SW_RST, dp->regs + FIFO_CNTL); +- writel(0, dp->regs + FIFO_CNTL); +- +- if (audio->format == AFMT_SPDIF) +- clk_disable_unprepare(dp->spdif_clk); +- +- return 0; +-} +- +-int cdn_dp_audio_mute(struct cdn_dp_device *dp, bool enable) +-{ +- int ret; +- +- ret = cdn_dp_reg_write_bit(dp, DP_VB_ID, 4, 1, enable); +- if (ret) +- DRM_DEV_ERROR(dp->dev, "audio mute failed: %d\n", ret); +- +- return ret; +-} +- +-static void cdn_dp_audio_config_i2s(struct cdn_dp_device *dp, +- struct audio_info *audio) +-{ +- int sub_pckt_num = 1, i2s_port_en_val = 0xf, i; +- u32 val; +- +- if (audio->channels == 2) { +- if (dp->max_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; +- } +- +- writel(0x0, dp->regs + SPDIF_CTRL_ADDR); +- +- writel(SYNC_WR_TO_CH_ZERO, dp->regs + 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); +- writel(val, dp->regs + 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; +- writel(val, dp->regs + 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); +- writel(val, dp->regs + 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; +- writel(val, dp->regs + COM_CH_STTS_BITS); +- +- writel(SMPL2PKT_EN, dp->regs + SMPL2PKT_CNTL); +- writel(I2S_DEC_START, dp->regs + AUDIO_SRC_CNTL); +-} +- +-static void cdn_dp_audio_config_spdif(struct cdn_dp_device *dp) +-{ +- u32 val; +- +- writel(SYNC_WR_TO_CH_ZERO, dp->regs + FIFO_CNTL); +- +- val = MAX_NUM_CH(2) | AUDIO_TYPE_LPCM | CFG_SUB_PCKT_NUM(4); +- writel(val, dp->regs + SMPL2PKT_CNFG); +- writel(SMPL2PKT_EN, dp->regs + SMPL2PKT_CNTL); +- +- val = SPDIF_ENABLE | SPDIF_AVG_SEL | SPDIF_JITTER_BYPASS; +- writel(val, dp->regs + SPDIF_CTRL_ADDR); +- +- clk_prepare_enable(dp->spdif_clk); +- clk_set_rate(dp->spdif_clk, CDN_DP_SPDIF_CLK); +-} +- +-int cdn_dp_audio_config(struct cdn_dp_device *dp, struct audio_info *audio) +-{ +- int ret; +- +- /* reset the spdif clk before config */ +- if (audio->format == AFMT_SPDIF) { +- reset_control_assert(dp->spdif_rst); +- reset_control_deassert(dp->spdif_rst); +- } +- +- ret = cdn_dp_reg_write(dp, CM_LANE_CTRL, LANE_REF_CYC); +- if (ret) +- goto err_audio_config; +- +- ret = cdn_dp_reg_write(dp, CM_CTRL, 0); +- if (ret) +- goto err_audio_config; +- +- if (audio->format == AFMT_I2S) +- cdn_dp_audio_config_i2s(dp, audio); +- else if (audio->format == AFMT_SPDIF) +- cdn_dp_audio_config_spdif(dp); +- +- ret = cdn_dp_reg_write(dp, AUDIO_PACK_CONTROL, AUDIO_PACK_EN); +- +-err_audio_config: +- if (ret) +- DRM_DEV_ERROR(dp->dev, "audio config failed: %d\n", ret); +- return ret; +-} +diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.h b/include/drm/bridge/cdns-mhdp.h +similarity index 53% +rename from drivers/gpu/drm/rockchip/cdn-dp-reg.h +rename to include/drm/bridge/cdns-mhdp.h +index 441248b7a79e..d76716d4edc6 100644 +--- a/drivers/gpu/drm/rockchip/cdn-dp-reg.h ++++ b/include/drm/bridge/cdns-mhdp.h +@@ -1,16 +1,31 @@ +-/* SPDX-License-Identifier: GPL-2.0-only */ ++/* SPDX-License-Identifier: GPL-2.0 */ + /* + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd + * Author: Chris Zhong ++ * ++ * 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. + */ + +-#ifndef _CDN_DP_REG_H +-#define _CDN_DP_REG_H ++#ifndef CDNS_MHDP_H_ ++#define CDNS_MHDP_H_ + ++#include ++#include ++#include ++#include ++#include + #include + + #define ADDR_IMEM 0x10000 + #define ADDR_DMEM 0x20000 ++#define ADDR_PHY_AFE 0x80000 + + /* APB CFG addr */ + #define APB_CTRL 0 +@@ -78,6 +93,10 @@ + #define SOURCE_PIF_SW_RESET 0x30834 + + /* bellow registers need access by mailbox */ ++/* source phy comp */ ++#define PHY_DATA_SEL 0x0818 ++#define LANES_CONFIG 0x0814 ++ + /* source car addr */ + #define SOURCE_HDTX_CAR 0x0900 + #define SOURCE_DPTX_CAR 0x0904 +@@ -89,6 +108,17 @@ + #define SOURCE_CIPHER_CAR 0x0920 + #define SOURCE_CRYPTO_CAR 0x0924 + ++/* mhdp tx_top_comp */ ++#define SCHEDULER_H_SIZE 0x1000 ++#define SCHEDULER_V_SIZE 0x1004 ++#define HDTX_SIGNAL_FRONT_WIDTH 0x100c ++#define HDTX_SIGNAL_SYNC_WIDTH 0x1010 ++#define HDTX_SIGNAL_BACK_WIDTH 0x1014 ++#define HDTX_CONTROLLER 0x1018 ++#define HDTX_HPD 0x1020 ++#define HDTX_CLOCK_REG_0 0x1024 ++#define HDTX_CLOCK_REG_1 0x1028 ++ + /* clock meters addr */ + #define CM_CTRL 0x0a00 + #define CM_I2S_CTRL 0x0a04 +@@ -308,18 +338,24 @@ + #define MB_SIZE_LSB_ID 3 + #define MB_DATA_ID 4 + +-#define MB_MODULE_ID_DP_TX 0x01 ++#define MB_MODULE_ID_DP_TX 0x01 ++#define MB_MODULE_ID_HDMI_TX 0x03 + #define MB_MODULE_ID_HDCP_TX 0x07 + #define MB_MODULE_ID_HDCP_RX 0x08 + #define MB_MODULE_ID_HDCP_GENERAL 0x09 +-#define MB_MODULE_ID_GENERAL 0x0a ++#define MB_MODULE_ID_GENERAL 0x0A + + /* general opcode */ + #define GENERAL_MAIN_CONTROL 0x01 + #define GENERAL_TEST_ECHO 0x02 + #define GENERAL_BUS_SETTINGS 0x03 + #define GENERAL_TEST_ACCESS 0x04 ++#define GENERAL_WRITE_REGISTER 0x05 ++#define GENERAL_WRITE_FIELD 0x06 ++#define GENERAL_READ_REGISTER 0x07 ++#define GENERAL_GET_HPD_STATE 0x11 + ++/* DPTX opcode */ + #define DPTX_SET_POWER_MNG 0x00 + #define DPTX_SET_HOST_CAPABILITIES 0x01 + #define DPTX_GET_EDID 0x02 +@@ -338,12 +374,24 @@ + #define DPTX_SET_LINK_BREAK_POINT 0x0f + #define DPTX_FORCE_LANES 0x10 + #define DPTX_HPD_STATE 0x11 ++#define DPTX_ADJUST_LT 0x12 ++ ++/* HDMI TX opcode */ ++#define HDMI_TX_READ 0x00 ++#define HDMI_TX_WRITE 0x01 ++#define HDMI_TX_UPDATE_READ 0x02 ++#define HDMI_TX_EDID 0x03 ++#define HDMI_TX_EVENTS 0x04 ++#define HDMI_TX_HPD_STATUS 0x05 ++#define HDMI_TX_DEBUG_ECHO 0xAA ++#define HDMI_TX_TEST 0xBB ++#define HDMI_TX_EDID_INTERNAL 0xF0 + + #define FW_STANDBY 0 + #define FW_ACTIVE 1 + +-#define DPTX_EVENT_ENABLE_HPD BIT(0) +-#define DPTX_EVENT_ENABLE_TRAINING BIT(1) ++#define MHDP_EVENT_ENABLE_HPD BIT(0) ++#define MHDP_EVENT_ENABLE_TRAINING BIT(1) + + #define LINK_TRAINING_NOT_ACTIVE 0 + #define LINK_TRAINING_RUN 1 +@@ -387,7 +435,35 @@ + #define HDCP_TX_IS_RECEIVER_ID_VALID_EVENT BIT(7) + + #define TU_SIZE 30 +-#define CDN_DP_MAX_LINK_RATE DP_LINK_BW_5_4 ++#define CDNS_DP_MAX_LINK_RATE 540000 ++ ++#define F_HDMI_ENCODING(x) (((x) & ((1 << 2) - 1)) << 16) ++#define F_VIF_DATA_WIDTH(x) (((x) & ((1 << 2) - 1)) << 2) ++#define F_HDMI_MODE(x) (((x) & ((1 << 2) - 1)) << 0) ++#define F_GCP_EN(x) (((x) & ((1 << 1) - 1)) << 12) ++#define F_DATA_EN(x) (((x) & ((1 << 1) - 1)) << 15) ++#define F_HDMI2_PREAMBLE_EN(x) (((x) & ((1 << 1) - 1)) << 18) ++#define F_PIC_3D(x) (((x) & ((1 << 4) - 1)) << 7) ++#define F_BCH_EN(x) (((x) & ((1 << 1) - 1)) << 11) ++#define F_SOURCE_PHY_MHDP_SEL(x) (((x) & ((1 << 2) - 1)) << 3) ++#define F_HPD_VALID_WIDTH(x) (((x) & ((1 << 12) - 1)) << 0) ++#define F_HPD_GLITCH_WIDTH(x) (((x) & ((1 << 8) - 1)) << 12) ++#define F_HDMI2_CTRL_IL_MODE(x) (((x) & ((1 << 1) - 1)) << 19) ++#define F_SOURCE_PHY_LANE0_SWAP(x) (((x) & ((1 << 2) - 1)) << 0) ++#define F_SOURCE_PHY_LANE1_SWAP(x) (((x) & ((1 << 2) - 1)) << 2) ++#define F_SOURCE_PHY_LANE2_SWAP(x) (((x) & ((1 << 2) - 1)) << 4) ++#define F_SOURCE_PHY_LANE3_SWAP(x) (((x) & ((1 << 2) - 1)) << 6) ++#define F_SOURCE_PHY_COMB_BYPASS(x) (((x) & ((1 << 1) - 1)) << 21) ++#define F_SOURCE_PHY_20_10(x) (((x) & ((1 << 1) - 1)) << 22) ++#define F_PKT_ALLOC_ADDRESS(x) (((x) & ((1 << 4) - 1)) << 0) ++#define F_ACTIVE_IDLE_TYPE(x) (((x) & ((1 << 1) - 1)) << 17) ++#define F_FIFO1_FLUSH(x) (((x) & ((1 << 1) - 1)) << 0) ++#define F_PKT_ALLOC_WR_EN(x) (((x) & ((1 << 1) - 1)) << 0) ++#define F_DATA_WR(x) (x) ++#define F_WR_ADDR(x) (((x) & ((1 << 4) - 1)) << 0) ++#define F_HOST_WR(x) (((x) & ((1 << 1) - 1)) << 0) ++#define F_TYPE_VALID(x) (((x) & ((1 << 1) - 1)) << 16) ++#define F_PACKET_TYPE(x) (((x) & ((1 << 8) - 1)) << 8) + + /* audio */ + #define AUDIO_PACK_EN BIT(8) +@@ -416,6 +492,24 @@ + /* Reference cycles when using lane clock as reference */ + #define LANE_REF_CYC 0x8000 + ++#define HOTPLUG_DEBOUNCE_MS 200 ++ ++#define IRQ_IN 0 ++#define IRQ_OUT 1 ++#define IRQ_NUM 2 ++ ++#define cdns_mhdp_plat_call(mhdp, operation) \ ++ (!(mhdp) ? -ENODEV : (((mhdp)->plat_data && (mhdp)->plat_data->operation) ? \ ++ (mhdp)->plat_data->operation(mhdp) : ENOIOCTLCMD)) ++ ++/* bus access type */ ++enum { ++ BUS_TYPE_NORMAL_APB = 0, ++ BUS_TYPE_NORMAL_SAPB = 1, ++ BUS_TYPE_LOW4K_APB = 2, ++ BUS_TYPE_LOW4K_SAPB = 3, ++}; ++ + enum voltage_swing_level { + VOLTAGE_LEVEL_0, + VOLTAGE_LEVEL_1, +@@ -451,24 +545,261 @@ enum vic_bt_type { + BT_709 = 0x1, + }; + +-void cdn_dp_clock_reset(struct cdn_dp_device *dp); +- +-void cdn_dp_set_fw_clk(struct cdn_dp_device *dp, unsigned long clk); +-int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem, +- u32 i_size, const u32 *d_mem, u32 d_size); +-int cdn_dp_set_firmware_active(struct cdn_dp_device *dp, bool enable); +-int cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8 lanes, bool flip); +-int cdn_dp_event_config(struct cdn_dp_device *dp); +-u32 cdn_dp_get_event(struct cdn_dp_device *dp); +-int cdn_dp_get_hpd_status(struct cdn_dp_device *dp); +-int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value); +-int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len); +-int cdn_dp_get_edid_block(void *dp, u8 *edid, +- unsigned int block, size_t length); +-int cdn_dp_train_link(struct cdn_dp_device *dp); +-int cdn_dp_set_video_status(struct cdn_dp_device *dp, int active); +-int cdn_dp_config_video(struct cdn_dp_device *dp); +-int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info *audio); +-int cdn_dp_audio_mute(struct cdn_dp_device *dp, bool enable); +-int cdn_dp_audio_config(struct cdn_dp_device *dp, struct audio_info *audio); +-#endif /* _CDN_DP_REG_H */ ++enum audio_format { ++ AFMT_I2S = 0, ++ AFMT_SPDIF_INT = 1, ++ AFMT_SPDIF_EXT = 2, ++ AFMT_UNUSED, ++}; ++ ++enum { ++ MODE_DVI, ++ MODE_HDMI_1_4, ++ MODE_HDMI_2_0, ++}; ++ ++struct audio_info { ++ enum audio_format format; ++ int sample_rate; ++ int channels; ++ int sample_width; ++ int connector_type; ++}; ++ ++enum vic_pxl_encoding_format { ++ PXL_RGB = 0x1, ++ YCBCR_4_4_4 = 0x2, ++ YCBCR_4_2_2 = 0x4, ++ YCBCR_4_2_0 = 0x8, ++ Y_ONLY = 0x10, ++}; ++ ++struct video_info { ++ bool h_sync_polarity; ++ bool v_sync_polarity; ++ bool interlaced; ++ int color_depth; ++ enum vic_pxl_encoding_format color_fmt; ++}; ++ ++struct cdns_mhdp_host { ++ unsigned int link_rate; ++ u8 lanes_cnt; ++ u8 volt_swing; ++ u8 pre_emphasis; ++ u8 pattern_supp; ++ u8 fast_link; ++ u8 lane_mapping; ++ u8 enhanced; ++}; ++ ++struct cdns_mhdp_sink { ++ unsigned int link_rate; ++ u8 lanes_cnt; ++ u8 pattern_supp; ++ u8 fast_link; ++ u8 enhanced; ++}; ++ ++struct cdns_mhdp_bridge; ++struct cdns_mhdp_connector; ++ ++struct cdns_mhdp_bridge { ++ struct cdns_mhdp_device *mhdp; ++ struct drm_bridge base; ++ int pbn; ++ int8_t stream_id; ++ struct cdns_mhdp_connector *connector; ++ bool is_active; ++}; ++ ++struct cdns_mhdp_connector { ++ struct drm_connector base; ++ bool is_mst_connector; ++ struct drm_dp_mst_port *port; ++ struct cdns_mhdp_bridge *bridge; ++}; ++ ++struct cdns_mhdp_cec { ++ struct cec_adapter *adap; ++ struct device *dev; ++ struct mutex lock; ++ ++ struct cec_msg msg; ++ struct task_struct *cec_worker; ++}; ++ ++struct cdns_plat_data { ++ /* Vendor PHY support */ ++ int (*bind)(struct platform_device *pdev, ++ struct drm_encoder *encoder, ++ struct cdns_mhdp_device *mhdp); ++ void (*unbind)(struct device *dev); ++ ++ void (*plat_init)(struct cdns_mhdp_device *mhdp); ++ void (*plat_deinit)(struct cdns_mhdp_device *mhdp); ++ ++ int (*phy_set)(struct cdns_mhdp_device *mhdp); ++ bool (*phy_video_valid)(struct cdns_mhdp_device *mhdp); ++ int (*firmware_init)(struct cdns_mhdp_device *mhdp); ++ void (*pclk_rate)(struct cdns_mhdp_device *mhdp); ++ ++ int (*suspend)(struct cdns_mhdp_device *mhdp); ++ int (*resume)(struct cdns_mhdp_device *mhdp); ++ ++ int (*power_on)(struct cdns_mhdp_device *mhdp); ++ int (*power_off)(struct cdns_mhdp_device *mhdp); ++ ++ int bus_type; ++ int video_format; ++ char is_dp; ++ char *plat_name; ++}; ++ ++struct cdns_mhdp_device { ++ void __iomem *regs_base; ++ void __iomem *regs_sec; ++ ++ int bus_type; ++ ++ struct device *dev; ++ ++ struct cdns_mhdp_connector connector; ++ struct clk *spdif_clk; ++ struct reset_control *spdif_rst; ++ ++ struct platform_device *audio_pdev; ++ struct audio_info audio_info; ++ ++ struct cdns_mhdp_bridge bridge; ++ struct phy *phy; ++ ++ struct video_info video_info; ++ struct drm_display_mode mode; ++ const struct drm_display_mode *valid_mode; ++ unsigned int fw_version; ++ ++ struct drm_dp_mst_topology_mgr mst_mgr; ++ struct delayed_work hotplug_work; ++ ++ u32 lane_mapping; ++ bool link_up; ++ bool power_up; ++ bool plugged; ++ bool force_mode_set; ++ bool is_hpd; ++ bool is_ls1028a; ++ struct mutex lock; ++ struct mutex iolock; ++ ++ int irq[IRQ_NUM]; ++ ++ union { ++ struct _dp_data { ++ u8 dpcd[DP_RECEIVER_CAP_SIZE]; ++ u32 rate; ++ u8 num_lanes; ++ struct drm_dp_aux aux; ++ struct cdns_mhdp_host host; ++ struct cdns_mhdp_sink sink; ++ bool is_mst; ++ bool can_mst; ++ } dp; ++ struct _hdmi_data { ++ struct cdns_mhdp_cec cec; ++ u32 char_rate; ++ u32 hdmi_type; ++ } hdmi; ++ }; ++ const struct cdns_plat_data *plat_data; ++ ++}; ++ ++u32 cdns_mhdp_bus_read(struct cdns_mhdp_device *mhdp, u32 offset); ++void cdns_mhdp_bus_write(u32 val, struct cdns_mhdp_device *mhdp, u32 offset); ++void cdns_mhdp_clock_reset(struct cdns_mhdp_device *mhdp); ++void cdns_mhdp_set_fw_clk(struct cdns_mhdp_device *mhdp, unsigned long clk); ++u32 cdns_mhdp_get_fw_clk(struct cdns_mhdp_device *mhdp); ++int cdns_mhdp_load_firmware(struct cdns_mhdp_device *mhdp, const u32 *i_mem, ++ u32 i_size, const u32 *d_mem, u32 d_size); ++int cdns_mhdp_set_firmware_active(struct cdns_mhdp_device *mhdp, bool enable); ++int cdns_mhdp_set_host_cap(struct cdns_mhdp_device *mhdp, bool flip); ++int cdns_mhdp_event_config(struct cdns_mhdp_device *mhdp); ++u32 cdns_mhdp_get_event(struct cdns_mhdp_device *mhdp); ++int cdns_mhdp_dpcd_write(struct cdns_mhdp_device *mhdp, u32 addr, u8 value); ++int cdns_mhdp_dpcd_read(struct cdns_mhdp_device *mhdp, ++ u32 addr, u8 *data, u16 len); ++int cdns_mhdp_get_edid_block(void *mhdp, u8 *edid, ++ unsigned int block, size_t length); ++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, ++ u8 start_bit, u8 bits_no, u32 val); ++int cdns_mhdp_adjust_lt(struct cdns_mhdp_device *mhdp, u8 nlanes, ++ u16 udelay, u8 *lanes_data, ++ u8 *dpcd); ++ ++int cdns_mhdp_read_hpd(struct cdns_mhdp_device *mhdp); ++u32 cdns_phy_reg_read(struct cdns_mhdp_device *mhdp, u32 addr); ++int cdns_phy_reg_write(struct cdns_mhdp_device *mhdp, u32 addr, u32 val); ++int cdns_mhdp_mailbox_send(struct cdns_mhdp_device *mhdp, u8 module_id, ++ u8 opcode, u16 size, u8 *message); ++int cdns_mhdp_mailbox_read_receive(struct cdns_mhdp_device *mhdp, ++ u8 *buff, u16 buff_size); ++int cdns_mhdp_mailbox_validate_receive(struct cdns_mhdp_device *mhdp, ++ u8 module_id, u8 opcode, ++ u16 req_size); ++int cdns_mhdp_mailbox_read(struct cdns_mhdp_device *mhdp); ++ ++void cdns_mhdp_infoframe_set(struct cdns_mhdp_device *mhdp, ++ u8 entry_id, u8 packet_len, u8 *packet, u8 packet_type); ++int cdns_hdmi_get_edid_block(void *data, u8 *edid, u32 block, size_t length); ++int cdns_hdmi_scdc_read(struct cdns_mhdp_device *mhdp, u8 addr, u8 *data); ++int cdns_hdmi_scdc_write(struct cdns_mhdp_device *mhdp, u8 addr, u8 value); ++int cdns_hdmi_ctrl_init(struct cdns_mhdp_device *mhdp, int protocol, u32 char_rate); ++int cdns_hdmi_mode_config(struct cdns_mhdp_device *mhdp, struct drm_display_mode *mode, ++ struct video_info *video_info); ++int cdns_hdmi_disable_gcp(struct cdns_mhdp_device *mhdp); ++int cdns_hdmi_enable_gcp(struct cdns_mhdp_device *mhdp); ++ ++bool cdns_mhdp_check_alive(struct cdns_mhdp_device *mhdp); ++ ++/* HDMI */ ++int cdns_hdmi_probe(struct platform_device *pdev, ++ struct cdns_mhdp_device *mhdp); ++void cdns_hdmi_remove(struct platform_device *pdev); ++void cdns_hdmi_unbind(struct device *dev); ++int cdns_hdmi_bind(struct platform_device *pdev, ++ struct drm_encoder *encoder, struct cdns_mhdp_device *mhdp); ++void cdns_hdmi_set_sample_rate(struct cdns_mhdp_device *mhdp, unsigned int rate); ++void cdns_hdmi_audio_enable(struct cdns_mhdp_device *mhdp); ++void cdns_hdmi_audio_disable(struct cdns_mhdp_device *mhdp); ++ ++/* DP */ ++int cdns_dp_probe(struct platform_device *pdev, ++ struct cdns_mhdp_device *mhdp); ++void cdns_dp_remove(struct platform_device *pdev); ++void cdns_dp_unbind(struct device *dev); ++int cdns_dp_bind(struct platform_device *pdev, ++ struct drm_encoder *encoder, struct cdns_mhdp_device *mhdp); ++ ++/* CEC */ ++#ifdef CONFIG_DRM_CDNS_HDMI_CEC ++int cdns_mhdp_register_cec_driver(struct device *dev); ++int cdns_mhdp_unregister_cec_driver(struct device *dev); ++#endif ++ ++#endif /* CDNS_MHDP_H_ */ +-- +2.29.2 + diff --git a/projects/NXP/devices/iMX8/patches/linux/0002-MLK-24065-1-drm-bridge-cadence-fix-dp_aux_transfer-w.patch b/projects/NXP/devices/iMX8/patches/linux/0002-MLK-24065-1-drm-bridge-cadence-fix-dp_aux_transfer-w.patch new file mode 100644 index 0000000000..1b560edf51 --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0002-MLK-24065-1-drm-bridge-cadence-fix-dp_aux_transfer-w.patch @@ -0,0 +1,32 @@ +From 55eb19200d650ead73139ee8444db9119718fd31 Mon Sep 17 00:00:00 2001 +From: Sergey Zhuravlevich +Date: Tue, 12 May 2020 14:23:15 +0200 +Subject: [PATCH 02/49] MLK-24065-1: drm: bridge: cadence: fix dp_aux_transfer + write return value + +After exiting the loop in DP_AUX_NATIVE_WRITE it was returning 0. It's supposed +to return the number of bytes transferred on success. + +Signed-off-by: Sergey Zhuravlevich +Acked-by: Sandor Yu +Tested-By: Sandor Yu +--- + drivers/gpu/drm/bridge/cadence/cdns-dp-core.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/gpu/drm/bridge/cadence/cdns-dp-core.c b/drivers/gpu/drm/bridge/cadence/cdns-dp-core.c +index acb5c860da73..aa92029f44e9 100644 +--- a/drivers/gpu/drm/bridge/cadence/cdns-dp-core.c ++++ b/drivers/gpu/drm/bridge/cadence/cdns-dp-core.c +@@ -67,6 +67,8 @@ static ssize_t dp_aux_transfer(struct drm_dp_aux *aux, + + return ret; + } ++ msg->reply = DP_AUX_NATIVE_REPLY_ACK; ++ return msg->size; + } + + if (msg->request == DP_AUX_NATIVE_READ) { +-- +2.29.2 + diff --git a/projects/NXP/devices/iMX8/patches/linux/0003-MLK-24065-3-drm-bridge-cadence-use-the-lane-mapping-.patch b/projects/NXP/devices/iMX8/patches/linux/0003-MLK-24065-3-drm-bridge-cadence-use-the-lane-mapping-.patch new file mode 100644 index 0000000000..76aad36aeb --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0003-MLK-24065-3-drm-bridge-cadence-use-the-lane-mapping-.patch @@ -0,0 +1,66 @@ +From 90e1a010995c0a87b0216706b1255ca5d0c36286 Mon Sep 17 00:00:00 2001 +From: Sergey Zhuravlevich +Date: Tue, 12 May 2020 14:23:15 +0200 +Subject: [PATCH 03/49] MLK-24065-3: drm: bridge: cadence: use the lane mapping + from dt when setting host capabilities + +Signed-off-by: Sergey Zhuravlevich +Acked-by: Sandor Yu +Tested-By: Sandor Yu +--- + drivers/gpu/drm/bridge/cadence/cdns-dp-core.c | 2 +- + drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c | 4 ++-- + include/drm/bridge/cdns-mhdp.h | 2 +- + 3 files changed, 4 insertions(+), 4 deletions(-) + +diff --git a/drivers/gpu/drm/bridge/cadence/cdns-dp-core.c b/drivers/gpu/drm/bridge/cadence/cdns-dp-core.c +index aa92029f44e9..c059d56b4f46 100644 +--- a/drivers/gpu/drm/bridge/cadence/cdns-dp-core.c ++++ b/drivers/gpu/drm/bridge/cadence/cdns-dp-core.c +@@ -152,7 +152,7 @@ static void cdns_dp_mode_set(struct cdns_mhdp_device *mhdp) + cdns_mhdp_reg_write(mhdp, LANES_CONFIG, 0x00400000 | lane_mapping); + + /* Set DP host capability */ +- ret = cdns_mhdp_set_host_cap(mhdp, false); ++ ret = cdns_mhdp_set_host_cap(mhdp); + if (ret) { + DRM_DEV_ERROR(mhdp->dev, "Failed to set host cap %d\n", ret); + return; +diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c +index 91d1cfd4b2af..9c0a2668e494 100644 +--- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c ++++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c +@@ -438,7 +438,7 @@ int cdns_mhdp_set_firmware_active(struct cdns_mhdp_device *mhdp, bool enable) + } + EXPORT_SYMBOL(cdns_mhdp_set_firmware_active); + +-int cdns_mhdp_set_host_cap(struct cdns_mhdp_device *mhdp, bool flip) ++int cdns_mhdp_set_host_cap(struct cdns_mhdp_device *mhdp) + { + u8 msg[8]; + int ret; +@@ -449,7 +449,7 @@ int cdns_mhdp_set_host_cap(struct cdns_mhdp_device *mhdp, bool flip) + msg[3] = PRE_EMPHASIS_LEVEL_3; + msg[4] = PTS1 | PTS2 | PTS3 | PTS4; + msg[5] = FAST_LT_NOT_SUPPORT; +- msg[6] = flip ? LANE_MAPPING_FLIPPED : LANE_MAPPING_NORMAL; ++ msg[6] = mhdp->lane_mapping; + msg[7] = ENHANCED; + + ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, +diff --git a/include/drm/bridge/cdns-mhdp.h b/include/drm/bridge/cdns-mhdp.h +index d76716d4edc6..4dc6e428b5f7 100644 +--- a/include/drm/bridge/cdns-mhdp.h ++++ b/include/drm/bridge/cdns-mhdp.h +@@ -723,7 +723,7 @@ u32 cdns_mhdp_get_fw_clk(struct cdns_mhdp_device *mhdp); + int cdns_mhdp_load_firmware(struct cdns_mhdp_device *mhdp, const u32 *i_mem, + u32 i_size, const u32 *d_mem, u32 d_size); + int cdns_mhdp_set_firmware_active(struct cdns_mhdp_device *mhdp, bool enable); +-int cdns_mhdp_set_host_cap(struct cdns_mhdp_device *mhdp, bool flip); ++int cdns_mhdp_set_host_cap(struct cdns_mhdp_device *mhdp); + int cdns_mhdp_event_config(struct cdns_mhdp_device *mhdp); + u32 cdns_mhdp_get_event(struct cdns_mhdp_device *mhdp); + int cdns_mhdp_dpcd_write(struct cdns_mhdp_device *mhdp, u32 addr, u8 value); +-- +2.29.2 + diff --git a/projects/NXP/devices/iMX8/patches/linux/0004-MLK-24065-2-drm-bridge-cadence-print-error-when-cloc.patch b/projects/NXP/devices/iMX8/patches/linux/0004-MLK-24065-2-drm-bridge-cadence-print-error-when-cloc.patch new file mode 100644 index 0000000000..23aa13e191 --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0004-MLK-24065-2-drm-bridge-cadence-print-error-when-cloc.patch @@ -0,0 +1,31 @@ +From 62c1852bc0f94efb6884d34c2c27dcf1efa3b282 Mon Sep 17 00:00:00 2001 +From: Sergey Zhuravlevich +Date: Tue, 12 May 2020 14:23:15 +0200 +Subject: [PATCH 04/49] MLK-24065-2: drm: bridge: cadence: print error when + clock recovery fails + +Signed-off-by: Sergey Zhuravlevich +Acked-by: Sandor Yu +Tested-By: Sandor Yu +--- + drivers/gpu/drm/bridge/cadence/cdns-mhdp-dp.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-dp.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-dp.c +index f025c39d12ea..a032e19765a4 100644 +--- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-dp.c ++++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-dp.c +@@ -106,7 +106,9 @@ static int cdns_mhdp_training_start(struct cdns_mhdp_device *mhdp) + if (ret) + goto err_training_start; + +- if (event[1] & EQ_PHASE_FINISHED) ++ if (event[1] & CLK_RECOVERY_FAILED) ++ DRM_DEV_ERROR(mhdp->dev, "clock recovery failed\n"); ++ else if (event[1] & EQ_PHASE_FINISHED) + return 0; + } + +-- +2.29.2 + diff --git a/projects/NXP/devices/iMX8/patches/linux/0005-LF-1511-drm-cdn-cec-replace-i-with-i-in-loop.patch b/projects/NXP/devices/iMX8/patches/linux/0005-LF-1511-drm-cdn-cec-replace-i-with-i-in-loop.patch new file mode 100644 index 0000000000..d3cbe09cb7 --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0005-LF-1511-drm-cdn-cec-replace-i-with-i-in-loop.patch @@ -0,0 +1,37 @@ +From eb19fd99254d6a0aa97bb08c09b9f82ebff306c5 Mon Sep 17 00:00:00 2001 +From: Sandor Yu +Date: Fri, 19 Jun 2020 15:32:28 +0800 +Subject: [PATCH 05/49] LF-1511: drm: cdn-cec: replace ++i with i++ in loop + +replace ++i with i++ in loop to prevent Coverity issue. +Coverity ID 9000767 + +Signed-off-by: Sandor Yu +Reviewed-by: Fancy Fang +--- + drivers/gpu/drm/bridge/cadence/cdns-mhdp-cec.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-cec.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-cec.c +index 5717bb0bcb75..029ad761606a 100644 +--- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-cec.c ++++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-cec.c +@@ -163,13 +163,13 @@ static int mhdp_cec_set_logical_addr(struct cdns_mhdp_cec *cec, u32 la) + + if (la == CEC_LOG_ADDR_INVALID) + /* invalid all LA address */ +- for (i = 0; i < CEC_MAX_LOG_ADDRS; ++i) { ++ for (i = 0; i < CEC_MAX_LOG_ADDRS; i++) { + mhdp_cec_write(cec, LOGICAL_ADDRESS_LA0 + (i * REG_ADDR_OFF), 0); + return 0; + } + + /* In fact cdns mhdp cec could support max 5 La address */ +- for (i = 0; i < CEC_MAX_LOG_ADDRS; ++i) { ++ for (i = 0; i < CEC_MAX_LOG_ADDRS; i++) { + la_reg = mhdp_cec_read(cec, LOGICAL_ADDRESS_LA0 + (i * REG_ADDR_OFF)); + /* Check LA already used */ + if (la_reg & 0x10) +-- +2.29.2 + diff --git a/projects/NXP/devices/iMX8/patches/linux/0006-LF-1512-drm-cdns-mhdp-avoid-potentially-overflowing.patch b/projects/NXP/devices/iMX8/patches/linux/0006-LF-1512-drm-cdns-mhdp-avoid-potentially-overflowing.patch new file mode 100644 index 0000000000..9e78a7707e --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0006-LF-1512-drm-cdns-mhdp-avoid-potentially-overflowing.patch @@ -0,0 +1,30 @@ +From 09dfa5b8ba1a38050e4e95faab1cf07c6a509dad Mon Sep 17 00:00:00 2001 +From: Sandor Yu +Date: Fri, 19 Jun 2020 16:05:42 +0800 +Subject: [PATCH 06/49] LF-1512: drm: cdns mhdp: avoid potentially overflowing + +covert to unsigned 64 bits to avoid potentially overflowing. +Report by Coverity ID 6652952 6652952. + +Signed-off-by: Sandor Yu +Reviewed-by: Fancy Fang +--- + drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c +index 9c0a2668e494..890add9b7c67 100644 +--- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c ++++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c +@@ -657,7 +657,7 @@ int cdns_mhdp_config_video(struct cdns_mhdp_device *mhdp) + */ + do { + tu_size_reg += 2; +- symbol = tu_size_reg * mode->clock * bit_per_pix; ++ symbol = (u64) tu_size_reg * mode->clock * bit_per_pix; + do_div(symbol, mhdp->dp.num_lanes * link_rate * 8); + rem = do_div(symbol, 1000); + if (tu_size_reg > 64) { +-- +2.29.2 + diff --git a/projects/NXP/devices/iMX8/patches/linux/0007-MLK-24335-drm-bridge-cdns-hdmi-support-work-in-DVI-m.patch b/projects/NXP/devices/iMX8/patches/linux/0007-MLK-24335-drm-bridge-cdns-hdmi-support-work-in-DVI-m.patch new file mode 100644 index 0000000000..32ae11b0e6 --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0007-MLK-24335-drm-bridge-cdns-hdmi-support-work-in-DVI-m.patch @@ -0,0 +1,41 @@ +From a1b02ef19cbc24603e1e212f4f4258ca2c59aaad Mon Sep 17 00:00:00 2001 +From: Sandor Yu +Date: Thu, 18 Jun 2020 14:18:04 +0800 +Subject: [PATCH 07/49] MLK-24335: drm: bridge: cdns: hdmi support work in DVI + mode + +hdmi support work in DVI mode. + +Signed-off-by: Sandor Yu +--- + drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +diff --git a/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c +index da40f62617ef..5f2442fa761f 100644 +--- a/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c ++++ b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c +@@ -32,8 +32,9 @@ static void hdmi_sink_config(struct cdns_mhdp_device *mhdp) + struct drm_scdc *scdc = &mhdp->connector.base.display_info.hdmi.scdc; + u8 buff = 0; + +- /* Default work in HDMI1.4 */ +- mhdp->hdmi.hdmi_type = MODE_HDMI_1_4; ++ /* return if hdmi work in DVI mode */ ++ if (mhdp->hdmi.hdmi_type == MODE_DVI) ++ return; + + /* check sink support SCDC or not */ + if (scdc->supported != true) { +@@ -264,6 +265,8 @@ static int cdns_hdmi_connector_get_modes(struct drm_connector *connector) + edid->header[6], edid->header[7]); + drm_connector_update_edid_property(connector, edid); + num_modes = drm_add_edid_modes(connector, edid); ++ mhdp->hdmi.hdmi_type = drm_detect_hdmi_monitor(edid) ? ++ MODE_HDMI_1_4 : MODE_DVI; + kfree(edid); + } + +-- +2.29.2 + diff --git a/projects/NXP/devices/iMX8/patches/linux/0008-LF-1762-21-gpu-drm-bridge-cadence-hdmi-update-API-.m.patch b/projects/NXP/devices/iMX8/patches/linux/0008-LF-1762-21-gpu-drm-bridge-cadence-hdmi-update-API-.m.patch new file mode 100644 index 0000000000..114fa3184b --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0008-LF-1762-21-gpu-drm-bridge-cadence-hdmi-update-API-.m.patch @@ -0,0 +1,42 @@ +From 83f932299b9969a1823b085d2269db677362f897 Mon Sep 17 00:00:00 2001 +From: Dong Aisheng +Date: Tue, 14 Jul 2020 19:18:25 +0800 +Subject: [PATCH 08/49] LF-1762-21 gpu: drm: bridge: cadence: hdmi: update API + .mode_valid() + +API changed since: +12c683e12cd8 ("drm: bridge: Pass drm_display_info to drm_bridge_funcs .mode_valid()") + +Signed-off-by: Dong Aisheng +--- + drivers/gpu/drm/bridge/cadence/cdns-dp-core.c | 1 + + drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c | 1 + + 2 files changed, 2 insertions(+) + +diff --git a/drivers/gpu/drm/bridge/cadence/cdns-dp-core.c b/drivers/gpu/drm/bridge/cadence/cdns-dp-core.c +index c059d56b4f46..cb4897c664f0 100644 +--- a/drivers/gpu/drm/bridge/cadence/cdns-dp-core.c ++++ b/drivers/gpu/drm/bridge/cadence/cdns-dp-core.c +@@ -256,6 +256,7 @@ static int cdns_dp_bridge_attach(struct drm_bridge *bridge, + + static enum drm_mode_status + cdns_dp_bridge_mode_valid(struct drm_bridge *bridge, ++ const struct drm_display_info *info, + const struct drm_display_mode *mode) + { + enum drm_mode_status mode_status = MODE_OK; +diff --git a/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c +index 5f2442fa761f..1e5130e295f7 100644 +--- a/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c ++++ b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c +@@ -359,6 +359,7 @@ static int cdns_hdmi_bridge_attach(struct drm_bridge *bridge, + + static enum drm_mode_status + cdns_hdmi_bridge_mode_valid(struct drm_bridge *bridge, ++ const struct drm_display_info *info, + const struct drm_display_mode *mode) + { + struct cdns_mhdp_device *mhdp = bridge->driver_private; +-- +2.29.2 + diff --git a/projects/NXP/devices/iMX8/patches/linux/0009-LF-2271-1-drm-bridge-cdns-Use-colorspace-connector-p.patch b/projects/NXP/devices/iMX8/patches/linux/0009-LF-2271-1-drm-bridge-cdns-Use-colorspace-connector-p.patch new file mode 100644 index 0000000000..feaf04ee25 --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0009-LF-2271-1-drm-bridge-cdns-Use-colorspace-connector-p.patch @@ -0,0 +1,107 @@ +From 150d291f3e5cb47a97790b89e79d8f1a5aa797dd Mon Sep 17 00:00:00 2001 +From: Laurentiu Palcu +Date: Fri, 28 Aug 2020 10:26:31 +0300 +Subject: [PATCH 09/49] LF-2271-1: drm/bridge/cdns: Use colorspace connector + property for imx8mq + +This patch achieves 2 goals: + * Make use of colorspace property when setting up the color_depth and + color_fmt. The userspace can now choose which colorspace to use by changing + the colorspace property; + * Do not use drm_display_mode private_flags to signal CRTC which pixel encoding + is being used by connector. Upstream is getting rid of 'private_flags' usage + and the declaration will probably be removed in the next release; + +Signed-off-by: Laurentiu Palcu +Reviewed-by: Robert Chiras +--- + .../gpu/drm/bridge/cadence/cdns-hdmi-core.c | 58 ++++++++++++------- + 1 file changed, 36 insertions(+), 22 deletions(-) + +diff --git a/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c +index 1e5130e295f7..2796252adf68 100644 +--- a/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c ++++ b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c +@@ -412,6 +412,7 @@ bool cdns_hdmi_bridge_mode_fixup(struct drm_bridge *bridge, + struct drm_display_mode *adjusted_mode) + { + struct cdns_mhdp_device *mhdp = bridge->driver_private; ++ struct drm_connector_state *conn_state = mhdp->connector.base.state; + struct drm_display_info *di = &mhdp->connector.base.display_info; + struct video_info *video = &mhdp->video_info; + int vic = drm_match_cea_mode(mode); +@@ -428,36 +429,49 @@ bool cdns_hdmi_bridge_mode_fixup(struct drm_bridge *bridge, + } + + /* imx8mq */ +- if (vic == 97 || vic == 96) { +- if (di->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_36) +- video->color_depth = 12; +- else if (di->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30) +- video->color_depth = 10; +- +- if (drm_mode_is_420_only(di, mode) || +- (drm_mode_is_420_also(di, mode) && +- video->color_depth > 8)) { ++ if (conn_state->colorspace == DRM_MODE_COLORIMETRY_DEFAULT) ++ return !drm_mode_is_420_only(di, mode); ++ ++ if (conn_state->colorspace == DRM_MODE_COLORIMETRY_BT2020_RGB) { ++ if (drm_mode_is_420_only(di, mode)) ++ return false; ++ ++ /* 10b RGB is not supported for following VICs */ ++ if (vic == 97 || vic == 96 || vic == 95 || vic == 93 || vic == 94) ++ return false; ++ ++ video->color_depth = 10; ++ ++ return true; ++ } ++ ++ if (conn_state->colorspace == DRM_MODE_COLORIMETRY_BT2020_CYCC || ++ conn_state->colorspace == DRM_MODE_COLORIMETRY_BT2020_YCC) { ++ if (drm_mode_is_420_only(di, mode)) { + video->color_fmt = YCBCR_4_2_0; + +- adjusted_mode->private_flags = 1; ++ if (di->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_36) ++ video->color_depth = 12; ++ else if (di->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30) ++ video->color_depth = 10; ++ else ++ return false; ++ + return true; + } + +- video->color_depth = 8; +- return true; +- } ++ video->color_fmt = YCBCR_4_2_2; ++ ++ if (!(di->edid_hdmi_dc_modes & DRM_EDID_HDMI_DC_36)) ++ return false; + +- /* Any defined maximum tmds clock limit we must not exceed*/ +- if ((di->edid_hdmi_dc_modes & DRM_EDID_HDMI_DC_36) && +- (mode->clock * 3 / 2 <= di->max_tmds_clock)) + video->color_depth = 12; +- else if ((di->edid_hdmi_dc_modes & DRM_EDID_HDMI_DC_30) && +- (mode->clock * 5 / 4 <= di->max_tmds_clock)) +- video->color_depth = 10; + +- /* 10-bit color depth for the following modes is not supported */ +- if ((vic == 95 || vic == 94 || vic == 93) && video->color_depth == 10) +- video->color_depth = 8; ++ return true; ++ } ++ ++ video->color_fmt = drm_mode_is_420_only(di, mode) ? YCBCR_4_2_0 : YCBCR_4_4_4; ++ video->color_depth = 8; + + return true; + } +-- +2.29.2 + diff --git a/projects/NXP/devices/iMX8/patches/linux/0010-MLK-24770-drm-mhdp-Sync-DPTX-capability-with-Cadence.patch b/projects/NXP/devices/iMX8/patches/linux/0010-MLK-24770-drm-mhdp-Sync-DPTX-capability-with-Cadence.patch new file mode 100644 index 0000000000..efc243e6f3 --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0010-MLK-24770-drm-mhdp-Sync-DPTX-capability-with-Cadence.patch @@ -0,0 +1,34 @@ +From 04a71f1da60e51f277d4979c698e52cacb028666 Mon Sep 17 00:00:00 2001 +From: Sandor Yu +Date: Mon, 14 Sep 2020 15:06:35 +0800 +Subject: [PATCH 10/49] MLK-24770: drm: mhdp: Sync DPTX capability with Cadence + sample code + +Sync the max vswing and pre-emphasis setting with Cadence sample code. +The max vswing is VOLTAGE_LEVEL_3 and +the max pre-emphasis is PRE_EMPHASIS_LEVEL_2 now. + +Signed-off-by: Sandor Yu +Reviewed-by: Robby Cai +--- + drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c +index 890add9b7c67..2043016f176b 100644 +--- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c ++++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c +@@ -445,8 +445,8 @@ int cdns_mhdp_set_host_cap(struct cdns_mhdp_device *mhdp) + + msg[0] = drm_dp_link_rate_to_bw_code(mhdp->dp.rate); + msg[1] = mhdp->dp.num_lanes | SCRAMBLER_EN; +- msg[2] = VOLTAGE_LEVEL_2; +- msg[3] = PRE_EMPHASIS_LEVEL_3; ++ msg[2] = VOLTAGE_LEVEL_3; ++ msg[3] = PRE_EMPHASIS_LEVEL_2; + msg[4] = PTS1 | PTS2 | PTS3 | PTS4; + msg[5] = FAST_LT_NOT_SUPPORT; + msg[6] = mhdp->lane_mapping; +-- +2.29.2 + diff --git a/projects/NXP/devices/iMX8/patches/linux/0011-MLK-24520-drm-bridge-cdns-increase-maximum-width-fro.patch b/projects/NXP/devices/iMX8/patches/linux/0011-MLK-24520-drm-bridge-cdns-increase-maximum-width-fro.patch new file mode 100644 index 0000000000..baf7c0f9dc --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0011-MLK-24520-drm-bridge-cdns-increase-maximum-width-fro.patch @@ -0,0 +1,48 @@ +From 11b66e4bdb8ba6dc4e6981ecef69534c3d6d8df8 Mon Sep 17 00:00:00 2001 +From: "Oliver F. Brown" +Date: Thu, 23 Jul 2020 18:24:23 -0500 +Subject: [PATCH 11/49] MLK-24520: drm: bridge: cdns: increase maximum width + from 4096 to 5120. + +This patch increases the maximum width to 5120. + +Signed-off-by: Oliver F. Brown +Reviewed-by: Liu Ying +--- + drivers/gpu/drm/bridge/cadence/cdns-dp-core.c | 4 ++-- + drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c | 4 ++-- + 2 files changed, 4 insertions(+), 4 deletions(-) + +diff --git a/drivers/gpu/drm/bridge/cadence/cdns-dp-core.c b/drivers/gpu/drm/bridge/cadence/cdns-dp-core.c +index cb4897c664f0..0f2a38d19a57 100644 +--- a/drivers/gpu/drm/bridge/cadence/cdns-dp-core.c ++++ b/drivers/gpu/drm/bridge/cadence/cdns-dp-core.c +@@ -270,8 +270,8 @@ cdns_dp_bridge_mode_valid(struct drm_bridge *bridge, + if (mode->clock > 594000) + return MODE_CLOCK_HIGH; + +- /* 4096x2160 is not supported now */ +- if (mode->hdisplay > 3840) ++ /* 5120 x 2160 is the maximum supported resulution */ ++ if (mode->hdisplay > 5120) + return MODE_BAD_HVALUE; + + if (mode->vdisplay > 2160) +diff --git a/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c +index 2796252adf68..442df6284c49 100644 +--- a/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c ++++ b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c +@@ -375,8 +375,8 @@ cdns_hdmi_bridge_mode_valid(struct drm_bridge *bridge, + if (mode->clock > 594000) + return MODE_CLOCK_HIGH; + +- /* 4096x2160 is not supported */ +- if (mode->hdisplay > 3840 || mode->vdisplay > 2160) ++ /* 5120 x 2160 is the maximum supported resolution */ ++ if (mode->hdisplay > 5120 || mode->vdisplay > 2160) + return MODE_BAD_HVALUE; + + mhdp->valid_mode = mode; +-- +2.29.2 + diff --git a/projects/NXP/devices/iMX8/patches/linux/0012-MLK-24521-drm-bridge-hdmi-Prevent-the-driver-from-re.patch b/projects/NXP/devices/iMX8/patches/linux/0012-MLK-24521-drm-bridge-hdmi-Prevent-the-driver-from-re.patch new file mode 100644 index 0000000000..00199349fd --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0012-MLK-24521-drm-bridge-hdmi-Prevent-the-driver-from-re.patch @@ -0,0 +1,44 @@ +From 4cb4fe3262fbbf6b31731b6b076698bcf951b9a1 Mon Sep 17 00:00:00 2001 +From: "Oliver F. Brown" +Date: Fri, 24 Jul 2020 14:28:05 -0500 +Subject: [PATCH 12/49] MLK-24521: drm: bridge: hdmi: Prevent the driver from + rejecting VIC 0 modes + +iMX8QM can support the non CEA modes, iMX8M cannot support non CEA modes. +So driver should allow non CEA modes for iMX8QM. + +Signed-off-by: Oliver F. Brown +Reviewed-by: Liu Ying +--- + drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c +index 442df6284c49..a8fa559de9e9 100644 +--- a/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c ++++ b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c +@@ -364,6 +364,7 @@ cdns_hdmi_bridge_mode_valid(struct drm_bridge *bridge, + { + struct cdns_mhdp_device *mhdp = bridge->driver_private; + enum drm_mode_status mode_status = MODE_OK; ++ u32 vic; + int ret; + + /* We don't support double-clocked and Interlaced modes */ +@@ -379,6 +380,13 @@ cdns_hdmi_bridge_mode_valid(struct drm_bridge *bridge, + if (mode->hdisplay > 5120 || mode->vdisplay > 2160) + return MODE_BAD_HVALUE; + ++ /* imx8mq-hdmi does not support non CEA modes */ ++ if (!strncmp("imx8mq-hdmi", mhdp->plat_data->plat_name, 11)) { ++ vic = drm_match_cea_mode(mode); ++ if (vic == 0) ++ return MODE_BAD; ++ } ++ + mhdp->valid_mode = mode; + ret = cdns_mhdp_plat_call(mhdp, phy_video_valid); + if (ret == false) +-- +2.29.2 + diff --git a/projects/NXP/devices/iMX8/patches/linux/0013-MLK-23642-1-drm-bridge-cadence-support-HBR-and-6-cha.patch b/projects/NXP/devices/iMX8/patches/linux/0013-MLK-23642-1-drm-bridge-cadence-support-HBR-and-6-cha.patch new file mode 100644 index 0000000000..7c97e9aabe --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0013-MLK-23642-1-drm-bridge-cadence-support-HBR-and-6-cha.patch @@ -0,0 +1,116 @@ +From cd7804fc3777e0b53d69d34058fee39accc72072 Mon Sep 17 00:00:00 2001 +From: Shengjiu Wang +Date: Wed, 29 Apr 2020 17:34:07 +0800 +Subject: [PATCH 13/49] MLK-23642-1: drm: bridge: cadence: support HBR and 6 + channel + +Support HBR and 6 channel. + +For HBR, it only support compressed bitstream, sample rate +is 192kHz, and 8 channels. + +Signed-off-by: Shengjiu Wang +Reviewed-by: Viorel Suman +--- + .../gpu/drm/bridge/cadence/cdns-mhdp-audio.c | 33 ++++++++++++++----- + include/drm/bridge/cdns-mhdp.h | 1 + + 2 files changed, 26 insertions(+), 8 deletions(-) + +diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-audio.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-audio.c +index 86174fb633bc..fa1dcf781539 100644 +--- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-audio.c ++++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-audio.c +@@ -72,6 +72,8 @@ static void hdmi_audio_avi_set(struct cdns_mhdp_device *mhdp, + frame.channel_allocation = 0; + else if (channels == 4) + frame.channel_allocation = 0x3; ++ else if (channels == 6) ++ frame.channel_allocation = 0xB; + else if (channels == 8) + frame.channel_allocation = 0x13; + +@@ -143,26 +145,38 @@ static void cdns_mhdp_audio_config_i2s(struct cdns_mhdp_device *mhdp, + { + int sub_pckt_num = 1, i2s_port_en_val = 0xf, i; + int idx = select_N_index(mhdp->mode.clock); ++ int numofchannels = audio->channels; + u32 val, ncts; ++ u32 disable_port3 = 0; ++ u32 audio_type = 0x2; /* L-PCM */ ++ u32 transmission_type = 0; /* not required for L-PCM */ + +- if (audio->channels == 2) { ++ if (numofchannels == 2) { + if (mhdp->dp.num_lanes == 1) + sub_pckt_num = 2; + else + sub_pckt_num = 4; + + i2s_port_en_val = 1; +- } else if (audio->channels == 4) { ++ } else if (numofchannels == 4) { + i2s_port_en_val = 3; ++ } else if (numofchannels == 6) { ++ numofchannels = 8; ++ disable_port3 = 1; ++ } else if ((numofchannels == 8) && (audio->non_pcm)) { ++ audio_type = 0x9; /* HBR packet type */ ++ transmission_type = 0x9; /* HBR packet type */ + } + + cdns_mhdp_bus_write(0x0, mhdp, SPDIF_CTRL_ADDR); + +- cdns_mhdp_bus_write(SYNC_WR_TO_CH_ZERO, mhdp, FIFO_CNTL); ++ val = SYNC_WR_TO_CH_ZERO; ++ val |= disable_port3 << 4; ++ cdns_mhdp_bus_write(val, mhdp, FIFO_CNTL); + +- val = MAX_NUM_CH(audio->channels); +- val |= NUM_OF_I2S_PORTS(audio->channels); +- val |= AUDIO_TYPE_LPCM; ++ val = MAX_NUM_CH(numofchannels); ++ val |= NUM_OF_I2S_PORTS(numofchannels); ++ val |= audio_type << 7; + val |= CFG_SUB_PCKT_NUM(sub_pckt_num); + cdns_mhdp_bus_write(val, mhdp, SMPL2PKT_CNFG); + +@@ -173,12 +187,13 @@ static void cdns_mhdp_audio_config_i2s(struct cdns_mhdp_device *mhdp, + else + val = 2 << 9; + +- val |= AUDIO_CH_NUM(audio->channels); ++ val |= AUDIO_CH_NUM(numofchannels); + val |= I2S_DEC_PORT_EN(i2s_port_en_val); + val |= TRANS_SMPL_WIDTH_32; ++ val |= transmission_type << 13; + cdns_mhdp_bus_write(val, mhdp, AUDIO_SRC_CNFG); + +- for (i = 0; i < (audio->channels + 1) / 2; i++) { ++ for (i = 0; i < (numofchannels + 1) / 2; i++) { + if (audio->sample_width == 16) + val = (0x02 << 8) | (0x02 << 20); + else if (audio->sample_width == 24) +@@ -323,6 +338,8 @@ static int audio_hw_params(struct device *dev, void *data, + goto out; + } + ++ audio.non_pcm = params->iec.status[0] & IEC958_AES0_NONAUDIO; ++ + ret = cdns_mhdp_audio_config(mhdp, &audio); + if (!ret) + mhdp->audio_info = audio; +diff --git a/include/drm/bridge/cdns-mhdp.h b/include/drm/bridge/cdns-mhdp.h +index 4dc6e428b5f7..1f8fd024cdfa 100644 +--- a/include/drm/bridge/cdns-mhdp.h ++++ b/include/drm/bridge/cdns-mhdp.h +@@ -564,6 +564,7 @@ struct audio_info { + int channels; + int sample_width; + int connector_type; ++ bool non_pcm; + }; + + enum vic_pxl_encoding_format { +-- +2.29.2 + diff --git a/projects/NXP/devices/iMX8/patches/linux/0014-MLK-24611-2-drm-bridge-cdns-Add-callback-function-fo.patch b/projects/NXP/devices/iMX8/patches/linux/0014-MLK-24611-2-drm-bridge-cdns-Add-callback-function-fo.patch new file mode 100644 index 0000000000..15b597e177 --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0014-MLK-24611-2-drm-bridge-cdns-Add-callback-function-fo.patch @@ -0,0 +1,157 @@ +From 46bf1dc2ba34440e8f83b3f70e3e4d6b3f9e6183 Mon Sep 17 00:00:00 2001 +From: Shengjiu Wang +Date: Mon, 31 Aug 2020 14:50:29 +0800 +Subject: [PATCH 14/49] MLK-24611-2: drm: bridge: cdns: Add callback function + for plug/unplug event + +cdns-hdmi-core exports a function cdns_hdmi_set_plugged_cb so +platform device can register the callback + +implement hook_plugged_cb to register callback function for hdmi cable +plug/unplug event. + +Signed-off-by: Shengjiu Wang +Reviewed-by: Sandor Yu +--- + .../gpu/drm/bridge/cadence/cdns-hdmi-core.c | 41 +++++++++++++++++-- + .../gpu/drm/bridge/cadence/cdns-mhdp-audio.c | 10 +++++ + include/drm/bridge/cdns-mhdp.h | 6 +++ + 3 files changed, 54 insertions(+), 3 deletions(-) + +diff --git a/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c +index a8fa559de9e9..5890da8aa1a1 100644 +--- a/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c ++++ b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c +@@ -225,11 +225,35 @@ void cdns_hdmi_mode_set(struct cdns_mhdp_device *mhdp) + } + } + ++static void handle_plugged_change(struct cdns_mhdp_device *mhdp, bool plugged) ++{ ++ if (mhdp->plugged_cb && mhdp->codec_dev) ++ mhdp->plugged_cb(mhdp->codec_dev, plugged); ++} ++ ++int cdns_hdmi_set_plugged_cb(struct cdns_mhdp_device *mhdp, ++ hdmi_codec_plugged_cb fn, ++ struct device *codec_dev) ++{ ++ bool plugged; ++ ++ mutex_lock(&mhdp->lock); ++ mhdp->plugged_cb = fn; ++ mhdp->codec_dev = codec_dev; ++ plugged = mhdp->last_connector_result == connector_status_connected; ++ handle_plugged_change(mhdp, plugged); ++ mutex_unlock(&mhdp->lock); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(cdns_hdmi_set_plugged_cb); ++ + static enum drm_connector_status + cdns_hdmi_connector_detect(struct drm_connector *connector, bool force) + { + struct cdns_mhdp_device *mhdp = + container_of(connector, struct cdns_mhdp_device, connector.base); ++ enum drm_connector_status result; + + u8 hpd = 0xf; + +@@ -237,15 +261,25 @@ cdns_hdmi_connector_detect(struct drm_connector *connector, bool force) + + if (hpd == 1) + /* Cable Connected */ +- return connector_status_connected; ++ result = connector_status_connected; + else if (hpd == 0) + /* Cable Disconnedted */ +- return connector_status_disconnected; ++ result = connector_status_disconnected; + else { + /* Cable status unknown */ + DRM_INFO("Unknow cable status, hdp=%u\n", hpd); +- return connector_status_unknown; ++ result = connector_status_unknown; ++ } ++ ++ mutex_lock(&mhdp->lock); ++ if (result != mhdp->last_connector_result) { ++ handle_plugged_change(mhdp, ++ result == connector_status_connected); ++ mhdp->last_connector_result = result; + } ++ mutex_unlock(&mhdp->lock); ++ ++ return result; + } + + static int cdns_hdmi_connector_get_modes(struct drm_connector *connector) +@@ -624,6 +658,7 @@ static int __cdns_hdmi_probe(struct platform_device *pdev, + #ifdef CONFIG_OF + mhdp->bridge.base.of_node = dev->of_node; + #endif ++ mhdp->last_connector_result = connector_status_disconnected; + + memset(&pdevinfo, 0, sizeof(pdevinfo)); + pdevinfo.parent = dev; +diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-audio.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-audio.c +index fa1dcf781539..f4f3f9ca437c 100644 +--- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-audio.c ++++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-audio.c +@@ -380,11 +380,21 @@ static int audio_get_eld(struct device *dev, void *data, + return 0; + } + ++static int audio_hook_plugged_cb(struct device *dev, void *data, ++ hdmi_codec_plugged_cb fn, ++ struct device *codec_dev) ++{ ++ struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev); ++ ++ return cdns_hdmi_set_plugged_cb(mhdp, fn, codec_dev); ++} ++ + 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, ++ .hook_plugged_cb = audio_hook_plugged_cb, + }; + + int cdns_mhdp_register_audio_driver(struct device *dev) +diff --git a/include/drm/bridge/cdns-mhdp.h b/include/drm/bridge/cdns-mhdp.h +index 1f8fd024cdfa..6bfd82a3d9a2 100644 +--- a/include/drm/bridge/cdns-mhdp.h ++++ b/include/drm/bridge/cdns-mhdp.h +@@ -22,6 +22,7 @@ + #include + #include + #include ++#include + + #define ADDR_IMEM 0x10000 + #define ADDR_DMEM 0x20000 +@@ -714,6 +715,9 @@ struct cdns_mhdp_device { + }; + const struct cdns_plat_data *plat_data; + ++ hdmi_codec_plugged_cb plugged_cb; ++ struct device *codec_dev; ++ enum drm_connector_status last_connector_result; + }; + + u32 cdns_mhdp_bus_read(struct cdns_mhdp_device *mhdp, u32 offset); +@@ -796,6 +800,8 @@ void cdns_dp_remove(struct platform_device *pdev); + void cdns_dp_unbind(struct device *dev); + int cdns_dp_bind(struct platform_device *pdev, + struct drm_encoder *encoder, struct cdns_mhdp_device *mhdp); ++int cdns_hdmi_set_plugged_cb(struct cdns_mhdp_device *mhdp, hdmi_codec_plugged_cb fn, ++ struct device *codec_dev); + + /* CEC */ + #ifdef CONFIG_DRM_CDNS_HDMI_CEC +-- +2.29.2 + diff --git a/projects/NXP/devices/iMX8/patches/linux/0015-gpu-drm-dridge-hdp-audio-change-to-mute_stream.patch b/projects/NXP/devices/iMX8/patches/linux/0015-gpu-drm-dridge-hdp-audio-change-to-mute_stream.patch new file mode 100644 index 0000000000..9745c16806 --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0015-gpu-drm-dridge-hdp-audio-change-to-mute_stream.patch @@ -0,0 +1,43 @@ +From 1b0a179061890c0a2f6748426f03e8cd2176d3e2 Mon Sep 17 00:00:00 2001 +From: Dong Aisheng +Date: Wed, 5 Aug 2020 21:31:04 +0800 +Subject: [PATCH 15/49] gpu: drm: dridge: hdp-audio: change to mute_stream + +To cope with upstream API change: +e2978c45e5ed ("ASoC: soc-dai: remove .digital_mute") + +Signed-off-by: Dong Aisheng +--- + drivers/gpu/drm/bridge/cadence/cdns-mhdp-audio.c | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-audio.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-audio.c +index f4f3f9ca437c..85f526175439 100644 +--- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-audio.c ++++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-audio.c +@@ -358,8 +358,8 @@ static void audio_shutdown(struct device *dev, void *data) + mhdp->audio_info.format = AFMT_UNUSED; + } + +-static int audio_digital_mute(struct device *dev, void *data, +- bool enable) ++static int audio_mute_stream(struct device *dev, void *data, ++ bool enable, int direction) + { + struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev); + int ret; +@@ -392,9 +392,10 @@ static int audio_hook_plugged_cb(struct device *dev, void *data, + static const struct hdmi_codec_ops audio_codec_ops = { + .hw_params = audio_hw_params, + .audio_shutdown = audio_shutdown, +- .digital_mute = audio_digital_mute, ++ .mute_stream = audio_mute_stream, + .get_eld = audio_get_eld, + .hook_plugged_cb = audio_hook_plugged_cb, ++ .no_capture_mute = 1, + }; + + int cdns_mhdp_register_audio_driver(struct device *dev) +-- +2.29.2 + diff --git a/projects/NXP/devices/iMX8/patches/linux/0016-LF-2744-drm-cdns-reset-force_mode_set-flag-in-atomic.patch b/projects/NXP/devices/iMX8/patches/linux/0016-LF-2744-drm-cdns-reset-force_mode_set-flag-in-atomic.patch new file mode 100644 index 0000000000..6be40ad2f3 --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0016-LF-2744-drm-cdns-reset-force_mode_set-flag-in-atomic.patch @@ -0,0 +1,40 @@ +From 4a406e182a709718a769c37d33530ed2e6b23b39 Mon Sep 17 00:00:00 2001 +From: Sandor Yu +Date: Tue, 17 Nov 2020 15:47:36 +0800 +Subject: [PATCH 16/49] LF-2744: drm: cdns: reset force_mode_set flag in + atomic_check + +Reset force_mode_set flag in atomic_check function +to avoid set mode_changed flag multi times when cable plugin. + +Signed-off-by: Sandor Yu +Reviewed-by: Robby Cai +--- + drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +diff --git a/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c +index 5890da8aa1a1..e796c2c0e895 100644 +--- a/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c ++++ b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c +@@ -1,7 +1,7 @@ + /* + * Cadence High-Definition Multimedia Interface (HDMI) driver + * +- * Copyright (C) 2019 NXP Semiconductor, Inc. ++ * Copyright (C) 2019-2020 NXP Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by +@@ -445,8 +445,6 @@ static void cdns_hdmi_bridge_mode_set(struct drm_bridge *bridge, + mutex_lock(&mhdp->lock); + cdns_hdmi_mode_set(mhdp); + mutex_unlock(&mhdp->lock); +- /* reset force mode set flag */ +- mhdp->force_mode_set = false; + } + + bool cdns_hdmi_bridge_mode_fixup(struct drm_bridge *bridge, +-- +2.29.2 + diff --git a/projects/NXP/devices/iMX8/patches/linux/0017-MLK-24081-03-drm-bridge-cdns-cec-support-hdmi-rx-cec.patch b/projects/NXP/devices/iMX8/patches/linux/0017-MLK-24081-03-drm-bridge-cdns-cec-support-hdmi-rx-cec.patch new file mode 100644 index 0000000000..69014b43b7 --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0017-MLK-24081-03-drm-bridge-cdns-cec-support-hdmi-rx-cec.patch @@ -0,0 +1,271 @@ +From f7f5ec54b815df2c9a92f0fd6edea4f5d0700937 Mon Sep 17 00:00:00 2001 +From: Sandor Yu +Date: Mon, 16 Nov 2020 10:56:44 +0800 +Subject: [PATCH 17/49] MLK-24081-03: drm: bridge: cdns-cec: support hdmi rx + cec + +Create struct cdns_mhdp_cec and cec specific bus_read/write function. +CEC driver could be reuse by hdmi rx. + +Signed-off-by: Sandor Yu +Reviewed-by: Robby Cai +--- + drivers/gpu/drm/bridge/cadence/Kconfig | 1 - + .../gpu/drm/bridge/cadence/cdns-hdmi-core.c | 18 ++++- + .../gpu/drm/bridge/cadence/cdns-mhdp-cec.c | 66 +++++++++++++------ + .../gpu/drm/bridge/cadence/cdns-mhdp-common.c | 6 -- + include/drm/bridge/cdns-mhdp.h | 19 +++--- + 5 files changed, 72 insertions(+), 38 deletions(-) + +diff --git a/drivers/gpu/drm/bridge/cadence/Kconfig b/drivers/gpu/drm/bridge/cadence/Kconfig +index bb1865b15aca..c271ab24a99a 100644 +--- a/drivers/gpu/drm/bridge/cadence/Kconfig ++++ b/drivers/gpu/drm/bridge/cadence/Kconfig +@@ -45,6 +45,5 @@ config DRM_CDNS_AUDIO + + config DRM_CDNS_HDMI_CEC + tristate "Cadence MHDP HDMI CEC driver" +- depends on DRM_CDNS_HDMI + select CEC_CORE + select CEC_NOTIFIER +diff --git a/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c +index e796c2c0e895..84c175997740 100644 +--- a/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c ++++ b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c +@@ -569,6 +569,19 @@ static void cdns_hdmi_parse_dt(struct cdns_mhdp_device *mhdp) + dev_info(mhdp->dev, "lane-mapping 0x%02x\n", mhdp->lane_mapping); + } + ++#ifdef CONFIG_DRM_CDNS_HDMI_CEC ++static void cdns_mhdp_cec_init(struct cdns_mhdp_device *mhdp) ++{ ++ struct cdns_mhdp_cec *cec = &mhdp->hdmi.cec; ++ ++ cec->dev = mhdp->dev; ++ cec->iolock = &mhdp->iolock; ++ cec->regs_base = mhdp->regs_base; ++ cec->regs_sec = mhdp->regs_sec; ++ cec->bus_type = mhdp->bus_type; ++} ++#endif ++ + static int __cdns_hdmi_probe(struct platform_device *pdev, + struct cdns_mhdp_device *mhdp) + { +@@ -669,7 +682,8 @@ static int __cdns_hdmi_probe(struct platform_device *pdev, + + /* register cec driver */ + #ifdef CONFIG_DRM_CDNS_HDMI_CEC +- cdns_mhdp_register_cec_driver(dev); ++ cdns_mhdp_cec_init(mhdp); ++ cdns_mhdp_register_cec_driver(&mhdp->hdmi.cec); + #endif + + return 0; +@@ -679,7 +693,7 @@ static void __cdns_hdmi_remove(struct cdns_mhdp_device *mhdp) + { + /* unregister cec driver */ + #ifdef CONFIG_DRM_CDNS_HDMI_CEC +- cdns_mhdp_unregister_cec_driver(mhdp->dev); ++ cdns_mhdp_unregister_cec_driver(&mhdp->hdmi.cec); + #endif + cdns_mhdp_unregister_audio_driver(mhdp->dev); + } +diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-cec.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-cec.c +index 029ad761606a..25cf9e91e64f 100644 +--- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-cec.c ++++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-cec.c +@@ -1,5 +1,5 @@ + /* +- * Copyright 2019 NXP ++ * Copyright 2019-2020 NXP + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License +@@ -74,16 +74,49 @@ enum { + + static u32 mhdp_cec_read(struct cdns_mhdp_cec *cec, u32 offset) + { +- struct cdns_mhdp_device *mhdp = +- container_of(cec, struct cdns_mhdp_device, hdmi.cec); +- return cdns_mhdp_bus_read(mhdp, offset); ++ u32 val; ++ ++ mutex_lock(cec->iolock); ++ ++ if (cec->bus_type == BUS_TYPE_LOW4K_HDMI_RX) { ++ /* Remap address to low 4K HDMI RX */ ++ writel(offset >> 12, cec->regs_sec + 4); ++ val = readl((offset & 0xfff) + cec->regs_base); ++ } else if (cec->bus_type == BUS_TYPE_LOW4K_APB) { ++ /* Remap address to low 4K memory */ ++ writel(offset >> 12, cec->regs_sec + 8); ++ val = readl((offset & 0xfff) + cec->regs_base); ++ } else ++ val = readl(cec->regs_base + offset); ++ ++ mutex_unlock(cec->iolock); ++ ++ return val; + } + + static void mhdp_cec_write(struct cdns_mhdp_cec *cec, u32 offset, u32 val) + { +- struct cdns_mhdp_device *mhdp = +- container_of(cec, struct cdns_mhdp_device, hdmi.cec); +- cdns_mhdp_bus_write(val, mhdp, offset); ++ mutex_lock(cec->iolock); ++ ++ if (cec->bus_type == BUS_TYPE_LOW4K_HDMI_RX) { ++ /* Remap address to low 4K SAPB bus */ ++ writel(offset >> 12, cec->regs_sec + 4); ++ writel(val, (offset & 0xfff) + cec->regs_base); ++ } else if (cec->bus_type == BUS_TYPE_LOW4K_APB) { ++ /* Remap address to low 4K memory */ ++ writel(offset >> 12, cec->regs_sec + 8); ++ writel(val, (offset & 0xfff) + cec->regs_base); ++ } else if (cec->bus_type == BUS_TYPE_NORMAL_SAPB) ++ writel(val, cec->regs_sec + offset); ++ else ++ writel(val, cec->regs_base + offset); ++ ++ mutex_unlock(cec->iolock); ++} ++ ++static u32 mhdp_get_fw_clk(struct cdns_mhdp_cec *cec) ++{ ++ return mhdp_cec_read(cec, SW_CLK_H); + } + + static void mhdp_cec_clear_rx_buffer(struct cdns_mhdp_cec *cec) +@@ -94,12 +127,10 @@ static void mhdp_cec_clear_rx_buffer(struct cdns_mhdp_cec *cec) + + static void mhdp_cec_set_divider(struct cdns_mhdp_cec *cec) + { +- struct cdns_mhdp_device *mhdp = +- container_of(cec, struct cdns_mhdp_device, hdmi.cec); + u32 clk_div; + + /* Set clock divider */ +- clk_div = cdns_mhdp_get_fw_clk(mhdp) * 10; ++ clk_div = mhdp_get_fw_clk(cec) * 10; + + mhdp_cec_write(cec, CLK_DIV_MSB, + (clk_div >> 8) & 0xFF); +@@ -291,10 +322,8 @@ static const struct cec_adap_ops cdns_mhdp_cec_adap_ops = { + .adap_transmit = mhdp_cec_adap_transmit, + }; + +-int cdns_mhdp_register_cec_driver(struct device *dev) ++int cdns_mhdp_register_cec_driver(struct cdns_mhdp_cec *cec) + { +- struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev); +- struct cdns_mhdp_cec *cec = &mhdp->hdmi.cec; + int ret; + + cec->adap = cec_allocate_adapter(&cdns_mhdp_cec_adap_ops, cec, +@@ -305,29 +334,24 @@ int cdns_mhdp_register_cec_driver(struct device *dev) + ret = PTR_ERR_OR_ZERO(cec->adap); + if (ret) + return ret; +- ret = cec_register_adapter(cec->adap, dev); ++ ret = cec_register_adapter(cec->adap, cec->dev); + if (ret) { + cec_delete_adapter(cec->adap); + return ret; + } + +- cec->dev = dev; +- + cec->cec_worker = kthread_create(mhdp_cec_poll_worker, cec, "cdns-mhdp-cec"); + if (IS_ERR(cec->cec_worker)) + dev_err(cec->dev, "failed create hdp cec thread\n"); + + wake_up_process(cec->cec_worker); + +- dev_dbg(dev, "CEC successfuly probed\n"); ++ dev_dbg(cec->dev, "CEC successfuly probed\n"); + return 0; + } + +-int cdns_mhdp_unregister_cec_driver(struct device *dev) ++int cdns_mhdp_unregister_cec_driver(struct cdns_mhdp_cec *cec) + { +- struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev); +- struct cdns_mhdp_cec *cec = &mhdp->hdmi.cec; +- + if (cec->cec_worker) { + kthread_stop(cec->cec_worker); + cec->cec_worker = NULL; +diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c +index 2043016f176b..ff37cc4e57e6 100644 +--- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c ++++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c +@@ -99,12 +99,6 @@ void cdns_mhdp_bus_write(u32 val, struct cdns_mhdp_device *mhdp, u32 offset) + } + EXPORT_SYMBOL(cdns_mhdp_bus_write); + +-u32 cdns_mhdp_get_fw_clk(struct cdns_mhdp_device *mhdp) +-{ +- return cdns_mhdp_bus_read(mhdp, SW_CLK_H); +-} +-EXPORT_SYMBOL(cdns_mhdp_get_fw_clk); +- + void cdns_mhdp_set_fw_clk(struct cdns_mhdp_device *mhdp, unsigned long clk) + { + cdns_mhdp_bus_write(clk / 1000000, mhdp, SW_CLK_H); +diff --git a/include/drm/bridge/cdns-mhdp.h b/include/drm/bridge/cdns-mhdp.h +index 6bfd82a3d9a2..338fa55b8bdf 100644 +--- a/include/drm/bridge/cdns-mhdp.h ++++ b/include/drm/bridge/cdns-mhdp.h +@@ -509,6 +509,7 @@ enum { + BUS_TYPE_NORMAL_SAPB = 1, + BUS_TYPE_LOW4K_APB = 2, + BUS_TYPE_LOW4K_SAPB = 3, ++ BUS_TYPE_LOW4K_HDMI_RX = 4, + }; + + enum voltage_swing_level { +@@ -623,12 +624,15 @@ struct cdns_mhdp_connector { + }; + + struct cdns_mhdp_cec { +- struct cec_adapter *adap; +- struct device *dev; +- struct mutex lock; ++ struct cec_adapter *adap; ++ struct device *dev; ++ struct mutex *iolock; ++ void __iomem *regs_base; ++ void __iomem *regs_sec; ++ int bus_type; + +- struct cec_msg msg; +- struct task_struct *cec_worker; ++ struct cec_msg msg; ++ struct task_struct *cec_worker; + }; + + struct cdns_plat_data { +@@ -724,7 +728,6 @@ u32 cdns_mhdp_bus_read(struct cdns_mhdp_device *mhdp, u32 offset); + void cdns_mhdp_bus_write(u32 val, struct cdns_mhdp_device *mhdp, u32 offset); + void cdns_mhdp_clock_reset(struct cdns_mhdp_device *mhdp); + void cdns_mhdp_set_fw_clk(struct cdns_mhdp_device *mhdp, unsigned long clk); +-u32 cdns_mhdp_get_fw_clk(struct cdns_mhdp_device *mhdp); + int cdns_mhdp_load_firmware(struct cdns_mhdp_device *mhdp, const u32 *i_mem, + u32 i_size, const u32 *d_mem, u32 d_size); + int cdns_mhdp_set_firmware_active(struct cdns_mhdp_device *mhdp, bool enable); +@@ -805,8 +808,8 @@ int cdns_hdmi_set_plugged_cb(struct cdns_mhdp_device *mhdp, hdmi_codec_plugged_c + + /* CEC */ + #ifdef CONFIG_DRM_CDNS_HDMI_CEC +-int cdns_mhdp_register_cec_driver(struct device *dev); +-int cdns_mhdp_unregister_cec_driver(struct device *dev); ++int cdns_mhdp_register_cec_driver(struct cdns_mhdp_cec *cec); ++int cdns_mhdp_unregister_cec_driver(struct cdns_mhdp_cec *cec); + #endif + + #endif /* CDNS_MHDP_H_ */ +-- +2.29.2 + diff --git a/projects/NXP/devices/iMX8/patches/linux/0018-MLK-25199-3-drm-bridge-mhdp_common-add-apb-config-fu.patch b/projects/NXP/devices/iMX8/patches/linux/0018-MLK-25199-3-drm-bridge-mhdp_common-add-apb-config-fu.patch new file mode 100644 index 0000000000..100881ce2a --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0018-MLK-25199-3-drm-bridge-mhdp_common-add-apb-config-fu.patch @@ -0,0 +1,143 @@ +From 0021b4b1afc0d88c013e2484009004b19bc2ece4 Mon Sep 17 00:00:00 2001 +From: Sandor Yu +Date: Wed, 30 Dec 2020 16:04:20 +0800 +Subject: [PATCH 18/49] MLK-25199-3: drm: bridge: mhdp_common: add apb config + function + +Add apb config function, move mhdp poll function +to mhdp head file, they will be used by hdcp driver. + +Signed-off-by: Sandor Yu +Reviewed-by: Robby Cai +--- + .../gpu/drm/bridge/cadence/cdns-mhdp-common.c | 54 ++++++++++--------- + drivers/gpu/drm/bridge/cadence/cdns-mhdp.h | 25 ++++++++- + 2 files changed, 54 insertions(+), 25 deletions(-) + +diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c +index ff37cc4e57e6..2a8ab0872f25 100644 +--- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c ++++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c +@@ -27,31 +27,10 @@ + #include + #include + ++#include "cdns-mhdp.h" ++ + #define CDNS_DP_SPDIF_CLK 200000000 + #define FW_ALIVE_TIMEOUT_US 1000000 +-#define MAILBOX_RETRY_US 1000 +-#define MAILBOX_TIMEOUT_US 5000000 +- +-#define mhdp_readx_poll_timeout(op, addr, offset, val, cond, sleep_us, timeout_us) \ +-({ \ +- u64 __timeout_us = (timeout_us); \ +- unsigned long __sleep_us = (sleep_us); \ +- ktime_t __timeout = ktime_add_us(ktime_get(), __timeout_us); \ +- might_sleep_if((__sleep_us) != 0); \ +- for (;;) { \ +- (val) = op(addr, offset); \ +- if (cond) \ +- break; \ +- if (__timeout_us && \ +- ktime_compare(ktime_get(), __timeout) > 0) { \ +- (val) = op(addr, offset); \ +- break; \ +- } \ +- if (__sleep_us) \ +- usleep_range((__sleep_us >> 2) + 1, __sleep_us); \ +- } \ +- (cond) ? 0 : -ETIMEDOUT; \ +-}) + + u32 cdns_mhdp_bus_read(struct cdns_mhdp_device *mhdp, u32 offset) + { +@@ -174,7 +153,7 @@ bool cdns_mhdp_check_alive(struct cdns_mhdp_device *mhdp) + } + EXPORT_SYMBOL(cdns_mhdp_check_alive); + +-static int mhdp_mailbox_read(struct cdns_mhdp_device *mhdp) ++int mhdp_mailbox_read(struct cdns_mhdp_device *mhdp) + { + int val, ret; + +@@ -432,6 +411,33 @@ int cdns_mhdp_set_firmware_active(struct cdns_mhdp_device *mhdp, bool enable) + } + EXPORT_SYMBOL(cdns_mhdp_set_firmware_active); + ++int cdns_mhdp_apb_conf(struct cdns_mhdp_device *mhdp, u8 sel) ++{ ++ u8 status; ++ int ret; ++ ++ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_GENERAL, GENERAL_BUS_SETTINGS, ++ sizeof(sel), &sel); ++ if (ret) ++ goto err_apb_conf; ++ ++ ret = cdns_mhdp_mailbox_validate_receive(mhdp, MB_MODULE_ID_GENERAL, ++ GENERAL_BUS_SETTINGS, sizeof(status)); ++ if (ret) ++ goto err_apb_conf; ++ ++ ret = cdns_mhdp_mailbox_read_receive(mhdp, &status, sizeof(status)); ++ if (ret) ++ goto err_apb_conf; ++ ++ return status; ++ ++err_apb_conf: ++ DRM_ERROR("apb conf failed: %d\n", ret); ++ return ret; ++} ++EXPORT_SYMBOL(cdns_mhdp_apb_conf); ++ + int cdns_mhdp_set_host_cap(struct cdns_mhdp_device *mhdp) + { + u8 msg[8]; +diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp.h b/drivers/gpu/drm/bridge/cadence/cdns-mhdp.h +index 399c3f6f86ad..8ad99eb8f86e 100644 +--- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp.h ++++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp.h +@@ -174,7 +174,6 @@ + #define CDNS_DP_MTPH_STATUS 0x226C + #define CDNS_DP_MTPH_ACT_STATUS BIT(0) + +- + #define CDNS_DPTX_GLOBAL 0x02300 + #define CDNS_DP_LANE_EN (CDNS_DPTX_GLOBAL + 0x00) + #define CDNS_DP_LANE_EN_LANES(x) GENMASK(x - 1, 0) +@@ -187,6 +186,30 @@ + + #define CDNS_MHDP_MAX_STREAMS 4 + ++#define MAILBOX_RETRY_US 1000 ++#define MAILBOX_TIMEOUT_US 5000000 ++ ++#define mhdp_readx_poll_timeout(op, addr, offset, val, cond, sleep_us, timeout_us) \ ++({ \ ++ u64 __timeout_us = (timeout_us); \ ++ unsigned long __sleep_us = (sleep_us); \ ++ ktime_t __timeout = ktime_add_us(ktime_get(), __timeout_us); \ ++ might_sleep_if((__sleep_us) != 0); \ ++ for (;;) { \ ++ (val) = op(addr, offset); \ ++ if (cond) \ ++ break; \ ++ if (__timeout_us && \ ++ ktime_compare(ktime_get(), __timeout) > 0) { \ ++ (val) = op(addr, offset); \ ++ break; \ ++ } \ ++ if (__sleep_us) \ ++ usleep_range((__sleep_us >> 2) + 1, __sleep_us); \ ++ } \ ++ (cond) ? 0 : -ETIMEDOUT; \ ++}) ++ + enum pixel_format { + PIXEL_FORMAT_RGB = 1, + PIXEL_FORMAT_YCBCR_444 = 2, +-- +2.29.2 + diff --git a/projects/NXP/devices/iMX8/patches/linux/0019-MLK-25199-4-drm-bridge-mhdp_hdmi-set-clear-avmute-bi.patch b/projects/NXP/devices/iMX8/patches/linux/0019-MLK-25199-4-drm-bridge-mhdp_hdmi-set-clear-avmute-bi.patch new file mode 100644 index 0000000000..4da60d1acc --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0019-MLK-25199-4-drm-bridge-mhdp_hdmi-set-clear-avmute-bi.patch @@ -0,0 +1,38 @@ +From 1793e95601a15f93e5d9e2846281f86eb19e8fe4 Mon Sep 17 00:00:00 2001 +From: Sandor Yu +Date: Wed, 30 Dec 2020 16:05:29 +0800 +Subject: [PATCH 19/49] MLK-25199-4: drm: bridge: mhdp_hdmi: set clear avmute + bit + +Sync HDMI TX configuation with 4.14 hdmi driver. +Clear avmute bit must be set otherwise imx8qm hdcp not work. + +Signed-off-by: Sandor Yu +Reviewed-by: Robby Cai +--- + drivers/gpu/drm/bridge/cadence/cdns-mhdp-hdmi.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-hdmi.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-hdmi.c +index c37a7ac6af9b..3ff43f7fb0a6 100644 +--- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-hdmi.c ++++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-hdmi.c +@@ -1,5 +1,5 @@ + /* +- * Copyright (C) 2019 NXP Semiconductor, Inc. ++ * Copyright (C) 2019-2021 NXP Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by +@@ -205,7 +205,7 @@ int cdns_hdmi_ctrl_init(struct cdns_mhdp_device *mhdp, + + /* set hdmi mode and preemble mode data enable */ + val = F_HDMI_MODE(protocol) | F_HDMI2_PREAMBLE_EN(1) | F_DATA_EN(1) | +- F_HDMI2_CTRL_IL_MODE(1) | F_BCH_EN(1) | F_PIC_3D(0XF); ++ F_HDMI2_CTRL_IL_MODE(1) | F_BCH_EN(1) | F_PIC_3D(0XF) | F_CLEAR_AVMUTE(1); + ret = cdns_mhdp_reg_write(mhdp, HDTX_CONTROLLER, val); + + return ret; +-- +2.29.2 + diff --git a/projects/NXP/devices/iMX8/patches/linux/0020-MLK-25199-5-drm-bridge-mhdp_hdcp-add-HDMI-TX-HDCP-dr.patch b/projects/NXP/devices/iMX8/patches/linux/0020-MLK-25199-5-drm-bridge-mhdp_hdcp-add-HDMI-TX-HDCP-dr.patch new file mode 100644 index 0000000000..9143889888 --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0020-MLK-25199-5-drm-bridge-mhdp_hdcp-add-HDMI-TX-HDCP-dr.patch @@ -0,0 +1,2006 @@ +From 9d940175bbffc82b5ec70b195312c6f32b35f51f Mon Sep 17 00:00:00 2001 +From: Sandor Yu +Date: Wed, 30 Dec 2020 16:07:41 +0800 +Subject: [PATCH 20/49] MLK-25199-5: drm: bridge: mhdp_hdcp: add HDMI TX HDCP + driver + +This patch adds an initial HDMI TX HDCP driver +for Cadence MHDP HDMI TX hardware. + +Both HDCP2.2 and HDCP1.4 are supported. + +HDCP function could be enabled by command: +modetest -w CONNECTOR_ID:"Content Protection":1 + +Signed-off-by: Sandor Yu +Reviewed-by: Robby Cai +--- + drivers/gpu/drm/bridge/cadence/Kconfig | 4 + + drivers/gpu/drm/bridge/cadence/Makefile | 1 + + .../gpu/drm/bridge/cadence/cdns-hdmi-core.c | 190 ++- + .../gpu/drm/bridge/cadence/cdns-hdmi-hdcp.c | 1167 +++++++++++++++++ + .../gpu/drm/bridge/cadence/cdns-mhdp-hdcp.c | 300 +++++ + .../gpu/drm/bridge/cadence/cdns-mhdp-hdcp.h | 36 + + include/drm/bridge/cdns-mhdp.h | 92 +- + 7 files changed, 1776 insertions(+), 14 deletions(-) + create mode 100644 drivers/gpu/drm/bridge/cadence/cdns-hdmi-hdcp.c + create mode 100644 drivers/gpu/drm/bridge/cadence/cdns-mhdp-hdcp.c + create mode 100644 drivers/gpu/drm/bridge/cadence/cdns-mhdp-hdcp.h + +diff --git a/drivers/gpu/drm/bridge/cadence/Kconfig b/drivers/gpu/drm/bridge/cadence/Kconfig +index c271ab24a99a..4c27836eb367 100644 +--- a/drivers/gpu/drm/bridge/cadence/Kconfig ++++ b/drivers/gpu/drm/bridge/cadence/Kconfig +@@ -43,6 +43,10 @@ config DRM_CDNS_AUDIO + tristate "Cadence MHDP Audio driver" + depends on DRM_CDNS_MHDP + ++config DRM_CDNS_HDMI_HDCP ++ tristate "Cadence MHDP HDMI HDCP driver" ++ depends on DRM_CDNS_HDMI ++ + config DRM_CDNS_HDMI_CEC + tristate "Cadence MHDP HDMI CEC driver" + select CEC_CORE +diff --git a/drivers/gpu/drm/bridge/cadence/Makefile b/drivers/gpu/drm/bridge/cadence/Makefile +index 618290870ba5..1b824252ae76 100644 +--- a/drivers/gpu/drm/bridge/cadence/Makefile ++++ b/drivers/gpu/drm/bridge/cadence/Makefile +@@ -8,6 +8,7 @@ cdns_mhdp_drmcore-y := cdns-mhdp-common.o cdns-mhdp-dp.o cdns-mhdp-hdmi.o + cdns_mhdp_drmcore-$(CONFIG_DRM_CDNS_HDMI) += cdns-hdmi-core.o + cdns_mhdp_drmcore-$(CONFIG_DRM_CDNS_DP) += cdns-dp-core.o + cdns_mhdp_drmcore-$(CONFIG_DRM_CDNS_AUDIO) += cdns-mhdp-audio.o ++cdns_mhdp_drmcore-$(CONFIG_DRM_CDNS_HDMI_HDCP) += cdns-mhdp-hdcp.o cdns-hdmi-hdcp.o + cdns_mhdp_drmcore-$(CONFIG_DRM_CDNS_HDMI_CEC) += cdns-mhdp-cec.o + + obj-$(CONFIG_DRM_CDNS_MHDP) += cdns_mhdp_drmcore.o +diff --git a/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c +index 84c175997740..dc393f6b75e7 100644 +--- a/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c ++++ b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c +@@ -1,7 +1,7 @@ + /* + * Cadence High-Definition Multimedia Interface (HDMI) driver + * +- * Copyright (C) 2019-2020 NXP Semiconductor, Inc. ++ * Copyright (C) 2019-2021 NXP Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by +@@ -13,6 +13,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -27,6 +28,131 @@ + #include + #include + ++#include "cdns-mhdp-hdcp.h" ++ ++static ssize_t HDCPTX_do_reauth_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t count); ++static struct device_attribute HDCPTX_do_reauth = __ATTR_WO(HDCPTX_do_reauth); ++ ++static ssize_t HDCPTX_do_reauth_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t count) ++{ ++ int value, ret; ++ struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev); ++ ++ ret = cdns_mhdp_hdcp_tx_reauth(mhdp, 1); ++ ++ sscanf(buf, "%d", &value); ++ ++ if (ret < 0) { ++ dev_err(dev, "%s cdns_mhdp_hdcp_tx_reauth failed\n", __func__); ++ return -1; ++ } ++ return count; ++} ++ ++static ssize_t HDCPTX_Version_show(struct device *dev, ++ struct device_attribute *attr, char *buf); ++static ssize_t HDCPTX_Version_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t count); ++static struct device_attribute HDCPTX_Version = __ATTR_RW(HDCPTX_Version); ++ ++static ssize_t HDCPTX_Version_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t count) ++{ ++ struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev); ++ int value; ++ ++ sscanf(buf, "%d", &value); ++ if (value == 2) ++ mhdp->hdcp.config = 2; ++ else if (value == 1) ++ mhdp->hdcp.config = 1; ++ else if (value == 3) ++ mhdp->hdcp.config = 3; ++ else ++ mhdp->hdcp.config = 0; ++ ++ return count; ++} ++ ++ssize_t HDCPTX_Version_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev); ++ return sprintf(buf, "%d\n", mhdp->hdcp.config); ++} ++ ++static ssize_t HDCPTX_Status_show(struct device *dev, ++ struct device_attribute *attr, char *buf); ++static ssize_t HDCPTX_Status_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t count); ++static struct device_attribute HDCPTX_Status = __ATTR_RW(HDCPTX_Status); ++ ++ssize_t HDCPTX_Status_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev); ++ ++ switch (mhdp->hdcp.state) { ++ case HDCP_STATE_NO_AKSV: ++ return sprintf(buf, "%d :HDCP_STATE_NO_AKSV \n", mhdp->hdcp.state); ++ case HDCP_STATE_INACTIVE: ++ return sprintf(buf, "%d :HDCP_STATE_INACTIVE \n", mhdp->hdcp.state); ++ case HDCP_STATE_ENABLING: ++ return sprintf(buf, "%d :HDCP_STATE_ENABLING \n", mhdp->hdcp.state); ++ case HDCP_STATE_AUTHENTICATING: ++ return sprintf(buf, "%d :HDCP_STATE_AUTHENTICATING \n", mhdp->hdcp.state); ++ case HDCP_STATE_AUTHENTICATED: ++ return sprintf(buf, "%d :HDCP_STATE_AUTHENTICATED \n", mhdp->hdcp.state); ++ case HDCP_STATE_DISABLING: ++ return sprintf(buf, "%d :HDCP_STATE_DISABLING \n", mhdp->hdcp.state); ++ case HDCP_STATE_AUTH_FAILED: ++ return sprintf(buf, "%d :HDCP_STATE_AUTH_FAILED \n", mhdp->hdcp.state); ++ default: ++ return sprintf(buf, "%d :HDCP_STATE don't exist \n", mhdp->hdcp.state); ++ } ++} ++ ++ssize_t HDCPTX_Status_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t count) ++{ ++ struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev); ++ int value; ++ ++ if (count == 2) { ++ sscanf(buf, "%d", &value); ++ if ((value >= HDCP_STATE_NO_AKSV) && (value <= HDCP_STATE_AUTH_FAILED)) { ++ mhdp->hdcp.state = value; ++ return count; ++ } else { ++ dev_err(dev, "%s &hdp->state invalid\n", __func__); ++ return -1; ++ } ++ } ++ ++ dev_info(dev, "%s &hdp->state desired %s count=%d\n ", __func__, buf, (int)count); ++ ++ if (strncmp(buf, "HDCP_STATE_NO_AKSV", count - 1) == 0) ++ mhdp->hdcp.state = HDCP_STATE_NO_AKSV; ++ else if (strncmp(buf, "HDCP_STATE_INACTIVE", count - 1) == 0) ++ mhdp->hdcp.state = HDCP_STATE_INACTIVE; ++ else if (strncmp(buf, "HDCP_STATE_ENABLING", count - 1) == 0) ++ mhdp->hdcp.state = HDCP_STATE_ENABLING; ++ else if (strncmp(buf, "HDCP_STATE_AUTHENTICATING", count - 1) == 0) ++ mhdp->hdcp.state = HDCP_STATE_AUTHENTICATING; ++ else if (strncmp(buf, "HDCP_STATE_AUTHENTICATED", count - 1) == 0) ++ mhdp->hdcp.state = HDCP_STATE_AUTHENTICATED; ++ else if (strncmp(buf, "HDCP_STATE_DISABLING", count - 1) == 0) ++ mhdp->hdcp.state = HDCP_STATE_DISABLING; ++ else if (strncmp(buf, "HDCP_STATE_AUTH_FAILED", count - 1) == 0) ++ mhdp->hdcp.state = HDCP_STATE_AUTH_FAILED; ++ else ++ dev_err(dev, "%s &hdp->state invalid\n", __func__); ++ return -1; ++ return count; ++} ++ + static void hdmi_sink_config(struct cdns_mhdp_device *mhdp) + { + struct drm_scdc *scdc = &mhdp->connector.base.display_info.hdmi.scdc; +@@ -319,6 +445,22 @@ static bool blob_equal(const struct drm_property_blob *a, + return !a == !b; + } + ++static void cdns_hdmi_bridge_disable(struct drm_bridge *bridge) ++{ ++ struct cdns_mhdp_device *mhdp = bridge->driver_private; ++ ++ cdns_hdmi_hdcp_disable(mhdp); ++} ++ ++static void cdns_hdmi_bridge_enable(struct drm_bridge *bridge) ++{ ++ struct cdns_mhdp_device *mhdp = bridge->driver_private; ++ struct drm_connector_state *conn_state = mhdp->connector.base.state; ++ ++ if (conn_state->content_protection == DRM_MODE_CONTENT_PROTECTION_DESIRED) ++ cdns_hdmi_hdcp_enable(mhdp); ++} ++ + static int cdns_hdmi_connector_atomic_check(struct drm_connector *connector, + struct drm_atomic_state *state) + { +@@ -329,12 +471,17 @@ static int cdns_hdmi_connector_atomic_check(struct drm_connector *connector, + struct drm_crtc *crtc = new_con_state->crtc; + struct drm_crtc_state *new_crtc_state; + ++ cdns_hdmi_hdcp_atomic_check(connector, old_con_state, new_con_state); ++ if (!new_con_state->crtc) ++ return 0; ++ ++ new_crtc_state = drm_atomic_get_crtc_state(state, crtc); ++ if (IS_ERR(new_crtc_state)) ++ return PTR_ERR(new_crtc_state); ++ + if (!blob_equal(new_con_state->hdr_output_metadata, + old_con_state->hdr_output_metadata) || + new_con_state->colorspace != old_con_state->colorspace) { +- new_crtc_state = drm_atomic_get_crtc_state(state, crtc); +- if (IS_ERR(new_crtc_state)) +- return PTR_ERR(new_crtc_state); + + new_crtc_state->mode_changed = + !new_con_state->hdr_output_metadata || +@@ -342,6 +489,15 @@ static int cdns_hdmi_connector_atomic_check(struct drm_connector *connector, + new_con_state->colorspace != old_con_state->colorspace; + } + ++ /* ++ * These properties are handled by fastset, and might not end up in a ++ * modeset. ++ */ ++ if (new_con_state->picture_aspect_ratio != ++ old_con_state->picture_aspect_ratio || ++ new_con_state->content_type != old_con_state->content_type || ++ new_con_state->scaling_mode != old_con_state->scaling_mode) ++ new_crtc_state->mode_changed = true; + return 0; + } + +@@ -388,6 +544,7 @@ static int cdns_hdmi_bridge_attach(struct drm_bridge *bridge, + + drm_connector_attach_encoder(connector, encoder); + ++ drm_connector_attach_content_protection_property(connector, true); + return 0; + } + +@@ -439,7 +596,7 @@ static void cdns_hdmi_bridge_mode_set(struct drm_bridge *bridge, + video->v_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NVSYNC); + video->h_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NHSYNC); + +- DRM_INFO("Mode: %dx%dp%d\n", mode->hdisplay, mode->vdisplay, mode->clock); ++ DRM_INFO("Mode: %dx%dp%d\n", mode->hdisplay, mode->vdisplay, mode->clock); + memcpy(&mhdp->mode, mode, sizeof(struct drm_display_mode)); + + mutex_lock(&mhdp->lock); +@@ -518,6 +675,8 @@ bool cdns_hdmi_bridge_mode_fixup(struct drm_bridge *bridge, + + static const struct drm_bridge_funcs cdns_hdmi_bridge_funcs = { + .attach = cdns_hdmi_bridge_attach, ++ .enable = cdns_hdmi_bridge_enable, ++ .disable = cdns_hdmi_bridge_disable, + .mode_set = cdns_hdmi_bridge_mode_set, + .mode_valid = cdns_hdmi_bridge_mode_valid, + .mode_fixup = cdns_hdmi_bridge_mode_fixup, +@@ -645,7 +804,7 @@ static int __cdns_hdmi_probe(struct platform_device *pdev, + mhdp->irq[IRQ_IN]); + return -EINVAL; + } +- ++ + irq_set_status_flags(mhdp->irq[IRQ_OUT], IRQ_NOAUTOEN); + ret = devm_request_threaded_irq(dev, mhdp->irq[IRQ_OUT], + NULL, cdns_hdmi_irq_thread, +@@ -659,6 +818,25 @@ static int __cdns_hdmi_probe(struct platform_device *pdev, + + cdns_hdmi_parse_dt(mhdp); + ++ ret = cdns_hdmi_hdcp_init(mhdp, pdev->dev.of_node); ++ if (ret < 0) ++ DRM_WARN("Failed to initialize HDCP\n"); ++ ++ if (device_create_file(mhdp->dev, &HDCPTX_do_reauth)) { ++ printk(KERN_ERR "Unable to create HDCPTX_do_reauth sysfs\n"); ++ device_remove_file(mhdp->dev, &HDCPTX_do_reauth); ++ } ++ ++ if (device_create_file(mhdp->dev, &HDCPTX_Version)) { ++ printk(KERN_ERR "Unable to create HDCPTX_Version sysfs\n"); ++ device_remove_file(mhdp->dev, &HDCPTX_Version); ++ } ++ ++ if (device_create_file(mhdp->dev, &HDCPTX_Status)) { ++ printk(KERN_ERR "Unable to create HDCPTX_Status sysfs\n"); ++ device_remove_file(mhdp->dev, &HDCPTX_Status); ++ } ++ + if (cdns_mhdp_read_hpd(mhdp)) + enable_irq(mhdp->irq[IRQ_OUT]); + else +diff --git a/drivers/gpu/drm/bridge/cadence/cdns-hdmi-hdcp.c b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-hdcp.c +new file mode 100644 +index 000000000000..e2a3bc7fb42b +--- /dev/null ++++ b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-hdcp.c +@@ -0,0 +1,1167 @@ ++/* ++ * Cadence HDMI HDCP driver ++ * ++ * Copyright (C) 2021 NXP Semiconductor, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ */ ++#include ++#include ++#include ++#include ++ ++#include "cdns-mhdp-hdcp.h" ++ ++/* Default will be to use KM unless it has been explicitly */ ++#ifndef HDCP_USE_KMKEY ++ #define HDCP_USE_KMKEY 1 ++#endif ++ ++#define CDNS_HDCP_ACTIVATE (0x1 << 2) ++ ++#define IMX_FW_TIMEOUT_MS (64 * 1000) ++#define IMX_HDCP_PAIRING_FIRMWARE "imx/hdcp-pairing.bin" ++ ++#define GENERAL_BUS_SETTINGS_DPCD_BUS_BIT 0 ++#define GENERAL_BUS_SETTINGS_DPCD_BUS_LOCK_BIT 1 ++#define GENERAL_BUS_SETTINGS_HDCP_BUS_BIT 2 ++#define GENERAL_BUS_SETTINGS_HDCP_BUS_LOCK_BIT 3 ++#define GENERAL_BUS_SETTINGS_CAPB_OWNER_BIT 4 ++#define GENERAL_BUS_SETTINGS_CAPB_OWNER_LOCK_BIT 5 ++ ++#define GENERAL_BUS_SETTINGS_RESP_DPCD_BUS_BIT 0 ++#define GENERAL_BUS_SETTINGS_RESP_HDCP_BUS_BIT 1 ++#define GENERAL_BUS_SETTINGS_RESP_CAPB_OWNER_BIT 2 ++ ++/* HDCP TX ports working mode (HDCP 2.2 or 1.4) */ ++enum { ++ HDCP_TX_2, /* lock only with HDCP2 */ ++ HDCP_TX_1, /* lock only with HDCP1 */ ++ HDCP_TX_BOTH, /* lock on HDCP2 or 1 depend on other side */ ++}; ++ ++/* HDCP TX ports stream type (relevant if receiver is repeater) */ ++enum { ++ HDCP_CONTENT_TYPE_0, /* May be transmitted by ++ The HDCP Repeater to all HDCP Devices. */ ++ HDCP_CONTENT_TYPE_1, /* Must not be transmitted by the HDCP Repeater to ++ HDCP 1.x-compliant Devices and HDCP 2.0-compliant Repeaters */ ++}; ++ ++/* different error types for HDCP_TX_STATUS_CHANGE */ ++enum { ++ HDCP_TRAN_ERR_NO_ERROR, ++ HDCP_TRAN_ERR_HPD_IS_DOWN, ++ HDCP_TRAN_ERR_SRM_FAILURE, ++ HDCP_TRAN_ERR_SIGNATURE_VERIFICATION, ++ HDCP_TRAN_ERR_H_TAG_DIFF_H, ++ HDCP_TRAN_ERR_V_TAG_DIFF_V, ++ HDCP_TRAN_ERR_LOCALITY_CHECK, ++ HDCP_TRAN_ERR_DDC, ++ HDCP_TRAN_ERR_REAUTH_REQ, ++ HDCP_TRAN_ERR_TOPOLOGY, ++ HDCP_TRAN_ERR_HDCP_RSVD1, ++ HDCP_TRAN_ERR_HDMI_CAPABILITY, ++ HDCP_TRAN_ERR_RI, ++ HDCP_TRAN_ERR_WATCHDOG_EXPIRED, ++}; ++ ++static char const *g_last_error[16] = { ++ "No Error", ++ "HPD is down", ++ "SRM failure", ++ "Signature verification error", ++ "h tag != h", ++ "V tag diff v", ++ "Locality check", ++ "DDC error", ++ "REAUTH_REQ", ++ "Topology error", ++ "Verify receiver ID list failed", ++ "HDCP_RSVD1 was not 0,0,0", ++ "HDMI capability or mode", ++ "RI result was different than expected", ++ "WatchDog expired", ++ "Repeater integrity failed" ++}; ++ ++#define HDCP_MAX_RECEIVERS 32 ++#define HDCP_RECEIVER_ID_SIZE_BYTES 5 ++#define HPD_EVENT 1 ++#define HDCP_STATUS_SIZE 0x5 ++#define HDCP_PORT_STS_AUTH 0x1 ++#define HDCP_PORT_STS_REPEATER 0x2 ++#define HDCP_PORT_STS_TYPE_MASK 0xc ++#define HDCP_PORT_STS_TYPE_SHIFT 0x2 ++#define HDCP_PORT_STS_AUTH_STREAM_ID_SHIFT 0x4 ++#define HDCP_PORT_STS_AUTH_STREAM_ID_MASK 0x10 ++#define HDCP_PORT_STS_LAST_ERR_SHIFT 0x5 ++#define HDCP_PORT_STS_LAST_ERR_MASK (0x0F << 5) ++#define GET_HDCP_PORT_STS_LAST_ERR(__sts__) \ ++ (((__sts__) & HDCP_PORT_STS_LAST_ERR_MASK) >> \ ++ HDCP_PORT_STS_LAST_ERR_SHIFT) ++#define HDCP_PORT_STS_1_1_FEATURES 0x200 ++ ++#define HDCP_CONFIG_NONE ((u8) 0) ++#define HDCP_CONFIG_1_4 ((u8) 1) /* use HDCP 1.4 only */ ++#define HDCP_CONFIG_2_2 ((u8) 2) /* use HDCP 2.2 only */ ++ ++/* Default timeout to use for wait4event in milliseconds */ ++#define HDCP_EVENT_TO_DEF 800 ++/* Timeout value to use for repeater receiver ID check, spec says 3s */ ++#define HDCP_EVENT_TO_RPT 3500 ++ ++static int hdmi_hdcp_check_link(struct cdns_mhdp_device *mhdp); ++ ++static void print_port_status(u16 sts) ++{ ++ char const *rx_type[4] = { "Unknown", "HDCP 1", "HDCP 2", "Unknown" }; ++ ++ DRM_DEBUG_KMS("INFO: HDCP Port Status: 0x%04x\n", sts); ++ DRM_DEBUG_KMS(" Authenticated: %d\n", sts & HDCP_PORT_STS_AUTH); ++ DRM_DEBUG_KMS(" Receiver is repeater: %d\n", sts & HDCP_PORT_STS_REPEATER); ++ DRM_DEBUG_KMS(" RX Type: %s\n", ++ rx_type[(sts & HDCP_PORT_STS_TYPE_MASK) >> HDCP_PORT_STS_TYPE_SHIFT]); ++ DRM_DEBUG_KMS(" AuthStreamId: %d\n", sts & HDCP_PORT_STS_AUTH_STREAM_ID_MASK); ++ DRM_DEBUG_KMS(" Last Error: %s\n", ++ g_last_error[(sts & HDCP_PORT_STS_LAST_ERR_MASK) >> HDCP_PORT_STS_LAST_ERR_SHIFT]); ++ DRM_DEBUG_KMS(" Enable 1.1 Features: %d\n", sts & HDCP_PORT_STS_1_1_FEATURES); ++} ++ ++static void print_events(u8 events) ++{ ++ if (events & HDMI_TX_HPD_EVENT) ++ DRM_INFO("INFO: HDMI_TX_HPD_EVENT\n"); ++ if (events & HDCPTX_STATUS_EVENT) ++ DRM_INFO("INFO: HDCPTX_STATUS_EVENT\n"); ++ if (events & HDCPTX_IS_KM_STORED_EVENT) ++ DRM_INFO("INFO: HDCPTX_IS_KM_STORED_EVENT\n"); ++ if (events & HDCPTX_STORE_KM_EVENT) ++ DRM_INFO("INFO: HDCPTX_STORE_KM_EVENT\n"); ++ if (events & HDCPTX_IS_RECEIVER_ID_VALID_EVENT) ++ DRM_INFO("INFO: HDCPTX_IS_RECEIVER_ID_VALID_EVENT\n"); ++} ++ ++static u8 wait4event(struct cdns_mhdp_device *mhdp, u8 *events, ++ u32 event_to_wait, u32 timeout_ms) ++{ ++ u8 reg_events; ++ u8 returned_events; ++ u8 event_mask = event_to_wait | HDCPTX_STATUS_EVENT; ++ unsigned timeout; ++ ++ timeout = timeout_ms; ++ do { ++ if (timeout == 0) ++ goto timeout_err; ++ timeout--; ++ udelay(1000); ++ reg_events = cdns_mhdp_get_event(mhdp); ++ *events |= reg_events; ++ } while (((event_mask & *events) == 0) && (event_to_wait > HDMI_TX_HPD_EVENT)); ++ ++ returned_events = *events & event_mask; ++ if (*events != returned_events) { ++ u32 unexpected_events = ~event_mask & *events; ++ ++ DRM_INFO("INFO: %s() all 0x%08x expected 0x%08x unexpected 0x%08x", ++ __func__, *events, returned_events, unexpected_events); ++ DRM_INFO("INFO: %s() All events:\n", __func__); ++ print_events(*events); ++ ++ DRM_INFO("INFO: %s() expected events:\n", __func__); ++ print_events(returned_events); ++ ++ DRM_INFO("INFO: %s() unexpected events:\n", __func__); ++ print_events(unexpected_events); ++ } else ++ print_events(*events); ++ ++ *events &= ~event_mask; ++ ++ return returned_events; ++ ++timeout_err: ++ DRM_INFO("INFO: %s() Timed out with events:\n", __func__); ++ print_events(event_to_wait); ++ return 0; ++} ++ ++static u16 hdmi_hdcp_get_status(struct cdns_mhdp_device *mhdp) ++{ ++ u8 hdcp_status[HDCP_STATUS_SIZE]; ++ u16 hdcp_port_status; ++ ++ cdns_mhdp_hdcp_tx_status_req(mhdp, hdcp_status, HDCP_STATUS_SIZE); ++ hdcp_port_status = (hdcp_status[0] << 8) | hdcp_status[1]; ++ ++ return hdcp_port_status; ++} ++ ++static inline u8 check_event(u8 events, u8 tested) ++{ ++ if ((events & tested) == 0) ++ return 0; ++ return 1; ++} ++ ++/* Prints status. Returns error code (0 = no error) */ ++static u8 hdmi_hdcp_handle_status(u16 status) ++{ ++ print_port_status(status); ++ if (status & HDCP_PORT_STS_LAST_ERR_MASK) ++ DRM_ERROR("ERROR: HDCP error was set to %s\n", ++ g_last_error[((status & HDCP_PORT_STS_LAST_ERR_MASK) ++ >> HDCP_PORT_STS_LAST_ERR_SHIFT)]); ++ return GET_HDCP_PORT_STS_LAST_ERR(status); ++} ++ ++static int hdmi_hdcp_set_config(struct cdns_mhdp_device *mhdp, u8 hdcp_config) ++{ ++ u8 bus_config, retEvents; ++ u16 hdcp_port_status; ++ int ret; ++ ++ /* Clearing out existing events */ ++ wait4event(mhdp, &mhdp->hdcp.events, HDMI_TX_HPD_EVENT, HDCP_EVENT_TO_DEF); ++ mhdp->hdcp.events = 0; ++ ++ if (!strncmp("imx8mq-hdmi", mhdp->plat_data->plat_name, 11)) { ++ DRM_DEBUG_KMS("INFO: Switching HDCP Commands to SAPB.\n"); ++ bus_config = (1 << GENERAL_BUS_SETTINGS_HDCP_BUS_BIT); ++ ret = cdns_mhdp_apb_conf(mhdp, bus_config); ++ if (ret) { ++ DRM_ERROR("Failed to set APB configuration.\n"); ++ if (ret & (1 << GENERAL_BUS_SETTINGS_RESP_HDCP_BUS_BIT))/* 1 - locked */ ++ DRM_ERROR("Failed to switch HDCP to SAPB Mailbox\n"); ++ return -1; ++ } ++ DRM_DEBUG_KMS("INFO: HDCP switched to SAPB\n"); ++ } ++ ++ /* HDCP 2.2(and/or 1.4) | activate | km-key | 0 */ ++ hdcp_config |= CDNS_HDCP_ACTIVATE | (HDCP_USE_KMKEY << 4) | (HDCP_CONTENT_TYPE_0 << 3); ++ ++ DRM_DEBUG_KMS("INFO: Enabling HDCP...\n"); ++ ret = cdns_mhdp_hdcp_tx_config(mhdp, hdcp_config); ++ if (ret < 0) ++ DRM_DEBUG_KMS("cdns_mhdp_hdcp_tx_config failed\n"); ++ ++ /* Wait until HDCP_TX_STATUS EVENT appears */ ++ DRM_DEBUG_KMS("INFO: wait4event -> HDCPTX_STATUS_EVENT\n"); ++ retEvents = wait4event(mhdp, &mhdp->hdcp.events, HDCPTX_STATUS_EVENT, HDCP_EVENT_TO_DEF); ++ ++ /* Set TX STATUS REQUEST */ ++ DRM_DEBUG_KMS("INFO: Getting port status\n"); ++ hdcp_port_status = hdmi_hdcp_get_status(mhdp); ++ if (hdmi_hdcp_handle_status(hdcp_port_status) != 0) ++ return -1; ++ ++ return 0; ++} ++ ++static int hdmi_hdcp_auth_check(struct cdns_mhdp_device *mhdp) ++{ ++ u16 hdcp_port_status; ++ int ret; ++ ++ DRM_DEBUG_KMS("INFO: wait4event -> HDCPTX_STATUS_EVENT\n"); ++ mhdp->hdcp.events = wait4event(mhdp, &mhdp->hdcp.events, HDCPTX_STATUS_EVENT, HDCP_EVENT_TO_DEF+HDCP_EVENT_TO_DEF); ++ if (mhdp->hdcp.events == 0) ++ return -1; ++ ++ DRM_DEBUG_KMS("HDCP: HDCPTX_STATUS_EVENT\n"); ++ hdcp_port_status = hdmi_hdcp_get_status(mhdp); ++ ret = hdmi_hdcp_handle_status(hdcp_port_status); ++ if (ret != 0) { ++ if (ret == HDCP_TRAN_ERR_REAUTH_REQ) { ++ DRM_ERROR("HDCP_TRAN_ERR_REAUTH_REQ-->one more try!\n"); ++ return 1; ++ } else ++ return -1; ++ } ++ ++ if (hdcp_port_status & HDCP_PORT_STS_AUTH) { ++ DRM_INFO("Authentication completed successfully!\n"); ++ /* Dump hdmi and phy register */ ++ mhdp->hdcp.state = HDCP_STATE_AUTHENTICATED; ++ return 0; ++ } ++ ++ DRM_WARN("Authentication failed\n"); ++ mhdp->hdcp.state = HDCP_STATE_AUTH_FAILED; ++ return -1; ++} ++ ++inline void hdmi_hdcp_swap_id(u8 *in, u8 *out) ++{ ++ int i; ++ ++ for (i = 0; i < HDCP_RECEIVER_ID_SIZE_BYTES; i++) ++ out[HDCP_RECEIVER_ID_SIZE_BYTES - (i + 1)] = in[i]; ++} ++ ++inline void hdmi_hdcp_swap_list(u8 *list_in, u8 *list_out, int num_ids) ++{ ++ int i; ++ ++ for (i = 0; i < num_ids; i++) ++ hdmi_hdcp_swap_id(&list_in[i * HDCP_RECEIVER_ID_SIZE_BYTES], ++ &list_out[i * HDCP_RECEIVER_ID_SIZE_BYTES]); ++} ++ ++static int hdmi_hdcp_check_receviers(struct cdns_mhdp_device *mhdp) ++{ ++ u8 ret_events; ++ u8 hdcp_num_rec, i; ++ u8 hdcp_rec_id[HDCP_MAX_RECEIVERS][HDCP_RECEIVER_ID_SIZE_BYTES]; ++ u8 hdcp_rec_id_temp[HDCP_MAX_RECEIVERS][HDCP_RECEIVER_ID_SIZE_BYTES]; ++ u16 hdcp_port_status = 0; ++ int ret; ++ ++ DRM_INFO("INFO: Waiting for Receiver ID valid event\n"); ++ ret_events = 0; ++ do { ++ u8 events = 0; ++ u8 hdcp_last_error = 0; ++ events = check_event(ret_events, ++ HDCPTX_IS_RECEIVER_ID_VALID_EVENT); ++ DRM_DEBUG_KMS("INFO: Waiting HDCPTX_IS_RECEIVER_ID_VALID_EVENT\n"); ++ ret_events = wait4event(mhdp, &mhdp->hdcp.events, ++ HDCPTX_IS_RECEIVER_ID_VALID_EVENT, ++ (mhdp->hdcp.sink_is_repeater ? ++ HDCP_EVENT_TO_RPT : HDCP_EVENT_TO_DEF)); ++ if (ret_events == 0) { ++ /* time out occurred, return error */ ++ DRM_ERROR("HDCP error did not get receiver IDs\n"); ++ return -1; ++ } ++ if (check_event(ret_events, HDCPTX_STATUS_EVENT) != 0) { ++ /* There was a status update, could be due to HPD ++ going down or some other error, check if an error ++ was set, if so exit. ++ */ ++ hdcp_port_status = hdmi_hdcp_get_status(mhdp); ++ hdcp_last_error = GET_HDCP_PORT_STS_LAST_ERR(hdcp_port_status); ++ if (hdmi_hdcp_handle_status(hdcp_port_status)) { ++ DRM_ERROR("HDCP error no: %u\n", hdcp_last_error); ++ return -1; ++ } else { ++ /* No error logged, keep going. ++ * If this somehow happened at same time, then need to ++ * put the HDCPTX_STATUS_EVENT back into the global ++ * events pool and checked later. */ ++ mhdp->hdcp.events |= HDCPTX_STATUS_EVENT; ++ ++ /* Special condition when connected to HDCP 1.4 repeater ++ * with no downstream devices attached, then will not ++ * get receiver ID list but instead will reach ++ * authenticated state. */ ++ if ((mhdp->hdcp.hdcp_version == HDCP_TX_1) && (mhdp->hdcp.sink_is_repeater == 1) && ++ ((hdcp_port_status & HDCP_PORT_STS_AUTH) == HDCP_PORT_STS_AUTH)) { ++ DRM_INFO("Connected to HDCP 1.4 repeater with no downstream devices!\n"); ++ return 0; ++ } ++ ++ msleep(20); ++ } ++ } ++ } while (check_event(ret_events, ++ HDCPTX_IS_RECEIVER_ID_VALID_EVENT) == 0); ++ ++ DRM_INFO("INFO: Requesting Receivers ID's\n"); ++ ++ hdcp_num_rec = 0; ++ memset(&hdcp_rec_id, 0, sizeof(hdcp_rec_id)); ++ ++ ret = cdns_mhdp_hdcp_tx_is_receiver_id_valid(mhdp, (u8 *)hdcp_rec_id, &hdcp_num_rec); ++ if (ret) { ++ DRM_DEV_ERROR(mhdp->dev, "Failed to hdcp tx receiver ID.\n"); ++ return -1; ++ } ++ ++ if (hdcp_num_rec == 0) { ++ DRM_DEBUG_KMS("WARN: Failed to get receiver list\n"); ++ /* Unknown problem, return error */ ++ return -1; ++ } ++ ++ DRM_INFO("INFO: Number of Receivers: %d\n", hdcp_num_rec); ++ ++ for (i = 0; i < hdcp_num_rec; ++i) { ++ DRM_INFO("\tReveiver ID%2d: %.2X%.2X%.2X%.2X%.2X\n", ++ i, ++ hdcp_rec_id[i][0], ++ hdcp_rec_id[i][1], ++ hdcp_rec_id[i][2], ++ hdcp_rec_id[i][3], ++ hdcp_rec_id[i][4] ++ ); ++ } ++ ++ /* swap ids byte order */ ++ hdmi_hdcp_swap_list(&hdcp_rec_id[0][0], ++ &hdcp_rec_id_temp[0][0], hdcp_num_rec); ++ ++ /* Check Receiver ID's against revocation list in SRM */ ++ if (drm_hdcp_check_ksvs_revoked(mhdp->drm_dev, (u8 *)hdcp_rec_id_temp, hdcp_num_rec)) { ++ mhdp->hdcp.state = HDCP_STATE_AUTH_FAILED; ++ DRM_ERROR("INFO: Receiver check fails\n"); ++ return -1; ++ } ++ ++ ret = cdns_mhdp_hdcp_tx_respond_receiver_id_valid(mhdp, 1); ++ DRM_INFO("INFO: Responding with Receiver ID's OK!, ret=%d\n", ret); ++ return ret; ++} ++ ++#ifdef STORE_PAIRING ++static int hdmi_hdcp_get_stored_pairing(struct cdns_mhdp_device *mhdp) ++{ ++ int ret = 0; ++ unsigned long timeout = jiffies + msecs_to_jiffies(IMX_FW_TIMEOUT_MS); ++ unsigned long sleep = 1000; ++ const struct firmware *fw; ++ ++ DRM_DEBUG_KMS("%s()\n", __func__); ++ ++ while (time_before(jiffies, timeout)) { ++ ret = request_firmware(&fw, hdmi_hdcp_PAIRING_FIRMWARE, mhdp->dev); ++ if (ret == -ENOENT) { ++ msleep(sleep); ++ sleep *= 2; ++ continue; ++ } else if (ret) { ++ DRM_DEV_INFO(mhdp->dev, "HDCP pairing data not found\n"); ++ goto out; ++ } ++ ++ mhdp->hdcp.num_paired = fw->size / ++ sizeof(struct hdcp_trans_pairing_data); ++ if (mhdp->hdcp.num_paired > MAX_STORED_KM) { ++ /* todo: handle dropping */ ++ mhdp->hdcp.num_paired = MAX_STORED_KM; ++ DRM_DEV_INFO(mhdp->dev, ++ "too many paired receivers - dropping older entries\n"); ++ } ++ memcpy(&mhdp->hdcp.pairing[0], fw->data, ++ sizeof(struct hdcp_trans_pairing_data) * mhdp->hdcp.num_paired); ++ release_firmware(fw); ++ goto out; ++ } ++ ++ DRM_DEV_ERROR(mhdp->dev, "Timed out trying to load firmware\n"); ++ ret = -ETIMEDOUT; ++ out: ++ return ret; ++} ++#endif ++ ++static int hdmi_hdcp_find_km_store(struct cdns_mhdp_device *mhdp, ++ u8 receiver[HDCP_PAIRING_R_ID]) ++{ ++ int i; ++ ++ DRM_DEBUG_KMS("%s()\n", __func__); ++ for (i = 0; i < mhdp->hdcp.num_paired; i++) { ++ if (memcmp(receiver, mhdp->hdcp.pairing[i].receiver_id, ++ HDCP_PAIRING_R_ID) == 0) { ++ DRM_INFO("HDCP: found receiver id: 0x%x%x%x%x%x\n", ++ receiver[0], receiver[1], receiver[2], receiver[3], receiver[4]); ++ return i; ++ } ++ } ++ DRM_INFO("HDCP: receiver id: 0x%x%x%x%x%x not stored\n", ++ receiver[0], receiver[1], receiver[2], receiver[3], receiver[4]); ++ return -1; ++} ++ ++static int hdmi_hdcp_store_km(struct cdns_mhdp_device *mhdp, ++ struct hdcp_trans_pairing_data *pairing, ++ int stored_km_index) ++{ ++ int i, temp_index; ++ struct hdcp_trans_pairing_data temp_pairing; ++ ++ DRM_DEBUG_KMS("%s()\n", __func__); ++ ++ if (stored_km_index < 0) { ++ /* drop one entry if array is full */ ++ if (mhdp->hdcp.num_paired == MAX_STORED_KM) ++ mhdp->hdcp.num_paired--; ++ ++ temp_index = mhdp->hdcp.num_paired; ++ mhdp->hdcp.num_paired++; ++ if (!pairing) { ++ DRM_ERROR("NULL HDCP pairing data!\n"); ++ return -1; ++ } else ++ /* save the new stored km */ ++ temp_pairing = *pairing; ++ } else { ++ /* save the current stored km */ ++ temp_index = stored_km_index; ++ temp_pairing = mhdp->hdcp.pairing[stored_km_index]; ++ } ++ ++ /* move entries one slot to the end */ ++ for (i = temp_index; i > 0; i--) ++ mhdp->hdcp.pairing[i] = mhdp->hdcp.pairing[i - 1]; ++ ++ /* save the current/new entry at the beginning */ ++ mhdp->hdcp.pairing[0] = temp_pairing; ++ ++ return 0; ++} ++ ++static inline int hdmi_hdcp_auth_22(struct cdns_mhdp_device *mhdp) ++{ ++ int km_idx = -1; ++ u8 retEvents; ++ u16 hdcp_port_status; ++ u8 resp[HDCP_STATUS_SIZE]; ++ struct hdcp_trans_pairing_data pairing; ++ int ret; ++ ++ DRM_DEBUG_KMS("HDCP: Start 2.2 Authentication\n"); ++ mhdp->hdcp.sink_is_repeater = 0; ++ ++ /* Wait until HDCP2_TX_IS_KM_STORED EVENT appears */ ++ retEvents = 0; ++ DRM_DEBUG_KMS("INFO: Wait until HDCP2_TX_IS_KM_STORED EVENT appears\n"); ++ while (check_event(retEvents, HDCPTX_IS_KM_STORED_EVENT) == 0) { ++ DRM_DEBUG_KMS("INFO: Waiting FOR _IS_KM_STORED EVENT\n"); ++ retEvents = wait4event(mhdp, &mhdp->hdcp.events, ++ HDCPTX_IS_KM_STORED_EVENT, HDCP_EVENT_TO_DEF); ++ if (retEvents == 0) ++ /* time out occurred, return error */ ++ return -1; ++ if (check_event(retEvents, HDCPTX_STATUS_EVENT) != 0) { ++ /* There was a status update, could be due to HPD ++ going down or some other error, check if an error ++ was set, if so exit. ++ */ ++ hdcp_port_status = hdmi_hdcp_get_status(mhdp); ++ if (hdmi_hdcp_handle_status(hdcp_port_status) != 0) ++ return -1; ++ } ++ } ++ ++ DRM_DEBUG_KMS("HDCP: HDCPTX_IS_KM_STORED_EVENT\n"); ++ ++ /* Set HDCP2 TX KM STORED REQUEST */ ++ ret = cdns_mhdp_hdcp2_tx_is_km_stored_req(mhdp, resp, HDCP_STATUS_SIZE); ++ if (ret) { ++ DRM_DEV_ERROR(mhdp->dev, "Failed to hdcp2 tx km stored.\n"); ++ return -1; ++ } ++ ++ DRM_DEBUG_KMS("HDCP: CDN_API_HDCP2_TX_IS_KM_STORED_REQ_blocking\n"); ++ DRM_DEBUG_KMS("HDCP: Receiver ID: 0x%x%x%x%x%x\n", ++ resp[0], resp[1], resp[2], resp[3], resp[4]); ++ ++ km_idx = hdmi_hdcp_find_km_store(mhdp, resp); ++ ++ /* Check if KM is stored */ ++ if (km_idx >= 0) { ++ DRM_DEBUG_KMS("INFO: KM is stored\n"); ++ /* Set HDCP2 TX RESPOND KM with stored KM */ ++ ret = cdns_mhdp_hdcp2_tx_respond_km(mhdp, (u8 *)&mhdp->hdcp.pairing[km_idx], ++ sizeof(struct hdcp_trans_pairing_data)); ++ ++ DRM_DEBUG_KMS("HDCP: CDN_API_HDCP2_TX_RESPOND_KM_blocking, ret=%d\n", ret); ++ } else { /* KM is not stored */ ++ /* Set HDCP2 TX RESPOND KM with empty data */ ++ ret = cdns_mhdp_hdcp2_tx_respond_km(mhdp, NULL, 0); ++ DRM_DEBUG_KMS("INFO: KM is not stored ret=%d\n", ret); ++ } ++ ++ if (hdmi_hdcp_check_receviers(mhdp)) ++ return -1; ++ ++ /* Check if KM is not stored */ ++ if (km_idx < 0) { ++ int loop_cnt = 0; ++ ++ /* Wait until HDCP2_TX_STORE_KM EVENT appears */ ++ retEvents = 0; ++ DRM_DEBUG_KMS("INFO: wait4event -> HDCPTX_STORE_KM_EVENT\n"); ++ while (check_event(retEvents, HDCPTX_STORE_KM_EVENT) == 0) { ++ retEvents = wait4event(mhdp, &mhdp->hdcp.events, ++ HDCPTX_STORE_KM_EVENT, HDCP_EVENT_TO_DEF); ++ if (check_event(retEvents, HDCPTX_STATUS_EVENT) ++ != 0) { ++ hdcp_port_status = hdmi_hdcp_get_status(mhdp); ++ if (hdmi_hdcp_handle_status(hdcp_port_status) ++ != 0) ++ return -1; ++ } ++ if (loop_cnt > 2) { ++ DRM_ERROR("Did not get event HDCPTX_STORE_KM_EVENT in time\n"); ++ return -1; ++ } else ++ loop_cnt++; ++ } ++ DRM_DEBUG_KMS("HDCP: HDCPTX_STORE_KM_EVENT\n"); ++ ++ /* Set HDCP2_TX_STORE_KM REQUEST */ ++ ret = cdns_mhdp_hdcp2_tx_store_km(mhdp, (u8 *)&pairing, sizeof(struct hdcp_trans_pairing_data)); ++ DRM_DEBUG_KMS("HDCP: CDN_API_HDCP2_TX_STORE_KM_REQ_blocking ret=%d\n", ret); ++ hdmi_hdcp_store_km(mhdp, &pairing, km_idx); ++ } else ++ hdmi_hdcp_store_km(mhdp, NULL, km_idx); ++ ++ /* Check if device was a repeater */ ++ hdcp_port_status = hdmi_hdcp_get_status(mhdp); ++ ++ /* Exit if there was any errors logged at this point... */ ++ if (GET_HDCP_PORT_STS_LAST_ERR(hdcp_port_status) > 0) { ++ hdmi_hdcp_handle_status(hdcp_port_status); ++ return -1; ++ } ++ ++ if (hdcp_port_status & HDCP_PORT_STS_REPEATER) ++ mhdp->hdcp.sink_is_repeater = 1; ++ ++ /* If sink was a repeater, we will be getting additional IDs to validate... ++ * Note that this one may take some time since spec allows up to 3s... */ ++ if (mhdp->hdcp.sink_is_repeater) ++ if (hdmi_hdcp_check_receviers(mhdp)) ++ return -1; ++ ++ /* Slight delay to allow firmware to finish setting up authenticated state */ ++ msleep(300); ++ ++ DRM_INFO("Finished hdmi_hdcp_auth_22\n"); ++ return 0; ++} ++ ++static inline int hdmi_hdcp_auth_14(struct cdns_mhdp_device *mhdp) ++{ ++ u16 hdcp_port_status; ++ int ret = 0; ++ ++ DRM_DEBUG_KMS("HDCP: Starting 1.4 Authentication\n"); ++ mhdp->hdcp.sink_is_repeater = 0; ++ ++ ret = hdmi_hdcp_check_receviers(mhdp); ++ if (ret) ++ return -1; ++ ++ /* Check if device was a repeater */ ++ hdcp_port_status = hdmi_hdcp_get_status(mhdp); ++ ++ /* Exit if there was any errors logged at this point... */ ++ if (GET_HDCP_PORT_STS_LAST_ERR(hdcp_port_status) > 0) { ++ hdmi_hdcp_handle_status(hdcp_port_status); ++ return -1; ++ } ++ ++ if (hdcp_port_status & HDCP_PORT_STS_REPEATER) { ++ DRM_INFO("Connected to a repeater\n"); ++ mhdp->hdcp.sink_is_repeater = 1; ++ } else ++ DRM_INFO("Connected to a normal sink\n"); ++ ++ /* If sink was a repeater, we will be getting additional IDs to validate... ++ * Note that this one may take some time since spec allows up to 3s... */ ++ if (mhdp->hdcp.sink_is_repeater) ++ ret = hdmi_hdcp_check_receviers(mhdp); ++ ++ /* Slight delay to allow firmware to finish setting up authenticated state */ ++ msleep(300); ++ ++ return ret; ++} ++ ++static int hdmi_hdcp_auth(struct cdns_mhdp_device *mhdp, u8 hdcp_config) ++{ ++ int ret = 0; ++ ++ DRM_DEBUG_KMS("HDCP: Start Authentication\n"); ++ ++ if (mhdp->hdcp.reauth_in_progress == 0) { ++ ret = hdmi_hdcp_set_config(mhdp, hdcp_config); ++ if (ret) { ++ DRM_ERROR("hdmi_hdcp_set_config failed\n"); ++ return -1; ++ } ++ } ++ ++ mhdp->hdcp.reauth_in_progress = 0; ++ mhdp->hdcp.sink_is_repeater = 0; ++ mhdp->hdcp.hdcp_version = hdcp_config; ++ ++ do { ++ if (mhdp->hdcp.cancel == 1) { ++ DRM_ERROR("mhdp->hdcp.cancel is TRUE\n"); ++ return -ECANCELED; ++ } ++ ++ if (hdcp_config == HDCP_TX_1) ++ ret = hdmi_hdcp_auth_14(mhdp); ++ else ++ ret = hdmi_hdcp_auth_22(mhdp); ++ if (ret) { ++ u16 hdcp_port_status; ++ DRM_ERROR("hdmi_hdcp_auth_%s failed\n", ++ (hdcp_config == HDCP_TX_1) ? "14" : "22"); ++ hdcp_port_status = hdmi_hdcp_get_status(mhdp); ++ hdmi_hdcp_handle_status(hdcp_port_status); ++ return -1; ++ } ++ ++ ret = hdmi_hdcp_auth_check(mhdp); ++ } while (ret == 1); ++ ++ return ret; ++} ++ ++static int _hdmi_hdcp_disable(struct cdns_mhdp_device *mhdp) ++{ ++ int ret = 0; ++ u8 hdcp_cfg = (HDCP_USE_KMKEY << 4); ++ ++ DRM_DEBUG_KMS("[%s:%d] HDCP is being disabled...\n", ++ mhdp->connector.base.name, mhdp->connector.base.base.id); ++ DRM_DEBUG_KMS("INFO: Disabling HDCP...\n"); ++ ++ ret = cdns_mhdp_hdcp_tx_config(mhdp, hdcp_cfg); ++ if (ret < 0) ++ DRM_DEBUG_KMS("cdns_mhdp_hdcp_tx_config failed\n"); ++ ++ DRM_DEBUG_KMS("HDCP is disabled\n"); ++ ++ mhdp->hdcp.events = 0; ++ ++ return ret; ++} ++ ++static int _hdmi_hdcp_enable(struct cdns_mhdp_device *mhdp) ++{ ++ int i, ret = 0, tries = 9; ++ u8 hpd_sts; ++ ++ hpd_sts = cdns_mhdp_read_hpd(mhdp); ++ if (1 != hpd_sts) { ++ dev_info(mhdp->dev, "%s HDP detected low, set state to DISABLING\n", __func__); ++ mhdp->hdcp.state = HDCP_STATE_DISABLING; ++ return -1; ++ } ++ ++ DRM_DEBUG_KMS("[%s:%d] HDCP is being enabled...\n", ++ mhdp->connector.base.name, mhdp->connector.base.base.id); ++ ++ mhdp->hdcp.events = 0; ++ ++ /* Incase of authentication failures, HDCP spec expects reauth. */ ++ /* TBD should this actually try 2.2 n times then 1.4? */ ++ for (i = 0; i < tries; i++) { ++ if (mhdp->hdcp.config & HDCP_CONFIG_2_2) { ++ ret = hdmi_hdcp_auth(mhdp, HDCP_TX_2); ++ if (ret == 0) ++ return 0; ++ else if (ret == -ECANCELED) ++ return ret; ++ _hdmi_hdcp_disable(mhdp); ++ } ++ } ++ ++ for (i = 0; i < tries; i++) { ++ if (mhdp->hdcp.config & HDCP_CONFIG_1_4) { ++ ret = hdmi_hdcp_auth(mhdp, HDCP_TX_1); ++ if (ret == 0) ++ return 0; ++ else if (ret == -ECANCELED) ++ return ret; ++ _hdmi_hdcp_disable(mhdp); ++ } ++ DRM_DEBUG_KMS("HDCP Auth failure (%d)\n", ret); ++ } ++ ++ DRM_ERROR("HDCP authentication failed (%d tries/%d)\n", tries, ret); ++ return ret; ++} ++ ++static void hdmi_hdcp_check_work(struct work_struct *work) ++{ ++ struct cdns_mhdp_hdcp *hdcp = container_of(work, ++ struct cdns_mhdp_hdcp, check_work.work); ++ struct cdns_mhdp_device *mhdp = container_of(hdcp, ++ struct cdns_mhdp_device, hdcp); ++ ++ /* todo: maybe we don't need to always schedule */ ++ hdmi_hdcp_check_link(mhdp); ++ schedule_delayed_work(&hdcp->check_work, 50); ++} ++ ++static void hdmi_hdcp_prop_work(struct work_struct *work) ++{ ++ struct cdns_mhdp_hdcp *hdcp = container_of(work, ++ struct cdns_mhdp_hdcp, prop_work); ++ struct cdns_mhdp_device *mhdp = container_of(hdcp, ++ struct cdns_mhdp_device, hdcp); ++ ++ struct drm_device *dev = mhdp->drm_dev; ++ struct drm_connector_state *state; ++ ++ drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); ++ mutex_lock(&mhdp->hdcp.mutex); ++ ++ /* ++ * This worker is only used to flip between ENABLED/DESIRED. Either of ++ * those to UNDESIRED is handled by core. If hdcp_value == UNDESIRED, ++ * we're running just after hdcp has been disabled, so just exit ++ */ ++ if (mhdp->hdcp.value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) { ++ state = mhdp->connector.base.state; ++ state->content_protection = mhdp->hdcp.value; ++ } ++ ++ mutex_unlock(&mhdp->hdcp.mutex); ++ drm_modeset_unlock(&dev->mode_config.connection_mutex); ++} ++ ++static void show_hdcp_supported(struct cdns_mhdp_device *mhdp) ++{ ++ if ((mhdp->hdcp.config & (HDCP_CONFIG_1_4 | HDCP_CONFIG_2_2)) == ++ (HDCP_CONFIG_1_4 | HDCP_CONFIG_2_2)) ++ DRM_INFO("Both HDCP 1.4 and 2 2 are enabled\n"); ++ else if (mhdp->hdcp.config & HDCP_CONFIG_1_4) ++ DRM_INFO("Only HDCP 1.4 is enabled\n"); ++ else if (mhdp->hdcp.config & HDCP_CONFIG_2_2) ++ DRM_INFO("Only HDCP 2.2 is enabled\n"); ++ else ++ DRM_INFO("HDCP is disabled\n"); ++} ++ ++#ifdef DEBUG ++void hdmi_hdcp_show_pairing(struct cdns_mhdp_device *mhdp, struct hdcp_trans_pairing_data *p) ++{ ++ char s[80]; ++ int i, k; ++ ++ DRM_INFO("Reveiver ID: %.2X%.2X%.2X%.2X%.2X\n", ++ p->receiver_id[0], ++ p->receiver_id[1], ++ p->receiver_id[2], ++ p->receiver_id[3], ++ p->receiver_id[4]); ++ for (k = 0, i = 0; k < 16; k++) ++ i += snprintf(&s[i], sizeof(s), "%02x", p->m[k]); ++ ++ DRM_INFO("\tm: %s\n", s); ++ ++ for (k = 0, i = 0; k < 16; k++) ++ i += snprintf(&s[i], sizeof(s), "%02x", p->km[k]); ++ ++ DRM_INFO("\tkm: %s\n", s); ++ ++ for (k = 0, i = 0; k < 16; k++) ++ i += snprintf(&s[i], sizeof(s), "%02x", p->ekh[k]); ++ ++ DRM_INFO("\tekh: %s\n", s); ++} ++#endif ++ ++void hdmi_hdcp_dump_pairing(struct seq_file *s, void *data) ++{ ++ struct cdns_mhdp_device *mhdp = data; ++#ifdef DEBUG ++ int i; ++ for (i = 0; i < mhdp->hdcp.num_paired; i++) ++ hdmi_hdcp_show_pairing(mhdp, &mhdp->hdcp.pairing[i]); ++#endif ++ seq_write(s, &mhdp->hdcp.pairing[0], ++ mhdp->hdcp.num_paired * sizeof(struct hdcp_trans_pairing_data)); ++} ++ ++static int hdmi_hdcp_pairing_show(struct seq_file *s, void *data) ++{ ++ hdmi_hdcp_dump_pairing(s, s->private); ++ return 0; ++} ++ ++static int hdmi_hdcp_dump_pairing_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, hdmi_hdcp_pairing_show, inode->i_private); ++} ++ ++static const struct file_operations hdmi_hdcp_dump_fops = { ++ .open = hdmi_hdcp_dump_pairing_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++static void hdmi_hdcp_debugfs_init(struct cdns_mhdp_device *mhdp) ++{ ++ struct dentry *d, *root; ++ ++ root = debugfs_create_dir("imx-hdcp", NULL); ++ if (IS_ERR(root) || !root) ++ goto err; ++ ++ d = debugfs_create_file("dump_pairing", 0444, root, mhdp, ++ &hdmi_hdcp_dump_fops); ++ if (!d) ++ goto err; ++ return; ++ ++err: ++ dev_err(mhdp->dev, "Unable to create debugfs entries\n"); ++} ++ ++int cdns_hdmi_hdcp_init(struct cdns_mhdp_device *mhdp, struct device_node *of_node) ++{ ++ const char *compat; ++ u32 temp; ++ int ret; ++ ++ ret = of_property_read_string(of_node, "compatible", &compat); ++ if (ret) { ++ DRM_ERROR("Failed to compatible dts string\n"); ++ return ret; ++ } ++ if (!strstr(compat, "hdmi")) ++ return -EPERM; ++ ++ ret = of_property_read_u32(of_node, "hdcp-config", &temp); ++ if (ret) { ++ /* using highest level by default */ ++ mhdp->hdcp.config = HDCP_CONFIG_2_2; ++ DRM_INFO("Failed to get HDCP config - using HDCP 2.2 only\n"); ++ } else { ++ mhdp->hdcp.config = temp; ++ show_hdcp_supported(mhdp); ++ } ++ ++ hdmi_hdcp_debugfs_init(mhdp); ++ ++#ifdef USE_DEBUG_KEYS /* reserve for hdcp test key */ ++ { ++ u8 hdcp_cfg; ++ hdcp_cfg = HDCP_TX_2 | (HDCP_USE_KMKEY << 4) | (HDCP_CONTENT_TYPE_0 << 3); ++ imx_hdmi_load_test_keys(mhdp, &hdcp_cfg); ++ } ++#endif ++ ++ mhdp->hdcp.state = HDCP_STATE_INACTIVE; ++ ++ mutex_init(&mhdp->hdcp.mutex); ++ INIT_DELAYED_WORK(&mhdp->hdcp.check_work, hdmi_hdcp_check_work); ++ INIT_WORK(&mhdp->hdcp.prop_work, hdmi_hdcp_prop_work); ++ ++ return 0; ++} ++ ++int cdns_hdmi_hdcp_enable(struct cdns_mhdp_device *mhdp) ++{ ++ int ret = 0; ++ ++ mhdp->hdcp.reauth_in_progress = 0; ++ ++#ifdef STORE_PAIRING ++ hdmi_hdcp_get_stored_pairing(mhdp); ++#endif ++ msleep(500); ++ ++ mutex_lock(&mhdp->hdcp.mutex); ++ ++ mhdp->hdcp.value = DRM_MODE_CONTENT_PROTECTION_DESIRED; ++ mhdp->hdcp.state = HDCP_STATE_ENABLING; ++ mhdp->hdcp.cancel = 0; ++ ++ schedule_work(&mhdp->hdcp.prop_work); ++ schedule_delayed_work(&mhdp->hdcp.check_work, 50); ++ ++ mutex_unlock(&mhdp->hdcp.mutex); ++ ++ return ret; ++} ++ ++int cdns_hdmi_hdcp_disable(struct cdns_mhdp_device *mhdp) ++{ ++ int ret = 0; ++ ++ mutex_lock(&mhdp->hdcp.mutex); ++ if (mhdp->hdcp.value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) { ++ mhdp->hdcp.value = DRM_MODE_CONTENT_PROTECTION_UNDESIRED; ++ mhdp->hdcp.state = HDCP_STATE_DISABLING; ++ mhdp->hdcp.cancel = 1; ++ schedule_work(&mhdp->hdcp.prop_work); ++ } ++ ++ mutex_unlock(&mhdp->hdcp.mutex); ++ ++ cancel_delayed_work_sync(&mhdp->hdcp.check_work); ++ ++ return ret; ++} ++ ++void cdns_hdmi_hdcp_atomic_check(struct drm_connector *connector, ++ struct drm_connector_state *old_state, ++ struct drm_connector_state *new_state) ++{ ++ u64 old_cp = old_state->content_protection; ++ u64 new_cp = new_state->content_protection; ++ struct drm_crtc_state *crtc_state; ++ ++ if (!new_state->crtc) { ++ /* ++ * If the connector is being disabled with CP enabled, mark it ++ * desired so it's re-enabled when the connector is brought back ++ */ ++ if (old_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED) ++ new_state->content_protection = ++ DRM_MODE_CONTENT_PROTECTION_DESIRED; ++ return; ++ } ++ ++ /* ++ * Nothing to do if the state didn't change, or HDCP was activated since ++ * the last commit ++ */ ++ if (old_cp == new_cp || ++ (old_cp == DRM_MODE_CONTENT_PROTECTION_DESIRED && ++ new_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED)) ++ return; ++ ++ crtc_state = drm_atomic_get_new_crtc_state(new_state->state, new_state->crtc); ++ crtc_state->mode_changed = true; ++} ++ ++static int hdmi_hdcp_check_link(struct cdns_mhdp_device *mhdp) ++{ ++ u16 hdcp_port_status = 0; ++ u8 hdcp_last_error = 0; ++ u8 hpd_sts; ++ int ret = 0; ++ ++ mhdp->hdcp.reauth_in_progress = 0; ++ mutex_lock(&mhdp->lock); ++ ++ if ((mhdp->hdcp.state == HDCP_STATE_AUTHENTICATED) || ++ (mhdp->hdcp.state == HDCP_STATE_AUTHENTICATING) || ++ (mhdp->hdcp.state == HDCP_STATE_REAUTHENTICATING) || ++ (mhdp->hdcp.state == HDCP_STATE_ENABLING)) { ++ ++ /* In active states, check the HPD signal. Because of the IRQ ++ * debounce delay, the state might not reflect the disconnection. ++ * The FW could already have detected the HDP down and reported error */ ++ hpd_sts = cdns_mhdp_read_hpd(mhdp); ++ if (1 != hpd_sts) ++ mhdp->hdcp.state = HDCP_STATE_DISABLING; ++ } ++ ++ if (mhdp->hdcp.state == HDCP_STATE_INACTIVE) ++ goto out; ++ ++ if (mhdp->hdcp.state == HDCP_STATE_DISABLING) { ++ _hdmi_hdcp_disable(mhdp); ++ mhdp->hdcp.state = HDCP_STATE_INACTIVE; ++ goto out; ++ } ++ ++/* TODO items: ++ Need to make sure that any requests from the firmware are actually ++ processed so want to remove this first jump to 'out', i.e. process ++ reauthentication requests, cleanup errors and repeater receiver id ++ checks. ++*/ ++ if (mhdp->hdcp.state == HDCP_STATE_AUTHENTICATED) { ++ /* get port status */ ++ hdcp_port_status = hdmi_hdcp_get_status(mhdp); ++ hdcp_last_error = GET_HDCP_PORT_STS_LAST_ERR(hdcp_port_status); ++ if (hdcp_last_error == HDCP_TRAN_ERR_REAUTH_REQ) { ++ DRM_INFO("Sink requesting re-authentication\n"); ++ mhdp->hdcp.state = HDCP_STATE_REAUTHENTICATING; ++ } else if (hdcp_last_error) { ++ DRM_ERROR("HDCP error no: %u\n", hdcp_last_error); ++ ++ if (mhdp->hdcp.value == DRM_MODE_CONTENT_PROTECTION_UNDESIRED) ++ goto out; ++ if (hdcp_port_status & HDCP_PORT_STS_AUTH) { ++ if (mhdp->hdcp.value != ++ DRM_MODE_CONTENT_PROTECTION_UNDESIRED) { ++ mhdp->hdcp.value = ++ DRM_MODE_CONTENT_PROTECTION_ENABLED; ++ schedule_work(&mhdp->hdcp.prop_work); ++ goto out; ++ } ++ } ++ ++ mhdp->hdcp.state = HDCP_STATE_AUTH_FAILED; ++ ++ } else if (mhdp->hdcp.sink_is_repeater) { ++ u8 new_events; ++ /* Check events... and process if HDCPTX_IS_RECEIVER_ID_VALID_EVENT. */ ++ new_events = cdns_mhdp_get_event(mhdp); ++ mhdp->hdcp.events |= new_events; ++ if (check_event(mhdp->hdcp.events, HDCPTX_IS_RECEIVER_ID_VALID_EVENT)) { ++ DRM_INFO("Sink repeater updating receiver ID list...\n"); ++ if (hdmi_hdcp_check_receviers(mhdp)) ++ mhdp->hdcp.state = HDCP_STATE_AUTH_FAILED; ++ } ++ } ++ } ++ ++ if (mhdp->hdcp.state == HDCP_STATE_REAUTHENTICATING) { ++ /* For now just deal with HDCP2.2 */ ++ if (mhdp->hdcp.hdcp_version == HDCP_TX_2) ++ mhdp->hdcp.reauth_in_progress = 1; ++ else ++ mhdp->hdcp.state = HDCP_STATE_AUTH_FAILED; ++ } ++ ++ if (mhdp->hdcp.state == HDCP_STATE_ENABLING) { ++ mhdp->hdcp.state = HDCP_STATE_AUTHENTICATING; ++ ret = _hdmi_hdcp_enable(mhdp); ++ if (ret == -ECANCELED) ++ goto out; ++ else if (ret) { ++ DRM_ERROR("Failed to enable hdcp (%d)\n", ret); ++ mhdp->hdcp.value = DRM_MODE_CONTENT_PROTECTION_DESIRED; ++ schedule_work(&mhdp->hdcp.prop_work); ++ goto out; ++ } ++ } ++ ++ if ((mhdp->hdcp.state == HDCP_STATE_AUTH_FAILED) || ++ (mhdp->hdcp.state == HDCP_STATE_REAUTHENTICATING)) { ++ ++ print_port_status(hdcp_port_status); ++ if (mhdp->hdcp.state == HDCP_STATE_AUTH_FAILED) { ++ DRM_DEBUG_KMS("[%s:%d] HDCP link failed, retrying authentication 0x%2x\n", ++ mhdp->connector.base.name, mhdp->connector.base.base.id, hdcp_port_status); ++ ret = _hdmi_hdcp_disable(mhdp); ++ if (ret) { ++ DRM_ERROR("Failed to disable hdcp (%d)\n", ret); ++ mhdp->hdcp.value = DRM_MODE_CONTENT_PROTECTION_DESIRED; ++ schedule_work(&mhdp->hdcp.prop_work); ++ goto out; ++ } ++ } else ++ DRM_DEBUG_KMS("[%s:%d] HDCP attempt reauthentication 0x%2x\n", ++ mhdp->connector.base.name, mhdp->connector.base.base.id, hdcp_port_status); ++ ++ ret = _hdmi_hdcp_enable(mhdp); ++ if (ret == -ECANCELED) ++ goto out; ++ else if (ret) { ++ DRM_ERROR("Failed to enable hdcp (%d)\n", ret); ++ mhdp->hdcp.value = DRM_MODE_CONTENT_PROTECTION_DESIRED; ++ schedule_work(&mhdp->hdcp.prop_work); ++ goto out; ++ } ++ } ++ ++out: ++ mutex_unlock(&mhdp->lock); ++ ++ return ret; ++} +diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-hdcp.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-hdcp.c +new file mode 100644 +index 000000000000..587c5f953489 +--- /dev/null ++++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-hdcp.c +@@ -0,0 +1,300 @@ ++/* ++ * Cadence HDCP API driver ++ * ++ * Copyright (C) 2021 NXP Semiconductor, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ */ ++ ++#include ++#include ++#include ++ ++#include "cdns-mhdp.h" ++ ++static u32 mhdp_hdcp_bus_read(struct cdns_mhdp_device *mhdp, u32 offset) ++{ ++ u32 val; ++ ++ mutex_lock(&mhdp->iolock); ++ ++ if (mhdp->bus_type == BUS_TYPE_LOW4K_APB) { ++ /* Remap address to low 4K APB bus */ ++ writel(offset >> 12, mhdp->regs_sec + 8); ++ val = readl((offset & 0xfff) + mhdp->regs_base); ++ } else if (mhdp->bus_type == BUS_TYPE_NORMAL_APB) ++ val = readl(mhdp->regs_sec + offset); ++ ++ mutex_unlock(&mhdp->iolock); ++ ++ return val; ++} ++ ++static void mhdp_hdcp_bus_write(u32 val, struct cdns_mhdp_device *mhdp, u32 offset) ++{ ++ mutex_lock(&mhdp->iolock); ++ ++ if (mhdp->bus_type == BUS_TYPE_LOW4K_APB) { ++ /* Remap address to low 4K APB bus */ ++ writel(offset >> 12, mhdp->regs_sec + 8); ++ writel(val, (offset & 0xfff) + mhdp->regs_base); ++ } else if (mhdp->bus_type == BUS_TYPE_NORMAL_APB) ++ writel(val, mhdp->regs_sec + offset); ++ ++ mutex_unlock(&mhdp->iolock); ++} ++ ++static int mhdp_hdcp_mailbox_read(struct cdns_mhdp_device *mhdp) ++{ ++ int val, ret; ++ ++ ret = mhdp_readx_poll_timeout(mhdp_hdcp_bus_read, mhdp, MAILBOX_EMPTY_ADDR, ++ val, !val, MAILBOX_RETRY_US, ++ MAILBOX_TIMEOUT_US); ++ if (ret < 0) ++ return ret; ++ ++ return mhdp_hdcp_bus_read(mhdp, MAILBOX0_RD_DATA) & 0xff; ++} ++ ++static int mhdp_hdcp_mailbox_write(struct cdns_mhdp_device *mhdp, u8 val) ++{ ++ int ret, full; ++ ++ ret = mhdp_readx_poll_timeout(mhdp_hdcp_bus_read, mhdp, MAILBOX_FULL_ADDR, ++ full, !full, MAILBOX_RETRY_US, ++ MAILBOX_TIMEOUT_US); ++ if (ret < 0) ++ return ret; ++ ++ mhdp_hdcp_bus_write(val, mhdp, MAILBOX0_WR_DATA); ++ ++ return 0; ++} ++ ++static int mhdp_hdcp_mailbox_validate_receive(struct cdns_mhdp_device *mhdp, ++ u8 module_id, u8 opcode, u16 req_size) ++{ ++ u32 mbox_size, i; ++ u8 header[4]; ++ int ret; ++ ++ /* read the header of the message */ ++ for (i = 0; i < 4; i++) { ++ ret = mhdp_hdcp_mailbox_read(mhdp); ++ if (ret < 0) ++ return ret; ++ ++ header[i] = ret; ++ } ++ ++ mbox_size = get_unaligned_be16(header + 2); ++ ++ if (opcode != header[0] || module_id != header[1] || ++ req_size != mbox_size) { ++ /* ++ * If the message in mailbox is not what we want, we need to ++ * clear the mailbox by reading its contents. ++ */ ++ for (i = 0; i < mbox_size; i++) ++ if (mhdp_hdcp_mailbox_read(mhdp) < 0) ++ break; ++ ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int mhdp_hdcp_mailbox_read_receive(struct cdns_mhdp_device *mhdp, ++ u8 *buff, u16 buff_size) ++{ ++ u32 i; ++ int ret; ++ ++ for (i = 0; i < buff_size; i++) { ++ ret = mhdp_hdcp_mailbox_read(mhdp); ++ if (ret < 0) ++ return ret; ++ ++ buff[i] = ret; ++ } ++ ++ return 0; ++} ++ ++static int mhdp_hdcp_mailbox_send(struct cdns_mhdp_device *mhdp, u8 module_id, ++ u8 opcode, u16 size, u8 *message) ++{ ++ u8 header[4]; ++ int ret, i; ++ ++ header[0] = opcode; ++ header[1] = module_id; ++ put_unaligned_be16(size, header + 2); ++ ++ for (i = 0; i < 4; i++) { ++ ret = mhdp_hdcp_mailbox_write(mhdp, header[i]); ++ if (ret) ++ return ret; ++ } ++ ++ for (i = 0; i < size; i++) { ++ ret = mhdp_hdcp_mailbox_write(mhdp, message[i]); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++/* HDCP API */ ++int cdns_mhdp_hdcp_tx_config(struct cdns_mhdp_device *mhdp, u8 config) ++{ ++ return mhdp_hdcp_mailbox_send(mhdp, MB_MODULE_ID_HDCP_TX, ++ HDCP_TX_CONFIGURATION, sizeof(config), &config); ++} ++EXPORT_SYMBOL(cdns_mhdp_hdcp_tx_config); ++ ++int cdns_mhdp_hdcp2_tx_respond_km(struct cdns_mhdp_device *mhdp, ++ u8 *msg, u16 len) ++{ ++ return mhdp_hdcp_mailbox_send(mhdp, MB_MODULE_ID_HDCP_TX, ++ HDCP2_TX_RESPOND_KM, len, msg); ++} ++EXPORT_SYMBOL(cdns_mhdp_hdcp2_tx_respond_km); ++ ++int cdns_mhdp_hdcp_tx_status_req(struct cdns_mhdp_device *mhdp, ++ u8 *status, u16 len) ++{ ++ int ret; ++ ++ ret = mhdp_hdcp_mailbox_send(mhdp, MB_MODULE_ID_HDCP_TX, ++ HDCP_TX_STATUS_CHANGE, 0, NULL); ++ if (ret) ++ goto err_tx_req; ++ ++ ret = mhdp_hdcp_mailbox_validate_receive(mhdp, MB_MODULE_ID_HDCP_TX, ++ HDCP_TX_STATUS_CHANGE, len); ++ if (ret) ++ goto err_tx_req; ++ ++ ret = mhdp_hdcp_mailbox_read_receive(mhdp, status, len); ++ if (ret) ++ goto err_tx_req; ++ ++err_tx_req: ++ if (ret) ++ DRM_ERROR("hdcp tx status req failed: %d\n", ret); ++ return ret; ++} ++EXPORT_SYMBOL(cdns_mhdp_hdcp_tx_status_req); ++ ++int cdns_mhdp_hdcp2_tx_is_km_stored_req(struct cdns_mhdp_device *mhdp, u8 *data, u16 len) ++{ ++ int ret; ++ ++ ret = mhdp_hdcp_mailbox_send(mhdp, MB_MODULE_ID_HDCP_TX, ++ HDCP2_TX_IS_KM_STORED, 0, NULL); ++ if (ret) ++ goto err_is_km; ++ ++ ret = mhdp_hdcp_mailbox_validate_receive(mhdp, MB_MODULE_ID_HDCP_TX, ++ HDCP2_TX_IS_KM_STORED, len); ++ if (ret) ++ goto err_is_km; ++ ++ ret = mhdp_hdcp_mailbox_read_receive(mhdp, data, len); ++ ++err_is_km: ++ if (ret) ++ DRM_ERROR("hdcp2 tx is km stored req failed: %d\n", ret); ++ return ret; ++} ++EXPORT_SYMBOL(cdns_mhdp_hdcp2_tx_is_km_stored_req); ++ ++int cdns_mhdp_hdcp2_tx_store_km(struct cdns_mhdp_device *mhdp, ++ u8 *resp, u16 len) ++{ ++ int ret; ++ ++ ret = mhdp_hdcp_mailbox_send(mhdp, MB_MODULE_ID_HDCP_TX, ++ HDCP2_TX_STORE_KM, 0, NULL); ++ if (ret) ++ goto err_store_km; ++ ++ ret = mhdp_hdcp_mailbox_validate_receive(mhdp, MB_MODULE_ID_HDCP_TX, ++ HDCP2_TX_STORE_KM, len); ++ if (ret) ++ goto err_store_km; ++ ++ ret = mhdp_hdcp_mailbox_read_receive(mhdp, resp, len); ++ ++err_store_km: ++ return ret; ++} ++EXPORT_SYMBOL(cdns_mhdp_hdcp2_tx_store_km); ++ ++int cdns_mhdp_hdcp_tx_is_receiver_id_valid(struct cdns_mhdp_device *mhdp, ++ u8 *rx_id, u8 *num) ++{ ++ u32 mbox_size, i; ++ u8 header[4]; ++ u8 temp; ++ int ret; ++ ++ ret = mhdp_hdcp_mailbox_send(mhdp, MB_MODULE_ID_HDCP_TX, ++ HDCP_TX_IS_RECEIVER_ID_VALID, 0, NULL); ++ if (ret) ++ goto err_rx_id; ++ ++ /* read the header of the message */ ++ for (i = 0; i < 4; i++) { ++ ret = mhdp_hdcp_mailbox_read(mhdp); ++ if (ret < 0) ++ return ret; ++ ++ header[i] = ret; ++ } ++ ++ mbox_size = get_unaligned_be16(header + 2); ++ ++ if (HDCP_TX_IS_RECEIVER_ID_VALID != header[0] || ++ MB_MODULE_ID_HDCP_TX != header[1]) ++ return -EINVAL; ++ ++ /* First get num of receivers */ ++ ret = mhdp_hdcp_mailbox_read_receive(mhdp, num, 1); ++ if (ret) ++ goto err_rx_id; ++ ++ /* skip second data */ ++ ret = mhdp_hdcp_mailbox_read_receive(mhdp, &temp, 1); ++ if (ret) ++ goto err_rx_id; ++ ++ /* get receivers ID */ ++ ret = mhdp_hdcp_mailbox_read_receive(mhdp, rx_id, mbox_size - 2); ++ ++err_rx_id: ++ return ret; ++} ++EXPORT_SYMBOL(cdns_mhdp_hdcp_tx_is_receiver_id_valid); ++ ++int cdns_mhdp_hdcp_tx_respond_receiver_id_valid( ++ struct cdns_mhdp_device *mhdp, u8 val) ++{ ++ return mhdp_hdcp_mailbox_send(mhdp, MB_MODULE_ID_HDCP_TX, ++ HDCP_TX_RESPOND_RECEIVER_ID_VALID, sizeof(val), &val); ++} ++EXPORT_SYMBOL(cdns_mhdp_hdcp_tx_respond_receiver_id_valid); ++ ++int cdns_mhdp_hdcp_tx_reauth(struct cdns_mhdp_device *mhdp, u8 msg) ++{ ++ return mhdp_hdcp_mailbox_send(mhdp, MB_MODULE_ID_HDCP_TX, ++ HDCP_TX_DO_AUTH_REQ, sizeof(msg), &msg); ++} ++EXPORT_SYMBOL(cdns_mhdp_hdcp_tx_reauth); +diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-hdcp.h b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-hdcp.h +new file mode 100644 +index 000000000000..4ce76dd1ee58 +--- /dev/null ++++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-hdcp.h +@@ -0,0 +1,36 @@ ++/* ++ * Copyright (C) 2021 NXP Semiconductor, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ */ ++ ++#ifndef CDNS_HDMI_HDCP_H ++#define CDNS_HDMI_HDCP_H ++ ++int cdns_mhdp_hdcp2_tx_respond_km(struct cdns_mhdp_device *mhdp, ++ u8 *msg, u16 len); ++int cdns_mhdp_hdcp_tx_config(struct cdns_mhdp_device *mhdp, u8 config); ++int cdns_mhdp_hdcp_tx_status_req(struct cdns_mhdp_device *mhdp, ++ u8 *status, u16 len); ++int cdns_mhdp_hdcp2_tx_is_km_stored_req(struct cdns_mhdp_device *mhdp, u8 *data, u16 len); ++int cdns_mhdp_hdcp2_tx_store_km(struct cdns_mhdp_device *mhdp, ++ u8 *reg, u16 len); ++int cdns_mhdp_hdcp_tx_is_receiver_id_valid(struct cdns_mhdp_device *mhdp, ++ u8 *rx_id, u8 *num); ++int cdns_mhdp_hdcp_tx_respond_receiver_id_valid(struct cdns_mhdp_device *mhdp, ++ u8 val); ++int cdns_mhdp_hdcp_tx_test_keys(struct cdns_mhdp_device *mhdp, u8 type, u8 resp); ++int cdns_mhdp_hdcp_tx_reauth(struct cdns_mhdp_device *mhdp, u8 msg); ++ ++int cdns_hdmi_hdcp_init(struct cdns_mhdp_device *mhdp, struct device_node *of_node); ++int cdns_hdmi_hdcp_enable(struct cdns_mhdp_device *mhdp); ++int cdns_hdmi_hdcp_disable(struct cdns_mhdp_device *mhdp); ++void cdns_hdmi_hdcp_atomic_check(struct drm_connector *connector, ++ struct drm_connector_state *old_state, ++ struct drm_connector_state *new_state); ++ ++#endif /* CDNS_HDMI_HDCP_H */ +diff --git a/include/drm/bridge/cdns-mhdp.h b/include/drm/bridge/cdns-mhdp.h +index 338fa55b8bdf..5752c47b1a16 100644 +--- a/include/drm/bridge/cdns-mhdp.h ++++ b/include/drm/bridge/cdns-mhdp.h +@@ -388,6 +388,27 @@ + #define HDMI_TX_TEST 0xBB + #define HDMI_TX_EDID_INTERNAL 0xF0 + ++/* HDCP General opcode */ ++#define HDCP_GENERAL_SET_LC_128 0x00 ++#define HDCP_GENERAL_SET_SEED 0x01 ++ ++/* HDCP TX opcode */ ++#define HDCP_TX_CONFIGURATION 0x00 ++#define HDCP2_TX_SET_PUBLIC_KEY_PARAMS 0x01 ++#define HDCP2_TX_SET_DEBUG_RANDOM_NUMBERS 0x02 ++#define HDCP2_TX_RESPOND_KM 0x03 ++#define HDCP1_TX_SEND_KEYS 0x04 ++#define HDCP1_TX_SEND_RANDOM_AN 0x05 ++#define HDCP_TX_STATUS_CHANGE 0x06 ++#define HDCP2_TX_IS_KM_STORED 0x07 ++#define HDCP2_TX_STORE_KM 0x08 ++#define HDCP_TX_IS_RECEIVER_ID_VALID 0x09 ++#define HDCP_TX_RESPOND_RECEIVER_ID_VALID 0x0A ++#define HDCP_TX_TEST_KEYS 0x0B ++#define HDCP2_TX_SET_KM_KEY_PARAMS 0x0C ++#define HDCP_TX_SET_CP_IRQ 0x0D ++#define HDCP_TX_DO_AUTH_REQ 0x0E ++ + #define FW_STANDBY 0 + #define FW_ACTIVE 1 + +@@ -428,12 +449,16 @@ + #define EQ_PHASE_FAILED BIT(6) + #define FASE_LT_FAILED BIT(7) + +-#define DPTX_HPD_EVENT BIT(0) +-#define DPTX_TRAINING_EVENT BIT(1) +-#define HDCP_TX_STATUS_EVENT BIT(4) +-#define HDCP2_TX_IS_KM_STORED_EVENT BIT(5) +-#define HDCP2_TX_STORE_KM_EVENT BIT(6) +-#define HDCP_TX_IS_RECEIVER_ID_VALID_EVENT BIT(7) ++#define DPTX_HPD_EVENT BIT(0) ++#define HDMI_TX_HPD_EVENT BIT(0) ++#define HDMI_RX_5V_EVENT BIT(0) ++#define DPTX_TRAINING_EVENT BIT(1) ++#define HDMI_RX_SCDC_CHANGE_EVENT BIT(1) ++#define HDCPTX_STATUS_EVENT BIT(4) ++#define HDCPRX_STATUS_EVENT BIT(4) ++#define HDCPTX_IS_KM_STORED_EVENT BIT(5) ++#define HDCPTX_STORE_KM_EVENT BIT(6) ++#define HDCPTX_IS_RECEIVER_ID_VALID_EVENT BIT(7) + + #define TU_SIZE 30 + #define CDNS_DP_MAX_LINK_RATE 540000 +@@ -442,6 +467,7 @@ + #define F_VIF_DATA_WIDTH(x) (((x) & ((1 << 2) - 1)) << 2) + #define F_HDMI_MODE(x) (((x) & ((1 << 2) - 1)) << 0) + #define F_GCP_EN(x) (((x) & ((1 << 1) - 1)) << 12) ++#define F_CLEAR_AVMUTE(x) (((x) & ((1 << 1) - 1)) << 14) + #define F_DATA_EN(x) (((x) & ((1 << 1) - 1)) << 15) + #define F_HDMI2_PREAMBLE_EN(x) (((x) & ((1 << 1) - 1)) << 18) + #define F_PIC_3D(x) (((x) & ((1 << 4) - 1)) << 7) +@@ -662,6 +688,55 @@ struct cdns_plat_data { + char *plat_name; + }; + ++/* HDCP */ ++#define MAX_STORED_KM 64 ++#define HDCP_PAIRING_M_LEN 16 ++#define HDCP_PAIRING_M_EKH 16 ++#define HDCP_PAIRING_R_ID 5 ++ ++/* HDCP2_TX_SET_DEBUG_RANDOM_NUMBERS */ ++#define DEBUG_RANDOM_NUMBERS_KM_LEN 16 ++#define DEBUG_RANDOM_NUMBERS_RN_LEN 8 ++#define DEBUG_RANDOM_NUMBERS_KS_LEN 16 ++#define DEBUG_RANDOM_NUMBERS_RIV_LEN 8 ++#define DEBUG_RANDOM_NUMBERS_RTX_LEN 8 ++ ++struct hdcp_trans_pairing_data { ++ u8 receiver_id[HDCP_PAIRING_R_ID]; ++ u8 m[HDCP_PAIRING_M_LEN]; ++ u8 km[DEBUG_RANDOM_NUMBERS_KM_LEN]; ++ u8 ekh[HDCP_PAIRING_M_EKH]; ++}; ++ ++enum hdmi_hdcp_state { ++ HDCP_STATE_NO_AKSV, ++ HDCP_STATE_INACTIVE, ++ HDCP_STATE_ENABLING, ++ HDCP_STATE_AUTHENTICATING, ++ HDCP_STATE_REAUTHENTICATING, ++ HDCP_STATE_AUTHENTICATED, ++ HDCP_STATE_DISABLING, ++ HDCP_STATE_AUTH_FAILED ++}; ++ ++struct cdns_mhdp_hdcp { ++ struct mutex mutex; ++ u64 value; /* protected by hdcp_mutex */ ++ struct delayed_work check_work; ++ struct work_struct prop_work; ++ u8 state; ++ u8 cancel; ++ u8 bus_type; ++ u8 config; ++ struct hdcp_trans_pairing_data pairing[MAX_STORED_KM]; ++ u8 num_paired; ++ ++ u8 events; ++ u8 sink_is_repeater; ++ u8 reauth_in_progress; ++ u8 hdcp_version; ++}; ++ + struct cdns_mhdp_device { + void __iomem *regs_base; + void __iomem *regs_sec; +@@ -669,6 +744,7 @@ struct cdns_mhdp_device { + int bus_type; + + struct device *dev; ++ struct drm_device *drm_dev; + + struct cdns_mhdp_connector connector; + struct clk *spdif_clk; +@@ -722,6 +798,7 @@ struct cdns_mhdp_device { + hdmi_codec_plugged_cb plugged_cb; + struct device *codec_dev; + enum drm_connector_status last_connector_result; ++ struct cdns_mhdp_hdcp hdcp; + }; + + u32 cdns_mhdp_bus_read(struct cdns_mhdp_device *mhdp, u32 offset); +@@ -742,6 +819,7 @@ int cdns_mhdp_get_edid_block(void *mhdp, u8 *edid, + 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); ++int cdns_mhdp_apb_conf(struct cdns_mhdp_device *mhdp, u8 sel); + + /* Audio */ + int cdns_mhdp_audio_stop(struct cdns_mhdp_device *mhdp, +@@ -770,8 +848,6 @@ int cdns_mhdp_mailbox_read_receive(struct cdns_mhdp_device *mhdp, + int cdns_mhdp_mailbox_validate_receive(struct cdns_mhdp_device *mhdp, + u8 module_id, u8 opcode, + u16 req_size); +-int cdns_mhdp_mailbox_read(struct cdns_mhdp_device *mhdp); +- + void cdns_mhdp_infoframe_set(struct cdns_mhdp_device *mhdp, + u8 entry_id, u8 packet_len, u8 *packet, u8 packet_type); + int cdns_hdmi_get_edid_block(void *data, u8 *edid, u32 block, size_t length); +-- +2.29.2 + diff --git a/projects/NXP/devices/iMX8/patches/linux/0021-LF-3272-drm-cdns_mhdp-fix-Coverity-Issue-11566406.patch b/projects/NXP/devices/iMX8/patches/linux/0021-LF-3272-drm-cdns_mhdp-fix-Coverity-Issue-11566406.patch new file mode 100644 index 0000000000..bf124e8f71 --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0021-LF-3272-drm-cdns_mhdp-fix-Coverity-Issue-11566406.patch @@ -0,0 +1,30 @@ +From 2a093769a29f03103195b34c269411ee21b646e2 Mon Sep 17 00:00:00 2001 +From: Sandor Yu +Date: Wed, 20 Jan 2021 10:37:09 +0800 +Subject: [PATCH 21/49] LF-3272: drm: cdns_mhdp: fix Coverity Issue: 11566406 + +Add default access hdcp bus to fix +Coverity Issue: 11566406 Uninitialized scalar variable. + +Signed-off-by: Sandor Yu +Reviewed-by: Robby Cai +--- + drivers/gpu/drm/bridge/cadence/cdns-mhdp-hdcp.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-hdcp.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-hdcp.c +index 587c5f953489..b3c931382013 100644 +--- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-hdcp.c ++++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-hdcp.c +@@ -27,6 +27,8 @@ static u32 mhdp_hdcp_bus_read(struct cdns_mhdp_device *mhdp, u32 offset) + val = readl((offset & 0xfff) + mhdp->regs_base); + } else if (mhdp->bus_type == BUS_TYPE_NORMAL_APB) + val = readl(mhdp->regs_sec + offset); ++ else ++ val = readl(mhdp->regs_base + offset); + + mutex_unlock(&mhdp->iolock); + +-- +2.29.2 + diff --git a/projects/NXP/devices/iMX8/patches/linux/0022-LF-3271-drm-cdns-hdmi-fix-Coverity-Issue-11566407.patch b/projects/NXP/devices/iMX8/patches/linux/0022-LF-3271-drm-cdns-hdmi-fix-Coverity-Issue-11566407.patch new file mode 100644 index 0000000000..af85c51c9d --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0022-LF-3271-drm-cdns-hdmi-fix-Coverity-Issue-11566407.patch @@ -0,0 +1,28 @@ +From 85ad1a878118a8dbaf9da5f85a2e088880d5ea01 Mon Sep 17 00:00:00 2001 +From: Sandor Yu +Date: Wed, 20 Jan 2021 10:44:17 +0800 +Subject: [PATCH 22/49] LF-3271: drm: cdns-hdmi: fix Coverity Issue: 11566407 + +Delete dead code to fix Coverity Issue: 11566407. + +Signed-off-by: Sandor Yu +Reviewed-by: Robby Cai +--- + drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c +index dc393f6b75e7..a89c8cba4788 100644 +--- a/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c ++++ b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c +@@ -150,7 +150,6 @@ ssize_t HDCPTX_Status_store(struct device *dev, + else + dev_err(dev, "%s &hdp->state invalid\n", __func__); + return -1; +- return count; + } + + static void hdmi_sink_config(struct cdns_mhdp_device *mhdp) +-- +2.29.2 + diff --git a/projects/NXP/devices/iMX8/patches/linux/0023-LF-3270-drm-cdns-hdmi-fix-coverity-Issue-11566405.patch b/projects/NXP/devices/iMX8/patches/linux/0023-LF-3270-drm-cdns-hdmi-fix-coverity-Issue-11566405.patch new file mode 100644 index 0000000000..2ece85029d --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0023-LF-3270-drm-cdns-hdmi-fix-coverity-Issue-11566405.patch @@ -0,0 +1,40 @@ +From ddfa5aeb97c12fb7a67e6507ef2ae051658f112b Mon Sep 17 00:00:00 2001 +From: Sandor Yu +Date: Wed, 20 Jan 2021 10:49:13 +0800 +Subject: [PATCH 23/49] LF-3270: drm: cdns-hdmi: fix coverity Issue: 11566405 + +Delete unused code to fix coverity Issue: 11566405. + +Signed-off-by: Sandor Yu +Reviewed-by: Robby Cai +--- + drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +diff --git a/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c +index a89c8cba4788..2300c3d8a91d 100644 +--- a/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c ++++ b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c +@@ -37,17 +37,15 @@ static struct device_attribute HDCPTX_do_reauth = __ATTR_WO(HDCPTX_do_reauth); + static ssize_t HDCPTX_do_reauth_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) + { +- int value, ret; ++ int ret; + struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev); + + ret = cdns_mhdp_hdcp_tx_reauth(mhdp, 1); +- +- sscanf(buf, "%d", &value); +- + if (ret < 0) { + dev_err(dev, "%s cdns_mhdp_hdcp_tx_reauth failed\n", __func__); + return -1; + } ++ + return count; + } + +-- +2.29.2 + diff --git a/projects/NXP/devices/iMX8/patches/linux/0024-LF-3269-drm-cdns-hdmi-fix-coverity-Issue-11566404.patch b/projects/NXP/devices/iMX8/patches/linux/0024-LF-3269-drm-cdns-hdmi-fix-coverity-Issue-11566404.patch new file mode 100644 index 0000000000..1395849b6f --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0024-LF-3269-drm-cdns-hdmi-fix-coverity-Issue-11566404.patch @@ -0,0 +1,35 @@ +From 2812d071eb348d903620f7ebadaf848024b3c672 Mon Sep 17 00:00:00 2001 +From: Sandor Yu +Date: Wed, 20 Jan 2021 11:04:41 +0800 +Subject: [PATCH 24/49] LF-3269: drm: cdns-hdmi: fix coverity Issue: 11566404 + +Check return value to fix coverity Issue: 11566404. + +Signed-off-by: Sandor Yu +Reviewed-by: Robby Cai +--- + drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +diff --git a/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c +index 2300c3d8a91d..df8ac87b3a54 100644 +--- a/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c ++++ b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c +@@ -59,9 +59,12 @@ static ssize_t HDCPTX_Version_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) + { + struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev); +- int value; ++ int value, ret; ++ ++ ret = sscanf(buf, "%d", &value); ++ if (ret != 1) ++ return -EINVAL; + +- sscanf(buf, "%d", &value); + if (value == 2) + mhdp->hdcp.config = 2; + else if (value == 1) +-- +2.29.2 + diff --git a/projects/NXP/devices/iMX8/patches/linux/0025-LF-3268-drm-cdns-hdmi-fix-Coverity-Issue-11566403.patch b/projects/NXP/devices/iMX8/patches/linux/0025-LF-3268-drm-cdns-hdmi-fix-Coverity-Issue-11566403.patch new file mode 100644 index 0000000000..0a3c6444ab --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0025-LF-3268-drm-cdns-hdmi-fix-Coverity-Issue-11566403.patch @@ -0,0 +1,36 @@ +From cd49375db5c05acb824fa18ae9d19290073cda08 Mon Sep 17 00:00:00 2001 +From: Sandor Yu +Date: Wed, 20 Jan 2021 11:07:32 +0800 +Subject: [PATCH 25/49] LF-3268: drm: cdns-hdmi: fix Coverity Issue: 11566403 + +Check return value to fix Coverity Issue: 11566403. + +Signed-off-by: Sandor Yu +Reviewed-by: Robby Cai +--- + drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +diff --git a/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c +index df8ac87b3a54..28193178140f 100644 +--- a/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c ++++ b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c +@@ -119,10 +119,13 @@ ssize_t HDCPTX_Status_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) + { + struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev); +- int value; ++ int value, ret; + + if (count == 2) { +- sscanf(buf, "%d", &value); ++ ret = sscanf(buf, "%d", &value); ++ if (ret != 1) ++ return -EINVAL; ++ + if ((value >= HDCP_STATE_NO_AKSV) && (value <= HDCP_STATE_AUTH_FAILED)) { + mhdp->hdcp.state = value; + return count; +-- +2.29.2 + diff --git a/projects/NXP/devices/iMX8/patches/linux/0026-LF-3367-1-drm-cdns_hdmi-HDCP_STATE_DISABLING-may-mis.patch b/projects/NXP/devices/iMX8/patches/linux/0026-LF-3367-1-drm-cdns_hdmi-HDCP_STATE_DISABLING-may-mis.patch new file mode 100644 index 0000000000..774398207e --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0026-LF-3367-1-drm-cdns_hdmi-HDCP_STATE_DISABLING-may-mis.patch @@ -0,0 +1,45 @@ +From 54a5d4d3ba2de923fa4a4e5ef5e90151fb7f2fd8 Mon Sep 17 00:00:00 2001 +From: Sandor Yu +Date: Thu, 18 Feb 2021 16:25:52 +0800 +Subject: [PATCH 26/49] LF-3367-1: drm: cdns_hdmi: HDCP_STATE_DISABLING may + missed by check link + +Polling thread check_work is designed to handle all hdcp state change. +In HDCP disable function, check_work thread will be stopped after +hdcp.state is set to HDCP_STATE_DISABLING. check_work thread may miss +the state change, call check link function make sure HDCP_STATE_DISABLING +state is properly handled. + +Signed-off-by: Sandor Yu +Reviewed-by: Robby Cai +Acked-by: Jason Liu +--- + drivers/gpu/drm/bridge/cadence/cdns-hdmi-hdcp.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/bridge/cadence/cdns-hdmi-hdcp.c b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-hdcp.c +index e2a3bc7fb42b..9119f2063098 100644 +--- a/drivers/gpu/drm/bridge/cadence/cdns-hdmi-hdcp.c ++++ b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-hdcp.c +@@ -988,6 +988,8 @@ int cdns_hdmi_hdcp_disable(struct cdns_mhdp_device *mhdp) + { + int ret = 0; + ++ cancel_delayed_work_sync(&mhdp->hdcp.check_work); ++ + mutex_lock(&mhdp->hdcp.mutex); + if (mhdp->hdcp.value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) { + mhdp->hdcp.value = DRM_MODE_CONTENT_PROTECTION_UNDESIRED; +@@ -998,7 +1000,8 @@ int cdns_hdmi_hdcp_disable(struct cdns_mhdp_device *mhdp) + + mutex_unlock(&mhdp->hdcp.mutex); + +- cancel_delayed_work_sync(&mhdp->hdcp.check_work); ++ /* Make sure HDCP_STATE_DISABLING state is handled */ ++ hdmi_hdcp_check_link(mhdp); + + return ret; + } +-- +2.29.2 + diff --git a/projects/NXP/devices/iMX8/patches/linux/0027-LF-3367-2-drm-mhdp-more-time-for-FW-alive-check.patch b/projects/NXP/devices/iMX8/patches/linux/0027-LF-3367-2-drm-mhdp-more-time-for-FW-alive-check.patch new file mode 100644 index 0000000000..9c39cf8250 --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0027-LF-3367-2-drm-mhdp-more-time-for-FW-alive-check.patch @@ -0,0 +1,31 @@ +From 42394af5975326eb20901d65eac47963847006e2 Mon Sep 17 00:00:00 2001 +From: Sandor Yu +Date: Fri, 19 Feb 2021 16:41:31 +0800 +Subject: [PATCH 27/49] LF-3367-2: drm: mhdp: more time for FW alive check + +FW alive check function may return false in hdcp enable/disable stress test. +Add more time for FW alive check, make sure get correct state. + +Signed-off-by: Sandor Yu +Reviewed-by: Robby Cai +Acked-by: Jason Liu +--- + drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c +index 2a8ab0872f25..3487a2fa335c 100644 +--- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c ++++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c +@@ -142,7 +142,7 @@ bool cdns_mhdp_check_alive(struct cdns_mhdp_device *mhdp) + alive = cdns_mhdp_bus_read(mhdp, KEEP_ALIVE); + + while (retries_left--) { +- udelay(2); ++ msleep(1); + + newalive = cdns_mhdp_bus_read(mhdp, KEEP_ALIVE); + if (alive == newalive) +-- +2.29.2 + diff --git a/projects/NXP/devices/iMX8/patches/linux/0028-LF-3367-3-drm-mhdp-hdcp-adjust-state-handle-priority.patch b/projects/NXP/devices/iMX8/patches/linux/0028-LF-3367-3-drm-mhdp-hdcp-adjust-state-handle-priority.patch new file mode 100644 index 0000000000..deeaae4375 --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0028-LF-3367-3-drm-mhdp-hdcp-adjust-state-handle-priority.patch @@ -0,0 +1,62 @@ +From 60f6b8c90766663303f6005468502798eb2b0f44 Mon Sep 17 00:00:00 2001 +From: Sandor Yu +Date: Fri, 19 Feb 2021 17:53:54 +0800 +Subject: [PATCH 28/49] LF-3367-3: drm: mhdp-hdcp: adjust state handle priority + +Handle HDCP_STATE_INACTIVE and HDCP_STATE_DISABLING state priority +to avoid unnecessary HPD state check, drm has check it when hdcp +enable/disable. + +Signed-off-by: Sandor Yu +Reviewed-by: Robby Cai +Acked-by: Jason Liu +--- + .../gpu/drm/bridge/cadence/cdns-hdmi-hdcp.c | 24 ++++++++++--------- + 1 file changed, 13 insertions(+), 11 deletions(-) + +diff --git a/drivers/gpu/drm/bridge/cadence/cdns-hdmi-hdcp.c b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-hdcp.c +index 9119f2063098..5dfbd7943306 100644 +--- a/drivers/gpu/drm/bridge/cadence/cdns-hdmi-hdcp.c ++++ b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-hdcp.c +@@ -1048,6 +1048,15 @@ static int hdmi_hdcp_check_link(struct cdns_mhdp_device *mhdp) + mhdp->hdcp.reauth_in_progress = 0; + mutex_lock(&mhdp->lock); + ++ if (mhdp->hdcp.state == HDCP_STATE_INACTIVE) ++ goto out; ++ ++ if (mhdp->hdcp.state == HDCP_STATE_DISABLING) { ++ _hdmi_hdcp_disable(mhdp); ++ mhdp->hdcp.state = HDCP_STATE_INACTIVE; ++ goto out; ++ } ++ + if ((mhdp->hdcp.state == HDCP_STATE_AUTHENTICATED) || + (mhdp->hdcp.state == HDCP_STATE_AUTHENTICATING) || + (mhdp->hdcp.state == HDCP_STATE_REAUTHENTICATING) || +@@ -1056,18 +1065,11 @@ static int hdmi_hdcp_check_link(struct cdns_mhdp_device *mhdp) + /* In active states, check the HPD signal. Because of the IRQ + * debounce delay, the state might not reflect the disconnection. + * The FW could already have detected the HDP down and reported error */ +- hpd_sts = cdns_mhdp_read_hpd(mhdp); +- if (1 != hpd_sts) ++ hpd_sts = cdns_mhdp_read_hpd(mhdp); ++ if (1 != hpd_sts) { + mhdp->hdcp.state = HDCP_STATE_DISABLING; +- } +- +- if (mhdp->hdcp.state == HDCP_STATE_INACTIVE) +- goto out; +- +- if (mhdp->hdcp.state == HDCP_STATE_DISABLING) { +- _hdmi_hdcp_disable(mhdp); +- mhdp->hdcp.state = HDCP_STATE_INACTIVE; +- goto out; ++ goto out; ++ } + } + + /* TODO items: +-- +2.29.2 + diff --git a/projects/NXP/devices/iMX8/patches/linux/0029-clk-imx8mq-add-27MHz-PHY-ref-clock.patch b/projects/NXP/devices/iMX8/patches/linux/0029-clk-imx8mq-add-27MHz-PHY-ref-clock.patch new file mode 100644 index 0000000000..fdfa29b214 --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0029-clk-imx8mq-add-27MHz-PHY-ref-clock.patch @@ -0,0 +1,52 @@ +From afbe8e0ae318f407d64bbc48b784d93c782b6564 Mon Sep 17 00:00:00 2001 +From: Laurentiu Palcu +Date: Thu, 5 Sep 2019 13:07:22 +0300 +Subject: [PATCH 29/49] clk: imx8mq: add 27MHz PHY ref clock + +This clock is a high precision clock on imx8mq-evk board that will be used by +HDMI phy. + +Signed-off-by: Laurentiu Palcu +--- + drivers/clk/imx/clk-imx8mq.c | 3 ++- + include/dt-bindings/clock/imx8mq-clock.h | 4 +++- + 2 files changed, 5 insertions(+), 2 deletions(-) + +diff --git a/drivers/clk/imx/clk-imx8mq.c b/drivers/clk/imx/clk-imx8mq.c +index 06292d4a98ff..6bd2fe0ae71d 100644 +--- a/drivers/clk/imx/clk-imx8mq.c ++++ b/drivers/clk/imx/clk-imx8mq.c +@@ -25,7 +25,7 @@ static u32 share_count_sai6; + static u32 share_count_dcss; + static u32 share_count_nand; + +-static const char * const pll_ref_sels[] = { "osc_25m", "osc_27m", "dummy", "dummy", }; ++static const char * const pll_ref_sels[] = { "osc_25m", "osc_27m", "phy_27m", "dummy", }; + static const char * const arm_pll_bypass_sels[] = {"arm_pll", "arm_pll_ref_sel", }; + static const char * const gpu_pll_bypass_sels[] = {"gpu_pll", "gpu_pll_ref_sel", }; + static const char * const vpu_pll_bypass_sels[] = {"vpu_pll", "vpu_pll_ref_sel", }; +@@ -304,6 +304,7 @@ static int imx8mq_clocks_probe(struct platform_device *pdev) + hws[IMX8MQ_CLK_EXT2] = imx_obtain_fixed_clk_hw(np, "clk_ext2"); + hws[IMX8MQ_CLK_EXT3] = imx_obtain_fixed_clk_hw(np, "clk_ext3"); + hws[IMX8MQ_CLK_EXT4] = imx_obtain_fixed_clk_hw(np, "clk_ext4"); ++ hws[IMX8MQ_CLK_PHY_27MHZ] = imx_clk_hw_fixed("phy_27m", 27000000); + + np = of_find_compatible_node(NULL, NULL, "fsl,imx8mq-anatop"); + base = of_iomap(np, 0); +diff --git a/include/dt-bindings/clock/imx8mq-clock.h b/include/dt-bindings/clock/imx8mq-clock.h +index 9b8045d75b8b..2a81f96b7c74 100644 +--- a/include/dt-bindings/clock/imx8mq-clock.h ++++ b/include/dt-bindings/clock/imx8mq-clock.h +@@ -431,6 +431,8 @@ + + #define IMX8MQ_CLK_A53_CORE 289 + +-#define IMX8MQ_CLK_END 290 ++#define IMX8MQ_CLK_PHY_27MHZ 290 ++ ++#define IMX8MQ_CLK_END 291 + + #endif /* __DT_BINDINGS_CLOCK_IMX8MQ_H */ +-- +2.29.2 + diff --git a/projects/NXP/devices/iMX8/patches/linux/0030-drm-imx-Add-mhdp-dp-hdmi-driver-for-imx8x-platform.patch b/projects/NXP/devices/iMX8/patches/linux/0030-drm-imx-Add-mhdp-dp-hdmi-driver-for-imx8x-platform.patch new file mode 100644 index 0000000000..0215193b42 --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0030-drm-imx-Add-mhdp-dp-hdmi-driver-for-imx8x-platform.patch @@ -0,0 +1,2654 @@ +From 09102ec28d08ae95d476ee241ed016d2fe9da894 Mon Sep 17 00:00:00 2001 +From: Sandor Yu +Date: Mon, 20 Apr 2020 22:47:36 +0800 +Subject: [PATCH 30/49] drm: imx: Add mhdp dp/hdmi driver for imx8x platform + +Added i.MX8MQ HDMI/DP driver. +Added i.MX8MQ HDMI/DP driver. +Added LS1028A DP driver. + +Signed-off-by: Sandor Yu +--- + drivers/gpu/drm/imx/Kconfig | 1 + + drivers/gpu/drm/imx/Makefile | 1 + + drivers/gpu/drm/imx/mhdp/Kconfig | 11 + + drivers/gpu/drm/imx/mhdp/Makefile | 5 + + drivers/gpu/drm/imx/mhdp/cdns-mhdp-dp-phy.c | 531 ++++++++++++ + drivers/gpu/drm/imx/mhdp/cdns-mhdp-hdmi-phy.c | 764 ++++++++++++++++++ + drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx.h | 75 ++ + drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx8qm.c | 638 +++++++++++++++ + drivers/gpu/drm/imx/mhdp/cdns-mhdp-imxdrv.c | 257 ++++++ + drivers/gpu/drm/imx/mhdp/cdns-mhdp-ls1028a.c | 110 +++ + drivers/gpu/drm/imx/mhdp/cdns-mhdp-phy.h | 155 ++++ + 11 files changed, 2548 insertions(+) + create mode 100644 drivers/gpu/drm/imx/mhdp/Kconfig + create mode 100644 drivers/gpu/drm/imx/mhdp/Makefile + create mode 100644 drivers/gpu/drm/imx/mhdp/cdns-mhdp-dp-phy.c + create mode 100644 drivers/gpu/drm/imx/mhdp/cdns-mhdp-hdmi-phy.c + create mode 100644 drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx.h + create mode 100644 drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx8qm.c + create mode 100644 drivers/gpu/drm/imx/mhdp/cdns-mhdp-imxdrv.c + create mode 100644 drivers/gpu/drm/imx/mhdp/cdns-mhdp-ls1028a.c + create mode 100644 drivers/gpu/drm/imx/mhdp/cdns-mhdp-phy.h + +diff --git a/drivers/gpu/drm/imx/Kconfig b/drivers/gpu/drm/imx/Kconfig +index 6231048aa5aa..4af2f575f04b 100644 +--- a/drivers/gpu/drm/imx/Kconfig ++++ b/drivers/gpu/drm/imx/Kconfig +@@ -41,3 +41,4 @@ config DRM_IMX_HDMI + Choose this if you want to use HDMI on i.MX6. + + source "drivers/gpu/drm/imx/dcss/Kconfig" ++source "drivers/gpu/drm/imx/mhdp/Kconfig" +diff --git a/drivers/gpu/drm/imx/Makefile b/drivers/gpu/drm/imx/Makefile +index b644deffe948..0b46c46b19a8 100644 +--- a/drivers/gpu/drm/imx/Makefile ++++ b/drivers/gpu/drm/imx/Makefile +@@ -10,3 +10,4 @@ obj-$(CONFIG_DRM_IMX_LDB) += imx-ldb.o + + obj-$(CONFIG_DRM_IMX_HDMI) += dw_hdmi-imx.o + obj-$(CONFIG_DRM_IMX_DCSS) += dcss/ ++obj-$(CONFIG_DRM_IMX_CDNS_MHDP) += mhdp/ +diff --git a/drivers/gpu/drm/imx/mhdp/Kconfig b/drivers/gpu/drm/imx/mhdp/Kconfig +new file mode 100644 +index 000000000000..86950badb947 +--- /dev/null ++++ b/drivers/gpu/drm/imx/mhdp/Kconfig +@@ -0,0 +1,11 @@ ++# SPDX-License-Identifier: GPL-2.0-only ++ ++config DRM_IMX_CDNS_MHDP ++ tristate "NXP i.MX MX8 DRM HDMI/DP" ++ 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. +diff --git a/drivers/gpu/drm/imx/mhdp/Makefile b/drivers/gpu/drm/imx/mhdp/Makefile +new file mode 100644 +index 000000000000..235fa2d515e9 +--- /dev/null ++++ b/drivers/gpu/drm/imx/mhdp/Makefile +@@ -0,0 +1,5 @@ ++# SPDX-License-Identifier: GPL-2.0 ++ ++cdns_mhdp_imx-objs := cdns-mhdp-imxdrv.o cdns-mhdp-dp-phy.o \ ++ cdns-mhdp-hdmi-phy.o cdns-mhdp-imx8qm.o cdns-mhdp-ls1028a.o ++obj-$(CONFIG_DRM_IMX_CDNS_MHDP) += cdns_mhdp_imx.o +diff --git a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-dp-phy.c b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-dp-phy.c +new file mode 100644 +index 000000000000..a6d03c94d196 +--- /dev/null ++++ b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-dp-phy.c +@@ -0,0 +1,531 @@ ++/* ++ * Cadence Display Port Interface (DP) PHY driver ++ * ++ * Copyright (C) 2019 NXP Semiconductor, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ */ ++#include ++#include ++#include ++#include ++#include "cdns-mhdp-phy.h" ++ ++enum dp_link_rate { ++ RATE_1_6 = 162000, ++ RATE_2_1 = 216000, ++ RATE_2_4 = 243000, ++ RATE_2_7 = 270000, ++ RATE_3_2 = 324000, ++ RATE_4_3 = 432000, ++ RATE_5_4 = 540000, ++ RATE_8_1 = 810000, ++}; ++ ++struct phy_pll_reg { ++ u16 val[7]; ++ u32 addr; ++}; ++ ++static const struct phy_pll_reg phy_pll_27m_cfg[] = { ++ /* 1.62 2.16 2.43 2.7 3.24 4.32 5.4 register address */ ++ {{ 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E }, CMN_PLL0_VCOCAL_INIT_TMR }, ++ {{ 0x001B, 0x001B, 0x001B, 0x001B, 0x001B, 0x001B, 0x001B }, CMN_PLL0_VCOCAL_ITER_TMR }, ++ {{ 0x30B9, 0x3087, 0x3096, 0x30B4, 0x30B9, 0x3087, 0x30B4 }, CMN_PLL0_VCOCAL_START }, ++ {{ 0x0077, 0x009F, 0x00B3, 0x00C7, 0x0077, 0x009F, 0x00C7 }, CMN_PLL0_INTDIV }, ++ {{ 0xF9DA, 0xF7CD, 0xF6C7, 0xF5C1, 0xF9DA, 0xF7CD, 0xF5C1 }, CMN_PLL0_FRACDIV }, ++ {{ 0x001E, 0x0028, 0x002D, 0x0032, 0x001E, 0x0028, 0x0032 }, CMN_PLL0_HIGH_THR }, ++ {{ 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020 }, CMN_PLL0_DSM_DIAG }, ++ {{ 0x0000, 0x1000, 0x1000, 0x1000, 0x0000, 0x1000, 0x1000 }, CMN_PLLSM0_USER_DEF_CTRL }, ++ {{ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }, CMN_DIAG_PLL0_OVRD }, ++ {{ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }, CMN_DIAG_PLL0_FBH_OVRD }, ++ {{ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }, CMN_DIAG_PLL0_FBL_OVRD }, ++ {{ 0x0006, 0x0007, 0x0007, 0x0007, 0x0006, 0x0007, 0x0007 }, CMN_DIAG_PLL0_V2I_TUNE }, ++ {{ 0x0043, 0x0043, 0x0043, 0x0042, 0x0043, 0x0043, 0x0042 }, CMN_DIAG_PLL0_CP_TUNE }, ++ {{ 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008 }, CMN_DIAG_PLL0_LF_PROG }, ++ {{ 0x0100, 0x0001, 0x0001, 0x0001, 0x0100, 0x0001, 0x0001 }, CMN_DIAG_PLL0_PTATIS_TUNE1 }, ++ {{ 0x0007, 0x0001, 0x0001, 0x0001, 0x0007, 0x0001, 0x0001 }, CMN_DIAG_PLL0_PTATIS_TUNE2 }, ++ {{ 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020 }, CMN_DIAG_PLL0_TEST_MODE}, ++ {{ 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016 }, CMN_PSM_CLK_CTRL } ++}; ++ ++static const struct phy_pll_reg phy_pll_24m_cfg[] = { ++ /* 1.62 2.16 2.43 2.7 3.24 4.32 5.4 register address */ ++ {{ 0x00F0, 0x00F0, 0x00F0, 0x00F0, 0x00F0, 0x00F0, 0x00F0 }, CMN_PLL0_VCOCAL_INIT_TMR }, ++ {{ 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018 }, CMN_PLL0_VCOCAL_ITER_TMR }, ++ {{ 0x3061, 0x3092, 0x30B3, 0x30D0, 0x3061, 0x3092, 0x30D0 }, CMN_PLL0_VCOCAL_START }, ++ {{ 0x0086, 0x00B3, 0x00CA, 0x00E0, 0x0086, 0x00B3, 0x00E0 }, CMN_PLL0_INTDIV }, ++ {{ 0xF917, 0xF6C7, 0x75A1, 0xF479, 0xF917, 0xF6C7, 0xF479 }, CMN_PLL0_FRACDIV }, ++ {{ 0x0022, 0x002D, 0x0033, 0x0038, 0x0022, 0x002D, 0x0038 }, CMN_PLL0_HIGH_THR }, ++ {{ 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020 }, CMN_PLL0_DSM_DIAG }, ++ {{ 0x0000, 0x1000, 0x1000, 0x1000, 0x0000, 0x1000, 0x1000 }, CMN_PLLSM0_USER_DEF_CTRL }, ++ {{ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }, CMN_DIAG_PLL0_OVRD }, ++ {{ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }, CMN_DIAG_PLL0_FBH_OVRD }, ++ {{ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }, CMN_DIAG_PLL0_FBL_OVRD }, ++ {{ 0x0006, 0x0007, 0x0007, 0x0007, 0x0006, 0x0007, 0x0007 }, CMN_DIAG_PLL0_V2I_TUNE }, ++ {{ 0x0026, 0x0029, 0x0029, 0x0029, 0x0026, 0x0029, 0x0029 }, CMN_DIAG_PLL0_CP_TUNE }, ++ {{ 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008 }, CMN_DIAG_PLL0_LF_PROG }, ++ {{ 0x008C, 0x008C, 0x008C, 0x008C, 0x008C, 0x008C, 0x008C }, CMN_DIAG_PLL0_PTATIS_TUNE1 }, ++ {{ 0x002E, 0x002E, 0x002E, 0x002E, 0x002E, 0x002E, 0x002E }, CMN_DIAG_PLL0_PTATIS_TUNE2 }, ++ {{ 0x0022, 0x0022, 0x0022, 0x0022, 0x0022, 0x0022, 0x0022 }, CMN_DIAG_PLL0_TEST_MODE}, ++ {{ 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016 }, CMN_PSM_CLK_CTRL } ++}; ++ ++static int link_rate_index(u32 rate) ++{ ++ switch (rate) { ++ case RATE_1_6: ++ return 0; ++ case RATE_2_1: ++ return 1; ++ case RATE_2_4: ++ return 2; ++ case RATE_2_7: ++ return 3; ++ case RATE_3_2: ++ return 4; ++ case RATE_4_3: ++ return 5; ++ case RATE_5_4: ++ return 6; ++ default: ++ return -1; ++ } ++} ++ ++static void dp_aux_cfg(struct cdns_mhdp_device *mhdp) ++{ ++ /* Power up Aux */ ++ cdns_phy_reg_write(mhdp, TXDA_CYA_AUXDA_CYA, 1); ++ ++ cdns_phy_reg_write(mhdp, TX_DIG_CTRL_REG_1, 0x3); ++ ndelay(150); ++ cdns_phy_reg_write(mhdp, TX_DIG_CTRL_REG_2, 36); ++ ndelay(150); ++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_2, 0x0100); ++ ndelay(150); ++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_2, 0x0300); ++ ndelay(150); ++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_3, 0x0000); ++ ndelay(150); ++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_1, 0x2008); ++ ndelay(150); ++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_1, 0x2018); ++ ndelay(150); ++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_1, 0xA018); ++ ndelay(150); ++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_2, 0x030C); ++ ndelay(150); ++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_5, 0x0000); ++ ndelay(150); ++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_4, 0x1001); ++ ndelay(150); ++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_1, 0xA098); ++ ndelay(150); ++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_1, 0xA198); ++ ndelay(150); ++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_2, 0x030d); ++ ndelay(150); ++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_2, 0x030f); ++} ++ ++/* PMA common configuration for 24MHz */ ++static void dp_phy_pma_cmn_cfg_24mhz(struct cdns_mhdp_device *mhdp) ++{ ++ int k; ++ u32 num_lanes = mhdp->dp.num_lanes; ++ u16 val; ++ ++ val = cdns_phy_reg_read(mhdp, PHY_PMA_CMN_CTRL1); ++ val &= 0xFFF7; ++ val |= 0x0008; ++ cdns_phy_reg_write(mhdp, PHY_PMA_CMN_CTRL1, val); ++ ++ for (k = 0; k < num_lanes; k++) { ++ /* Transceiver control and diagnostic registers */ ++ cdns_phy_reg_write(mhdp, XCVR_DIAG_LANE_FCM_EN_MGN_TMR | (k << 9), 0x0090); ++ /* Transmitter receiver detect registers */ ++ cdns_phy_reg_write(mhdp, TX_RCVDET_EN_TMR | (k << 9), 0x0960); ++ cdns_phy_reg_write(mhdp, TX_RCVDET_ST_TMR | (k << 9), 0x0030); ++ } ++} ++ ++/* Valid for 24 MHz only */ ++static void dp_phy_pma_cmn_pll0_24mhz(struct cdns_mhdp_device *mhdp) ++{ ++ u32 num_lanes = mhdp->dp.num_lanes; ++ u32 link_rate = mhdp->dp.rate; ++ u16 val; ++ int index, i, k; ++ ++ /* ++ * PLL reference clock source select ++ * for single ended reference clock val |= 0x0030; ++ * for differential clock val |= 0x0000; ++ */ ++ val = cdns_phy_reg_read(mhdp, PHY_PMA_CMN_CTRL1); ++ val = val & 0xFF8F; ++ val = val | 0x0030; ++ cdns_phy_reg_write(mhdp, PHY_PMA_CMN_CTRL1, val); ++ ++ /* DP PLL data rate 0/1 clock divider value */ ++ val = cdns_phy_reg_read(mhdp, PHY_HDP_CLK_CTL); ++ val &= 0x00FF; ++ if (link_rate <= RATE_2_7) ++ val |= 0x2400; ++ else ++ val |= 0x1200; ++ cdns_phy_reg_write(mhdp, PHY_HDP_CLK_CTL, val); ++ ++ /* High speed clock 0/1 div */ ++ val = cdns_phy_reg_read(mhdp, CMN_DIAG_HSCLK_SEL); ++ val &= 0xFFCC; ++ if (link_rate <= RATE_2_7) ++ val |= 0x0011; ++ cdns_phy_reg_write(mhdp, CMN_DIAG_HSCLK_SEL, val); ++ ++ for (k = 0; k < num_lanes; k = k + 1) { ++ val = cdns_phy_reg_read(mhdp, (XCVR_DIAG_HSCLK_SEL | (k << 9))); ++ val &= 0xCFFF; ++ if (link_rate <= RATE_2_7) ++ val |= 0x1000; ++ cdns_phy_reg_write(mhdp, (XCVR_DIAG_HSCLK_SEL | (k << 9)), val); ++ } ++ ++ /* DP PHY PLL 24MHz configuration */ ++ index = link_rate_index(link_rate); ++ for (i = 0; i < ARRAY_SIZE(phy_pll_24m_cfg); i++) ++ cdns_phy_reg_write(mhdp, phy_pll_24m_cfg[i].addr, phy_pll_24m_cfg[i].val[index]); ++ ++ /* Transceiver control and diagnostic registers */ ++ for (k = 0; k < num_lanes; k = k + 1) { ++ val = cdns_phy_reg_read(mhdp, (XCVR_DIAG_PLLDRC_CTRL | (k << 9))); ++ val &= 0x8FFF; ++ if (link_rate <= RATE_2_7) ++ val |= 0x2000; ++ else ++ val |= 0x1000; ++ cdns_phy_reg_write(mhdp, (XCVR_DIAG_PLLDRC_CTRL | (k << 9)), val); ++ } ++ ++ for (k = 0; k < num_lanes; k = k + 1) { ++ cdns_phy_reg_write(mhdp, (XCVR_PSM_RCTRL | (k << 9)), 0xBEFC); ++ cdns_phy_reg_write(mhdp, (TX_PSC_A0 | (k << 9)), 0x6799); ++ cdns_phy_reg_write(mhdp, (TX_PSC_A1 | (k << 9)), 0x6798); ++ cdns_phy_reg_write(mhdp, (TX_PSC_A2 | (k << 9)), 0x0098); ++ cdns_phy_reg_write(mhdp, (TX_PSC_A3 | (k << 9)), 0x0098); ++ } ++} ++ ++/* PMA common configuration for 27MHz */ ++static void dp_phy_pma_cmn_cfg_27mhz(struct cdns_mhdp_device *mhdp) ++{ ++ u32 num_lanes = mhdp->dp.num_lanes; ++ u16 val; ++ int k; ++ ++ val = cdns_phy_reg_read(mhdp, PHY_PMA_CMN_CTRL1); ++ val &= 0xFFF7; ++ val |= 0x0008; ++ cdns_phy_reg_write(mhdp, PHY_PMA_CMN_CTRL1, val); ++ ++ /* Startup state machine registers */ ++ cdns_phy_reg_write(mhdp, CMN_SSM_BIAS_TMR, 0x0087); ++ cdns_phy_reg_write(mhdp, CMN_PLLSM0_PLLEN_TMR, 0x001B); ++ cdns_phy_reg_write(mhdp, CMN_PLLSM0_PLLPRE_TMR, 0x0036); ++ cdns_phy_reg_write(mhdp, CMN_PLLSM0_PLLVREF_TMR, 0x001B); ++ cdns_phy_reg_write(mhdp, CMN_PLLSM0_PLLLOCK_TMR, 0x006C); ++ ++ /* Current calibration registers */ ++ cdns_phy_reg_write(mhdp, CMN_ICAL_INIT_TMR, 0x0044); ++ cdns_phy_reg_write(mhdp, CMN_ICAL_ITER_TMR, 0x0006); ++ cdns_phy_reg_write(mhdp, CMN_ICAL_ADJ_INIT_TMR, 0x0022); ++ cdns_phy_reg_write(mhdp, CMN_ICAL_ADJ_ITER_TMR, 0x0006); ++ ++ /* Resistor calibration registers */ ++ cdns_phy_reg_write(mhdp, CMN_TXPUCAL_INIT_TMR, 0x0022); ++ cdns_phy_reg_write(mhdp, CMN_TXPUCAL_ITER_TMR, 0x0006); ++ cdns_phy_reg_write(mhdp, CMN_TXPU_ADJ_INIT_TMR, 0x0022); ++ cdns_phy_reg_write(mhdp, CMN_TXPU_ADJ_ITER_TMR, 0x0006); ++ cdns_phy_reg_write(mhdp, CMN_TXPDCAL_INIT_TMR, 0x0022); ++ cdns_phy_reg_write(mhdp, CMN_TXPDCAL_ITER_TMR, 0x0006); ++ cdns_phy_reg_write(mhdp, CMN_TXPD_ADJ_INIT_TMR, 0x0022); ++ cdns_phy_reg_write(mhdp, CMN_TXPD_ADJ_ITER_TMR, 0x0006); ++ cdns_phy_reg_write(mhdp, CMN_RXCAL_INIT_TMR, 0x0022); ++ cdns_phy_reg_write(mhdp, CMN_RXCAL_ITER_TMR, 0x0006); ++ cdns_phy_reg_write(mhdp, CMN_RX_ADJ_INIT_TMR, 0x0022); ++ cdns_phy_reg_write(mhdp, CMN_RX_ADJ_ITER_TMR, 0x0006); ++ ++ for (k = 0; k < num_lanes; k = k + 1) { ++ /* Power state machine registers */ ++ cdns_phy_reg_write(mhdp, XCVR_PSM_CAL_TMR | (k << 9), 0x016D); ++ cdns_phy_reg_write(mhdp, XCVR_PSM_A0IN_TMR | (k << 9), 0x016D); ++ /* Transceiver control and diagnostic registers */ ++ cdns_phy_reg_write(mhdp, XCVR_DIAG_LANE_FCM_EN_MGN_TMR | (k << 9), 0x00A2); ++ cdns_phy_reg_write(mhdp, TX_DIAG_BGREF_PREDRV_DELAY | (k << 9), 0x0097); ++ /* Transmitter receiver detect registers */ ++ cdns_phy_reg_write(mhdp, TX_RCVDET_EN_TMR | (k << 9), 0x0A8C); ++ cdns_phy_reg_write(mhdp, TX_RCVDET_ST_TMR | (k << 9), 0x0036); ++ } ++} ++ ++static void dp_phy_pma_cmn_pll0_27mhz(struct cdns_mhdp_device *mhdp) ++{ ++ u32 num_lanes = mhdp->dp.num_lanes; ++ u32 link_rate = mhdp->dp.rate; ++ u16 val; ++ int index, i, k; ++ ++ /* ++ * PLL reference clock source select ++ * for single ended reference clock val |= 0x0030; ++ * for differential clock val |= 0x0000; ++ */ ++ val = cdns_phy_reg_read(mhdp, PHY_PMA_CMN_CTRL1); ++ val &= 0xFF8F; ++ cdns_phy_reg_write(mhdp, PHY_PMA_CMN_CTRL1, val); ++ ++ /* for differential clock on the refclk_p and refclk_m off chip pins: ++ * CMN_DIAG_ACYA[8]=1'b1 ++ */ ++ cdns_phy_reg_write(mhdp, CMN_DIAG_ACYA, 0x0100); ++ ++ /* DP PLL data rate 0/1 clock divider value */ ++ val = cdns_phy_reg_read(mhdp, PHY_HDP_CLK_CTL); ++ val &= 0x00FF; ++ if (link_rate <= RATE_2_7) ++ val |= 0x2400; ++ else ++ val |= 0x1200; ++ cdns_phy_reg_write(mhdp, PHY_HDP_CLK_CTL, val); ++ ++ /* High speed clock 0/1 div */ ++ val = cdns_phy_reg_read(mhdp, CMN_DIAG_HSCLK_SEL); ++ val &= 0xFFCC; ++ if (link_rate <= RATE_2_7) ++ val |= 0x0011; ++ cdns_phy_reg_write(mhdp, CMN_DIAG_HSCLK_SEL, val); ++ ++ for (k = 0; k < num_lanes; k++) { ++ val = cdns_phy_reg_read(mhdp, (XCVR_DIAG_HSCLK_SEL | (k << 9))); ++ val = val & 0xCFFF; ++ if (link_rate <= RATE_2_7) ++ val |= 0x1000; ++ cdns_phy_reg_write(mhdp, (XCVR_DIAG_HSCLK_SEL | (k << 9)), val); ++ } ++ ++ /* DP PHY PLL 27MHz configuration */ ++ index = link_rate_index(link_rate); ++ for (i = 0; i < ARRAY_SIZE(phy_pll_27m_cfg); i++) ++ cdns_phy_reg_write(mhdp, phy_pll_27m_cfg[i].addr, phy_pll_27m_cfg[i].val[index]); ++ ++ /* Transceiver control and diagnostic registers */ ++ for (k = 0; k < num_lanes; k++) { ++ val = cdns_phy_reg_read(mhdp, (XCVR_DIAG_PLLDRC_CTRL | (k << 9))); ++ val = val & 0x8FFF; ++ if (link_rate <= RATE_2_7) ++ val |= 0x2000; ++ else ++ val |= 0x1000; ++ cdns_phy_reg_write(mhdp, (XCVR_DIAG_PLLDRC_CTRL | (k << 9)), val); ++ } ++ ++ for (k = 0; k < num_lanes; k = k + 1) { ++ /* Power state machine registers */ ++ cdns_phy_reg_write(mhdp, (XCVR_PSM_RCTRL | (k << 9)), 0xBEFC); ++ cdns_phy_reg_write(mhdp, (TX_PSC_A0 | (k << 9)), 0x6799); ++ cdns_phy_reg_write(mhdp, (TX_PSC_A1 | (k << 9)), 0x6798); ++ cdns_phy_reg_write(mhdp, (TX_PSC_A2 | (k << 9)), 0x0098); ++ cdns_phy_reg_write(mhdp, (TX_PSC_A3 | (k << 9)), 0x0098); ++ /* Receiver calibration power state definition register */ ++ val = cdns_phy_reg_read(mhdp, RX_PSC_CAL | (k << 9)); ++ val &= 0xFFBB; ++ cdns_phy_reg_write(mhdp, (RX_PSC_CAL | (k << 9)), val); ++ val = cdns_phy_reg_read(mhdp, RX_PSC_A0 | (k << 9)); ++ val &= 0xFFBB; ++ cdns_phy_reg_write(mhdp, (RX_PSC_A0 | (k << 9)), val); ++ } ++} ++ ++static void dp_phy_power_down(struct cdns_mhdp_device *mhdp) ++{ ++ u16 val; ++ int i; ++ ++ if (!mhdp->power_up) ++ return; ++ ++ /* Place the PHY lanes in the A3 power state. */ ++ cdns_phy_reg_write(mhdp, PHY_HDP_MODE_CTRL, 0x8); ++ /* Wait for Power State A3 Ack */ ++ for (i = 0; i < 10; i++) { ++ val = cdns_phy_reg_read(mhdp, PHY_HDP_MODE_CTRL); ++ if (val & (1 << 7)) ++ break; ++ msleep(20); ++ } ++ if (i == 10) { ++ dev_err(mhdp->dev, "Wait A3 Ack failed\n"); ++ return; ++ } ++ ++ /* Disable HDP PLL’s data rate and full rate clocks out of PMA. */ ++ val = cdns_phy_reg_read(mhdp, PHY_HDP_CLK_CTL); ++ val &= ~(1 << 2); ++ cdns_phy_reg_write(mhdp, PHY_HDP_CLK_CTL, val); ++ /* Wait for PLL clock gate ACK */ ++ for (i = 0; i < 10; i++) { ++ val = cdns_phy_reg_read(mhdp, PHY_HDP_CLK_CTL); ++ if (!(val & (1 << 3))) ++ break; ++ msleep(20); ++ } ++ if (i == 10) { ++ dev_err(mhdp->dev, "Wait PLL clock gate Ack failed\n"); ++ return; ++ } ++ ++ /* Disable HDP PLL’s for high speed clocks */ ++ val = cdns_phy_reg_read(mhdp, PHY_HDP_CLK_CTL); ++ val &= ~(1 << 0); ++ cdns_phy_reg_write(mhdp, PHY_HDP_CLK_CTL, val); ++ /* Wait for PLL disable ACK */ ++ for (i = 0; i < 10; i++) { ++ val = cdns_phy_reg_read(mhdp, PHY_HDP_CLK_CTL); ++ if (!(val & (1 << 1))) ++ break; ++ msleep(20); ++ } ++ if (i == 10) { ++ dev_err(mhdp->dev, "Wait PLL disable Ack failed\n"); ++ return; ++ } ++} ++ ++static int dp_phy_power_up(struct cdns_mhdp_device *mhdp) ++{ ++ u32 val, i; ++ ++ /* Enable HDP PLL’s for high speed clocks */ ++ val = cdns_phy_reg_read(mhdp, PHY_HDP_CLK_CTL); ++ val |= (1 << 0); ++ cdns_phy_reg_write(mhdp, PHY_HDP_CLK_CTL, val); ++ /* Wait for PLL ready ACK */ ++ for (i = 0; i < 10; i++) { ++ val = cdns_phy_reg_read(mhdp, PHY_HDP_CLK_CTL); ++ if (val & (1 << 1)) ++ break; ++ msleep(20); ++ } ++ if (i == 10) { ++ dev_err(mhdp->dev, "Wait PLL Ack failed\n"); ++ return -1; ++ } ++ ++ /* Enable HDP PLL’s data rate and full rate clocks out of PMA. */ ++ val = cdns_phy_reg_read(mhdp, PHY_HDP_CLK_CTL); ++ val |= (1 << 2); ++ cdns_phy_reg_write(mhdp, PHY_HDP_CLK_CTL, val); ++ /* Wait for PLL clock enable ACK */ ++ for (i = 0; i < 10; i++) { ++ val = cdns_phy_reg_read(mhdp, PHY_HDP_CLK_CTL); ++ if (val & (1 << 3)) ++ break; ++ msleep(20); ++ } ++ if (i == 10) { ++ dev_err(mhdp->dev, "Wait PLL clock enable ACk failed\n"); ++ return -1; ++ } ++ ++ /* Configure PHY in A2 Mode */ ++ cdns_phy_reg_write(mhdp, PHY_HDP_MODE_CTRL, 0x0004); ++ /* Wait for Power State A2 Ack */ ++ for (i = 0; i < 10; i++) { ++ val = cdns_phy_reg_read(mhdp, PHY_HDP_MODE_CTRL); ++ if (val & (1 << 6)) ++ break; ++ msleep(20); ++ } ++ if (i == 10) { ++ dev_err(mhdp->dev, "Wait A2 Ack failed\n"); ++ return -1; ++ } ++ ++ /* Configure PHY in A0 mode (PHY must be in the A0 power ++ * state in order to transmit data) ++ */ ++ cdns_phy_reg_write(mhdp, PHY_HDP_MODE_CTRL, 0x0101); ++ ++ /* Wait for Power State A0 Ack */ ++ for (i = 0; i < 10; i++) { ++ val = cdns_phy_reg_read(mhdp, PHY_HDP_MODE_CTRL); ++ if (val & (1 << 4)) ++ break; ++ msleep(20); ++ } ++ if (i == 10) { ++ dev_err(mhdp->dev, "Wait A0 Ack failed\n"); ++ return -1; ++ } ++ ++ mhdp->power_up = true; ++ ++ return 0; ++} ++ ++int cdns_dp_phy_set_imx8mq(struct cdns_mhdp_device *mhdp) ++{ ++ int ret; ++ ++ /* Disable phy clock if PHY in power up state */ ++ dp_phy_power_down(mhdp); ++ ++ dp_phy_pma_cmn_cfg_27mhz(mhdp); ++ ++ dp_phy_pma_cmn_pll0_27mhz(mhdp); ++ ++ cdns_phy_reg_write(mhdp, TX_DIAG_ACYA_0, 1); ++ cdns_phy_reg_write(mhdp, TX_DIAG_ACYA_1, 1); ++ cdns_phy_reg_write(mhdp, TX_DIAG_ACYA_2, 1); ++ cdns_phy_reg_write(mhdp, TX_DIAG_ACYA_3, 1); ++ ++ /* PHY power up */ ++ ret = dp_phy_power_up(mhdp); ++ if (ret < 0) ++ return ret; ++ ++ dp_aux_cfg(mhdp); ++ ++ return ret; ++} ++ ++int cdns_dp_phy_set_imx8qm(struct cdns_mhdp_device *mhdp) ++{ ++ int ret; ++ ++ /* Disable phy clock if PHY in power up state */ ++ dp_phy_power_down(mhdp); ++ ++ dp_phy_pma_cmn_cfg_24mhz(mhdp); ++ ++ dp_phy_pma_cmn_pll0_24mhz(mhdp); ++ ++ cdns_phy_reg_write(mhdp, TX_DIAG_ACYA_0, 1); ++ cdns_phy_reg_write(mhdp, TX_DIAG_ACYA_1, 1); ++ cdns_phy_reg_write(mhdp, TX_DIAG_ACYA_2, 1); ++ cdns_phy_reg_write(mhdp, TX_DIAG_ACYA_3, 1); ++ ++ /* PHY power up */ ++ ret = dp_phy_power_up(mhdp); ++ if (ret < 0) ++ return ret; ++ ++ dp_aux_cfg(mhdp); ++ ++ return true; ++} +diff --git a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-hdmi-phy.c b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-hdmi-phy.c +new file mode 100644 +index 000000000000..120300e6a2df +--- /dev/null ++++ b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-hdmi-phy.c +@@ -0,0 +1,764 @@ ++/* ++ * Cadence High-Definition Multimedia Interface (HDMI) driver ++ * ++ * Copyright (C) 2019 NXP Semiconductor, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include "cdns-mhdp-phy.h" ++ ++/* HDMI TX clock control settings */ ++struct hdmi_ctrl { ++ u32 pixel_clk_freq_min; ++ u32 pixel_clk_freq_max; ++ u32 feedback_factor; ++ u32 data_range_kbps_min; ++ u32 data_range_kbps_max; ++ u32 cmnda_pll0_ip_div; ++ u32 cmn_ref_clk_dig_div; ++ u32 ref_clk_divider_scaler; ++ u32 pll_fb_div_total; ++ u32 cmnda_pll0_fb_div_low; ++ u32 cmnda_pll0_fb_div_high; ++ u32 pixel_div_total; ++ u32 cmnda_pll0_pxdiv_low; ++ u32 cmnda_pll0_pxdiv_high; ++ u32 vco_freq_min; ++ u32 vco_freq_max; ++ u32 vco_ring_select; ++ u32 cmnda_hs_clk_0_sel; ++ u32 cmnda_hs_clk_1_sel; ++ u32 hsclk_div_at_xcvr; ++ u32 hsclk_div_tx_sub_rate; ++ u32 cmnda_pll0_hs_sym_div_sel; ++ u32 cmnda_pll0_clk_freq_min; ++ u32 cmnda_pll0_clk_freq_max; ++}; ++ ++/* HDMI TX clock control settings, pixel clock is output */ ++static const struct hdmi_ctrl imx8mq_ctrl_table[] = { ++/*Minclk Maxclk Fdbak DR_min DR_max ip_d dig DS Totl */ ++{ 27000, 27000, 1000, 270000, 270000, 0x03, 0x1, 0x1, 240, 0x0BC, 0x030, 80, 0x026, 0x026, 2160000, 2160000, 0, 2, 2, 2, 4, 0x3, 27000, 27000}, ++{ 27000, 27000, 1250, 337500, 337500, 0x03, 0x1, 0x1, 300, 0x0EC, 0x03C, 100, 0x030, 0x030, 2700000, 2700000, 0, 2, 2, 2, 4, 0x3, 33750, 33750}, ++{ 27000, 27000, 1500, 405000, 405000, 0x03, 0x1, 0x1, 360, 0x11C, 0x048, 120, 0x03A, 0x03A, 3240000, 3240000, 0, 2, 2, 2, 4, 0x3, 40500, 40500}, ++{ 27000, 27000, 2000, 540000, 540000, 0x03, 0x1, 0x1, 240, 0x0BC, 0x030, 80, 0x026, 0x026, 2160000, 2160000, 0, 2, 2, 2, 4, 0x2, 54000, 54000}, ++{ 54000, 54000, 1000, 540000, 540000, 0x03, 0x1, 0x1, 480, 0x17C, 0x060, 80, 0x026, 0x026, 4320000, 4320000, 1, 2, 2, 2, 4, 0x3, 54000, 54000}, ++{ 54000, 54000, 1250, 675000, 675000, 0x04, 0x1, 0x1, 400, 0x13C, 0x050, 50, 0x017, 0x017, 2700000, 2700000, 0, 1, 1, 2, 4, 0x2, 67500, 67500}, ++{ 54000, 54000, 1500, 810000, 810000, 0x04, 0x1, 0x1, 480, 0x17C, 0x060, 60, 0x01C, 0x01C, 3240000, 3240000, 0, 2, 2, 2, 2, 0x2, 81000, 81000}, ++{ 54000, 54000, 2000, 1080000, 1080000, 0x03, 0x1, 0x1, 240, 0x0BC, 0x030, 40, 0x012, 0x012, 2160000, 2160000, 0, 2, 2, 2, 1, 0x1, 108000, 108000}, ++{ 74250, 74250, 1000, 742500, 742500, 0x03, 0x1, 0x1, 660, 0x20C, 0x084, 80, 0x026, 0x026, 5940000, 5940000, 1, 2, 2, 2, 4, 0x3, 74250, 74250}, ++{ 74250, 74250, 1250, 928125, 928125, 0x04, 0x1, 0x1, 550, 0x1B4, 0x06E, 50, 0x017, 0x017, 3712500, 3712500, 1, 1, 1, 2, 4, 0x2, 92812, 92812}, ++{ 74250, 74250, 1500, 1113750, 1113750, 0x04, 0x1, 0x1, 660, 0x20C, 0x084, 60, 0x01C, 0x01C, 4455000, 4455000, 1, 2, 2, 2, 2, 0x2, 111375, 111375}, ++{ 74250, 74250, 2000, 1485000, 1485000, 0x03, 0x1, 0x1, 330, 0x104, 0x042, 40, 0x012, 0x012, 2970000, 2970000, 0, 2, 2, 2, 1, 0x1, 148500, 148500}, ++{ 99000, 99000, 1000, 990000, 990000, 0x03, 0x1, 0x1, 440, 0x15C, 0x058, 40, 0x012, 0x012, 3960000, 3960000, 1, 2, 2, 2, 2, 0x2, 99000, 99000}, ++{ 99000, 99000, 1250, 1237500, 1237500, 0x03, 0x1, 0x1, 275, 0x0D8, 0x037, 25, 0x00B, 0x00A, 2475000, 2475000, 0, 1, 1, 2, 2, 0x1, 123750, 123750}, ++{ 99000, 99000, 1500, 1485000, 1485000, 0x03, 0x1, 0x1, 330, 0x104, 0x042, 30, 0x00D, 0x00D, 2970000, 2970000, 0, 2, 2, 2, 1, 0x1, 148500, 148500}, ++{ 99000, 99000, 2000, 1980000, 1980000, 0x03, 0x1, 0x1, 440, 0x15C, 0x058, 40, 0x012, 0x012, 3960000, 3960000, 1, 2, 2, 2, 1, 0x1, 198000, 198000}, ++{148500, 148500, 1000, 1485000, 1485000, 0x03, 0x1, 0x1, 660, 0x20C, 0x084, 40, 0x012, 0x012, 5940000, 5940000, 1, 2, 2, 2, 2, 0x2, 148500, 148500}, ++{148500, 148500, 1250, 1856250, 1856250, 0x04, 0x1, 0x1, 550, 0x1B4, 0x06E, 25, 0x00B, 0x00A, 3712500, 3712500, 1, 1, 1, 2, 2, 0x1, 185625, 185625}, ++{148500, 148500, 1500, 2227500, 2227500, 0x03, 0x1, 0x1, 495, 0x188, 0x063, 30, 0x00D, 0x00D, 4455000, 4455000, 1, 1, 1, 2, 2, 0x1, 222750, 222750}, ++{148500, 148500, 2000, 2970000, 2970000, 0x03, 0x1, 0x1, 660, 0x20C, 0x084, 40, 0x012, 0x012, 5940000, 5940000, 1, 2, 2, 2, 1, 0x1, 297000, 297000}, ++{198000, 198000, 1000, 1980000, 1980000, 0x03, 0x1, 0x1, 220, 0x0AC, 0x02C, 10, 0x003, 0x003, 1980000, 1980000, 0, 1, 1, 2, 1, 0x0, 198000, 198000}, ++{198000, 198000, 1250, 2475000, 2475000, 0x03, 0x1, 0x1, 550, 0x1B4, 0x06E, 25, 0x00B, 0x00A, 4950000, 4950000, 1, 1, 1, 2, 2, 0x1, 247500, 247500}, ++{198000, 198000, 1500, 2970000, 2970000, 0x03, 0x1, 0x1, 330, 0x104, 0x042, 15, 0x006, 0x005, 2970000, 2970000, 0, 1, 1, 2, 1, 0x0, 297000, 297000}, ++{198000, 198000, 2000, 3960000, 3960000, 0x03, 0x1, 0x1, 440, 0x15C, 0x058, 20, 0x008, 0x008, 3960000, 3960000, 1, 1, 1, 2, 1, 0x0, 396000, 396000}, ++{297000, 297000, 1000, 2970000, 2970000, 0x03, 0x1, 0x1, 330, 0x104, 0x042, 10, 0x003, 0x003, 2970000, 2970000, 0, 1, 1, 2, 1, 0x0, 297000, 297000}, ++{297000, 297000, 1500, 4455000, 4455000, 0x03, 0x1, 0x1, 495, 0x188, 0x063, 15, 0x006, 0x005, 4455000, 4455000, 1, 1, 1, 2, 1, 0x0, 445500, 445500}, ++{297000, 297000, 2000, 5940000, 5940000, 0x03, 0x1, 0x1, 660, 0x20C, 0x084, 20, 0x008, 0x008, 5940000, 5940000, 1, 1, 1, 2, 1, 0x0, 594000, 594000}, ++{594000, 594000, 1000, 5940000, 5940000, 0x03, 0x1, 0x1, 660, 0x20C, 0x084, 10, 0x003, 0x003, 5940000, 5940000, 1, 1, 1, 2, 1, 0x0, 594000, 594000}, ++{594000, 594000, 750, 4455000, 4455000, 0x03, 0x1, 0x1, 495, 0x188, 0x063, 10, 0x003, 0x003, 4455000, 4455000, 1, 1, 1, 2, 1, 0x0, 445500, 445500}, ++{594000, 594000, 625, 3712500, 3712500, 0x04, 0x1, 0x1, 550, 0x1B4, 0x06E, 10, 0x003, 0x003, 3712500, 3712500, 1, 1, 1, 2, 1, 0x0, 371250, 371250}, ++{594000, 594000, 500, 2970000, 2970000, 0x03, 0x1, 0x1, 660, 0x20C, 0x084, 10, 0x003, 0x003, 5940000, 5940000, 1, 1, 1, 2, 2, 0x1, 297000, 297000}, ++}; ++ ++/* HDMI TX clock control settings, pixel clock is input */ ++static const struct hdmi_ctrl imx8qm_ctrl_table[] = { ++/*pclk_l pclk_h fd DRR_L DRR_H PLLD */ ++{ 25000, 42500, 1000, 250000, 425000, 0x05, 0x01, 0x01, 400, 0x182, 0x00A, 0, 0, 0, 2000000, 3400000, 0, 2, 2, 2, 4, 0x03, 25000, 42500}, ++{ 42500, 85000, 1000, 425000, 850000, 0x08, 0x03, 0x01, 320, 0x132, 0x00A, 0, 0, 0, 1700000, 3400000, 0, 1, 1, 2, 4, 0x02, 42500, 85000}, ++{ 85000, 170000, 1000, 850000, 1700000, 0x11, 0x00, 0x07, 340, 0x146, 0x00A, 0, 0, 0, 1700000, 3400000, 0, 1, 1, 2, 2, 0x01, 85000, 170000}, ++{170000, 340000, 1000, 1700000, 3400000, 0x22, 0x01, 0x07, 340, 0x146, 0x00A, 0, 0, 0, 1700000, 3400000, 0, 1, 1, 2, 1, 0x00, 170000, 340000}, ++{340000, 600000, 1000, 3400000, 6000000, 0x3C, 0x03, 0x06, 600, 0x24A, 0x00A, 0, 0, 0, 3400000, 6000000, 1, 1, 1, 2, 1, 0x00, 340000, 600000}, ++{ 25000, 34000, 1205, 312500, 425000, 0x04, 0x01, 0x01, 400, 0x182, 0x00A, 0, 0, 0, 2500000, 3400000, 0, 2, 2, 2, 4, 0x03, 31250, 42500}, ++{ 34000, 68000, 1205, 425000, 850000, 0x06, 0x02, 0x01, 300, 0x11E, 0x00A, 0, 0, 0, 1700000, 3400000, 0, 1, 1, 2, 4, 0x02, 42500, 85000}, ++{ 68000, 136000, 1205, 850000, 1700000, 0x0D, 0x02, 0x02, 325, 0x137, 0x00A, 0, 0, 0, 1700000, 3400000, 0, 1, 1, 2, 2, 0x01, 85000, 170000}, ++{136000, 272000, 1205, 1700000, 3400000, 0x1A, 0x02, 0x04, 325, 0x137, 0x00A, 0, 0, 0, 1700000, 3400000, 0, 1, 1, 2, 1, 0x00, 170000, 340000}, ++{272000, 480000, 1205, 3400000, 6000000, 0x30, 0x03, 0x05, 600, 0x24A, 0x00A, 0, 0, 0, 3400000, 6000000, 1, 1, 1, 2, 1, 0x00, 340000, 600000}, ++{ 25000, 28000, 1500, 375000, 420000, 0x03, 0x01, 0x01, 360, 0x15A, 0x00A, 0, 0, 0, 3000000, 3360000, 0, 2, 2, 2, 4, 0x03, 37500, 42000}, ++{ 28000, 56000, 1500, 420000, 840000, 0x06, 0x02, 0x01, 360, 0x15A, 0x00A, 0, 0, 0, 1680000, 3360000, 0, 1, 1, 2, 4, 0x02, 42000, 84000}, ++{ 56000, 113000, 1500, 840000, 1695000, 0x0B, 0x00, 0x05, 330, 0x13C, 0x00A, 0, 0, 0, 1680000, 3390000, 0, 1, 1, 2, 2, 0x01, 84000, 169500}, ++{113000, 226000, 1500, 1695000, 3390000, 0x16, 0x01, 0x05, 330, 0x13C, 0x00A, 0, 0, 0, 1695000, 3390000, 0, 1, 1, 2, 1, 0x00, 169500, 339000}, ++{226000, 400000, 1500, 3390000, 6000000, 0x28, 0x03, 0x04, 600, 0x24A, 0x00A, 0, 0, 0, 3390000, 6000000, 1, 1, 1, 2, 1, 0x00, 339000, 600000}, ++{ 25000, 42500, 2000, 500000, 850000, 0x05, 0x01, 0x01, 400, 0x182, 0x00A, 0, 0, 0, 2000000, 3400000, 0, 1, 1, 2, 4, 0x02, 50000, 85000}, ++{ 42500, 85000, 2000, 850000, 1700000, 0x08, 0x03, 0x01, 320, 0x132, 0x00A, 0, 0, 0, 1700000, 3400000, 0, 1, 1, 2, 2, 0x01, 85000, 170000}, ++{ 85000, 170000, 2000, 1700000, 3400000, 0x11, 0x00, 0x07, 340, 0x146, 0x00A, 0, 0, 0, 1700000, 3400000, 0, 1, 1, 2, 1, 0x00, 170000, 340000}, ++{170000, 300000, 2000, 3400000, 6000000, 0x22, 0x01, 0x06, 680, 0x29A, 0x00A, 0, 0, 0, 3400000, 6000000, 1, 1, 1, 2, 1, 0x00, 340000, 600000}, ++{594000, 594000, 5000, 2970000, 2970000, 0x3C, 0x03, 0x06, 600, 0x24A, 0x00A, 0, 0, 0, 5940000, 5940000, 1, 1, 1, 2, 2, 0x01, 297000, 297000}, ++{594000, 594000, 6250, 3712500, 3712500, 0x3C, 0x03, 0x06, 375, 0x169, 0x00A, 0, 0, 0, 3712500, 3712500, 1, 1, 1, 2, 1, 0x00, 371250, 371250}, ++{594000, 594000, 7500, 4455000, 4455000, 0x3C, 0x03, 0x06, 450, 0x1B4, 0x00A, 0, 0, 0, 4455000, 4455000, 1, 1, 1, 2, 1, 0x00, 445500, 445500}, ++}; ++ ++/* HDMI TX PLL tuning settings */ ++struct hdmi_pll_tuning { ++ u32 vco_freq_bin; ++ u32 vco_freq_min; ++ u32 vco_freq_max; ++ u32 volt_to_current_coarse; ++ u32 volt_to_current; ++ u32 ndac_ctrl; ++ u32 pmos_ctrl; ++ u32 ptat_ndac_ctrl; ++ u32 feedback_div_total; ++ u32 charge_pump_gain; ++ u32 coarse_code; ++ u32 v2i_code; ++ u32 vco_cal_code; ++}; ++ ++/* HDMI TX PLL tuning settings, pixel clock is output */ ++static const struct hdmi_pll_tuning imx8mq_pll_table[] = { ++/* bin VCO_freq min/max coar cod NDAC PMOS PTAT div-T P-Gain Coa V2I CAL */ ++ { 1, 1980000, 1980000, 0x4, 0x3, 0x0, 0x09, 0x09, 220, 0x42, 160, 5, 183 }, ++ { 2, 2160000, 2160000, 0x4, 0x3, 0x0, 0x09, 0x09, 240, 0x42, 166, 6, 208 }, ++ { 3, 2475000, 2475000, 0x5, 0x3, 0x1, 0x00, 0x07, 275, 0x42, 167, 6, 209 }, ++ { 4, 2700000, 2700000, 0x5, 0x3, 0x1, 0x00, 0x07, 300, 0x42, 188, 6, 230 }, ++ { 4, 2700000, 2700000, 0x5, 0x3, 0x1, 0x00, 0x07, 400, 0x4C, 188, 6, 230 }, ++ { 5, 2970000, 2970000, 0x6, 0x3, 0x1, 0x00, 0x07, 330, 0x42, 183, 6, 225 }, ++ { 6, 3240000, 3240000, 0x6, 0x3, 0x1, 0x00, 0x07, 360, 0x42, 203, 7, 256 }, ++ { 6, 3240000, 3240000, 0x6, 0x3, 0x1, 0x00, 0x07, 480, 0x4C, 203, 7, 256 }, ++ { 7, 3712500, 3712500, 0x4, 0x3, 0x0, 0x07, 0x0F, 550, 0x4C, 212, 7, 257 }, ++ { 8, 3960000, 3960000, 0x5, 0x3, 0x0, 0x07, 0x0F, 440, 0x42, 184, 6, 226 }, ++ { 9, 4320000, 4320000, 0x5, 0x3, 0x1, 0x07, 0x0F, 480, 0x42, 205, 7, 258 }, ++ { 10, 4455000, 4455000, 0x5, 0x3, 0x0, 0x07, 0x0F, 495, 0x42, 219, 7, 272 }, ++ { 10, 4455000, 4455000, 0x5, 0x3, 0x0, 0x07, 0x0F, 660, 0x4C, 219, 7, 272 }, ++ { 11, 4950000, 4950000, 0x6, 0x3, 0x1, 0x00, 0x07, 550, 0x42, 213, 7, 258 }, ++ { 12, 5940000, 5940000, 0x7, 0x3, 0x1, 0x00, 0x07, 660, 0x42, 244, 8, 292 }, ++}; ++ ++/* HDMI TX PLL tuning settings, pixel clock is input */ ++static const struct hdmi_pll_tuning imx8qm_pll_table[] = { ++/* bin VCO_freq min/max coar cod NDAC PMOS PTAT div-T P-Gain pad only */ ++ { 0, 1700000, 2000000, 0x3, 0x1, 0x0, 0x8C, 0x2E, 300, 0x08D, 0, 0, 0 }, ++ { 0, 1700000, 2000000, 0x3, 0x1, 0x0, 0x8C, 0x2E, 320, 0x08E, 0, 0, 0 }, ++ { 0, 1700000, 2000000, 0x3, 0x1, 0x0, 0x8C, 0x2E, 325, 0x08E, 0, 0, 0 }, ++ { 0, 1700000, 2000000, 0x3, 0x1, 0x0, 0x8C, 0x2E, 330, 0x08E, 0, 0, 0 }, ++ { 0, 1700000, 2000000, 0x3, 0x1, 0x0, 0x8C, 0x2E, 340, 0x08F, 0, 0, 0 }, ++ { 0, 1700000, 2000000, 0x3, 0x1, 0x0, 0x8C, 0x2E, 360, 0x0A7, 0, 0, 0 }, ++ { 0, 1700000, 2000000, 0x3, 0x1, 0x0, 0x8C, 0x2E, 400, 0x0C5, 0, 0, 0 }, ++ { 1, 2000000, 2400000, 0x3, 0x1, 0x0, 0x8C, 0x2E, 300, 0x086, 0, 0, 0 }, ++ { 1, 2000000, 2400000, 0x3, 0x1, 0x0, 0x8C, 0x2E, 320, 0x087, 0, 0, 0 }, ++ { 1, 2000000, 2400000, 0x3, 0x1, 0x0, 0x8C, 0x2E, 325, 0x087, 0, 0, 0 }, ++ { 1, 2000000, 2400000, 0x3, 0x1, 0x0, 0x8C, 0x2E, 330, 0x104, 0, 0, 0 }, ++ { 1, 2000000, 2400000, 0x3, 0x1, 0x0, 0x8C, 0x2E, 340, 0x08B, 0, 0, 0 }, ++ { 1, 2000000, 2400000, 0x3, 0x1, 0x0, 0x8C, 0x2E, 360, 0x08D, 0, 0, 0 }, ++ { 1, 2000000, 2400000, 0x3, 0x1, 0x0, 0x8C, 0x2E, 400, 0x0A6, 0, 0, 0 }, ++ { 2, 2400000, 2800000, 0x3, 0x1, 0x0, 0x04, 0x0D, 300, 0x04E, 0, 0, 0 }, ++ { 2, 2400000, 2800000, 0x3, 0x1, 0x0, 0x04, 0x0D, 320, 0x04F, 0, 0, 0 }, ++ { 2, 2400000, 2800000, 0x3, 0x1, 0x0, 0x04, 0x0D, 325, 0x04F, 0, 0, 0 }, ++ { 2, 2400000, 2800000, 0x3, 0x1, 0x0, 0x04, 0x0D, 330, 0x085, 0, 0, 0 }, ++ { 2, 2400000, 2800000, 0x3, 0x1, 0x0, 0x04, 0x0D, 340, 0x085, 0, 0, 0 }, ++ { 2, 2400000, 2800000, 0x3, 0x1, 0x0, 0x04, 0x0D, 360, 0x086, 0, 0, 0 }, ++ { 2, 2400000, 2800000, 0x3, 0x1, 0x0, 0x04, 0x0D, 400, 0x08B, 0, 0, 0 }, ++ { 3, 2800000, 3400000, 0x3, 0x1, 0x0, 0x04, 0x0D, 300, 0x047, 0, 0, 0 }, ++ { 3, 2800000, 3400000, 0x3, 0x1, 0x0, 0x04, 0x0D, 320, 0x04B, 0, 0, 0 }, ++ { 3, 2800000, 3400000, 0x3, 0x1, 0x0, 0x04, 0x0D, 325, 0x04B, 0, 0, 0 }, ++ { 3, 2800000, 3400000, 0x3, 0x1, 0x0, 0x04, 0x0D, 330, 0x04B, 0, 0, 0 }, ++ { 3, 2800000, 3400000, 0x3, 0x1, 0x0, 0x04, 0x0D, 340, 0x04D, 0, 0, 0 }, ++ { 3, 2800000, 3400000, 0x3, 0x1, 0x0, 0x04, 0x0D, 360, 0x04E, 0, 0, 0 }, ++ { 3, 2800000, 3400000, 0x3, 0x1, 0x0, 0x04, 0x0D, 400, 0x085, 0, 0, 0 }, ++ { 4, 3400000, 3900000, 0x7, 0x1, 0x0, 0x8E, 0x2F, 375, 0x041, 0, 0, 0 }, ++ { 4, 3400000, 3900000, 0x7, 0x1, 0x0, 0x8E, 0x2F, 600, 0x08D, 0, 0, 0 }, ++ { 4, 3400000, 3900000, 0x7, 0x1, 0x0, 0x8E, 0x2F, 680, 0x0A6, 0, 0, 0 }, ++ { 5, 3900000, 4500000, 0x7, 0x1, 0x0, 0x8E, 0x2F, 450, 0x041, 0, 0, 0 }, ++ { 5, 3900000, 4500000, 0x7, 0x1, 0x0, 0x8E, 0x2F, 600, 0x087, 0, 0, 0 }, ++ { 5, 3900000, 4500000, 0x7, 0x1, 0x0, 0x8E, 0x2F, 680, 0x0A4, 0, 0, 0 }, ++ { 6, 4500000, 5200000, 0x7, 0x1, 0x0, 0x04, 0x0D, 600, 0x04F, 0, 0, 0 }, ++ { 6, 4500000, 5200000, 0x7, 0x1, 0x0, 0x04, 0x0D, 680, 0x086, 0, 0, 0 }, ++ { 7, 5200000, 6000000, 0x7, 0x1, 0x0, 0x04, 0x0D, 600, 0x04D, 0, 0, 0 }, ++ { 7, 5200000, 6000000, 0x7, 0x1, 0x0, 0x04, 0x0D, 680, 0x04F, 0, 0, 0 } ++}; ++ ++static void hdmi_arc_config(struct cdns_mhdp_device *mhdp) ++{ ++ u16 txpu_calib_code; ++ u16 txpd_calib_code; ++ u16 txpu_adj_calib_code; ++ u16 txpd_adj_calib_code; ++ u16 prev_calib_code; ++ u16 new_calib_code; ++ u16 rdata; ++ ++ /* Power ARC */ ++ cdns_phy_reg_write(mhdp, TXDA_CYA_AUXDA_CYA, 0x0001); ++ ++ prev_calib_code = cdns_phy_reg_read(mhdp, TX_DIG_CTRL_REG_2); ++ txpu_calib_code = cdns_phy_reg_read(mhdp, CMN_TXPUCAL_CTRL); ++ txpd_calib_code = cdns_phy_reg_read(mhdp, CMN_TXPDCAL_CTRL); ++ txpu_adj_calib_code = cdns_phy_reg_read(mhdp, CMN_TXPU_ADJ_CTRL); ++ txpd_adj_calib_code = cdns_phy_reg_read(mhdp, CMN_TXPD_ADJ_CTRL); ++ ++ new_calib_code = ((txpu_calib_code + txpd_calib_code) / 2) ++ + txpu_adj_calib_code + txpd_adj_calib_code; ++ ++ if (new_calib_code != prev_calib_code) { ++ rdata = cdns_phy_reg_read(mhdp, TX_ANA_CTRL_REG_1); ++ rdata &= 0xDFFF; ++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_1, rdata); ++ cdns_phy_reg_write(mhdp, TX_DIG_CTRL_REG_2, new_calib_code); ++ mdelay(10); ++ rdata |= 0x2000; ++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_1, rdata); ++ udelay(150); ++ } ++ ++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_2, 0x0100); ++ udelay(100); ++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_2, 0x0300); ++ udelay(100); ++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_3, 0x0000); ++ udelay(100); ++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_1, 0x2008); ++ udelay(100); ++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_1, 0x2018); ++ udelay(100); ++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_1, 0x2098); ++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_2, 0x030C); ++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_5, 0x0010); ++ udelay(100); ++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_4, 0x4001); ++ mdelay(5); ++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_1, 0x2198); ++ mdelay(5); ++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_2, 0x030D); ++ udelay(100); ++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_2, 0x030F); ++} ++ ++static void hdmi_phy_set_vswing(struct cdns_mhdp_device *mhdp) ++{ ++ const u32 num_lanes = 4; ++ u32 k; ++ ++ for (k = 0; k < num_lanes; k++) { ++ cdns_phy_reg_write(mhdp, (TX_DIAG_TX_DRV | (k << 9)), 0x7c0); ++ cdns_phy_reg_write(mhdp, (TX_TXCC_CPOST_MULT_00_0 | (k << 9)), 0x0); ++ cdns_phy_reg_write(mhdp, (TX_TXCC_CAL_SCLR_MULT_0 | (k << 9)), 0x120); ++ } ++} ++ ++static int hdmi_feedback_factor(struct cdns_mhdp_device *mhdp) ++{ ++ u32 feedback_factor; ++ ++ switch (mhdp->video_info.color_fmt) { ++ case YCBCR_4_2_2: ++ feedback_factor = 1000; ++ break; ++ case YCBCR_4_2_0: ++ switch (mhdp->video_info.color_depth) { ++ case 8: ++ feedback_factor = 500; ++ break; ++ case 10: ++ feedback_factor = 625; ++ break; ++ case 12: ++ feedback_factor = 750; ++ break; ++ case 16: ++ feedback_factor = 1000; ++ break; ++ default: ++ DRM_ERROR("Invalid ColorDepth\n"); ++ return 0; ++ } ++ break; ++ default: ++ /* Assume RGB/YUV444 */ ++ switch (mhdp->video_info.color_depth) { ++ case 10: ++ feedback_factor = 1250; ++ break; ++ case 12: ++ feedback_factor = 1500; ++ break; ++ case 16: ++ feedback_factor = 2000; ++ break; ++ default: ++ feedback_factor = 1000; ++ } ++ } ++ return feedback_factor; ++} ++ ++static int hdmi_phy_config(struct cdns_mhdp_device *mhdp, ++ const struct hdmi_ctrl *p_ctrl_table, ++ const struct hdmi_pll_tuning *p_pll_table, ++ char pclk_in) ++{ ++ const u32 num_lanes = 4; ++ u32 val, i, k; ++ ++ /* enable PHY isolation mode only for CMN */ ++ cdns_phy_reg_write(mhdp, PHY_PMA_ISOLATION_CTRL, 0xD000); ++ ++ /* set cmn_pll0_clk_datart1_div/cmn_pll0_clk_datart0_div dividers */ ++ val = cdns_phy_reg_read(mhdp, PHY_PMA_ISO_PLL_CTRL1); ++ val &= 0xFF00; ++ val |= 0x0012; ++ cdns_phy_reg_write(mhdp, PHY_PMA_ISO_PLL_CTRL1, val); ++ ++ /* assert PHY reset from isolation register */ ++ cdns_phy_reg_write(mhdp, PHY_ISO_CMN_CTRL, 0x0000); ++ /* assert PMA CMN reset */ ++ cdns_phy_reg_write(mhdp, PHY_PMA_ISO_CMN_CTRL, 0x0000); ++ ++ /* register XCVR_DIAG_BIDI_CTRL */ ++ for (k = 0; k < num_lanes; k++) ++ cdns_phy_reg_write(mhdp, XCVR_DIAG_BIDI_CTRL | (k << 9), 0x00FF); ++ ++ /* Describing Task phy_cfg_hdp */ ++ ++ val = cdns_phy_reg_read(mhdp, PHY_PMA_CMN_CTRL1); ++ val &= 0xFFF7; ++ val |= 0x0008; ++ cdns_phy_reg_write(mhdp, PHY_PMA_CMN_CTRL1, val); ++ ++ /* PHY Registers */ ++ val = cdns_phy_reg_read(mhdp, PHY_PMA_CMN_CTRL1); ++ val &= 0xCFFF; ++ val |= p_ctrl_table->cmn_ref_clk_dig_div << 12; ++ cdns_phy_reg_write(mhdp, PHY_PMA_CMN_CTRL1, val); ++ ++ val = cdns_phy_reg_read(mhdp, PHY_HDP_CLK_CTL); ++ val &= 0x00FF; ++ val |= 0x1200; ++ cdns_phy_reg_write(mhdp, PHY_HDP_CLK_CTL, val); ++ ++ /* Common control module control and diagnostic registers */ ++ val = cdns_phy_reg_read(mhdp, CMN_CDIAG_REFCLK_CTRL); ++ val &= 0x8FFF; ++ val |= p_ctrl_table->ref_clk_divider_scaler << 12; ++ val |= 0x00C0; ++ cdns_phy_reg_write(mhdp, CMN_CDIAG_REFCLK_CTRL, val); ++ ++ /* High speed clock used */ ++ val = cdns_phy_reg_read(mhdp, CMN_DIAG_HSCLK_SEL); ++ val &= 0xFF00; ++ val |= (p_ctrl_table->cmnda_hs_clk_0_sel >> 1) << 0; ++ val |= (p_ctrl_table->cmnda_hs_clk_1_sel >> 1) << 4; ++ cdns_phy_reg_write(mhdp, CMN_DIAG_HSCLK_SEL, val); ++ ++ for (k = 0; k < num_lanes; k++) { ++ val = cdns_phy_reg_read(mhdp, (XCVR_DIAG_HSCLK_SEL | (k << 9))); ++ val &= 0xCFFF; ++ val |= (p_ctrl_table->cmnda_hs_clk_0_sel >> 1) << 12; ++ cdns_phy_reg_write(mhdp, (XCVR_DIAG_HSCLK_SEL | (k << 9)), val); ++ } ++ ++ /* PLL 0 control state machine registers */ ++ val = p_ctrl_table->vco_ring_select << 12; ++ cdns_phy_reg_write(mhdp, CMN_PLLSM0_USER_DEF_CTRL, val); ++ ++ if (pclk_in == true) ++ val = 0x30A0; ++ else { ++ val = cdns_phy_reg_read(mhdp, CMN_PLL0_VCOCAL_START); ++ val &= 0xFE00; ++ val |= p_pll_table->vco_cal_code; ++ } ++ cdns_phy_reg_write(mhdp, CMN_PLL0_VCOCAL_START, val); ++ ++ cdns_phy_reg_write(mhdp, CMN_PLL0_VCOCAL_INIT_TMR, 0x0064); ++ cdns_phy_reg_write(mhdp, CMN_PLL0_VCOCAL_ITER_TMR, 0x000A); ++ ++ /* Common functions control and diagnostics registers */ ++ val = p_ctrl_table->cmnda_pll0_hs_sym_div_sel << 8; ++ val |= p_ctrl_table->cmnda_pll0_ip_div; ++ cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_INCLK_CTRL, val); ++ ++ cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_OVRD, 0x0000); ++ ++ val = p_ctrl_table->cmnda_pll0_fb_div_high; ++ val |= (1 << 15); ++ cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_FBH_OVRD, val); ++ ++ val = p_ctrl_table->cmnda_pll0_fb_div_low; ++ val |= (1 << 15); ++ cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_FBL_OVRD, val); ++ ++ if (pclk_in == false) { ++ val = p_ctrl_table->cmnda_pll0_pxdiv_low; ++ cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_PXL_DIVL, val); ++ ++ val = p_ctrl_table->cmnda_pll0_pxdiv_high; ++ val |= (1 << 15); ++ cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_PXL_DIVH, val); ++ } ++ ++ val = p_pll_table->volt_to_current_coarse; ++ val |= (p_pll_table->volt_to_current) << 4; ++ cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_V2I_TUNE, val); ++ ++ val = p_pll_table->charge_pump_gain; ++ cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_CP_TUNE, val); ++ ++ cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_LF_PROG, 0x0008); ++ ++ val = p_pll_table->pmos_ctrl; ++ val |= (p_pll_table->ndac_ctrl) << 8; ++ cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_PTATIS_TUNE1, val); ++ ++ val = p_pll_table->ptat_ndac_ctrl; ++ cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_PTATIS_TUNE2, val); ++ ++ if (pclk_in == true) ++ cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_TEST_MODE, 0x0022); ++ else ++ cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_TEST_MODE, 0x0020); ++ cdns_phy_reg_write(mhdp, CMN_PSM_CLK_CTRL, 0x0016); ++ ++ /* Transceiver control and diagnostic registers */ ++ for (k = 0; k < num_lanes; k++) { ++ val = cdns_phy_reg_read(mhdp, (XCVR_DIAG_PLLDRC_CTRL | (k << 9))); ++ val &= 0xBFFF; ++ cdns_phy_reg_write(mhdp, (XCVR_DIAG_PLLDRC_CTRL | (k << 9)), val); ++ } ++ ++ for (k = 0; k < num_lanes; k++) { ++ val = cdns_phy_reg_read(mhdp, (TX_DIAG_TX_CTRL | (k << 9))); ++ val &= 0xFF3F; ++ val |= (p_ctrl_table->hsclk_div_tx_sub_rate >> 1) << 6; ++ cdns_phy_reg_write(mhdp, (TX_DIAG_TX_CTRL | (k << 9)), val); ++ } ++ ++ /* ++ * for single ended reference clock val |= 0x0030; ++ * for differential clock val |= 0x0000; ++ */ ++ val = cdns_phy_reg_read(mhdp, PHY_PMA_CMN_CTRL1); ++ val &= 0xFF8F; ++ if (pclk_in == true) ++ val |= 0x0030; ++ cdns_phy_reg_write(mhdp, PHY_PMA_CMN_CTRL1, val); ++ ++ /* for differential clock on the refclk_p and ++ * refclk_m off chip pins: CMN_DIAG_ACYA[8]=1'b1 */ ++ cdns_phy_reg_write(mhdp, CMN_DIAG_ACYA, 0x0100); ++ ++ /* Deassert PHY reset */ ++ cdns_phy_reg_write(mhdp, PHY_ISO_CMN_CTRL, 0x0001); ++ cdns_phy_reg_write(mhdp, PHY_PMA_ISO_CMN_CTRL, 0x0003); ++ ++ /* Power state machine registers */ ++ for (k = 0; k < num_lanes; k++) ++ cdns_phy_reg_write(mhdp, XCVR_PSM_RCTRL | (k << 9), 0xFEFC); ++ ++ /* Assert cmn_macro_pwr_en */ ++ cdns_phy_reg_write(mhdp, PHY_PMA_ISO_CMN_CTRL, 0x0013); ++ ++ /* wait for cmn_macro_pwr_en_ack */ ++ for (i = 0; i < 10; i++) { ++ val = cdns_phy_reg_read(mhdp, PHY_PMA_ISO_CMN_CTRL); ++ if (val & (1 << 5)) ++ break; ++ msleep(20); ++ } ++ if (i == 10) { ++ DRM_ERROR("PMA ouput macro power up failed\n"); ++ return false; ++ } ++ ++ /* wait for cmn_ready */ ++ for (i = 0; i < 10; i++) { ++ val = cdns_phy_reg_read(mhdp, PHY_PMA_CMN_CTRL1); ++ if (val & (1 << 0)) ++ break; ++ msleep(20); ++ } ++ if (i == 10) { ++ DRM_ERROR("PMA output ready failed\n"); ++ return false; ++ } ++ ++ for (k = 0; k < num_lanes; k++) { ++ cdns_phy_reg_write(mhdp, TX_PSC_A0 | (k << 9), 0x6791); ++ cdns_phy_reg_write(mhdp, TX_PSC_A1 | (k << 9), 0x6790); ++ cdns_phy_reg_write(mhdp, TX_PSC_A2 | (k << 9), 0x0090); ++ cdns_phy_reg_write(mhdp, TX_PSC_A3 | (k << 9), 0x0090); ++ ++ val = cdns_phy_reg_read(mhdp, RX_PSC_CAL | (k << 9)); ++ val &= 0xFFBB; ++ cdns_phy_reg_write(mhdp, RX_PSC_CAL | (k << 9), val); ++ ++ val = cdns_phy_reg_read(mhdp, RX_PSC_A0 | (k << 9)); ++ val &= 0xFFBB; ++ cdns_phy_reg_write(mhdp, RX_PSC_A0 | (k << 9), val); ++ } ++ return true; ++} ++ ++static int hdmi_phy_cfg_t28hpc(struct cdns_mhdp_device *mhdp, ++ struct drm_display_mode *mode) ++{ ++ const struct hdmi_ctrl *p_ctrl_table; ++ const struct hdmi_pll_tuning *p_pll_table; ++ const u32 refclk_freq_khz = 27000; ++ const u8 pclk_in = false; ++ u32 pixel_freq = mode->clock; ++ u32 vco_freq, char_freq; ++ u32 div_total, feedback_factor; ++ u32 i, ret; ++ ++ feedback_factor = hdmi_feedback_factor(mhdp); ++ ++ char_freq = pixel_freq * feedback_factor / 1000; ++ ++ DRM_INFO("Pixel clock: %d KHz, character clock: %d, bpc is %0d-bit.\n", ++ pixel_freq, char_freq, mhdp->video_info.color_depth); ++ ++ /* Get right row from the ctrl_table table. ++ * Check if 'pixel_freq_khz' value matches the PIXEL_CLK_FREQ column. ++ * Consider only the rows with FEEDBACK_FACTOR column matching feedback_factor. */ ++ for (i = 0; i < ARRAY_SIZE(imx8mq_ctrl_table); i++) { ++ if (feedback_factor == imx8mq_ctrl_table[i].feedback_factor && ++ pixel_freq == imx8mq_ctrl_table[i].pixel_clk_freq_min) { ++ p_ctrl_table = &imx8mq_ctrl_table[i]; ++ break; ++ } ++ } ++ if (i == ARRAY_SIZE(imx8mq_ctrl_table)) { ++ DRM_WARN("Pixel clk (%d KHz) not supported, color depth (%0d-bit)\n", ++ pixel_freq, mhdp->video_info.color_depth); ++ return 0; ++ } ++ ++ div_total = p_ctrl_table->pll_fb_div_total; ++ vco_freq = refclk_freq_khz * div_total / p_ctrl_table->cmnda_pll0_ip_div; ++ ++ /* Get right row from the imx8mq_pll_table table. ++ * Check if vco_freq_khz and feedback_div_total ++ * column matching with imx8mq_pll_table. */ ++ for (i = 0; i < ARRAY_SIZE(imx8mq_pll_table); i++) { ++ if (vco_freq == imx8mq_pll_table[i].vco_freq_min && ++ div_total == imx8mq_pll_table[i].feedback_div_total) { ++ p_pll_table = &imx8mq_pll_table[i]; ++ break; ++ } ++ } ++ if (i == ARRAY_SIZE(imx8mq_pll_table)) { ++ DRM_WARN("VCO (%d KHz) not supported\n", vco_freq); ++ return 0; ++ } ++ DRM_INFO("VCO frequency is %d KHz\n", vco_freq); ++ ++ ret = hdmi_phy_config(mhdp, p_ctrl_table, p_pll_table, pclk_in); ++ if (ret == false) ++ return 0; ++ ++ return char_freq; ++} ++ ++static int hdmi_phy_cfg_ss28fdsoi(struct cdns_mhdp_device *mhdp, ++ struct drm_display_mode *mode) ++{ ++ const struct hdmi_ctrl *p_ctrl_table; ++ const struct hdmi_pll_tuning *p_pll_table; ++ const u8 pclk_in = true; ++ u32 pixel_freq = mode->clock; ++ u32 vco_freq, char_freq; ++ u32 div_total, feedback_factor; ++ u32 ret, i; ++ ++ feedback_factor = hdmi_feedback_factor(mhdp); ++ ++ char_freq = pixel_freq * feedback_factor / 1000; ++ ++ DRM_INFO("Pixel clock: %d KHz, character clock: %d, bpc is %0d-bit.\n", ++ pixel_freq, char_freq, mhdp->video_info.color_depth); ++ ++ /* Get right row from the ctrl_table table. ++ * Check if 'pixel_freq_khz' value matches the PIXEL_CLK_FREQ column. ++ * Consider only the rows with FEEDBACK_FACTOR column matching feedback_factor. */ ++ for (i = 0; i < ARRAY_SIZE(imx8qm_ctrl_table); i++) { ++ if (feedback_factor == imx8qm_ctrl_table[i].feedback_factor && ++ pixel_freq >= imx8qm_ctrl_table[i].pixel_clk_freq_min && ++ pixel_freq <= imx8qm_ctrl_table[i].pixel_clk_freq_max) { ++ p_ctrl_table = &imx8qm_ctrl_table[i]; ++ break; ++ } ++ } ++ if (i == ARRAY_SIZE(imx8qm_ctrl_table)) { ++ DRM_WARN("Pixel clk (%d KHz) not supported, color depth (%0d-bit)\n", ++ pixel_freq, mhdp->video_info.color_depth); ++ return 0; ++ } ++ ++ div_total = p_ctrl_table->pll_fb_div_total; ++ vco_freq = pixel_freq * div_total / p_ctrl_table->cmnda_pll0_ip_div; ++ ++ /* Get right row from the imx8mq_pll_table table. ++ * Check if vco_freq_khz and feedback_div_total ++ * column matching with imx8mq_pll_table. */ ++ for (i = 0; i < ARRAY_SIZE(imx8qm_pll_table); i++) { ++ if (vco_freq >= imx8qm_pll_table[i].vco_freq_min && ++ vco_freq < imx8qm_pll_table[i].vco_freq_max && ++ div_total == imx8qm_pll_table[i].feedback_div_total) { ++ p_pll_table = &imx8qm_pll_table[i]; ++ break; ++ } ++ } ++ if (i == ARRAY_SIZE(imx8qm_pll_table)) { ++ DRM_WARN("VCO (%d KHz) not supported\n", vco_freq); ++ return 0; ++ } ++ DRM_INFO("VCO frequency is %d KHz\n", vco_freq); ++ ++ ret = hdmi_phy_config(mhdp, p_ctrl_table, p_pll_table, pclk_in); ++ if (ret == false) ++ return 0; ++ ++ return char_freq; ++} ++ ++static int hdmi_phy_power_up(struct cdns_mhdp_device *mhdp) ++{ ++ u32 val, i; ++ ++ /* set Power State to A2 */ ++ cdns_phy_reg_write(mhdp, PHY_HDP_MODE_CTRL, 0x0004); ++ ++ cdns_phy_reg_write(mhdp, TX_DIAG_ACYA_0, 1); ++ cdns_phy_reg_write(mhdp, TX_DIAG_ACYA_1, 1); ++ cdns_phy_reg_write(mhdp, TX_DIAG_ACYA_2, 1); ++ cdns_phy_reg_write(mhdp, TX_DIAG_ACYA_3, 1); ++ ++ /* Wait for Power State A2 Ack */ ++ for (i = 0; i < 10; i++) { ++ val = cdns_phy_reg_read(mhdp, PHY_HDP_MODE_CTRL); ++ if (val & (1 << 6)) ++ break; ++ msleep(20); ++ } ++ if (i == 10) { ++ dev_err(mhdp->dev, "Wait A2 Ack failed\n"); ++ return -1; ++ } ++ ++ /* Power up ARC */ ++ hdmi_arc_config(mhdp); ++ ++ /* Configure PHY in A0 mode (PHY must be in the A0 power ++ * state in order to transmit data) ++ */ ++ //cdns_phy_reg_write(mhdp, PHY_HDP_MODE_CTRL, 0x0101); //imx8mq ++ cdns_phy_reg_write(mhdp, PHY_HDP_MODE_CTRL, 0x0001); ++ ++ /* Wait for Power State A0 Ack */ ++ for (i = 0; i < 10; i++) { ++ val = cdns_phy_reg_read(mhdp, PHY_HDP_MODE_CTRL); ++ if (val & (1 << 4)) ++ break; ++ msleep(20); ++ } ++ if (i == 10) { ++ dev_err(mhdp->dev, "Wait A0 Ack failed\n"); ++ return -1; ++ } ++ return 0; ++} ++ ++bool cdns_hdmi_phy_video_valid_imx8mq(struct cdns_mhdp_device *mhdp) ++{ ++ u32 rate = mhdp->valid_mode->clock; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(imx8mq_ctrl_table); i++) ++ if(rate == imx8mq_ctrl_table[i].pixel_clk_freq_min) ++ return true; ++ return false; ++} ++ ++int cdns_hdmi_phy_set_imx8mq(struct cdns_mhdp_device *mhdp) ++{ ++ struct drm_display_mode *mode = &mhdp->mode; ++ int ret; ++ ++ /* Check HDMI FW alive before HDMI PHY init */ ++ ret = cdns_mhdp_check_alive(mhdp); ++ if (ret == false) { ++ DRM_ERROR("NO HDMI FW running\n"); ++ return -ENXIO; ++ } ++ ++ /* Configure PHY */ ++ mhdp->hdmi.char_rate = hdmi_phy_cfg_t28hpc(mhdp, mode); ++ if (mhdp->hdmi.char_rate == 0) { ++ DRM_ERROR("failed to set phy pclock\n"); ++ return -EINVAL; ++ } ++ ++ ret = hdmi_phy_power_up(mhdp); ++ if (ret < 0) ++ return ret; ++ ++ hdmi_phy_set_vswing(mhdp); ++ ++ return true; ++} ++ ++bool cdns_hdmi_phy_video_valid_imx8qm(struct cdns_mhdp_device *mhdp) ++{ ++ u32 rate = mhdp->valid_mode->clock; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(imx8qm_ctrl_table); i++) ++ if(rate >= imx8qm_ctrl_table[i].pixel_clk_freq_min && ++ rate <= imx8qm_ctrl_table[i].pixel_clk_freq_max) ++ return true; ++ return false; ++} ++ ++int cdns_hdmi_phy_set_imx8qm(struct cdns_mhdp_device *mhdp) ++{ ++ struct drm_display_mode *mode = &mhdp->mode; ++ int ret; ++ ++ /* Check HDMI FW alive before HDMI PHY init */ ++ ret = cdns_mhdp_check_alive(mhdp); ++ if (ret == false) { ++ DRM_ERROR("NO HDMI FW running\n"); ++ return -ENXIO; ++ } ++ ++ /* Configure PHY */ ++ mhdp->hdmi.char_rate = hdmi_phy_cfg_ss28fdsoi(mhdp, mode); ++ if (mhdp->hdmi.char_rate == 0) { ++ DRM_ERROR("failed to set phy pclock\n"); ++ return -EINVAL; ++ } ++ ++ ret = hdmi_phy_power_up(mhdp); ++ if (ret < 0) ++ return ret; ++ ++ hdmi_phy_set_vswing(mhdp); ++ ++ return true; ++} +diff --git a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx.h b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx.h +new file mode 100644 +index 000000000000..fc3247dada2d +--- /dev/null ++++ b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx.h +@@ -0,0 +1,75 @@ ++/* ++ * Cadence High-Definition Multimedia Interface (HDMI) driver ++ * ++ * Copyright (C) 2019 NXP Semiconductor, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ */ ++#ifndef CDNS_MHDP_IMX_H_ ++#define CDNS_MHDP_IMX_H_ ++ ++#include ++#include ++ ++ ++struct imx_mhdp_device; ++ ++struct imx_hdp_clks { ++ struct clk *av_pll; ++ struct clk *dig_pll; ++ struct clk *clk_ipg; ++ struct clk *clk_core; ++ struct clk *clk_pxl; ++ struct clk *clk_pxl_mux; ++ struct clk *clk_pxl_link; ++ ++ struct clk *lpcg_hdp; ++ struct clk *lpcg_msi; ++ struct clk *lpcg_pxl; ++ struct clk *lpcg_vif; ++ struct clk *lpcg_lis; ++ struct clk *lpcg_apb; ++ struct clk *lpcg_apb_csr; ++ struct clk *lpcg_apb_ctrl; ++ ++ struct clk *lpcg_i2s; ++ struct clk *clk_i2s_bypass; ++}; ++ ++struct imx_mhdp_device { ++ struct cdns_mhdp_device mhdp; ++ struct drm_encoder encoder; ++ ++ struct mutex audio_mutex; ++ spinlock_t audio_lock; ++ bool connected; ++ bool active; ++ bool suspended; ++ struct imx_hdp_clks clks; ++ const struct firmware *fw; ++ const char *firmware_name; ++ ++ int bus_type; ++ ++ struct device *pd_mhdp_dev; ++ struct device *pd_pll0_dev; ++ struct device *pd_pll1_dev; ++ struct device_link *pd_mhdp_link; ++ struct device_link *pd_pll0_link; ++ struct device_link *pd_pll1_link; ++}; ++ ++void cdns_mhdp_plat_init_imx8qm(struct cdns_mhdp_device *mhdp); ++void cdns_mhdp_plat_deinit_imx8qm(struct cdns_mhdp_device *mhdp); ++void cdns_mhdp_pclk_rate_imx8qm(struct cdns_mhdp_device *mhdp); ++int cdns_mhdp_firmware_init_imx8qm(struct cdns_mhdp_device *mhdp); ++int cdns_mhdp_resume_imx8qm(struct cdns_mhdp_device *mhdp); ++int cdns_mhdp_suspend_imx8qm(struct cdns_mhdp_device *mhdp); ++int cdns_mhdp_power_on_imx8qm(struct cdns_mhdp_device *mhdp); ++int cdns_mhdp_power_on_ls1028a(struct cdns_mhdp_device *mhdp); ++void cdns_mhdp_pclk_rate_ls1028a(struct cdns_mhdp_device *mhdp); ++#endif /* CDNS_MHDP_IMX_H_ */ +diff --git a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx8qm.c b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx8qm.c +new file mode 100644 +index 000000000000..a3ba3da4b05d +--- /dev/null ++++ b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx8qm.c +@@ -0,0 +1,638 @@ ++/* ++ * copyright (c) 2019 nxp semiconductor, inc. ++ * ++ * this program is free software; you can redistribute it and/or modify ++ * it under the terms of the gnu general public license version 2 as ++ * published by the free software foundation. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "cdns-mhdp-imx.h" ++ ++#define FW_IRAM_OFFSET 0x2000 ++#define FW_IRAM_SIZE 0x10000 ++#define FW_DRAM_SIZE 0x8000 ++ ++#define PLL_800MHZ (800000000) ++ ++#define HDP_DUAL_MODE_MIN_PCLK_RATE 300000 /* KHz */ ++#define HDP_SINGLE_MODE_MAX_WIDTH 1920 ++ ++#define CSR_PIXEL_LINK_MUX_CTL 0x00 ++#define CSR_PIXEL_LINK_MUX_VCP_OFFSET 5 ++#define CSR_PIXEL_LINK_MUX_HCP_OFFSET 4 ++ ++static bool imx8qm_video_dual_mode(struct cdns_mhdp_device *mhdp) ++{ ++ struct drm_display_mode *mode = &mhdp->mode; ++ return (mode->clock > HDP_DUAL_MODE_MIN_PCLK_RATE || ++ mode->hdisplay > HDP_SINGLE_MODE_MAX_WIDTH) ? true : false; ++} ++ ++static void imx8qm_pixel_link_mux(struct imx_mhdp_device *imx_mhdp) ++{ ++ struct drm_display_mode *mode = &imx_mhdp->mhdp.mode; ++ bool dual_mode; ++ u32 val; ++ ++ dual_mode = imx8qm_video_dual_mode(&imx_mhdp->mhdp); ++ ++ val = 0x4; /* RGB */ ++ if (dual_mode) ++ val |= 0x2; /* pixel link 0 and 1 are active */ ++ if (mode->flags & DRM_MODE_FLAG_PVSYNC) ++ val |= 1 << CSR_PIXEL_LINK_MUX_VCP_OFFSET; ++ if (mode->flags & DRM_MODE_FLAG_PHSYNC) ++ val |= 1 << CSR_PIXEL_LINK_MUX_HCP_OFFSET; ++ if (mode->flags & DRM_MODE_FLAG_INTERLACE) ++ val |= 0x2; ++ ++ writel(val, imx_mhdp->mhdp.regs_sec); ++} ++ ++static void imx8qm_pixel_link_valid(u32 dual_mode) ++{ ++ struct imx_sc_ipc *handle; ++ ++ imx_scu_get_handle(&handle); ++ ++ imx_sc_misc_set_control(handle, IMX_SC_R_DC_0, IMX_SC_C_PXL_LINK_MST1_VLD, 1); ++ if (dual_mode) ++ imx_sc_misc_set_control(handle, IMX_SC_R_DC_0, IMX_SC_C_PXL_LINK_MST2_VLD, 1); ++} ++ ++static void imx8qm_pixel_link_invalid(u32 dual_mode) ++{ ++ struct imx_sc_ipc *handle; ++ ++ imx_scu_get_handle(&handle); ++ ++ imx_sc_misc_set_control(handle, IMX_SC_R_DC_0, IMX_SC_C_PXL_LINK_MST1_VLD, 0); ++ if (dual_mode) ++ imx_sc_misc_set_control(handle, IMX_SC_R_DC_0, IMX_SC_C_PXL_LINK_MST2_VLD, 0); ++} ++ ++static void imx8qm_pixel_link_sync_enable(u32 dual_mode) ++{ ++ struct imx_sc_ipc *handle; ++ ++ imx_scu_get_handle(&handle); ++ ++ if (dual_mode) ++ imx_sc_misc_set_control(handle, IMX_SC_R_DC_0, IMX_SC_C_SYNC_CTRL, 3); ++ else ++ imx_sc_misc_set_control(handle, IMX_SC_R_DC_0, IMX_SC_C_SYNC_CTRL0, 1); ++} ++ ++static void imx8qm_pixel_link_sync_disable(u32 dual_mode) ++{ ++ struct imx_sc_ipc *handle; ++ ++ imx_scu_get_handle(&handle); ++ ++ if (dual_mode) ++ imx_sc_misc_set_control(handle, IMX_SC_R_DC_0, IMX_SC_C_SYNC_CTRL, 0); ++ else ++ imx_sc_misc_set_control(handle, IMX_SC_R_DC_0, IMX_SC_C_SYNC_CTRL0, 0); ++} ++ ++static void imx8qm_phy_reset(u8 reset) ++{ ++ struct imx_sc_ipc *handle; ++ ++ imx_scu_get_handle(&handle); ++ ++ /* set the pixel link mode and pixel type */ ++ imx_sc_misc_set_control(handle, IMX_SC_R_HDMI, IMX_SC_C_PHY_RESET, reset); ++} ++ ++static void imx8qm_clk_mux(u8 is_dp) ++{ ++ struct imx_sc_ipc *handle; ++ ++ imx_scu_get_handle(&handle); ++ ++ if (is_dp) ++ /* Enable the 24MHz for HDP PHY */ ++ imx_sc_misc_set_control(handle, IMX_SC_R_HDMI, IMX_SC_C_MODE, 1); ++ else ++ imx_sc_misc_set_control(handle, IMX_SC_R_HDMI, IMX_SC_C_MODE, 0); ++} ++ ++int imx8qm_clocks_init(struct imx_mhdp_device *imx_mhdp) ++{ ++ struct device *dev = imx_mhdp->mhdp.dev; ++ struct imx_hdp_clks *clks = &imx_mhdp->clks; ++ ++ clks->dig_pll = devm_clk_get(dev, "dig_pll"); ++ if (IS_ERR(clks->dig_pll)) { ++ dev_warn(dev, "failed to get dig pll clk\n"); ++ return PTR_ERR(clks->dig_pll); ++ } ++ ++ clks->av_pll = devm_clk_get(dev, "av_pll"); ++ if (IS_ERR(clks->av_pll)) { ++ dev_warn(dev, "failed to get av pll clk\n"); ++ return PTR_ERR(clks->av_pll); ++ } ++ ++ clks->clk_ipg = devm_clk_get(dev, "clk_ipg"); ++ if (IS_ERR(clks->clk_ipg)) { ++ dev_warn(dev, "failed to get dp ipg clk\n"); ++ return PTR_ERR(clks->clk_ipg); ++ } ++ ++ clks->clk_core = devm_clk_get(dev, "clk_core"); ++ if (IS_ERR(clks->clk_core)) { ++ dev_warn(dev, "failed to get hdp core clk\n"); ++ return PTR_ERR(clks->clk_core); ++ } ++ ++ clks->clk_pxl = devm_clk_get(dev, "clk_pxl"); ++ if (IS_ERR(clks->clk_pxl)) { ++ dev_warn(dev, "failed to get pxl clk\n"); ++ return PTR_ERR(clks->clk_pxl); ++ } ++ ++ clks->clk_pxl_mux = devm_clk_get(dev, "clk_pxl_mux"); ++ if (IS_ERR(clks->clk_pxl_mux)) { ++ dev_warn(dev, "failed to get pxl mux clk\n"); ++ return PTR_ERR(clks->clk_pxl_mux); ++ } ++ ++ clks->clk_pxl_link = devm_clk_get(dev, "clk_pxl_link"); ++ if (IS_ERR(clks->clk_pxl_mux)) { ++ dev_warn(dev, "failed to get pxl link clk\n"); ++ return PTR_ERR(clks->clk_pxl_link); ++ } ++ ++ clks->lpcg_hdp = devm_clk_get(dev, "lpcg_hdp"); ++ if (IS_ERR(clks->lpcg_hdp)) { ++ dev_warn(dev, "failed to get lpcg hdp clk\n"); ++ return PTR_ERR(clks->lpcg_hdp); ++ } ++ ++ clks->lpcg_msi = devm_clk_get(dev, "lpcg_msi"); ++ if (IS_ERR(clks->lpcg_msi)) { ++ dev_warn(dev, "failed to get lpcg msi clk\n"); ++ return PTR_ERR(clks->lpcg_msi); ++ } ++ ++ clks->lpcg_pxl = devm_clk_get(dev, "lpcg_pxl"); ++ if (IS_ERR(clks->lpcg_pxl)) { ++ dev_warn(dev, "failed to get lpcg pxl clk\n"); ++ return PTR_ERR(clks->lpcg_pxl); ++ } ++ ++ clks->lpcg_vif = devm_clk_get(dev, "lpcg_vif"); ++ if (IS_ERR(clks->lpcg_vif)) { ++ dev_warn(dev, "failed to get lpcg vif clk\n"); ++ return PTR_ERR(clks->lpcg_vif); ++ } ++ ++ clks->lpcg_lis = devm_clk_get(dev, "lpcg_lis"); ++ if (IS_ERR(clks->lpcg_lis)) { ++ dev_warn(dev, "failed to get lpcg lis clk\n"); ++ return PTR_ERR(clks->lpcg_lis); ++ } ++ ++ clks->lpcg_apb = devm_clk_get(dev, "lpcg_apb"); ++ if (IS_ERR(clks->lpcg_apb)) { ++ dev_warn(dev, "failed to get lpcg apb clk\n"); ++ return PTR_ERR(clks->lpcg_apb); ++ } ++ ++ clks->lpcg_apb_csr = devm_clk_get(dev, "lpcg_apb_csr"); ++ if (IS_ERR(clks->lpcg_apb_csr)) { ++ dev_warn(dev, "failed to get apb csr clk\n"); ++ return PTR_ERR(clks->lpcg_apb_csr); ++ } ++ ++ clks->lpcg_apb_ctrl = devm_clk_get(dev, "lpcg_apb_ctrl"); ++ if (IS_ERR(clks->lpcg_apb_ctrl)) { ++ dev_warn(dev, "failed to get lpcg apb ctrl clk\n"); ++ return PTR_ERR(clks->lpcg_apb_ctrl); ++ } ++ ++ clks->clk_i2s_bypass = devm_clk_get(dev, "clk_i2s_bypass"); ++ if (IS_ERR(clks->clk_i2s_bypass)) { ++ dev_err(dev, "failed to get i2s bypass clk\n"); ++ return PTR_ERR(clks->clk_i2s_bypass); ++ } ++ ++ clks->lpcg_i2s = devm_clk_get(dev, "lpcg_i2s"); ++ if (IS_ERR(clks->lpcg_i2s)) { ++ dev_err(dev, "failed to get lpcg i2s clk\n"); ++ return PTR_ERR(clks->lpcg_i2s); ++ } ++ return true; ++} ++ ++static int imx8qm_pixel_clk_enable(struct imx_mhdp_device *imx_mhdp) ++{ ++ struct imx_hdp_clks *clks = &imx_mhdp->clks; ++ struct device *dev = imx_mhdp->mhdp.dev; ++ int ret; ++ ++ ret = clk_prepare_enable(clks->av_pll); ++ if (ret < 0) { ++ dev_err(dev, "%s, pre av pll error\n", __func__); ++ return ret; ++ } ++ ++ ret = clk_prepare_enable(clks->clk_pxl); ++ if (ret < 0) { ++ dev_err(dev, "%s, pre clk pxl error\n", __func__); ++ return ret; ++ } ++ ret = clk_prepare_enable(clks->clk_pxl_mux); ++ if (ret < 0) { ++ dev_err(dev, "%s, pre clk pxl mux error\n", __func__); ++ return ret; ++ } ++ ++ ret = clk_prepare_enable(clks->clk_pxl_link); ++ if (ret < 0) { ++ dev_err(dev, "%s, pre clk pxl link error\n", __func__); ++ return ret; ++ } ++ ret = clk_prepare_enable(clks->lpcg_vif); ++ if (ret < 0) { ++ dev_err(dev, "%s, pre clk vif error\n", __func__); ++ return ret; ++ } ++ ret = clk_prepare_enable(clks->lpcg_pxl); ++ if (ret < 0) { ++ dev_err(dev, "%s, pre lpcg pxl error\n", __func__); ++ return ret; ++ } ++ ret = clk_prepare_enable(clks->lpcg_hdp); ++ if (ret < 0) { ++ dev_err(dev, "%s, pre lpcg hdp error\n", __func__); ++ return ret; ++ } ++ return ret; ++} ++ ++static void imx8qm_pixel_clk_disable(struct imx_mhdp_device *imx_mhdp) ++{ ++ struct imx_hdp_clks *clks = &imx_mhdp->clks; ++ ++ clk_disable_unprepare(clks->lpcg_pxl); ++ clk_disable_unprepare(clks->lpcg_hdp); ++ clk_disable_unprepare(clks->lpcg_vif); ++ clk_disable_unprepare(clks->clk_pxl); ++ clk_disable_unprepare(clks->clk_pxl_link); ++ clk_disable_unprepare(clks->clk_pxl_mux); ++ clk_disable_unprepare(clks->av_pll); ++} ++ ++static void imx8qm_pixel_clk_set_rate(struct imx_mhdp_device *imx_mhdp, u32 pclock) ++{ ++ bool dual_mode = imx8qm_video_dual_mode(&imx_mhdp->mhdp); ++ struct imx_hdp_clks *clks = &imx_mhdp->clks; ++ ++ /* pixel clock for HDMI */ ++ clk_set_rate(clks->av_pll, pclock); ++ ++ if (dual_mode == true) { ++ clk_set_rate(clks->clk_pxl, pclock/2); ++ clk_set_rate(clks->clk_pxl_link, pclock/2); ++ } else { ++ clk_set_rate(clks->clk_pxl_link, pclock); ++ clk_set_rate(clks->clk_pxl, pclock); ++ } ++ clk_set_rate(clks->clk_pxl_mux, pclock); ++} ++ ++static int imx8qm_ipg_clk_enable(struct imx_mhdp_device *imx_mhdp) ++{ ++ int ret; ++ struct imx_hdp_clks *clks = &imx_mhdp->clks; ++ struct device *dev = imx_mhdp->mhdp.dev; ++ ++ ret = clk_prepare_enable(clks->dig_pll); ++ if (ret < 0) { ++ dev_err(dev, "%s, pre dig pll error\n", __func__); ++ return ret; ++ } ++ ++ ret = clk_prepare_enable(clks->clk_ipg); ++ if (ret < 0) { ++ dev_err(dev, "%s, pre clk_ipg error\n", __func__); ++ return ret; ++ } ++ ++ ret = clk_prepare_enable(clks->clk_core); ++ if (ret < 0) { ++ dev_err(dev, "%s, pre clk core error\n", __func__); ++ return ret; ++ } ++ ++ ret = clk_prepare_enable(clks->lpcg_apb); ++ if (ret < 0) { ++ dev_err(dev, "%s, pre clk apb error\n", __func__); ++ return ret; ++ } ++ ret = clk_prepare_enable(clks->lpcg_lis); ++ if (ret < 0) { ++ dev_err(dev, "%s, pre clk lis error\n", __func__); ++ return ret; ++ } ++ ret = clk_prepare_enable(clks->lpcg_msi); ++ if (ret < 0) { ++ dev_err(dev, "%s, pre clk msierror\n", __func__); ++ return ret; ++ } ++ ret = clk_prepare_enable(clks->lpcg_apb_csr); ++ if (ret < 0) { ++ dev_err(dev, "%s, pre clk apb csr error\n", __func__); ++ return ret; ++ } ++ ret = clk_prepare_enable(clks->lpcg_apb_ctrl); ++ if (ret < 0) { ++ dev_err(dev, "%s, pre clk apb ctrl error\n", __func__); ++ return ret; ++ } ++ ret = clk_prepare_enable(clks->lpcg_i2s); ++ if (ret < 0) { ++ dev_err(dev, "%s, pre clk i2s error\n", __func__); ++ return ret; ++ } ++ ret = clk_prepare_enable(clks->clk_i2s_bypass); ++ if (ret < 0) { ++ dev_err(dev, "%s, pre clk i2s bypass error\n", __func__); ++ return ret; ++ } ++ return ret; ++} ++ ++static void imx8qm_ipg_clk_set_rate(struct imx_mhdp_device *imx_mhdp) ++{ ++ struct imx_hdp_clks *clks = &imx_mhdp->clks; ++ ++ /* ipg/core clock */ ++ clk_set_rate(clks->dig_pll, PLL_800MHZ); ++ clk_set_rate(clks->clk_core, PLL_800MHZ/4); ++ clk_set_rate(clks->clk_ipg, PLL_800MHZ/8); ++} ++ ++static void imx8qm_detach_pm_domains(struct imx_mhdp_device *imx_mhdp) ++{ ++ if (imx_mhdp->pd_pll1_link && !IS_ERR(imx_mhdp->pd_pll1_link)) ++ device_link_del(imx_mhdp->pd_pll1_link); ++ if (imx_mhdp->pd_pll1_dev && !IS_ERR(imx_mhdp->pd_pll1_dev)) ++ dev_pm_domain_detach(imx_mhdp->pd_pll1_dev, true); ++ ++ if (imx_mhdp->pd_pll0_link && !IS_ERR(imx_mhdp->pd_pll0_link)) ++ device_link_del(imx_mhdp->pd_pll0_link); ++ if (imx_mhdp->pd_pll0_dev && !IS_ERR(imx_mhdp->pd_pll0_dev)) ++ dev_pm_domain_detach(imx_mhdp->pd_pll0_dev, true); ++ ++ if (imx_mhdp->pd_mhdp_link && !IS_ERR(imx_mhdp->pd_mhdp_link)) ++ device_link_del(imx_mhdp->pd_mhdp_link); ++ if (imx_mhdp->pd_mhdp_dev && !IS_ERR(imx_mhdp->pd_mhdp_dev)) ++ dev_pm_domain_detach(imx_mhdp->pd_mhdp_dev, true); ++ ++ imx_mhdp->pd_mhdp_dev = NULL; ++ imx_mhdp->pd_mhdp_link = NULL; ++ imx_mhdp->pd_pll0_dev = NULL; ++ imx_mhdp->pd_pll0_link = NULL; ++ imx_mhdp->pd_pll1_dev = NULL; ++ imx_mhdp->pd_pll1_link = NULL; ++} ++ ++static int imx8qm_attach_pm_domains(struct imx_mhdp_device *imx_mhdp) ++{ ++ struct device *dev = imx_mhdp->mhdp.dev; ++ u32 flags = DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE; ++ int ret = 0; ++ ++ imx_mhdp->pd_mhdp_dev = dev_pm_domain_attach_by_name(dev, "hdmi"); ++ if (IS_ERR(imx_mhdp->pd_mhdp_dev)) { ++ ret = PTR_ERR(imx_mhdp->pd_mhdp_dev); ++ dev_err(dev, "Failed to attach dc pd dev: %d\n", ret); ++ goto fail; ++ } ++ imx_mhdp->pd_mhdp_link = device_link_add(dev, imx_mhdp->pd_mhdp_dev, flags); ++ if (IS_ERR(imx_mhdp->pd_mhdp_link)) { ++ ret = PTR_ERR(imx_mhdp->pd_mhdp_link); ++ dev_err(dev, "Failed to add device link to dc pd dev: %d\n", ++ ret); ++ goto fail; ++ } ++ ++ imx_mhdp->pd_pll0_dev = dev_pm_domain_attach_by_name(dev, "pll0"); ++ if (IS_ERR(imx_mhdp->pd_pll0_dev)) { ++ ret = PTR_ERR(imx_mhdp->pd_pll0_dev); ++ dev_err(dev, "Failed to attach pll0 pd dev: %d\n", ret); ++ goto fail; ++ } ++ imx_mhdp->pd_pll0_link = device_link_add(dev, imx_mhdp->pd_pll0_dev, flags); ++ if (IS_ERR(imx_mhdp->pd_pll0_link)) { ++ ret = PTR_ERR(imx_mhdp->pd_pll0_link); ++ dev_err(dev, "Failed to add device link to pll0 pd dev: %d\n", ++ ret); ++ goto fail; ++ } ++ ++ imx_mhdp->pd_pll1_dev = dev_pm_domain_attach_by_name(dev, "pll1"); ++ if (IS_ERR(imx_mhdp->pd_pll1_dev)) { ++ ret = PTR_ERR(imx_mhdp->pd_pll1_dev); ++ dev_err(dev, "Failed to attach pll0 pd dev: %d\n", ret); ++ goto fail; ++ } ++ imx_mhdp->pd_pll1_link = device_link_add(dev, imx_mhdp->pd_pll1_dev, flags); ++ if (IS_ERR(imx_mhdp->pd_pll1_link)) { ++ ret = PTR_ERR(imx_mhdp->pd_pll1_link); ++ dev_err(dev, "Failed to add device link to pll1 pd dev: %d\n", ++ ret); ++ goto fail; ++ } ++fail: ++ imx8qm_detach_pm_domains(imx_mhdp); ++ return ret; ++} ++ ++int cdns_mhdp_power_on_imx8qm(struct cdns_mhdp_device *mhdp) ++{ ++ struct imx_mhdp_device *imx_mhdp = ++ container_of(mhdp, struct imx_mhdp_device, mhdp); ++ /* Power on PM Domains */ ++ ++ imx8qm_attach_pm_domains(imx_mhdp); ++ ++ /* clock init and rate set */ ++ imx8qm_clocks_init(imx_mhdp); ++ ++ imx8qm_ipg_clk_set_rate(imx_mhdp); ++ ++ /* Init pixel clock with 148.5MHz before FW init */ ++ imx8qm_pixel_clk_set_rate(imx_mhdp, 148500000); ++ ++ imx8qm_ipg_clk_enable(imx_mhdp); ++ ++ imx8qm_clk_mux(imx_mhdp->mhdp.plat_data->is_dp); ++ ++ imx8qm_pixel_clk_enable(imx_mhdp); ++ ++ imx8qm_phy_reset(1); ++ ++ return 0; ++} ++ ++void cdns_mhdp_plat_init_imx8qm(struct cdns_mhdp_device *mhdp) ++{ ++ struct imx_mhdp_device *imx_mhdp = ++ container_of(mhdp, struct imx_mhdp_device, mhdp); ++ bool dual_mode = imx8qm_video_dual_mode(&imx_mhdp->mhdp); ++ ++ imx8qm_pixel_link_sync_disable(dual_mode); ++ imx8qm_pixel_link_invalid(dual_mode); ++} ++ ++void cdns_mhdp_plat_deinit_imx8qm(struct cdns_mhdp_device *mhdp) ++{ ++ struct imx_mhdp_device *imx_mhdp = ++ container_of(mhdp, struct imx_mhdp_device, mhdp); ++ bool dual_mode = imx8qm_video_dual_mode(&imx_mhdp->mhdp); ++ ++ imx8qm_pixel_link_valid(dual_mode); ++ imx8qm_pixel_link_sync_enable(dual_mode); ++} ++ ++void cdns_mhdp_pclk_rate_imx8qm(struct cdns_mhdp_device *mhdp) ++{ ++ struct imx_mhdp_device *imx_mhdp = ++ container_of(mhdp, struct imx_mhdp_device, mhdp); ++ ++ /* set pixel clock before video mode setup */ ++ imx8qm_pixel_clk_disable(imx_mhdp); ++ ++ imx8qm_pixel_clk_set_rate(imx_mhdp, imx_mhdp->mhdp.mode.clock * 1000); ++ ++ imx8qm_pixel_clk_enable(imx_mhdp); ++ ++ /* Config pixel link mux */ ++ imx8qm_pixel_link_mux(imx_mhdp); ++} ++ ++int cdns_mhdp_firmware_write_section(struct imx_mhdp_device *imx_mhdp, ++ const u8 *data, int size, int addr) ++{ ++ int i; ++ ++ for (i = 0; i < size; i += 4) { ++ u32 val = (unsigned int)data[i] << 0 | ++ (unsigned int)data[i + 1] << 8 | ++ (unsigned int)data[i + 2] << 16 | ++ (unsigned int)data[i + 3] << 24; ++ cdns_mhdp_bus_write(val, &imx_mhdp->mhdp, addr + i); ++ } ++ ++ return 0; ++} ++ ++static void cdns_mhdp_firmware_load_cont(const struct firmware *fw, void *context) ++{ ++ struct imx_mhdp_device *imx_mhdp = context; ++ ++ imx_mhdp->fw = fw; ++} ++ ++static int cdns_mhdp_firmware_load(struct imx_mhdp_device *imx_mhdp) ++{ ++ const u8 *iram; ++ const u8 *dram; ++ u32 rate; ++ int ret; ++ ++ /* configure HDMI/DP core clock */ ++ rate = clk_get_rate(imx_mhdp->clks.clk_core); ++ if (imx_mhdp->mhdp.is_ls1028a) ++ rate = rate / 4; ++ ++ cdns_mhdp_set_fw_clk(&imx_mhdp->mhdp, rate); ++ ++ /* skip fw loading if none is specified */ ++ if (!imx_mhdp->firmware_name) ++ goto out; ++ ++ if (!imx_mhdp->fw) { ++ ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOHOTPLUG, ++ imx_mhdp->firmware_name, ++ imx_mhdp->mhdp.dev, GFP_KERNEL, ++ imx_mhdp, ++ cdns_mhdp_firmware_load_cont); ++ if (ret < 0) { ++ DRM_ERROR("failed to load firmware\n"); ++ return -ENOENT; ++ } ++ } else { ++ iram = imx_mhdp->fw->data + FW_IRAM_OFFSET; ++ dram = iram + FW_IRAM_SIZE; ++ ++ cdns_mhdp_firmware_write_section(imx_mhdp, iram, FW_IRAM_SIZE, ADDR_IMEM); ++ cdns_mhdp_firmware_write_section(imx_mhdp, dram, FW_DRAM_SIZE, ADDR_DMEM); ++ } ++ ++out: ++ /* un-reset ucpu */ ++ cdns_mhdp_bus_write(0, &imx_mhdp->mhdp, APB_CTRL); ++ DRM_INFO("Started firmware!\n"); ++ ++ return 0; ++} ++ ++int cdns_mhdp_firmware_init_imx8qm(struct cdns_mhdp_device *mhdp) ++{ ++ struct imx_mhdp_device *imx_mhdp = ++ container_of(mhdp, struct imx_mhdp_device, mhdp); ++ int ret; ++ ++ /* load firmware */ ++ ret = cdns_mhdp_firmware_load(imx_mhdp); ++ if (ret) ++ return ret; ++ ++ ret = cdns_mhdp_check_alive(&imx_mhdp->mhdp); ++ if (ret == false) { ++ DRM_ERROR("NO HDMI FW running\n"); ++ return -ENXIO; ++ } ++ ++ /* turn on IP activity */ ++ cdns_mhdp_set_firmware_active(&imx_mhdp->mhdp, 1); ++ ++ DRM_INFO("HDP FW Version - ver %d verlib %d\n", ++ cdns_mhdp_bus_read(mhdp, VER_L) + (cdns_mhdp_bus_read(mhdp, VER_H) << 8), ++ cdns_mhdp_bus_read(mhdp, VER_LIB_H_ADDR) + (cdns_mhdp_bus_read(mhdp, VER_LIB_H_ADDR) << 8)); ++ ++ return 0; ++} ++ ++int cdns_mhdp_suspend_imx8qm(struct cdns_mhdp_device *mhdp) ++{ ++ struct imx_mhdp_device *imx_mhdp = ++ container_of(mhdp, struct imx_mhdp_device, mhdp); ++ ++ imx8qm_pixel_clk_disable(imx_mhdp); ++ ++ return 0; ++} ++ ++int cdns_mhdp_resume_imx8qm(struct cdns_mhdp_device *mhdp) ++{ ++ struct imx_mhdp_device *imx_mhdp = ++ container_of(mhdp, struct imx_mhdp_device, mhdp); ++ ++ imx8qm_pixel_clk_enable(imx_mhdp); ++ ++ return cdns_mhdp_firmware_init_imx8qm(mhdp); ++} +diff --git a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imxdrv.c b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imxdrv.c +new file mode 100644 +index 000000000000..3acbdf575ee2 +--- /dev/null ++++ b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imxdrv.c +@@ -0,0 +1,257 @@ ++/* ++ * copyright (c) 2019 nxp semiconductor, inc. ++ * ++ * this program is free software; you can redistribute it and/or modify ++ * it under the terms of the gnu general public license version 2 as ++ * published by the free software foundation. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "cdns-mhdp-imx.h" ++#include "cdns-mhdp-phy.h" ++#include "../imx-drm.h" ++ ++static void cdns_mhdp_imx_encoder_disable(struct drm_encoder *encoder) ++{ ++ struct drm_bridge *bridge = drm_bridge_chain_get_first_bridge(encoder); ++ struct cdns_mhdp_device *mhdp = bridge->driver_private; ++ ++ cdns_mhdp_plat_call(mhdp, plat_init); ++} ++ ++static void cdns_mhdp_imx_encoder_enable(struct drm_encoder *encoder) ++{ ++ struct drm_bridge *bridge = drm_bridge_chain_get_first_bridge(encoder); ++ struct cdns_mhdp_device *mhdp = bridge->driver_private; ++ ++ cdns_mhdp_plat_call(mhdp, plat_deinit); ++} ++ ++static int cdns_mhdp_imx_encoder_atomic_check(struct drm_encoder *encoder, ++ struct drm_crtc_state *crtc_state, ++ struct drm_connector_state *conn_state) ++{ ++ struct imx_crtc_state *imx_crtc_state = to_imx_crtc_state(crtc_state); ++ struct drm_bridge *bridge = drm_bridge_chain_get_first_bridge(encoder); ++ struct cdns_mhdp_device *mhdp = bridge->driver_private; ++ ++ if (mhdp->plat_data->video_format != 0) ++ imx_crtc_state->bus_format = mhdp->plat_data->video_format; ++ ++ if (mhdp->force_mode_set) ++ crtc_state->mode_changed = true; ++ ++ return 0; ++} ++ ++static const struct drm_encoder_helper_funcs cdns_mhdp_imx_encoder_helper_funcs = { ++ .enable = cdns_mhdp_imx_encoder_enable, ++ .disable = cdns_mhdp_imx_encoder_disable, ++ .atomic_check = cdns_mhdp_imx_encoder_atomic_check, ++}; ++ ++static const struct drm_encoder_funcs cdns_mhdp_imx_encoder_funcs = { ++ .destroy = drm_encoder_cleanup, ++}; ++ ++static struct cdns_plat_data imx8mq_hdmi_drv_data = { ++ .plat_name = "imx8mq-hdmi", ++ .bind = cdns_hdmi_bind, ++ .unbind = cdns_hdmi_unbind, ++ .phy_set = cdns_hdmi_phy_set_imx8mq, ++ .phy_video_valid = cdns_hdmi_phy_video_valid_imx8mq, ++ .bus_type = BUS_TYPE_NORMAL_APB, ++}; ++ ++static struct cdns_plat_data imx8mq_dp_drv_data = { ++ .plat_name = "imx8mq-dp", ++ .bind = cdns_dp_bind, ++ .unbind = cdns_dp_unbind, ++ .phy_set = cdns_dp_phy_set_imx8mq, ++ .bus_type = BUS_TYPE_NORMAL_APB, ++}; ++ ++static struct cdns_plat_data imx8qm_hdmi_drv_data = { ++ .plat_name = "imx8qm-hdmi", ++ .bind = cdns_hdmi_bind, ++ .unbind = cdns_hdmi_unbind, ++ .phy_set = cdns_hdmi_phy_set_imx8qm, ++ .phy_video_valid = cdns_hdmi_phy_video_valid_imx8qm, ++ .power_on = cdns_mhdp_power_on_imx8qm, ++ .firmware_init = cdns_mhdp_firmware_init_imx8qm, ++ .resume = cdns_mhdp_resume_imx8qm, ++ .suspend = cdns_mhdp_suspend_imx8qm, ++ .pclk_rate = cdns_mhdp_pclk_rate_imx8qm, ++ .plat_init = cdns_mhdp_plat_init_imx8qm, ++ .plat_deinit = cdns_mhdp_plat_deinit_imx8qm, ++ .bus_type = BUS_TYPE_LOW4K_APB, ++ .video_format = MEDIA_BUS_FMT_RGB101010_1X30, ++}; ++ ++static struct cdns_plat_data imx8qm_dp_drv_data = { ++ .plat_name = "imx8qm-dp", ++ .bind = cdns_dp_bind, ++ .unbind = cdns_dp_unbind, ++ .phy_set = cdns_dp_phy_set_imx8qm, ++ .power_on = cdns_mhdp_power_on_imx8qm, ++ .firmware_init = cdns_mhdp_firmware_init_imx8qm, ++ .pclk_rate = cdns_mhdp_pclk_rate_imx8qm, ++ .plat_init = cdns_mhdp_plat_init_imx8qm, ++ .plat_deinit = cdns_mhdp_plat_deinit_imx8qm, ++ .bus_type = BUS_TYPE_LOW4K_APB, ++ .video_format = MEDIA_BUS_FMT_RGB101010_1X30, ++ .is_dp = true, ++}; ++ ++static struct cdns_plat_data ls1028a_dp_drv_data = { ++ .bind = cdns_dp_bind, ++ .unbind = cdns_dp_unbind, ++ .phy_set = cdns_dp_phy_set_imx8mq, ++ .power_on = cdns_mhdp_power_on_ls1028a, ++ .firmware_init = cdns_mhdp_firmware_init_imx8qm, ++ .pclk_rate = cdns_mhdp_pclk_rate_ls1028a, ++ .bus_type = BUS_TYPE_NORMAL_APB, ++}; ++ ++static const struct of_device_id cdns_mhdp_imx_dt_ids[] = { ++ { .compatible = "cdn,imx8mq-hdmi", ++ .data = &imx8mq_hdmi_drv_data ++ }, ++ { .compatible = "cdn,imx8mq-dp", ++ .data = &imx8mq_dp_drv_data ++ }, ++ { .compatible = "cdn,imx8qm-hdmi", ++ .data = &imx8qm_hdmi_drv_data ++ }, ++ { .compatible = "cdn,imx8qm-dp", ++ .data = &imx8qm_dp_drv_data ++ }, ++ { .compatible = "cdn,ls1028a-dp", ++ .data = &ls1028a_dp_drv_data ++ }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, cdns_mhdp_imx_dt_ids); ++ ++static int cdns_mhdp_imx_bind(struct device *dev, struct device *master, ++ void *data) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ const struct cdns_plat_data *plat_data; ++ const struct of_device_id *match; ++ struct drm_device *drm = data; ++ struct drm_encoder *encoder; ++ struct imx_mhdp_device *imx_mhdp; ++ int ret; ++ ++ if (!pdev->dev.of_node) ++ return -ENODEV; ++ ++ imx_mhdp = devm_kzalloc(&pdev->dev, sizeof(*imx_mhdp), GFP_KERNEL); ++ if (!imx_mhdp) ++ return -ENOMEM; ++ ++ match = of_match_node(cdns_mhdp_imx_dt_ids, pdev->dev.of_node); ++ plat_data = match->data; ++ encoder = &imx_mhdp->encoder; ++ ++ encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node); ++ ++ ret = of_property_read_string(pdev->dev.of_node, "firmware-name", ++ &imx_mhdp->firmware_name); ++ /* ++ * If we failed to find the CRTC(s) which this encoder is ++ * supposed to be connected to, it's because the CRTC has ++ * not been registered yet. Defer probing, and hope that ++ * the required CRTC is added later. ++ */ ++ if (encoder->possible_crtcs == 0) ++ return -EPROBE_DEFER; ++ ++ drm_encoder_helper_add(encoder, &cdns_mhdp_imx_encoder_helper_funcs); ++ drm_encoder_init(drm, encoder, &cdns_mhdp_imx_encoder_funcs, ++ DRM_MODE_ENCODER_TMDS, NULL); ++ ++ ++ imx_mhdp->mhdp.plat_data = plat_data; ++ imx_mhdp->mhdp.dev = dev; ++ imx_mhdp->mhdp.bus_type = plat_data->bus_type; ++ ret = plat_data->bind(pdev, encoder, &imx_mhdp->mhdp); ++ /* ++ * If cdns_mhdp_bind() fails we'll never call cdns_mhdp_unbind(), ++ * which would have called the encoder cleanup. Do it manually. ++ */ ++ if (ret < 0) ++ drm_encoder_cleanup(encoder); ++ ++ return ret; ++} ++ ++static void cdns_mhdp_imx_unbind(struct device *dev, struct device *master, ++ void *data) ++{ ++ struct imx_mhdp_device *imx_mhdp = dev_get_drvdata(dev); ++ ++ imx_mhdp->mhdp.plat_data->unbind(dev); ++} ++ ++static const struct component_ops cdns_mhdp_imx_ops = { ++ .bind = cdns_mhdp_imx_bind, ++ .unbind = cdns_mhdp_imx_unbind, ++}; ++ ++static int cdns_mhdp_imx_suspend(struct device *dev) ++{ ++ struct imx_mhdp_device *imx_mhdp = dev_get_drvdata(dev); ++ ++ cdns_mhdp_plat_call(&imx_mhdp->mhdp, suspend); ++ ++ return 0; ++} ++ ++static int cdns_mhdp_imx_resume(struct device *dev) ++{ ++ struct imx_mhdp_device *imx_mhdp = dev_get_drvdata(dev); ++ ++ cdns_mhdp_plat_call(&imx_mhdp->mhdp, resume); ++ ++ return 0; ++} ++ ++static int cdns_mhdp_imx_probe(struct platform_device *pdev) ++{ ++ return component_add(&pdev->dev, &cdns_mhdp_imx_ops); ++} ++ ++static int cdns_mhdp_imx_remove(struct platform_device *pdev) ++{ ++ component_del(&pdev->dev, &cdns_mhdp_imx_ops); ++ ++ return 0; ++} ++ ++static const struct dev_pm_ops cdns_mhdp_imx_pm_ops = { ++ SET_LATE_SYSTEM_SLEEP_PM_OPS(cdns_mhdp_imx_suspend, cdns_mhdp_imx_resume) ++}; ++ ++static struct platform_driver cdns_mhdp_imx_platform_driver = { ++ .probe = cdns_mhdp_imx_probe, ++ .remove = cdns_mhdp_imx_remove, ++ .driver = { ++ .name = "cdns-mhdp-imx", ++ .of_match_table = cdns_mhdp_imx_dt_ids, ++ .pm = &cdns_mhdp_imx_pm_ops, ++ }, ++}; ++ ++module_platform_driver(cdns_mhdp_imx_platform_driver); ++ ++MODULE_AUTHOR("Sandor YU "); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform:cdnhdmi-imx"); +diff --git a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-ls1028a.c b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-ls1028a.c +new file mode 100644 +index 000000000000..4cc71301f5fe +--- /dev/null ++++ b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-ls1028a.c +@@ -0,0 +1,110 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright 2019 NXP ++ * ++ */ ++#include ++#include ++#include ++#include ++#include ++ ++#include "cdns-mhdp-imx.h" ++ ++static const struct of_device_id scfg_device_ids[] = { ++ { .compatible = "fsl,ls1028a-scfg", }, ++ {} ++}; ++ ++static void ls1028a_phy_reset(u8 reset) ++{ ++ struct device_node *scfg_node; ++ void __iomem *scfg_base = NULL; ++ ++ scfg_node = of_find_matching_node(NULL, scfg_device_ids); ++ if (scfg_node) ++ scfg_base = of_iomap(scfg_node, 0); ++ ++ iowrite32(reset, scfg_base + 0x230); ++} ++ ++int ls1028a_clocks_init(struct imx_mhdp_device *imx_mhdp) ++{ ++ struct device *dev = imx_mhdp->mhdp.dev; ++ struct imx_hdp_clks *clks = &imx_mhdp->clks; ++ ++ clks->clk_core = devm_clk_get(dev, "clk_core"); ++ if (IS_ERR(clks->clk_core)) { ++ dev_warn(dev, "failed to get hdp core clk\n"); ++ return PTR_ERR(clks->clk_core); ++ } ++ ++ clks->clk_pxl = devm_clk_get(dev, "clk_pxl"); ++ if (IS_ERR(clks->clk_pxl)) { ++ dev_warn(dev, "failed to get pxl clk\n"); ++ return PTR_ERR(clks->clk_pxl); ++ } ++ ++ return true; ++} ++ ++static int ls1028a_pixel_clk_enable(struct imx_mhdp_device *imx_mhdp) ++{ ++ struct imx_hdp_clks *clks = &imx_mhdp->clks; ++ struct device *dev = imx_mhdp->mhdp.dev; ++ int ret; ++ ++ ret = clk_prepare_enable(clks->clk_pxl); ++ if (ret < 0) { ++ dev_err(dev, "%s, pre clk pxl error\n", __func__); ++ return ret; ++ } ++ ++ return ret; ++} ++ ++static void ls1028a_pixel_clk_disable(struct imx_mhdp_device *imx_mhdp) ++{ ++ struct imx_hdp_clks *clks = &imx_mhdp->clks; ++ ++ clk_disable_unprepare(clks->clk_pxl); ++} ++ ++static void ls1028a_pixel_clk_set_rate(struct imx_mhdp_device *imx_mhdp, ++ u32 pclock) ++{ ++ struct imx_hdp_clks *clks = &imx_mhdp->clks; ++ ++ clk_set_rate(clks->clk_pxl, pclock); ++} ++ ++int cdns_mhdp_power_on_ls1028a(struct cdns_mhdp_device *mhdp) ++{ ++ struct imx_mhdp_device *imx_mhdp = container_of ++ (mhdp, struct imx_mhdp_device, mhdp); ++ ++ /* clock init and rate set */ ++ ls1028a_clocks_init(imx_mhdp); ++ ++ ls1028a_pixel_clk_enable(imx_mhdp); ++ ++ /* Init pixel clock with 148.5MHz before FW init */ ++ ls1028a_pixel_clk_set_rate(imx_mhdp, 148500000); ++ ++ ls1028a_phy_reset(1); ++ ++ return 0; ++} ++ ++void cdns_mhdp_pclk_rate_ls1028a(struct cdns_mhdp_device *mhdp) ++{ ++ struct imx_mhdp_device *imx_mhdp = container_of ++ (mhdp, struct imx_mhdp_device, mhdp); ++ ++ /* set pixel clock before video mode setup */ ++ ls1028a_pixel_clk_disable(imx_mhdp); ++ ++ ls1028a_pixel_clk_set_rate(imx_mhdp, imx_mhdp->mhdp.mode.clock * 1000); ++ ++ ls1028a_pixel_clk_enable(imx_mhdp); ++} +diff --git a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-phy.h b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-phy.h +new file mode 100644 +index 000000000000..5682b9fbc90f +--- /dev/null ++++ b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-phy.h +@@ -0,0 +1,155 @@ ++/* ++ * Copyright (C) 2019 NXP Semiconductor, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ */ ++ ++#ifndef _CDN_DP_PHY_H ++#define _CDN_DP_PHY_H ++ ++#include ++ ++#define CMN_SSM_BIAS_TMR 0x0022 ++#define CMN_PLLSM0_PLLEN_TMR 0x0029 ++#define CMN_PLLSM0_PLLPRE_TMR 0x002A ++#define CMN_PLLSM0_PLLVREF_TMR 0x002B ++#define CMN_PLLSM0_PLLLOCK_TMR 0x002C ++#define CMN_PLLSM0_USER_DEF_CTRL 0x002F ++#define CMN_PSM_CLK_CTRL 0x0061 ++#define CMN_CDIAG_REFCLK_CTRL 0x0062 ++#define CMN_PLL0_VCOCAL_START 0x0081 ++#define CMN_PLL0_VCOCAL_INIT_TMR 0x0084 ++#define CMN_PLL0_VCOCAL_ITER_TMR 0x0085 ++#define CMN_PLL0_INTDIV 0x0094 ++#define CMN_PLL0_FRACDIV 0x0095 ++#define CMN_PLL0_HIGH_THR 0x0096 ++#define CMN_PLL0_DSM_DIAG 0x0097 ++#define CMN_PLL0_SS_CTRL1 0x0098 ++#define CMN_PLL0_SS_CTRL2 0x0099 ++#define CMN_ICAL_INIT_TMR 0x00C4 ++#define CMN_ICAL_ITER_TMR 0x00C5 ++#define CMN_RXCAL_INIT_TMR 0x00D4 ++#define CMN_RXCAL_ITER_TMR 0x00D5 ++#define CMN_TXPUCAL_CTRL 0x00E0 ++#define CMN_TXPUCAL_INIT_TMR 0x00E4 ++#define CMN_TXPUCAL_ITER_TMR 0x00E5 ++#define CMN_TXPDCAL_CTRL 0x00F0 ++#define CMN_TXPDCAL_INIT_TMR 0x00F4 ++#define CMN_TXPDCAL_ITER_TMR 0x00F5 ++#define CMN_ICAL_ADJ_INIT_TMR 0x0102 ++#define CMN_ICAL_ADJ_ITER_TMR 0x0103 ++#define CMN_RX_ADJ_INIT_TMR 0x0106 ++#define CMN_RX_ADJ_ITER_TMR 0x0107 ++#define CMN_TXPU_ADJ_CTRL 0x0108 ++#define CMN_TXPU_ADJ_INIT_TMR 0x010A ++#define CMN_TXPU_ADJ_ITER_TMR 0x010B ++#define CMN_TXPD_ADJ_CTRL 0x010c ++#define CMN_TXPD_ADJ_INIT_TMR 0x010E ++#define CMN_TXPD_ADJ_ITER_TMR 0x010F ++#define CMN_DIAG_PLL0_FBH_OVRD 0x01C0 ++#define CMN_DIAG_PLL0_FBL_OVRD 0x01C1 ++#define CMN_DIAG_PLL0_OVRD 0x01C2 ++#define CMN_DIAG_PLL0_TEST_MODE 0x01C4 ++#define CMN_DIAG_PLL0_V2I_TUNE 0x01C5 ++#define CMN_DIAG_PLL0_CP_TUNE 0x01C6 ++#define CMN_DIAG_PLL0_LF_PROG 0x01C7 ++#define CMN_DIAG_PLL0_PTATIS_TUNE1 0x01C8 ++#define CMN_DIAG_PLL0_PTATIS_TUNE2 0x01C9 ++#define CMN_DIAG_PLL0_INCLK_CTRL 0x01CA ++#define CMN_DIAG_PLL0_PXL_DIVH 0x01CB ++#define CMN_DIAG_PLL0_PXL_DIVL 0x01CC ++#define CMN_DIAG_HSCLK_SEL 0x01E0 ++#define CMN_DIAG_PER_CAL_ADJ 0x01EC ++#define CMN_DIAG_CAL_CTRL 0x01ED ++#define CMN_DIAG_ACYA 0x01FF ++#define XCVR_PSM_RCTRL 0x4001 ++#define XCVR_PSM_CAL_TMR 0x4002 ++#define XCVR_PSM_A0IN_TMR 0x4003 ++#define TX_TXCC_CAL_SCLR_MULT_0 0x4047 ++#define TX_TXCC_CPOST_MULT_00_0 0x404C ++#define TX_TXCC_MGNFS_MULT_000_0 0x4050 ++#define XCVR_DIAG_PLLDRC_CTRL 0x40E0 ++#define XCVR_DIAG_PLLDRC_CTRL 0x40E0 ++#define XCVR_DIAG_HSCLK_SEL 0x40E1 ++#define XCVR_DIAG_BIDI_CTRL 0x40E8 ++#define XCVR_DIAG_LANE_FCM_EN_MGN_TMR 0x40F2 ++#define XCVR_DIAG_LANE_FCM_EN_MGN 0x40F2 ++#define TX_PSC_A0 0x4100 ++#define TX_PSC_A1 0x4101 ++#define TX_PSC_A2 0x4102 ++#define TX_PSC_A3 0x4103 ++#define TX_RCVDET_CTRL 0x4120 ++#define TX_RCVDET_EN_TMR 0x4122 ++#define TX_RCVDET_EN_TMR 0x4122 ++#define TX_RCVDET_ST_TMR 0x4123 ++#define TX_RCVDET_ST_TMR 0x4123 ++#define TX_BIST_CTRL 0x4140 ++#define TX_BIST_UDDWR 0x4141 ++#define TX_DIAG_TX_CTRL 0x41E0 ++#define TX_DIAG_TX_DRV 0x41E1 ++#define TX_DIAG_BGREF_PREDRV_DELAY 0x41E7 ++#define TX_DIAG_BGREF_PREDRV_DELAY 0x41E7 ++#define XCVR_PSM_RCTRL_1 0x4201 ++#define TX_TXCC_CAL_SCLR_MULT_1 0x4247 ++#define TX_TXCC_CPOST_MULT_00_1 0x424C ++#define TX_TXCC_MGNFS_MULT_000_1 0x4250 ++#define XCVR_DIAG_PLLDRC_CTRL_1 0x42E0 ++#define XCVR_DIAG_HSCLK_SEL_1 0x42E1 ++#define XCVR_DIAG_LANE_FCM_EN_MGN_TMR_1 0x42F2 ++#define TX_RCVDET_EN_TMR_1 0x4322 ++#define TX_RCVDET_ST_TMR_1 0x4323 ++#define TX_DIAG_ACYA_0 0x41FF ++#define TX_DIAG_ACYA_1 0x43FF ++#define TX_DIAG_ACYA_2 0x45FF ++#define TX_DIAG_ACYA_3 0x47FF ++#define TX_ANA_CTRL_REG_1 0x5020 ++#define TX_ANA_CTRL_REG_2 0x5021 ++#define TXDA_COEFF_CALC 0x5022 ++#define TX_DIG_CTRL_REG_1 0x5023 ++#define TX_DIG_CTRL_REG_2 0x5024 ++#define TXDA_CYA_AUXDA_CYA 0x5025 ++#define TX_ANA_CTRL_REG_3 0x5026 ++#define TX_ANA_CTRL_REG_4 0x5027 ++#define TX_ANA_CTRL_REG_5 0x5029 ++#define RX_PSC_A0 0x8000 ++#define RX_PSC_CAL 0x8006 ++#define PMA_LANE_CFG 0xC000 ++#define PIPE_CMN_CTRL1 0xC001 ++#define PIPE_CMN_CTRL2 0xC002 ++#define PIPE_COM_LOCK_CFG1 0xC003 ++#define PIPE_COM_LOCK_CFG2 0xC004 ++#define PIPE_RCV_DET_INH 0xC005 ++#define PHY_HDP_MODE_CTRL 0xC008 ++#define PHY_HDP_CLK_CTL 0xC009 ++#define STS 0xC00F ++#define PHY_ISO_CMN_CTRL 0xC010 ++#define PHY_ISO_CMN_CTRL 0xC010 ++#define PHY_HDP_TX_CTL_L0 0xC408 ++#define PHY_DP_TX_CTL 0xC408 ++#define PHY_HDP_TX_CTL_L1 0xC448 ++#define PHY_HDP_TX_CTL_L2 0xC488 ++#define PHY_HDP_TX_CTL_L3 0xC4C8 ++#define PHY_PMA_CMN_CTRL1 0xC800 ++#define PMA_CMN_CTRL1 0xC800 ++#define PHY_PMA_ISO_CMN_CTRL 0xC810 ++#define PHY_PMA_ISO_PLL_CTRL1 0xC812 ++#define PHY_PMA_ISOLATION_CTRL 0xC81F ++#define PHY_ISOLATION_CTRL 0xC81F ++#define PHY_PMA_ISO_XCVR_CTRL 0xCC11 ++#define PHY_PMA_ISO_LINK_MODE 0xCC12 ++#define PHY_PMA_ISO_PWRST_CTRL 0xCC13 ++#define PHY_PMA_ISO_TX_DATA_LO 0xCC14 ++#define PHY_PMA_ISO_TX_DATA_HI 0xCC15 ++#define PHY_PMA_ISO_RX_DATA_LO 0xCC16 ++#define PHY_PMA_ISO_RX_DATA_HI 0xCC17 ++ ++int cdns_dp_phy_set_imx8mq(struct cdns_mhdp_device *hdp); ++int cdns_dp_phy_set_imx8qm(struct cdns_mhdp_device *hdp); ++bool cdns_hdmi_phy_video_valid_imx8mq(struct cdns_mhdp_device *hdp); ++bool cdns_hdmi_phy_video_valid_imx8qm(struct cdns_mhdp_device *hdp); ++int cdns_hdmi_phy_set_imx8mq(struct cdns_mhdp_device *hdp); ++int cdns_hdmi_phy_set_imx8qm(struct cdns_mhdp_device *hdp); ++#endif /* _CDNS_MHDP_PHY_H */ +-- +2.29.2 + diff --git a/projects/NXP/devices/iMX8/patches/linux/0031-LF-1514-drm-cdns-mhdp-check-link-rate-index.patch b/projects/NXP/devices/iMX8/patches/linux/0031-LF-1514-drm-cdns-mhdp-check-link-rate-index.patch new file mode 100644 index 0000000000..180ec68d82 --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0031-LF-1514-drm-cdns-mhdp-check-link-rate-index.patch @@ -0,0 +1,43 @@ +From 8aa7d7baa5eb142261ddafc91b0ba884aa670421 Mon Sep 17 00:00:00 2001 +From: Sandor Yu +Date: Fri, 19 Jun 2020 16:17:55 +0800 +Subject: [PATCH 31/49] LF-1514: drm: cdns-mhdp: check link rate index + +Check link rate index to advoid negative array index read. +report by Coverity ID:6652950 6652949. + +Signed-off-by: Sandor Yu +Reviewed-by: Fancy Fang +--- + drivers/gpu/drm/imx/mhdp/cdns-mhdp-dp-phy.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-dp-phy.c b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-dp-phy.c +index a6d03c94d196..5c75e7d40cc0 100644 +--- a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-dp-phy.c ++++ b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-dp-phy.c +@@ -198,6 +198,10 @@ static void dp_phy_pma_cmn_pll0_24mhz(struct cdns_mhdp_device *mhdp) + + /* DP PHY PLL 24MHz configuration */ + index = link_rate_index(link_rate); ++ if (index < 0) { ++ dev_err(mhdp->dev, "wrong link rate index\n"); ++ return; ++ } + for (i = 0; i < ARRAY_SIZE(phy_pll_24m_cfg); i++) + cdns_phy_reg_write(mhdp, phy_pll_24m_cfg[i].addr, phy_pll_24m_cfg[i].val[index]); + +@@ -320,6 +324,10 @@ static void dp_phy_pma_cmn_pll0_27mhz(struct cdns_mhdp_device *mhdp) + + /* DP PHY PLL 27MHz configuration */ + index = link_rate_index(link_rate); ++ if (index < 0) { ++ dev_err(mhdp->dev, "wrong link rate index\n"); ++ return; ++ } + for (i = 0; i < ARRAY_SIZE(phy_pll_27m_cfg); i++) + cdns_phy_reg_write(mhdp, phy_pll_27m_cfg[i].addr, phy_pll_27m_cfg[i].val[index]); + +-- +2.29.2 + diff --git a/projects/NXP/devices/iMX8/patches/linux/0032-LF-1516-drm-cdns-mhdp-fix-error-check-variable-name-.patch b/projects/NXP/devices/iMX8/patches/linux/0032-LF-1516-drm-cdns-mhdp-fix-error-check-variable-name-.patch new file mode 100644 index 0000000000..f42d867842 --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0032-LF-1516-drm-cdns-mhdp-fix-error-check-variable-name-.patch @@ -0,0 +1,31 @@ +From b2ea44969c5e51a5809622384728859d7f3a2b8a Mon Sep 17 00:00:00 2001 +From: Sandor Yu +Date: Fri, 19 Jun 2020 16:25:51 +0800 +Subject: [PATCH 32/49] LF-1516: drm: cdns-mhdp: fix error check variable name + for clk_pxl_link + +fix error check variable name for clk_pxl_link. +Report by Coverity ID:6652947 + +Signed-off-by: Sandor Yu +Reviewed-by: Fancy Fang +--- + drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx8qm.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx8qm.c b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx8qm.c +index a3ba3da4b05d..2ee4e8748b77 100644 +--- a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx8qm.c ++++ b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx8qm.c +@@ -167,7 +167,7 @@ int imx8qm_clocks_init(struct imx_mhdp_device *imx_mhdp) + } + + clks->clk_pxl_link = devm_clk_get(dev, "clk_pxl_link"); +- if (IS_ERR(clks->clk_pxl_mux)) { ++ if (IS_ERR(clks->clk_pxl_link)) { + dev_warn(dev, "failed to get pxl link clk\n"); + return PTR_ERR(clks->clk_pxl_link); + } +-- +2.29.2 + diff --git a/projects/NXP/devices/iMX8/patches/linux/0033-MLK-24601-drm-imx-mhdp-DP-PHY-support-1-2-lanes-mode.patch b/projects/NXP/devices/iMX8/patches/linux/0033-MLK-24601-drm-imx-mhdp-DP-PHY-support-1-2-lanes-mode.patch new file mode 100644 index 0000000000..f3076760c9 --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0033-MLK-24601-drm-imx-mhdp-DP-PHY-support-1-2-lanes-mode.patch @@ -0,0 +1,58 @@ +From c789945d09e4c77eb30af1a8db1425cefab52080 Mon Sep 17 00:00:00 2001 +From: Sandor Yu +Date: Fri, 28 Aug 2020 10:09:12 +0800 +Subject: [PATCH 33/49] MLK-24601: drm: imx: mhdp: DP PHY support 1/2 lanes + mode + +All four lanes should be configurated for 1/2/4 lanes modes in driver. +The DP FW will power down unused PHY lanes after negotiation. + +Signed-off-by: Sandor Yu +Reviewed-by: Robby Cai +--- + drivers/gpu/drm/imx/mhdp/cdns-mhdp-dp-phy.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-dp-phy.c b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-dp-phy.c +index 5c75e7d40cc0..3d17840b0941 100644 +--- a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-dp-phy.c ++++ b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-dp-phy.c +@@ -137,7 +137,7 @@ static void dp_aux_cfg(struct cdns_mhdp_device *mhdp) + static void dp_phy_pma_cmn_cfg_24mhz(struct cdns_mhdp_device *mhdp) + { + int k; +- u32 num_lanes = mhdp->dp.num_lanes; ++ u32 num_lanes = 4; + u16 val; + + val = cdns_phy_reg_read(mhdp, PHY_PMA_CMN_CTRL1); +@@ -157,7 +157,7 @@ static void dp_phy_pma_cmn_cfg_24mhz(struct cdns_mhdp_device *mhdp) + /* Valid for 24 MHz only */ + static void dp_phy_pma_cmn_pll0_24mhz(struct cdns_mhdp_device *mhdp) + { +- u32 num_lanes = mhdp->dp.num_lanes; ++ u32 num_lanes = 4; + u32 link_rate = mhdp->dp.rate; + u16 val; + int index, i, k; +@@ -228,7 +228,7 @@ static void dp_phy_pma_cmn_pll0_24mhz(struct cdns_mhdp_device *mhdp) + /* PMA common configuration for 27MHz */ + static void dp_phy_pma_cmn_cfg_27mhz(struct cdns_mhdp_device *mhdp) + { +- u32 num_lanes = mhdp->dp.num_lanes; ++ u32 num_lanes = 4; + u16 val; + int k; + +@@ -279,7 +279,7 @@ static void dp_phy_pma_cmn_cfg_27mhz(struct cdns_mhdp_device *mhdp) + + static void dp_phy_pma_cmn_pll0_27mhz(struct cdns_mhdp_device *mhdp) + { +- u32 num_lanes = mhdp->dp.num_lanes; ++ u32 num_lanes = 4; + u32 link_rate = mhdp->dp.rate; + u16 val; + int index, i, k; +-- +2.29.2 + diff --git a/projects/NXP/devices/iMX8/patches/linux/0034-MLK-24519-2-gpu-imx-Increase-maximum-single-pipe-wid.patch b/projects/NXP/devices/iMX8/patches/linux/0034-MLK-24519-2-gpu-imx-Increase-maximum-single-pipe-wid.patch new file mode 100644 index 0000000000..799824c90b --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0034-MLK-24519-2-gpu-imx-Increase-maximum-single-pipe-wid.patch @@ -0,0 +1,30 @@ +From 7772a57acd0e05353caead7eb7d064e36bcb92e6 Mon Sep 17 00:00:00 2001 +From: Sandor Yu +Date: Sun, 20 Sep 2020 19:32:28 +0800 +Subject: [PATCH 34/49] MLK-24519-2 gpu: imx: Increase maximum single pipe + width to 2560 + +This patch increase the DPU single pipe maximum from 1920 to 2560 for HDMI/DP. + +Signed-off-by: Oliver F. Brown +Reviewed-by: Liu Ying +--- + drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx8qm.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx8qm.c b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx8qm.c +index 2ee4e8748b77..cda4d245bab8 100644 +--- a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx8qm.c ++++ b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx8qm.c +@@ -22,7 +22,7 @@ + #define PLL_800MHZ (800000000) + + #define HDP_DUAL_MODE_MIN_PCLK_RATE 300000 /* KHz */ +-#define HDP_SINGLE_MODE_MAX_WIDTH 1920 ++#define HDP_SINGLE_MODE_MAX_WIDTH 2560 + + #define CSR_PIXEL_LINK_MUX_CTL 0x00 + #define CSR_PIXEL_LINK_MUX_VCP_OFFSET 5 +-- +2.29.2 + diff --git a/projects/NXP/devices/iMX8/patches/linux/0035-MLK-24072-drm-imx8-correct-mhdp-files-copyright.patch b/projects/NXP/devices/iMX8/patches/linux/0035-MLK-24072-drm-imx8-correct-mhdp-files-copyright.patch new file mode 100644 index 0000000000..987690f888 --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0035-MLK-24072-drm-imx8-correct-mhdp-files-copyright.patch @@ -0,0 +1,39 @@ +From 60077991d60b1ba96e52d5a6568ae65ae7143ee2 Mon Sep 17 00:00:00 2001 +From: Sandor Yu +Date: Wed, 20 May 2020 10:56:53 +0800 +Subject: [PATCH 35/49] MLK-24072: drm: imx8: correct mhdp files copyright + +Correct mhdp files copyright. + +Signed-off-by: Sandor Yu +Reviewed-by: Robby Cai +--- + drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx8qm.c | 2 +- + drivers/gpu/drm/imx/mhdp/cdns-mhdp-imxdrv.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx8qm.c b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx8qm.c +index cda4d245bab8..38f9defa42f8 100644 +--- a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx8qm.c ++++ b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx8qm.c +@@ -1,5 +1,5 @@ + /* +- * copyright (c) 2019 nxp semiconductor, inc. ++ * Copyright (c) 2019 NXP semiconductor, inc. + * + * this program is free software; you can redistribute it and/or modify + * it under the terms of the gnu general public license version 2 as +diff --git a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imxdrv.c b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imxdrv.c +index 3acbdf575ee2..cc429fe48abd 100644 +--- a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imxdrv.c ++++ b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imxdrv.c +@@ -1,5 +1,5 @@ + /* +- * copyright (c) 2019 nxp semiconductor, inc. ++ * Copyright (c) 2019 NXP semiconductor, inc. + * + * this program is free software; you can redistribute it and/or modify + * it under the terms of the gnu general public license version 2 as +-- +2.29.2 + diff --git a/projects/NXP/devices/iMX8/patches/linux/0036-LF-2744-drm-cdns-reset-force_mode_set-flag-in-atomic.patch b/projects/NXP/devices/iMX8/patches/linux/0036-LF-2744-drm-cdns-reset-force_mode_set-flag-in-atomic.patch new file mode 100644 index 0000000000..8586df89c8 --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0036-LF-2744-drm-cdns-reset-force_mode_set-flag-in-atomic.patch @@ -0,0 +1,42 @@ +From 93502b984119af556f8a204bf80a62bc1c21fbfd Mon Sep 17 00:00:00 2001 +From: Sandor Yu +Date: Tue, 17 Nov 2020 15:47:36 +0800 +Subject: [PATCH 36/49] LF-2744: drm: cdns: reset force_mode_set flag in + atomic_check + +Reset force_mode_set flag in atomic_check function +to avoid set mode_changed flag multi times when cable plugin. + +Signed-off-by: Sandor Yu +Reviewed-by: Robby Cai +--- + drivers/gpu/drm/imx/mhdp/cdns-mhdp-imxdrv.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +diff --git a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imxdrv.c b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imxdrv.c +index cc429fe48abd..9fa0df74ad7c 100644 +--- a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imxdrv.c ++++ b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imxdrv.c +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 2019 NXP semiconductor, inc. ++ * Copyright (c) 2019-2020 NXP semiconductor, inc. + * + * this program is free software; you can redistribute it and/or modify + * it under the terms of the gnu general public license version 2 as +@@ -44,8 +44,11 @@ static int cdns_mhdp_imx_encoder_atomic_check(struct drm_encoder *encoder, + if (mhdp->plat_data->video_format != 0) + imx_crtc_state->bus_format = mhdp->plat_data->video_format; + +- if (mhdp->force_mode_set) ++ if (mhdp->force_mode_set) { + crtc_state->mode_changed = true; ++ /* reset force mode set flag */ ++ mhdp->force_mode_set = false; ++ } + + return 0; + } +-- +2.29.2 + diff --git a/projects/NXP/devices/iMX8/patches/linux/0037-MLK-25199-1-drm-mhdp-Add-hdmi-phy-reset-poweroff-fun.patch b/projects/NXP/devices/iMX8/patches/linux/0037-MLK-25199-1-drm-mhdp-Add-hdmi-phy-reset-poweroff-fun.patch new file mode 100644 index 0000000000..e70e44eb2e --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0037-MLK-25199-1-drm-mhdp-Add-hdmi-phy-reset-poweroff-fun.patch @@ -0,0 +1,161 @@ +From 38f1f4ecd038628f4ce7a47114455123e5db3367 Mon Sep 17 00:00:00 2001 +From: Sandor Yu +Date: Wed, 30 Dec 2020 16:02:52 +0800 +Subject: [PATCH 37/49] MLK-25199-1: drm: mhdp: Add hdmi phy reset/poweroff + function + +Add hdmi phy reset and power off function. + +Signed-off-by: Sandor Yu +Reviewed-by: Robby Cai +--- + drivers/gpu/drm/imx/mhdp/cdns-mhdp-hdmi-phy.c | 28 ++++++++++++++++++- + drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx.h | 3 +- + drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx8qm.c | 4 +-- + drivers/gpu/drm/imx/mhdp/cdns-mhdp-imxdrv.c | 2 ++ + drivers/gpu/drm/imx/mhdp/cdns-mhdp-phy.h | 3 +- + 5 files changed, 35 insertions(+), 5 deletions(-) + +diff --git a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-hdmi-phy.c b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-hdmi-phy.c +index 120300e6a2df..212f3f4f1e26 100644 +--- a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-hdmi-phy.c ++++ b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-hdmi-phy.c +@@ -1,7 +1,7 @@ + /* + * Cadence High-Definition Multimedia Interface (HDMI) driver + * +- * Copyright (C) 2019 NXP Semiconductor, Inc. ++ * Copyright (C) 2019-2021 NXP Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by +@@ -21,6 +21,7 @@ + + #include + #include "cdns-mhdp-phy.h" ++#include "cdns-mhdp-imx.h" + + /* HDMI TX clock control settings */ + struct hdmi_ctrl { +@@ -746,6 +747,7 @@ int cdns_hdmi_phy_set_imx8qm(struct cdns_mhdp_device *mhdp) + DRM_ERROR("NO HDMI FW running\n"); + return -ENXIO; + } ++ imx8qm_phy_reset(0); + + /* Configure PHY */ + mhdp->hdmi.char_rate = hdmi_phy_cfg_ss28fdsoi(mhdp, mode); +@@ -753,6 +755,7 @@ int cdns_hdmi_phy_set_imx8qm(struct cdns_mhdp_device *mhdp) + DRM_ERROR("failed to set phy pclock\n"); + return -EINVAL; + } ++ imx8qm_phy_reset(1); + + ret = hdmi_phy_power_up(mhdp); + if (ret < 0) +@@ -762,3 +765,26 @@ int cdns_hdmi_phy_set_imx8qm(struct cdns_mhdp_device *mhdp) + + return true; + } ++ ++int cdns_hdmi_phy_shutdown(struct cdns_mhdp_device *mhdp) ++{ ++ int timeout; ++ u32 reg_val; ++ ++ reg_val = cdns_phy_reg_read(mhdp, PHY_HDP_MODE_CTRL); ++ reg_val &= 0xfff0; ++ /* PHY_DP_MODE_CTL set to A3 power state*/ ++ cdns_phy_reg_write(mhdp, PHY_HDP_MODE_CTRL, reg_val | 0x8); ++ ++ /* PHY_DP_MODE_CTL */ ++ timeout = 0; ++ do { ++ reg_val = cdns_phy_reg_read(mhdp, PHY_HDP_MODE_CTRL); ++ DRM_INFO("Reg val is 0x%04x\n", reg_val); ++ timeout++; ++ msleep(100); ++ } while (!(reg_val & (0x8 << 4)) && (timeout < 10)); /* Wait for A3 acknowledge */ ++ ++ DRM_INFO("hdmi phy shutdown complete\n"); ++ return 0; ++} +diff --git a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx.h b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx.h +index fc3247dada2d..a12005ae4c53 100644 +--- a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx.h ++++ b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx.h +@@ -1,7 +1,7 @@ + /* + * Cadence High-Definition Multimedia Interface (HDMI) driver + * +- * Copyright (C) 2019 NXP Semiconductor, Inc. ++ * Copyright (C) 2019-2021 NXP Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by +@@ -72,4 +72,5 @@ int cdns_mhdp_suspend_imx8qm(struct cdns_mhdp_device *mhdp); + int cdns_mhdp_power_on_imx8qm(struct cdns_mhdp_device *mhdp); + int cdns_mhdp_power_on_ls1028a(struct cdns_mhdp_device *mhdp); + void cdns_mhdp_pclk_rate_ls1028a(struct cdns_mhdp_device *mhdp); ++void imx8qm_phy_reset(u8 reset); + #endif /* CDNS_MHDP_IMX_H_ */ +diff --git a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx8qm.c b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx8qm.c +index 38f9defa42f8..46c0500da4c3 100644 +--- a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx8qm.c ++++ b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx8qm.c +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 2019 NXP semiconductor, inc. ++ * Copyright (c) 2019-2021 NXP semiconductor, inc. + * + * this program is free software; you can redistribute it and/or modify + * it under the terms of the gnu general public license version 2 as +@@ -102,7 +102,7 @@ static void imx8qm_pixel_link_sync_disable(u32 dual_mode) + imx_sc_misc_set_control(handle, IMX_SC_R_DC_0, IMX_SC_C_SYNC_CTRL0, 0); + } + +-static void imx8qm_phy_reset(u8 reset) ++void imx8qm_phy_reset(u8 reset) + { + struct imx_sc_ipc *handle; + +diff --git a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imxdrv.c b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imxdrv.c +index 9fa0df74ad7c..4c4ce9d3c847 100644 +--- a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imxdrv.c ++++ b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imxdrv.c +@@ -22,6 +22,7 @@ static void cdns_mhdp_imx_encoder_disable(struct drm_encoder *encoder) + struct drm_bridge *bridge = drm_bridge_chain_get_first_bridge(encoder); + struct cdns_mhdp_device *mhdp = bridge->driver_private; + ++ cdns_hdmi_phy_shutdown(mhdp); + cdns_mhdp_plat_call(mhdp, plat_init); + } + +@@ -184,6 +185,7 @@ static int cdns_mhdp_imx_bind(struct device *dev, struct device *master, + + imx_mhdp->mhdp.plat_data = plat_data; + imx_mhdp->mhdp.dev = dev; ++ imx_mhdp->mhdp.drm_dev = drm; + imx_mhdp->mhdp.bus_type = plat_data->bus_type; + ret = plat_data->bind(pdev, encoder, &imx_mhdp->mhdp); + /* +diff --git a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-phy.h b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-phy.h +index 5682b9fbc90f..9035f1f71eee 100644 +--- a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-phy.h ++++ b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-phy.h +@@ -1,5 +1,5 @@ + /* +- * Copyright (C) 2019 NXP Semiconductor, Inc. ++ * Copyright (C) 2019-2021 NXP Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by +@@ -152,4 +152,5 @@ bool cdns_hdmi_phy_video_valid_imx8mq(struct cdns_mhdp_device *hdp); + bool cdns_hdmi_phy_video_valid_imx8qm(struct cdns_mhdp_device *hdp); + int cdns_hdmi_phy_set_imx8mq(struct cdns_mhdp_device *hdp); + int cdns_hdmi_phy_set_imx8qm(struct cdns_mhdp_device *hdp); ++int cdns_hdmi_phy_shutdown(struct cdns_mhdp_device *mhdp); + #endif /* _CDNS_MHDP_PHY_H */ +-- +2.29.2 + diff --git a/projects/NXP/devices/iMX8/patches/linux/0038-MLK-25199-2-drm-mhdp-Fix-typo-for-hdmi-phy-configura.patch b/projects/NXP/devices/iMX8/patches/linux/0038-MLK-25199-2-drm-mhdp-Fix-typo-for-hdmi-phy-configura.patch new file mode 100644 index 0000000000..9fc25f46f8 --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0038-MLK-25199-2-drm-mhdp-Fix-typo-for-hdmi-phy-configura.patch @@ -0,0 +1,38 @@ +From d77cbee9949eda85baba634bdf6c6c2afe0b64e4 Mon Sep 17 00:00:00 2001 +From: Sandor Yu +Date: Thu, 31 Dec 2020 10:13:55 +0800 +Subject: [PATCH 38/49] MLK-25199-2: drm: mhdp: Fix typo for hdmi phy + configuration table + +Fix typo for imx8qm hdmi phy configuration table. + +Signed-off-by: Sandor Yu +Reviewed-by: Robby Cai +--- + drivers/gpu/drm/imx/mhdp/cdns-mhdp-hdmi-phy.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-hdmi-phy.c b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-hdmi-phy.c +index 212f3f4f1e26..f96b200885df 100644 +--- a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-hdmi-phy.c ++++ b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-hdmi-phy.c +@@ -95,11 +95,11 @@ static const struct hdmi_ctrl imx8qm_ctrl_table[] = { + { 85000, 170000, 1000, 850000, 1700000, 0x11, 0x00, 0x07, 340, 0x146, 0x00A, 0, 0, 0, 1700000, 3400000, 0, 1, 1, 2, 2, 0x01, 85000, 170000}, + {170000, 340000, 1000, 1700000, 3400000, 0x22, 0x01, 0x07, 340, 0x146, 0x00A, 0, 0, 0, 1700000, 3400000, 0, 1, 1, 2, 1, 0x00, 170000, 340000}, + {340000, 600000, 1000, 3400000, 6000000, 0x3C, 0x03, 0x06, 600, 0x24A, 0x00A, 0, 0, 0, 3400000, 6000000, 1, 1, 1, 2, 1, 0x00, 340000, 600000}, +-{ 25000, 34000, 1205, 312500, 425000, 0x04, 0x01, 0x01, 400, 0x182, 0x00A, 0, 0, 0, 2500000, 3400000, 0, 2, 2, 2, 4, 0x03, 31250, 42500}, +-{ 34000, 68000, 1205, 425000, 850000, 0x06, 0x02, 0x01, 300, 0x11E, 0x00A, 0, 0, 0, 1700000, 3400000, 0, 1, 1, 2, 4, 0x02, 42500, 85000}, +-{ 68000, 136000, 1205, 850000, 1700000, 0x0D, 0x02, 0x02, 325, 0x137, 0x00A, 0, 0, 0, 1700000, 3400000, 0, 1, 1, 2, 2, 0x01, 85000, 170000}, +-{136000, 272000, 1205, 1700000, 3400000, 0x1A, 0x02, 0x04, 325, 0x137, 0x00A, 0, 0, 0, 1700000, 3400000, 0, 1, 1, 2, 1, 0x00, 170000, 340000}, +-{272000, 480000, 1205, 3400000, 6000000, 0x30, 0x03, 0x05, 600, 0x24A, 0x00A, 0, 0, 0, 3400000, 6000000, 1, 1, 1, 2, 1, 0x00, 340000, 600000}, ++{ 25000, 34000, 1250, 312500, 425000, 0x04, 0x01, 0x01, 400, 0x182, 0x00A, 0, 0, 0, 2500000, 3400000, 0, 2, 2, 2, 4, 0x03, 31250, 42500}, ++{ 34000, 68000, 1250, 425000, 850000, 0x06, 0x02, 0x01, 300, 0x11E, 0x00A, 0, 0, 0, 1700000, 3400000, 0, 1, 1, 2, 4, 0x02, 42500, 85000}, ++{ 68000, 136000, 1250, 850000, 1700000, 0x0D, 0x02, 0x02, 325, 0x137, 0x00A, 0, 0, 0, 1700000, 3400000, 0, 1, 1, 2, 2, 0x01, 85000, 170000}, ++{136000, 272000, 1250, 1700000, 3400000, 0x1A, 0x02, 0x04, 325, 0x137, 0x00A, 0, 0, 0, 1700000, 3400000, 0, 1, 1, 2, 1, 0x00, 170000, 340000}, ++{272000, 480000, 1250, 3400000, 6000000, 0x30, 0x03, 0x05, 600, 0x24A, 0x00A, 0, 0, 0, 3400000, 6000000, 1, 1, 1, 2, 1, 0x00, 340000, 600000}, + { 25000, 28000, 1500, 375000, 420000, 0x03, 0x01, 0x01, 360, 0x15A, 0x00A, 0, 0, 0, 3000000, 3360000, 0, 2, 2, 2, 4, 0x03, 37500, 42000}, + { 28000, 56000, 1500, 420000, 840000, 0x06, 0x02, 0x01, 360, 0x15A, 0x00A, 0, 0, 0, 1680000, 3360000, 0, 1, 1, 2, 4, 0x02, 42000, 84000}, + { 56000, 113000, 1500, 840000, 1695000, 0x0B, 0x00, 0x05, 330, 0x13C, 0x00A, 0, 0, 0, 1680000, 3390000, 0, 1, 1, 2, 2, 0x01, 84000, 169500}, +-- +2.29.2 + diff --git a/projects/NXP/devices/iMX8/patches/linux/0039-drm-imx-dcss-fix-unused-but-set-variable-warnings.patch b/projects/NXP/devices/iMX8/patches/linux/0039-drm-imx-dcss-fix-unused-but-set-variable-warnings.patch new file mode 100644 index 0000000000..6b7a19f3d5 --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0039-drm-imx-dcss-fix-unused-but-set-variable-warnings.patch @@ -0,0 +1,48 @@ +From 6b147fc07b475ede9ede99cf381f1706be5de6f9 Mon Sep 17 00:00:00 2001 +From: Wang ShaoBo +Date: Fri, 11 Sep 2020 09:44:14 +0800 +Subject: [PATCH 39/49] drm/imx/dcss: fix unused but set variable warnings +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Fix unused but set variable warning building with `make W=1`: + +drivers/gpu/drm/imx/dcss/dcss-plane.c:270:6: warning: + variable ‘pixel_format’ set but not used [-Wunused-but-set-variable] + u32 pixel_format; + ^~~~~~~~~~~~ + +Fixes: 9021c317b770 ("drm/imx: Add initial support for DCSS on iMX8MQ") +Reported-by: Hulk Robot +Signed-off-by: Wang ShaoBo +Reviewed-by: Laurentiu Palcu +Signed-off-by: Lucas Stach +Link: https://patchwork.freedesktop.org/patch/msgid/20200911014414.4663-1-bobo.shaobowang@huawei.com +--- + drivers/gpu/drm/imx/dcss/dcss-plane.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/drivers/gpu/drm/imx/dcss/dcss-plane.c b/drivers/gpu/drm/imx/dcss/dcss-plane.c +index 961d671f171b..e13652e3a115 100644 +--- a/drivers/gpu/drm/imx/dcss/dcss-plane.c ++++ b/drivers/gpu/drm/imx/dcss/dcss-plane.c +@@ -267,7 +267,6 @@ static void dcss_plane_atomic_update(struct drm_plane *plane, + struct dcss_plane *dcss_plane = to_dcss_plane(plane); + struct dcss_dev *dcss = plane->dev->dev_private; + struct drm_framebuffer *fb = state->fb; +- u32 pixel_format; + struct drm_crtc_state *crtc_state; + bool modifiers_present; + u32 src_w, src_h, dst_w, dst_h; +@@ -277,7 +276,6 @@ static void dcss_plane_atomic_update(struct drm_plane *plane, + if (!fb || !state->crtc || !state->visible) + return; + +- pixel_format = state->fb->format->format; + crtc_state = state->crtc->state; + modifiers_present = !!(fb->flags & DRM_MODE_FB_MODIFIERS); + +-- +2.29.2 + diff --git a/projects/NXP/devices/iMX8/patches/linux/0040-drm-imx-dcss-use-the-external-27MHz-phy-clock.patch b/projects/NXP/devices/iMX8/patches/linux/0040-drm-imx-dcss-use-the-external-27MHz-phy-clock.patch new file mode 100644 index 0000000000..2bd14ddf27 --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0040-drm-imx-dcss-use-the-external-27MHz-phy-clock.patch @@ -0,0 +1,108 @@ +From c2af9b24bfa69ffb12e72153f89ed3bb3245fafb Mon Sep 17 00:00:00 2001 +From: Laurentiu Palcu +Date: Fri, 22 Nov 2019 10:00:56 +0200 +Subject: [PATCH 40/49] drm/imx/dcss: use the external 27MHz phy clock + +The 27MHz external oscillator offers a high precision low jitter clock and +is suitable for high pixel clocks modes(ie 4K@60). + +Signed-off-by: Laurentiu Palcu +--- + drivers/gpu/drm/imx/dcss/dcss-dev.c | 25 +++++++++++++++++++------ + drivers/gpu/drm/imx/dcss/dcss-dtg.c | 11 +++++++++++ + 2 files changed, 30 insertions(+), 6 deletions(-) + +diff --git a/drivers/gpu/drm/imx/dcss/dcss-dev.c b/drivers/gpu/drm/imx/dcss/dcss-dev.c +index c849533ca83e..1977f6b058f8 100644 +--- a/drivers/gpu/drm/imx/dcss/dcss-dev.c ++++ b/drivers/gpu/drm/imx/dcss/dcss-dev.c +@@ -17,6 +17,11 @@ + + static void dcss_clocks_enable(struct dcss_dev *dcss) + { ++ if (dcss->hdmi_output) { ++ clk_prepare_enable(dcss->pll_phy_ref_clk); ++ clk_prepare_enable(dcss->pll_src_clk); ++ } ++ + clk_prepare_enable(dcss->axi_clk); + clk_prepare_enable(dcss->apb_clk); + clk_prepare_enable(dcss->rtrm_clk); +@@ -31,6 +36,11 @@ static void dcss_clocks_disable(struct dcss_dev *dcss) + clk_disable_unprepare(dcss->rtrm_clk); + clk_disable_unprepare(dcss->apb_clk); + clk_disable_unprepare(dcss->axi_clk); ++ ++ if (dcss->hdmi_output) { ++ clk_disable_unprepare(dcss->pll_src_clk); ++ clk_disable_unprepare(dcss->pll_phy_ref_clk); ++ } + } + + static void dcss_disable_dtg_and_ss_cb(void *data) +@@ -133,17 +143,20 @@ static int dcss_clks_init(struct dcss_dev *dcss) + struct { + const char *id; + struct clk **clk; ++ bool required; + } clks[] = { +- {"apb", &dcss->apb_clk}, +- {"axi", &dcss->axi_clk}, +- {"pix", &dcss->pix_clk}, +- {"rtrm", &dcss->rtrm_clk}, +- {"dtrc", &dcss->dtrc_clk}, ++ {"apb", &dcss->apb_clk, true}, ++ {"axi", &dcss->axi_clk, true}, ++ {"pix", &dcss->pix_clk, true}, ++ {"rtrm", &dcss->rtrm_clk, true}, ++ {"dtrc", &dcss->dtrc_clk, true}, ++ {"pll_src", &dcss->pll_src_clk, dcss->hdmi_output}, ++ {"pll_phy_ref", &dcss->pll_phy_ref_clk, dcss->hdmi_output}, + }; + + for (i = 0; i < ARRAY_SIZE(clks); i++) { + *clks[i].clk = devm_clk_get(dcss->dev, clks[i].id); +- if (IS_ERR(*clks[i].clk)) { ++ if (IS_ERR(*clks[i].clk) && clks[i].required) { + dev_err(dcss->dev, "failed to get %s clock\n", + clks[i].id); + return PTR_ERR(*clks[i].clk); +diff --git a/drivers/gpu/drm/imx/dcss/dcss-dtg.c b/drivers/gpu/drm/imx/dcss/dcss-dtg.c +index 30de00540f63..b70785d69ad9 100644 +--- a/drivers/gpu/drm/imx/dcss/dcss-dtg.c ++++ b/drivers/gpu/drm/imx/dcss/dcss-dtg.c +@@ -83,6 +83,7 @@ struct dcss_dtg { + u32 ctx_id; + + bool in_use; ++ bool hdmi_output; + + u32 dis_ulc_x; + u32 dis_ulc_y; +@@ -159,6 +160,7 @@ int dcss_dtg_init(struct dcss_dev *dcss, unsigned long dtg_base) + dcss->dtg = dtg; + dtg->dev = dcss->dev; + dtg->ctxld = dcss->ctxld; ++ dtg->hdmi_output = dcss->hdmi_output; + + dtg->base_reg = ioremap(dtg_base, SZ_4K); + if (!dtg->base_reg) { +@@ -221,6 +223,15 @@ void dcss_dtg_sync_set(struct dcss_dtg *dtg, struct videomode *vm) + vm->vactive - 1; + + clk_disable_unprepare(dcss->pix_clk); ++ if (dcss->hdmi_output) { ++ int err; ++ ++ clk_disable_unprepare(dcss->pll_src_clk); ++ err = clk_set_parent(dcss->pll_src_clk, dcss->pll_phy_ref_clk); ++ if (err < 0) ++ dev_warn(dcss->dev, "clk_set_parent() returned %d", err); ++ clk_prepare_enable(dcss->pll_src_clk); ++ } + clk_set_rate(dcss->pix_clk, vm->pixelclock); + clk_prepare_enable(dcss->pix_clk); + +-- +2.29.2 + diff --git a/projects/NXP/devices/iMX8/patches/linux/0041-drm-imx-dcss-add-component-framework-functionality.patch b/projects/NXP/devices/iMX8/patches/linux/0041-drm-imx-dcss-add-component-framework-functionality.patch new file mode 100644 index 0000000000..129438175a --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0041-drm-imx-dcss-add-component-framework-functionality.patch @@ -0,0 +1,255 @@ +From ec59d2988d1ac50acea0fdaa63513f216ddf016d Mon Sep 17 00:00:00 2001 +From: Laurentiu Palcu +Date: Thu, 9 Jul 2020 19:47:31 +0300 +Subject: [PATCH 41/49] drm/imx/dcss: add component framework functionality + +Component framework is needed by HDP driver. + +Signed-off-by: Laurentiu Palcu +--- + drivers/gpu/drm/imx/dcss/dcss-drv.c | 89 ++++++++++++++++++++++------- + drivers/gpu/drm/imx/dcss/dcss-kms.c | 23 +++++--- + drivers/gpu/drm/imx/dcss/dcss-kms.h | 4 +- + 3 files changed, 85 insertions(+), 31 deletions(-) + +diff --git a/drivers/gpu/drm/imx/dcss/dcss-drv.c b/drivers/gpu/drm/imx/dcss/dcss-drv.c +index 8dc2f85c514b..09d0ac28e28a 100644 +--- a/drivers/gpu/drm/imx/dcss/dcss-drv.c ++++ b/drivers/gpu/drm/imx/dcss/dcss-drv.c +@@ -6,6 +6,7 @@ + #include + #include + #include ++#include + #include + + #include "dcss-dev.h" +@@ -14,6 +15,8 @@ + struct dcss_drv { + struct dcss_dev *dcss; + struct dcss_kms_dev *kms; ++ ++ bool is_componentized; + }; + + struct dcss_dev *dcss_drv_dev_to_dcss(struct device *dev) +@@ -30,30 +33,18 @@ struct drm_device *dcss_drv_dev_to_drm(struct device *dev) + return mdrv ? &mdrv->kms->base : NULL; + } + +-static int dcss_drv_platform_probe(struct platform_device *pdev) ++static int dcss_drv_init(struct device *dev, bool componentized) + { +- struct device *dev = &pdev->dev; +- struct device_node *remote; + struct dcss_drv *mdrv; + int err = 0; +- bool hdmi_output = true; +- +- if (!dev->of_node) +- return -ENODEV; +- +- remote = of_graph_get_remote_node(dev->of_node, 0, 0); +- if (!remote) +- return -ENODEV; +- +- hdmi_output = !of_device_is_compatible(remote, "fsl,imx8mq-nwl-dsi"); +- +- of_node_put(remote); + + mdrv = kzalloc(sizeof(*mdrv), GFP_KERNEL); + if (!mdrv) + return -ENOMEM; + +- mdrv->dcss = dcss_dev_create(dev, hdmi_output); ++ mdrv->is_componentized = componentized; ++ ++ mdrv->dcss = dcss_dev_create(dev, componentized); + if (IS_ERR(mdrv->dcss)) { + err = PTR_ERR(mdrv->dcss); + goto err; +@@ -61,7 +52,7 @@ static int dcss_drv_platform_probe(struct platform_device *pdev) + + dev_set_drvdata(dev, mdrv); + +- mdrv->kms = dcss_kms_attach(mdrv->dcss); ++ mdrv->kms = dcss_kms_attach(mdrv->dcss, componentized); + if (IS_ERR(mdrv->kms)) { + err = PTR_ERR(mdrv->kms); + goto dcss_shutoff; +@@ -79,19 +70,73 @@ static int dcss_drv_platform_probe(struct platform_device *pdev) + return err; + } + +-static int dcss_drv_platform_remove(struct platform_device *pdev) ++static void dcss_drv_deinit(struct device *dev, bool componentized) + { +- struct dcss_drv *mdrv = dev_get_drvdata(&pdev->dev); ++ struct dcss_drv *mdrv = dev_get_drvdata(dev); + + if (!mdrv) +- return 0; ++ return; + +- dcss_kms_detach(mdrv->kms); ++ dcss_kms_detach(mdrv->kms, componentized); + dcss_dev_destroy(mdrv->dcss); + +- dev_set_drvdata(&pdev->dev, NULL); ++ dev_set_drvdata(dev, NULL); + + kfree(mdrv); ++} ++ ++static int dcss_drv_bind(struct device *dev) ++{ ++ return dcss_drv_init(dev, true); ++} ++ ++static void dcss_drv_unbind(struct device *dev) ++{ ++ return dcss_drv_deinit(dev, true); ++} ++ ++static const struct component_master_ops dcss_master_ops = { ++ .bind = dcss_drv_bind, ++ .unbind = dcss_drv_unbind, ++}; ++ ++static int compare_of(struct device *dev, void *data) ++{ ++ return dev->of_node == data; ++} ++ ++static int dcss_drv_platform_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct component_match *match = NULL; ++ struct device_node *remote; ++ ++ if (!dev->of_node) ++ return -ENODEV; ++ ++ remote = of_graph_get_remote_node(dev->of_node, 0, 0); ++ if (!remote) ++ return -ENODEV; ++ ++ if (of_device_is_compatible(remote, "fsl,imx8mq-nwl-dsi")) { ++ of_node_put(remote); ++ return dcss_drv_init(dev, false); ++ } ++ ++ drm_of_component_match_add(dev, &match, compare_of, remote); ++ of_node_put(remote); ++ ++ return component_master_add_with_match(dev, &dcss_master_ops, match); ++} ++ ++static int dcss_drv_platform_remove(struct platform_device *pdev) ++{ ++ struct dcss_drv *mdrv = dev_get_drvdata(&pdev->dev); ++ ++ if (mdrv->is_componentized) ++ component_master_del(&pdev->dev, &dcss_master_ops); ++ else ++ dcss_drv_deinit(&pdev->dev, false); + + return 0; + } +diff --git a/drivers/gpu/drm/imx/dcss/dcss-kms.c b/drivers/gpu/drm/imx/dcss/dcss-kms.c +index 135a62366ab8..cafb09df6c75 100644 +--- a/drivers/gpu/drm/imx/dcss/dcss-kms.c ++++ b/drivers/gpu/drm/imx/dcss/dcss-kms.c +@@ -13,6 +13,7 @@ + #include + #include + #include ++#include + + #include "dcss-dev.h" + #include "dcss-kms.h" +@@ -123,7 +124,7 @@ static int dcss_kms_bridge_connector_init(struct dcss_kms_dev *kms) + return 0; + } + +-struct dcss_kms_dev *dcss_kms_attach(struct dcss_dev *dcss) ++struct dcss_kms_dev *dcss_kms_attach(struct dcss_dev *dcss, bool componentized) + { + struct dcss_kms_dev *kms; + struct drm_device *drm; +@@ -148,19 +149,23 @@ struct dcss_kms_dev *dcss_kms_attach(struct dcss_dev *dcss) + + drm->irq_enabled = true; + +- ret = dcss_kms_bridge_connector_init(kms); ++ ret = dcss_crtc_init(crtc, drm); + if (ret) + goto cleanup_mode_config; + +- ret = dcss_crtc_init(crtc, drm); ++ if (componentized) ++ ret = component_bind_all(dcss->dev, kms); ++ else ++ ret = dcss_kms_bridge_connector_init(kms); + if (ret) +- goto cleanup_mode_config; ++ goto cleanup_crtc; + + drm_mode_config_reset(drm); + + drm_kms_helper_poll_init(drm); + +- drm_bridge_connector_enable_hpd(kms->connector); ++ if (!componentized) ++ drm_bridge_connector_enable_hpd(kms->connector); + + ret = drm_dev_register(drm, 0); + if (ret) +@@ -171,7 +176,8 @@ struct dcss_kms_dev *dcss_kms_attach(struct dcss_dev *dcss) + return kms; + + cleanup_crtc: +- drm_bridge_connector_disable_hpd(kms->connector); ++ if (!componentized) ++ drm_bridge_connector_disable_hpd(kms->connector); + drm_kms_helper_poll_fini(drm); + dcss_crtc_deinit(crtc, drm); + +@@ -182,9 +188,10 @@ struct dcss_kms_dev *dcss_kms_attach(struct dcss_dev *dcss) + return ERR_PTR(ret); + } + +-void dcss_kms_detach(struct dcss_kms_dev *kms) ++void dcss_kms_detach(struct dcss_kms_dev *kms, bool componentized) + { + struct drm_device *drm = &kms->base; ++ struct dcss_dev *dcss = drm->dev_private; + + drm_dev_unregister(drm); + drm_bridge_connector_disable_hpd(kms->connector); +@@ -194,5 +201,7 @@ void dcss_kms_detach(struct dcss_kms_dev *kms) + drm->irq_enabled = false; + drm_mode_config_cleanup(drm); + dcss_crtc_deinit(&kms->crtc, drm); ++ if (componentized) ++ component_unbind_all(dcss->dev, drm); + drm->dev_private = NULL; + } +diff --git a/drivers/gpu/drm/imx/dcss/dcss-kms.h b/drivers/gpu/drm/imx/dcss/dcss-kms.h +index dfe5dd99eea3..e98d9c587a43 100644 +--- a/drivers/gpu/drm/imx/dcss/dcss-kms.h ++++ b/drivers/gpu/drm/imx/dcss/dcss-kms.h +@@ -32,8 +32,8 @@ struct dcss_kms_dev { + struct drm_connector *connector; + }; + +-struct dcss_kms_dev *dcss_kms_attach(struct dcss_dev *dcss); +-void dcss_kms_detach(struct dcss_kms_dev *kms); ++struct dcss_kms_dev *dcss_kms_attach(struct dcss_dev *dcss, bool componentized); ++void dcss_kms_detach(struct dcss_kms_dev *kms, bool componentized); + int dcss_crtc_init(struct dcss_crtc *crtc, struct drm_device *drm); + void dcss_crtc_deinit(struct dcss_crtc *crtc, struct drm_device *drm); + struct dcss_plane *dcss_plane_init(struct drm_device *drm, +-- +2.29.2 + diff --git a/projects/NXP/devices/iMX8/patches/linux/0042-drm-imx-dcss-fix-coccinelle-warning.patch b/projects/NXP/devices/iMX8/patches/linux/0042-drm-imx-dcss-fix-coccinelle-warning.patch new file mode 100644 index 0000000000..bb636d3299 --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0042-drm-imx-dcss-fix-coccinelle-warning.patch @@ -0,0 +1,33 @@ +From c869538d2a0e04edf0ad9abf89ef6ff70502ed86 Mon Sep 17 00:00:00 2001 +From: Laurentiu Palcu +Date: Thu, 5 Nov 2020 16:01:26 +0200 +Subject: [PATCH 42/49] drm/imx/dcss: fix coccinelle warning + +This small patch fixes a warning that I got while running coccinelle: + + CHECK drivers/gpu/drm/imx/dcss/dcss-plane.c + drivers/gpu/drm/imx/dcss/dcss-plane.c:107:21-23: WARNING !A || A && B is equivalent to !A || B + +Fixes: 9021c317b770 ("drm/imx: Add initial support for DCSS on iMX8MQ") +Signed-off-by: Laurentiu Palcu +--- + drivers/gpu/drm/imx/dcss/dcss-plane.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/drivers/gpu/drm/imx/dcss/dcss-plane.c b/drivers/gpu/drm/imx/dcss/dcss-plane.c +index e13652e3a115..c8dc6f4436e1 100644 +--- a/drivers/gpu/drm/imx/dcss/dcss-plane.c ++++ b/drivers/gpu/drm/imx/dcss/dcss-plane.c +@@ -103,8 +103,7 @@ static bool dcss_plane_can_rotate(const struct drm_format_info *format, + bool mod_present, u64 modifier, + unsigned int rotation) + { +- bool linear_format = !mod_present || +- (mod_present && modifier == DRM_FORMAT_MOD_LINEAR); ++ bool linear_format = !mod_present || modifier == DRM_FORMAT_MOD_LINEAR; + u32 supported_rotation = DRM_MODE_ROTATE_0; + + if (!format->is_yuv && linear_format) +-- +2.29.2 + diff --git a/projects/NXP/devices/iMX8/patches/linux/0043-arm64-dts-imx8mq-add-DCSS-node.patch b/projects/NXP/devices/iMX8/patches/linux/0043-arm64-dts-imx8mq-add-DCSS-node.patch new file mode 100644 index 0000000000..889d2bfe7a --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0043-arm64-dts-imx8mq-add-DCSS-node.patch @@ -0,0 +1,49 @@ +From bd9c83ea41380f584fdd8f2781112b530c84ebba Mon Sep 17 00:00:00 2001 +From: Laurentiu Palcu +Date: Thu, 9 Jul 2020 19:47:33 +0300 +Subject: [PATCH 43/49] arm64: dts: imx8mq: add DCSS node + +This patch adds the node for iMX8MQ Display Controller Subsystem. + +Signed-off-by: Laurentiu Palcu +--- + arch/arm64/boot/dts/freescale/imx8mq.dtsi | 23 +++++++++++++++++++++++ + 1 file changed, 23 insertions(+) + +diff --git a/arch/arm64/boot/dts/freescale/imx8mq.dtsi b/arch/arm64/boot/dts/freescale/imx8mq.dtsi +index 5e0e7d0f1bc4..5a617f9ed8b5 100644 +--- a/arch/arm64/boot/dts/freescale/imx8mq.dtsi ++++ b/arch/arm64/boot/dts/freescale/imx8mq.dtsi +@@ -1103,6 +1103,29 @@ bus@32c00000 { /* AIPS4 */ + #size-cells = <1>; + ranges = <0x32c00000 0x32c00000 0x400000>; + ++ dcss: display-controller@32e00000 { ++ compatible = "nxp,imx8mq-dcss"; ++ reg = <0x32e00000 0x2d000>, <0x32e2f000 0x1000>; ++ interrupts = <6>, <8>, <9>; ++ interrupt-names = "ctxld", "ctxld_kick", "vblank"; ++ interrupt-parent = <&irqsteer>; ++ clocks = <&clk IMX8MQ_CLK_DISP_APB_ROOT>, ++ <&clk IMX8MQ_CLK_DISP_AXI_ROOT>, ++ <&clk IMX8MQ_CLK_DISP_RTRM_ROOT>, ++ <&clk IMX8MQ_VIDEO2_PLL_OUT>, ++ <&clk IMX8MQ_CLK_DISP_DTRC>; ++ clock-names = "apb", "axi", "rtrm", "pix", "dtrc"; ++ assigned-clocks = <&clk IMX8MQ_CLK_DISP_AXI>, ++ <&clk IMX8MQ_CLK_DISP_RTRM>, ++ <&clk IMX8MQ_VIDEO2_PLL1_REF_SEL>; ++ assigned-clock-parents = <&clk IMX8MQ_SYS1_PLL_800M>, ++ <&clk IMX8MQ_SYS1_PLL_800M>, ++ <&clk IMX8MQ_CLK_27M>; ++ assigned-clock-rates = <800000000>, ++ <400000000>; ++ status = "disabled"; ++ }; ++ + irqsteer: interrupt-controller@32e2d000 { + compatible = "fsl,imx8m-irqsteer", "fsl,imx-irqsteer"; + reg = <0x32e2d000 0x1000>; +-- +2.29.2 + diff --git a/projects/NXP/devices/iMX8/patches/linux/0044-arm64-dts-imx8mq-add-DCSS-external-oscillator-suppor.patch b/projects/NXP/devices/iMX8/patches/linux/0044-arm64-dts-imx8mq-add-DCSS-external-oscillator-suppor.patch new file mode 100644 index 0000000000..af0fe275a3 --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0044-arm64-dts-imx8mq-add-DCSS-external-oscillator-suppor.patch @@ -0,0 +1,35 @@ +From 8e5a885158f430de3ea36b1439dd8c0058ce95df Mon Sep 17 00:00:00 2001 +From: Laurentiu Palcu +Date: Fri, 22 Nov 2019 10:12:50 +0200 +Subject: [PATCH 44/49] arm64: dts: imx8mq: add DCSS external oscillator + support + +The external oscillator, which is high precision, will be used when DCSS output +goes to HDMI. + +Signed-off-by: Laurentiu Palcu +--- + arch/arm64/boot/dts/freescale/imx8mq.dtsi | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +diff --git a/arch/arm64/boot/dts/freescale/imx8mq.dtsi b/arch/arm64/boot/dts/freescale/imx8mq.dtsi +index 5a617f9ed8b5..b75252a65c44 100644 +--- a/arch/arm64/boot/dts/freescale/imx8mq.dtsi ++++ b/arch/arm64/boot/dts/freescale/imx8mq.dtsi +@@ -1113,8 +1113,11 @@ dcss: display-controller@32e00000 { + <&clk IMX8MQ_CLK_DISP_AXI_ROOT>, + <&clk IMX8MQ_CLK_DISP_RTRM_ROOT>, + <&clk IMX8MQ_VIDEO2_PLL_OUT>, +- <&clk IMX8MQ_CLK_DISP_DTRC>; +- clock-names = "apb", "axi", "rtrm", "pix", "dtrc"; ++ <&clk IMX8MQ_CLK_DISP_DTRC>, ++ <&clk IMX8MQ_VIDEO2_PLL1_REF_SEL>, ++ <&clk IMX8MQ_CLK_PHY_27MHZ>; ++ clock-names = "apb", "axi", "rtrm", "pix", "dtrc", "pll_src", ++ "pll_phy_ref"; + assigned-clocks = <&clk IMX8MQ_CLK_DISP_AXI>, + <&clk IMX8MQ_CLK_DISP_RTRM>, + <&clk IMX8MQ_VIDEO2_PLL1_REF_SEL>; +-- +2.29.2 + diff --git a/projects/NXP/devices/iMX8/patches/linux/0045-arm64-dts-fsl-imx8mq-add-HDP-bridge-node.patch b/projects/NXP/devices/iMX8/patches/linux/0045-arm64-dts-fsl-imx8mq-add-HDP-bridge-node.patch new file mode 100644 index 0000000000..7eab52d86c --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0045-arm64-dts-fsl-imx8mq-add-HDP-bridge-node.patch @@ -0,0 +1,34 @@ +From 0327e9fc14269069711cd2d45d60130b318532fe Mon Sep 17 00:00:00 2001 +From: Lucas Stach +Date: Tue, 13 Feb 2018 12:30:58 +0100 +Subject: [PATCH 45/49] arm64: dts: fsl: imx8mq: add HDP bridge node + +Signed-off-by: Lucas Stach +--- + arch/arm64/boot/dts/freescale/imx8mq.dtsi | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/arch/arm64/boot/dts/freescale/imx8mq.dtsi b/arch/arm64/boot/dts/freescale/imx8mq.dtsi +index b75252a65c44..aad21d6f1da7 100644 +--- a/arch/arm64/boot/dts/freescale/imx8mq.dtsi ++++ b/arch/arm64/boot/dts/freescale/imx8mq.dtsi +@@ -1103,6 +1103,16 @@ bus@32c00000 { /* AIPS4 */ + #size-cells = <1>; + ranges = <0x32c00000 0x32c00000 0x400000>; + ++ hdmi: hdmi@32c00000 { ++ reg = <0x32c00000 0x33800>, /* HDP registers */ ++ <0x32e40000 0x40000>, /* HDP SEC register */ ++ <0x32e2f000 0x10>; /* RESET register */ ++ interrupts = , ++ ; ++ interrupt-names = "plug_in", "plug_out"; ++ status = "disabled"; ++ }; ++ + dcss: display-controller@32e00000 { + compatible = "nxp,imx8mq-dcss"; + reg = <0x32e00000 0x2d000>, <0x32e2f000 0x1000>; +-- +2.29.2 + diff --git a/projects/NXP/devices/iMX8/patches/linux/0046-arm64-dts-fsl-imx8mq-evk-enable-DCSS-and-HDMI.patch b/projects/NXP/devices/iMX8/patches/linux/0046-arm64-dts-fsl-imx8mq-evk-enable-DCSS-and-HDMI.patch new file mode 100644 index 0000000000..48d5c6da67 --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0046-arm64-dts-fsl-imx8mq-evk-enable-DCSS-and-HDMI.patch @@ -0,0 +1,53 @@ +From 96ab278661207096c013ad1b39ed36f5f9a35ffd Mon Sep 17 00:00:00 2001 +From: Lucas Stach +Date: Tue, 13 Feb 2018 12:47:09 +0100 +Subject: [PATCH 46/49] arm64: dts: fsl: imx8mq-evk: enable DCSS and HDMI + +Signed-off-by: Lucas Stach +--- + arch/arm64/boot/dts/freescale/imx8mq-evk.dts | 22 ++++++++++++++++++++ + 1 file changed, 22 insertions(+) + +diff --git a/arch/arm64/boot/dts/freescale/imx8mq-evk.dts b/arch/arm64/boot/dts/freescale/imx8mq-evk.dts +index 2418cca00bc5..71eeda6de3d7 100644 +--- a/arch/arm64/boot/dts/freescale/imx8mq-evk.dts ++++ b/arch/arm64/boot/dts/freescale/imx8mq-evk.dts +@@ -132,6 +132,16 @@ opp-800M { + }; + }; + ++&dcss { ++ status = "okay"; ++ ++ port { ++ dcss_out: endpoint { ++ remote-endpoint = <&hdmi_in>; ++ }; ++ }; ++}; ++ + &dphy { + status = "okay"; + }; +@@ -168,6 +178,18 @@ wl-reg-on-hog { + }; + }; + ++&hdmi { ++ compatible = "cdn,imx8mq-hdmi"; ++ lane-mapping = <0xe4>; ++ status = "okay"; ++ ++ port { ++ hdmi_in: endpoint { ++ remote-endpoint = <&dcss_out>; ++ }; ++ }; ++}; ++ + &i2c1 { + clock-frequency = <100000>; + pinctrl-names = "default"; +-- +2.29.2 + diff --git a/projects/NXP/devices/iMX8/patches/linux/0047-arm64-dts-fsl-imx8mq-pico-pi-enable-DCSS-and-HDMI.patch b/projects/NXP/devices/iMX8/patches/linux/0047-arm64-dts-fsl-imx8mq-pico-pi-enable-DCSS-and-HDMI.patch new file mode 100644 index 0000000000..d9a5d06075 --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0047-arm64-dts-fsl-imx8mq-pico-pi-enable-DCSS-and-HDMI.patch @@ -0,0 +1,52 @@ +From 5a139d07d03076be7972db4b022558dffcfd685b Mon Sep 17 00:00:00 2001 +From: Lukas Rusak +Date: Tue, 9 Mar 2021 10:47:27 -0800 +Subject: [PATCH 47/49] arm64: dts: fsl: imx8mq-pico-pi: enable DCSS and HDMI + +--- + .../boot/dts/freescale/imx8mq-pico-pi.dts | 22 +++++++++++++++++++ + 1 file changed, 22 insertions(+) + +diff --git a/arch/arm64/boot/dts/freescale/imx8mq-pico-pi.dts b/arch/arm64/boot/dts/freescale/imx8mq-pico-pi.dts +index 89cbec5c41b2..03734145c50e 100644 +--- a/arch/arm64/boot/dts/freescale/imx8mq-pico-pi.dts ++++ b/arch/arm64/boot/dts/freescale/imx8mq-pico-pi.dts +@@ -37,6 +37,16 @@ reg_usb_otg_vbus: regulator-usb-otg-vbus { + }; + }; + ++&dcss { ++ status = "okay"; ++ ++ port { ++ dcss_out: endpoint { ++ remote-endpoint = <&hdmi_in>; ++ }; ++ }; ++}; ++ + &fec1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_fec1 &pinctrl_enet_3v3>; +@@ -56,6 +66,18 @@ ethphy0: ethernet-phy@1 { + }; + }; + ++&hdmi { ++ compatible = "cdn,imx8mq-hdmi"; ++ lane-mapping = <0xe4>; ++ status = "okay"; ++ ++ port { ++ hdmi_in: endpoint { ++ remote-endpoint = <&dcss_out>; ++ }; ++ }; ++}; ++ + &i2c1 { + clock-frequency = <100000>; + pinctrl-names = "default"; +-- +2.29.2 + diff --git a/projects/NXP/devices/iMX8/patches/linux/0048-drm-imx-mhdp-don-t-depend-on-DRM_IMX.patch b/projects/NXP/devices/iMX8/patches/linux/0048-drm-imx-mhdp-don-t-depend-on-DRM_IMX.patch new file mode 100644 index 0000000000..c49bd36ee2 --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0048-drm-imx-mhdp-don-t-depend-on-DRM_IMX.patch @@ -0,0 +1,23 @@ +From f94717816b9a39869219ede859fe74af3f2ecd19 Mon Sep 17 00:00:00 2001 +From: Lukas Rusak +Date: Wed, 24 Mar 2021 14:27:43 -0700 +Subject: [PATCH 48/49] drm: imx: mhdp: don't depend on DRM_IMX + +--- + drivers/gpu/drm/imx/mhdp/Kconfig | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/drivers/gpu/drm/imx/mhdp/Kconfig b/drivers/gpu/drm/imx/mhdp/Kconfig +index 86950badb947..cf7dfacdd434 100644 +--- a/drivers/gpu/drm/imx/mhdp/Kconfig ++++ b/drivers/gpu/drm/imx/mhdp/Kconfig +@@ -6,6 +6,5 @@ config DRM_IMX_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. +-- +2.29.2 + diff --git a/projects/NXP/devices/iMX8/patches/linux/0049-drm-cadence-shutup-cec-logging.patch b/projects/NXP/devices/iMX8/patches/linux/0049-drm-cadence-shutup-cec-logging.patch new file mode 100644 index 0000000000..5589e19370 --- /dev/null +++ b/projects/NXP/devices/iMX8/patches/linux/0049-drm-cadence-shutup-cec-logging.patch @@ -0,0 +1,27 @@ +From 3111faf58971c2c517457e62f84d138a3d62464e Mon Sep 17 00:00:00 2001 +From: Lukas Rusak +Date: Wed, 24 Mar 2021 15:14:57 -0700 +Subject: [PATCH 49/49] drm: cadence: shutup cec logging + +--- + drivers/gpu/drm/bridge/cadence/cdns-mhdp-cec.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-cec.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-cec.c +index 25cf9e91e64f..e91de13eae58 100644 +--- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-cec.c ++++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-cec.c +@@ -171,8 +171,8 @@ static u32 mhdp_cec_write_message(struct cdns_mhdp_cec *cec, struct cec_msg *msg + return -EINVAL; + } + +- for (i = 0; i < msg->len; ++i) +- printk("msg[%d]=0x%x\n",i, msg->msg[i]); ++ // for (i = 0; i < msg->len; ++i) ++ // printk("msg[%d]=0x%x\n",i, msg->msg[i]); + + /* Write Message to register */ + for (i = 0; i < msg->len; ++i) { +-- +2.29.2 +