mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-11-03 14:34:27 -05:00 
			
		
		
		
	Add patches for linux-5.4. The patches are from NXP LSDK-20.04 release which was tagged LSDK-20.04-V5.4. https://source.codeaurora.org/external/qoriq/qoriq-components/linux/ For boards LS1021A-IOT, and Traverse-LS1043 which are not involved in LSDK, port the dts patches from 4.14. The patches are sorted into the following categories: 301-arch-xxxx 302-dts-xxxx 303-core-xxxx 701-net-xxxx 801-audio-xxxx 802-can-xxxx 803-clock-xxxx 804-crypto-xxxx 805-display-xxxx 806-dma-xxxx 807-gpio-xxxx 808-i2c-xxxx 809-jailhouse-xxxx 810-keys-xxxx 811-kvm-xxxx 812-pcie-xxxx 813-pm-xxxx 814-qe-xxxx 815-sata-xxxx 816-sdhc-xxxx 817-spi-xxxx 818-thermal-xxxx 819-uart-xxxx 820-usb-xxxx 821-vfio-xxxx Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
		
			
				
	
	
		
			1418 lines
		
	
	
		
			39 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			1418 lines
		
	
	
		
			39 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
From 9d6e1670f14a77c092ce32b559de52d7ddea3748 Mon Sep 17 00:00:00 2001
 | 
						|
From: Sandor Yu <Sandor.yu@nxp.com>
 | 
						|
Date: Fri, 23 Aug 2019 13:57:49 +0800
 | 
						|
Subject: [PATCH] drm: bridge: Add Cadence DP/HDMI core driver
 | 
						|
 | 
						|
Add HDMI and DP core driver.
 | 
						|
 | 
						|
Signed-off-by: Sandor Yu <Sandor.yu@nxp.com>
 | 
						|
---
 | 
						|
 drivers/gpu/drm/bridge/cadence/Kconfig          |   6 +
 | 
						|
 drivers/gpu/drm/bridge/cadence/Makefile         |   2 +
 | 
						|
 drivers/gpu/drm/bridge/cadence/cdns-dp-core.c   | 605 ++++++++++++++++++++++
 | 
						|
 drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c | 643 ++++++++++++++++++++++++
 | 
						|
 include/drm/bridge/cdns-mhdp-imx.h              | 121 +++++
 | 
						|
 5 files changed, 1377 insertions(+)
 | 
						|
 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 include/drm/bridge/cdns-mhdp-imx.h
 | 
						|
 | 
						|
--- a/drivers/gpu/drm/bridge/cadence/Kconfig
 | 
						|
+++ b/drivers/gpu/drm/bridge/cadence/Kconfig
 | 
						|
@@ -5,3 +5,9 @@ config DRM_CDNS_MHDP
 | 
						|
 	depends on OF
 | 
						|
 	help
 | 
						|
 	  Support Cadence MHDP API library.
 | 
						|
+
 | 
						|
+config DRM_CDNS_HDMI
 | 
						|
+	tristate "Cadence HDMI DRM driver"
 | 
						|
+
 | 
						|
+config DRM_CDNS_DP
 | 
						|
+	tristate "Cadence DP DRM driver"
 | 
						|
--- a/drivers/gpu/drm/bridge/cadence/Makefile
 | 
						|
+++ b/drivers/gpu/drm/bridge/cadence/Makefile
 | 
						|
@@ -1,3 +1,5 @@
 | 
						|
 #ccflags-y := -Iinclude/drm
 | 
						|
 
 | 
						|
 obj-$(CONFIG_DRM_CDNS_MHDP) += cdns-mhdp-common.o cdns-mhdp-hdmi.o
 | 
						|
+obj-$(CONFIG_DRM_CDNS_HDMI) += cdns-hdmi-core.o
 | 
						|
+obj-$(CONFIG_DRM_CDNS_DP) += cdns-dp-core.o
 | 
						|
--- /dev/null
 | 
						|
+++ b/drivers/gpu/drm/bridge/cadence/cdns-dp-core.c
 | 
						|
@@ -0,0 +1,605 @@
 | 
						|
+/*
 | 
						|
+ * 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 <drm/bridge/cdns-mhdp-imx.h>
 | 
						|
+#include <drm/drm_atomic_helper.h>
 | 
						|
+#include <drm/drm_crtc_helper.h>
 | 
						|
+#include <drm/drm_edid.h>
 | 
						|
+#include <drm/drm_encoder_slave.h>
 | 
						|
+#include <drm/drm_of.h>
 | 
						|
+#include <drm/drm_probe_helper.h>
 | 
						|
+#include <drm/drmP.h>
 | 
						|
+#include <linux/delay.h>
 | 
						|
+#include <linux/err.h>
 | 
						|
+#include <linux/irq.h>
 | 
						|
+#include <linux/module.h>
 | 
						|
+#include <linux/mutex.h>
 | 
						|
+#include <linux/of_device.h>
 | 
						|
+
 | 
						|
+#define aux_to_hdp(x) container_of(x, struct imx_mhdp_device, aux)
 | 
						|
+
 | 
						|
+/*
 | 
						|
+ * 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 imx_mhdp_device *dp,
 | 
						|
+			const struct drm_display_mode *mode)
 | 
						|
+{
 | 
						|
+	struct drm_dp_link link;
 | 
						|
+	struct cdns_mhdp_device *mhdp = &dp->mhdp;
 | 
						|
+	u32 lane_mapping = mhdp->dp.lane_mapping;
 | 
						|
+	int ret;
 | 
						|
+	char linkid[6];
 | 
						|
+
 | 
						|
+	memcpy(&mhdp->mode, mode, sizeof(struct drm_display_mode));
 | 
						|
+
 | 
						|
+	dp->dual_mode = video_is_dual_mode(mode);
 | 
						|
+
 | 
						|
+	dp_pixel_clk_reset(mhdp);
 | 
						|
+
 | 
						|
+	hdp_plat_call(dp, pclock_change);
 | 
						|
+
 | 
						|
+	hdp_plat_call(dp, phy_init);
 | 
						|
+
 | 
						|
+	ret = drm_dp_downstream_id(&mhdp->dp.aux, linkid);
 | 
						|
+	if (ret < 0) {
 | 
						|
+		DRM_INFO("Failed to Get DP link ID: %d\n", ret);
 | 
						|
+		return;
 | 
						|
+	}
 | 
						|
+	DRM_INFO("DP link id: %s, 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
 | 
						|
+		 linkid, linkid[0], linkid[1], linkid[2], linkid[3], linkid[4],
 | 
						|
+		 linkid[5]);
 | 
						|
+
 | 
						|
+	/* Check dp link */
 | 
						|
+	ret = drm_dp_link_probe(&mhdp->dp.aux, &link);
 | 
						|
+	if (ret < 0) {
 | 
						|
+		DRM_INFO("Failed to probe DP link: %d\n", ret);
 | 
						|
+		return;
 | 
						|
+	}
 | 
						|
+	DRM_INFO("DP revision: 0x%x\n", link.revision);
 | 
						|
+	DRM_INFO("DP rate: %d Mbps\n", link.rate);
 | 
						|
+	DRM_INFO("DP number of lanes: %d\n", link.num_lanes);
 | 
						|
+	DRM_INFO("DP capabilities: 0x%lx\n", link.capabilities);
 | 
						|
+
 | 
						|
+	drm_dp_link_power_up(&mhdp->dp.aux, &mhdp->dp.link);
 | 
						|
+	if (ret < 0) {
 | 
						|
+		DRM_INFO("Failed to power DP link: %d\n", ret);
 | 
						|
+		return;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* always use the number of lanes from the display*/
 | 
						|
+	mhdp->dp.link.num_lanes = link.num_lanes;
 | 
						|
+
 | 
						|
+	/* Use the lower link rate */
 | 
						|
+	if (mhdp->dp.link_rate != 0) {
 | 
						|
+		mhdp->dp.link.rate = min(mhdp->dp.link_rate, (u32)link.rate);
 | 
						|
+		DRM_DEBUG("DP actual link rate:  0x%x\n", link.rate);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* initialize phy if lanes or link rate differnt */
 | 
						|
+	if (mhdp->dp.link.num_lanes != mhdp->dp.num_lanes ||
 | 
						|
+			mhdp->dp.link.rate != mhdp->dp.link_rate)
 | 
						|
+		hdp_plat_call(dp, phy_init);
 | 
						|
+
 | 
						|
+	/* 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, mhdp->dp.link.num_lanes, 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;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* 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;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	return;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/* -----------------------------------------------------------------------------
 | 
						|
+ * dp TX Setup
 | 
						|
+ */
 | 
						|
+static enum drm_connector_status
 | 
						|
+cdns_dp_connector_detect(struct drm_connector *connector, bool force)
 | 
						|
+{
 | 
						|
+	struct imx_mhdp_device *dp = container_of(connector,
 | 
						|
+					struct imx_mhdp_device, mhdp.connector.base);
 | 
						|
+	u8 hpd = 0xf;
 | 
						|
+
 | 
						|
+	hpd = cdns_mhdp_read_hpd(&dp->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 imx_mhdp_device *dp = container_of(connector,
 | 
						|
+						struct imx_mhdp_device, mhdp.connector.base);
 | 
						|
+	int num_modes = 0;
 | 
						|
+	struct edid *edid;
 | 
						|
+
 | 
						|
+	edid = drm_do_get_edid(&dp->mhdp.connector.base,
 | 
						|
+				   cdns_mhdp_get_edid_block, &dp->mhdp);
 | 
						|
+	if (edid) {
 | 
						|
+		dev_info(dp->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)
 | 
						|
+{
 | 
						|
+	struct imx_mhdp_device *dp = bridge->driver_private;
 | 
						|
+	struct drm_encoder *encoder = bridge->encoder;
 | 
						|
+	struct drm_connector *connector = &dp->mhdp.connector.base;
 | 
						|
+
 | 
						|
+	connector->interlace_allowed = 1;
 | 
						|
+	connector->polled = DRM_CONNECTOR_POLL_HPD;
 | 
						|
+
 | 
						|
+	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 imx_mhdp_device *dp = bridge->driver_private;
 | 
						|
+	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:
 | 
						|
+		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); 
 | 
						|
+
 | 
						|
+	mutex_lock(&dp->lock);
 | 
						|
+
 | 
						|
+	cdns_dp_mode_set(dp, mode);
 | 
						|
+
 | 
						|
+	mutex_unlock(&dp->lock);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void cdn_hdp_bridge_enable(struct drm_bridge *bridge)
 | 
						|
+{
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void cdn_hdp_bridge_disable(struct drm_bridge *bridge)
 | 
						|
+{	
 | 
						|
+	struct imx_mhdp_device *dp = bridge->driver_private;
 | 
						|
+	struct cdns_mhdp_device *mhdp = &dp->mhdp;
 | 
						|
+
 | 
						|
+	cdns_mhdp_set_video_status(mhdp, CONTROL_VIDEO_IDLE);
 | 
						|
+	drm_dp_link_power_down(&mhdp->dp.aux, &mhdp->dp.link);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static const struct drm_bridge_funcs cdns_dp_bridge_funcs = {
 | 
						|
+	.attach = cdns_dp_bridge_attach,
 | 
						|
+	.enable = cdn_hdp_bridge_enable,
 | 
						|
+	.disable = cdn_hdp_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 imx_mhdp_device *dp = container_of(work,
 | 
						|
+					   struct imx_mhdp_device, hotplug_work.work);
 | 
						|
+	struct drm_connector *connector = &dp->mhdp.connector.base;
 | 
						|
+
 | 
						|
+	drm_helper_hpd_irq_event(connector->dev);
 | 
						|
+
 | 
						|
+	if (connector->status == connector_status_connected) {
 | 
						|
+		DRM_INFO("HDMI/DP Cable Plug In\n");
 | 
						|
+		enable_irq(dp->irq[IRQ_OUT]);
 | 
						|
+	} else if (connector->status == connector_status_disconnected) {
 | 
						|
+		/* Cable Disconnedted  */
 | 
						|
+		DRM_INFO("HDMI/DP Cable Plug Out\n");
 | 
						|
+		enable_irq(dp->irq[IRQ_IN]);
 | 
						|
+	}
 | 
						|
+}
 | 
						|
+
 | 
						|
+static irqreturn_t cdns_dp_irq_thread(int irq, void *data)
 | 
						|
+{
 | 
						|
+	struct imx_mhdp_device *dp = data;
 | 
						|
+
 | 
						|
+	disable_irq_nosync(irq);
 | 
						|
+
 | 
						|
+	mod_delayed_work(system_wq, &dp->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->dp.lane_mapping);
 | 
						|
+	if (ret) {
 | 
						|
+		mhdp->dp.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->dp.lane_mapping);
 | 
						|
+
 | 
						|
+	ret = of_property_read_u32(of_node, "link-rate", &mhdp->dp.link_rate);
 | 
						|
+	if (ret) {
 | 
						|
+		mhdp->dp.link_rate = 162000 ;
 | 
						|
+		dev_warn(mhdp->dev, "Failed to get link-rate, use default 1620MHz\n");
 | 
						|
+	}
 | 
						|
+	dev_info(mhdp->dev, "link-rate %d\n", mhdp->dp.link_rate);
 | 
						|
+	
 | 
						|
+	ret = of_property_read_u32(of_node, "num-lanes", &mhdp->dp.num_lanes);
 | 
						|
+	if (ret) {
 | 
						|
+		mhdp->dp.num_lanes = 4;
 | 
						|
+		dev_warn(mhdp->dev, "Failed to get num_lanes - using default\n");
 | 
						|
+	}
 | 
						|
+	dev_info(mhdp->dev, "dp_num_lanes 0x%02x\n", mhdp->dp.num_lanes);
 | 
						|
+
 | 
						|
+	mhdp->dp.link.num_lanes = mhdp->dp.num_lanes;
 | 
						|
+	mhdp->dp.link.rate= mhdp->dp.link_rate;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static struct imx_mhdp_device *
 | 
						|
+__cdns_dp_probe(struct platform_device *pdev,
 | 
						|
+		const struct cdn_plat_data *plat_data)
 | 
						|
+{
 | 
						|
+	struct device *dev = &pdev->dev;
 | 
						|
+	struct imx_mhdp_device *dp;
 | 
						|
+	struct resource *iores = NULL;
 | 
						|
+	int ret;
 | 
						|
+
 | 
						|
+	dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL);
 | 
						|
+	if (!dp)
 | 
						|
+		return ERR_PTR(-ENOMEM);
 | 
						|
+
 | 
						|
+	dp->plat_data = plat_data;
 | 
						|
+	dp->mhdp.dev = dev;
 | 
						|
+
 | 
						|
+	mutex_init(&dp->lock);
 | 
						|
+	mutex_init(&dp->audio_mutex);
 | 
						|
+	spin_lock_init(&dp->audio_lock);
 | 
						|
+
 | 
						|
+	INIT_DELAYED_WORK(&dp->hotplug_work, hotplug_work_func);
 | 
						|
+
 | 
						|
+	iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 | 
						|
+	dp->mhdp.regs = devm_ioremap(dev, iores->start, resource_size(iores));
 | 
						|
+	if (IS_ERR(dp->mhdp.regs)) {
 | 
						|
+		ret = PTR_ERR(dp->mhdp.regs);
 | 
						|
+		goto err_out;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+#if 0
 | 
						|
+	iores = platform_get_resource(pdev, IORESOURCE_MEM, 1);
 | 
						|
+	dp->regs_ss = devm_ioremap(dev, iores->start, resource_size(iores));
 | 
						|
+	if (IS_ERR(dp->regs_ss)) {
 | 
						|
+		ret = PTR_ERR(dp->regs_ss);
 | 
						|
+		goto err_out;
 | 
						|
+	}
 | 
						|
+#endif
 | 
						|
+
 | 
						|
+	dp->irq[IRQ_IN] = platform_get_irq_byname(pdev, "plug_in");
 | 
						|
+	if (dp->irq[IRQ_IN] < 0)
 | 
						|
+		dev_info(&pdev->dev, "No plug_in irq number\n");
 | 
						|
+
 | 
						|
+	dp->irq[IRQ_OUT] = platform_get_irq_byname(pdev, "plug_out");
 | 
						|
+	if (dp->irq[IRQ_OUT] < 0)
 | 
						|
+		dev_info(&pdev->dev, "No plug_out irq number\n");
 | 
						|
+
 | 
						|
+	cdns_dp_parse_dt(&dp->mhdp);
 | 
						|
+
 | 
						|
+	dp->dual_mode = false;
 | 
						|
+	hdp_plat_call(dp, fw_init);
 | 
						|
+
 | 
						|
+	/* DP FW alive check */
 | 
						|
+	ret = cdns_mhdp_check_alive(&dp->mhdp);
 | 
						|
+	if (ret == false) {
 | 
						|
+		DRM_ERROR("NO dp FW running\n");
 | 
						|
+		return ERR_PTR(-ENXIO);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* DP PHY init before AUX init */
 | 
						|
+	hdp_plat_call(dp, phy_init);
 | 
						|
+
 | 
						|
+	/* Enable Hotplug Detect IRQ thread */
 | 
						|
+	irq_set_status_flags(dp->irq[IRQ_IN], IRQ_NOAUTOEN);
 | 
						|
+	ret = devm_request_threaded_irq(dev, dp->irq[IRQ_IN],
 | 
						|
+					NULL, cdns_dp_irq_thread,
 | 
						|
+					IRQF_ONESHOT, dev_name(dev),
 | 
						|
+					dp);
 | 
						|
+	if (ret) {
 | 
						|
+		dev_err(&pdev->dev, "can't claim irq %d\n",
 | 
						|
+						dp->irq[IRQ_IN]);
 | 
						|
+		goto err_out;
 | 
						|
+	}
 | 
						|
+	
 | 
						|
+	irq_set_status_flags(dp->irq[IRQ_OUT], IRQ_NOAUTOEN);
 | 
						|
+	ret = devm_request_threaded_irq(dev, dp->irq[IRQ_OUT],
 | 
						|
+					NULL, cdns_dp_irq_thread,
 | 
						|
+					IRQF_ONESHOT, dev_name(dev),
 | 
						|
+					dp);
 | 
						|
+	if (ret) {
 | 
						|
+		dev_err(&pdev->dev, "can't claim irq %d\n",
 | 
						|
+						dp->irq[IRQ_OUT]);
 | 
						|
+		goto err_out;
 | 
						|
+	}
 | 
						|
+	if (cdns_mhdp_read_hpd(&dp->mhdp))
 | 
						|
+		enable_irq(dp->irq[IRQ_OUT]);
 | 
						|
+	else
 | 
						|
+		enable_irq(dp->irq[IRQ_IN]);
 | 
						|
+
 | 
						|
+	dp->mhdp.bridge.base.driver_private = dp;
 | 
						|
+	dp->mhdp.bridge.base.funcs = &cdns_dp_bridge_funcs;
 | 
						|
+#ifdef CONFIG_OF
 | 
						|
+	dp->mhdp.bridge.base.of_node = pdev->dev.of_node;
 | 
						|
+#endif
 | 
						|
+
 | 
						|
+	platform_set_drvdata(pdev, dp);
 | 
						|
+	
 | 
						|
+	dp_aux_init(&dp->mhdp, dev);
 | 
						|
+
 | 
						|
+	return dp;
 | 
						|
+
 | 
						|
+err_out:
 | 
						|
+	return ERR_PTR(ret);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void __cdns_dp_remove(struct imx_mhdp_device *dp)
 | 
						|
+{
 | 
						|
+	dp_aux_destroy(&dp->mhdp);
 | 
						|
+}
 | 
						|
+
 | 
						|
+/* -----------------------------------------------------------------------------
 | 
						|
+ * Probe/remove API, used from platforms based on the DRM bridge API.
 | 
						|
+ */
 | 
						|
+int cdns_dp_probe(struct platform_device *pdev,
 | 
						|
+		  const struct cdn_plat_data *plat_data)
 | 
						|
+{
 | 
						|
+	struct imx_mhdp_device *dp;
 | 
						|
+
 | 
						|
+	dp = __cdns_dp_probe(pdev, plat_data);
 | 
						|
+	if (IS_ERR(dp))
 | 
						|
+		return PTR_ERR(dp);
 | 
						|
+
 | 
						|
+	drm_bridge_add(&dp->mhdp.bridge.base);
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+EXPORT_SYMBOL_GPL(cdns_dp_probe);
 | 
						|
+
 | 
						|
+void cdns_dp_remove(struct platform_device *pdev)
 | 
						|
+{
 | 
						|
+	struct imx_mhdp_device *dp = platform_get_drvdata(pdev);
 | 
						|
+
 | 
						|
+	drm_bridge_remove(&dp->mhdp.bridge.base);
 | 
						|
+
 | 
						|
+	__cdns_dp_remove(dp);
 | 
						|
+}
 | 
						|
+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,
 | 
						|
+		 const struct cdn_plat_data *plat_data)
 | 
						|
+{
 | 
						|
+	struct imx_mhdp_device *dp;
 | 
						|
+	int ret;
 | 
						|
+
 | 
						|
+	dp = __cdns_dp_probe(pdev, plat_data);
 | 
						|
+	if (IS_ERR(dp))
 | 
						|
+		return PTR_ERR(dp);
 | 
						|
+
 | 
						|
+	ret = drm_bridge_attach(encoder, &dp->mhdp.bridge.base, NULL);
 | 
						|
+	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 imx_mhdp_device *dp = dev_get_drvdata(dev);
 | 
						|
+
 | 
						|
+	__cdns_dp_remove(dp);
 | 
						|
+}
 | 
						|
+EXPORT_SYMBOL_GPL(cdns_dp_unbind);
 | 
						|
+
 | 
						|
+MODULE_AUTHOR("Sandor Yu <sandor.yu@nxp.com>");
 | 
						|
+MODULE_DESCRIPTION("Cadence Display Port transmitter driver");
 | 
						|
+MODULE_LICENSE("GPL");
 | 
						|
+MODULE_ALIAS("platform:cdn-dp");
 | 
						|
--- /dev/null
 | 
						|
+++ b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c
 | 
						|
@@ -0,0 +1,643 @@
 | 
						|
+/*
 | 
						|
+ * 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 <drm/bridge/cdns-mhdp-imx.h>
 | 
						|
+#include <drm/drm_atomic_helper.h>
 | 
						|
+#include <drm/drm_crtc_helper.h>
 | 
						|
+#include <drm/drm_edid.h>
 | 
						|
+#include <drm/drm_encoder_slave.h>
 | 
						|
+#include <drm/drm_of.h>
 | 
						|
+#include <drm/drm_probe_helper.h>
 | 
						|
+#include <drm/drmP.h>
 | 
						|
+#include <linux/delay.h>
 | 
						|
+#include <linux/err.h>
 | 
						|
+#include <linux/hdmi.h>
 | 
						|
+#include <linux/irq.h>
 | 
						|
+#include <linux/module.h>
 | 
						|
+#include <linux/mfd/syscon.h>
 | 
						|
+#include <linux/mutex.h>
 | 
						|
+#include <linux/regmap.h>
 | 
						|
+#include <linux/of_device.h>
 | 
						|
+
 | 
						|
+static void hdmi_writel(struct cdns_mhdp_device *mhdp, u32 val, u32 offset)
 | 
						|
+{
 | 
						|
+	struct imx_mhdp_device *hdmi = container_of(mhdp, struct imx_mhdp_device, mhdp);
 | 
						|
+
 | 
						|
+	/* TODO */
 | 
						|
+	if (offset >= 0x1000 && hdmi->regmap_csr) {
 | 
						|
+		/* Remap address to low 4K memory */
 | 
						|
+		regmap_write(hdmi->regmap_csr, hdmi->csr_ctrl0_reg, offset >> 12);
 | 
						|
+		writel(val, (offset & 0xfff) + mhdp->regs);
 | 
						|
+		/* Restore address mapping */
 | 
						|
+		regmap_write(hdmi->regmap_csr, hdmi->csr_ctrl0_reg, 0);
 | 
						|
+
 | 
						|
+	} else
 | 
						|
+		writel(val, mhdp->regs + offset);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int hdmi_sink_config(struct cdns_mhdp_device *mhdp)
 | 
						|
+{
 | 
						|
+	struct drm_scdc *scdc = &mhdp->connector.base.display_info.hdmi.scdc;
 | 
						|
+	u8 buff;
 | 
						|
+	int ret;
 | 
						|
+
 | 
						|
+	if (mhdp->hdmi.char_rate > 340000) {
 | 
						|
+		/*
 | 
						|
+		 * TMDS Character Rate above 340MHz should working in HDMI2.0
 | 
						|
+		 * Enable scrambling and TMDS_Bit_Clock_Ratio
 | 
						|
+		 */
 | 
						|
+		buff = 3;
 | 
						|
+		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 = 1;
 | 
						|
+		mhdp->hdmi.hdmi_type = MODE_HDMI_2_0;
 | 
						|
+	} else {
 | 
						|
+		/* Default work in HDMI1.4 */
 | 
						|
+		buff = 0;
 | 
						|
+		mhdp->hdmi.hdmi_type = MODE_HDMI_1_4;
 | 
						|
+	 }
 | 
						|
+
 | 
						|
+	/* TMDS config */
 | 
						|
+	ret = cdns_hdmi_scdc_write(mhdp, 0x20, buff);
 | 
						|
+	return ret;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int hdmi_lanes_config(struct cdns_mhdp_device *mhdp)
 | 
						|
+{
 | 
						|
+	int ret;
 | 
						|
+
 | 
						|
+	/* TODO */
 | 
						|
+	/* Set the lane swapping */
 | 
						|
+//	if (cpu_is_imx8qm())
 | 
						|
+		ret = cdns_mhdp_reg_write(mhdp, LANES_CONFIG,
 | 
						|
+						    F_SOURCE_PHY_LANE0_SWAP(3) |
 | 
						|
+						    F_SOURCE_PHY_LANE1_SWAP(0) |
 | 
						|
+						    F_SOURCE_PHY_LANE2_SWAP(1) |
 | 
						|
+						    F_SOURCE_PHY_LANE3_SWAP(2) |
 | 
						|
+						    F_SOURCE_PHY_COMB_BYPASS(0) |
 | 
						|
+							F_SOURCE_PHY_20_10(1));
 | 
						|
+#if 0
 | 
						|
+	else
 | 
						|
+		ret = cdns_mhdp_reg_write(mhdp, LANES_CONFIG,
 | 
						|
+						    F_SOURCE_PHY_LANE0_SWAP(0) |
 | 
						|
+						    F_SOURCE_PHY_LANE1_SWAP(1) |
 | 
						|
+						    F_SOURCE_PHY_LANE2_SWAP(2) |
 | 
						|
+						    F_SOURCE_PHY_LANE3_SWAP(3) |
 | 
						|
+						    F_SOURCE_PHY_COMB_BYPASS(0) |
 | 
						|
+							F_SOURCE_PHY_20_10(1));
 | 
						|
+#endif
 | 
						|
+	return ret;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void hdmi_info_frame_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);
 | 
						|
+	hdmi_writel(mhdp, val, SOURCE_PIF_PKT_ALLOC_REG);
 | 
						|
+	hdmi_writel(mhdp, F_PKT_ALLOC_WR_EN(1), SOURCE_PIF_PKT_ALLOC_WR_EN);
 | 
						|
+
 | 
						|
+	/* flush fifo 1 */
 | 
						|
+	hdmi_writel(mhdp, F_FIFO1_FLUSH(1), SOURCE_PIF_FIFO1_FLUSH);
 | 
						|
+
 | 
						|
+	/* write packet into memory */
 | 
						|
+	packet32 = (u32 *)packet;
 | 
						|
+	len32 = packet_len / 4;
 | 
						|
+	for (i = 0; i < len32; i++)
 | 
						|
+		hdmi_writel(mhdp, F_DATA_WR(packet32[i]), SOURCE_PIF_DATA_WR);
 | 
						|
+
 | 
						|
+	/* write entry id */
 | 
						|
+	hdmi_writel(mhdp, F_WR_ADDR(entry_id), SOURCE_PIF_WR_ADDR);
 | 
						|
+
 | 
						|
+	/* write request */
 | 
						|
+	hdmi_writel(mhdp, F_HOST_WR(1), 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);
 | 
						|
+	hdmi_writel(mhdp, val, SOURCE_PIF_PKT_ALLOC_REG);
 | 
						|
+
 | 
						|
+	hdmi_writel(mhdp, F_PKT_ALLOC_WR_EN(1), SOURCE_PIF_PKT_ALLOC_WR_EN);
 | 
						|
+}
 | 
						|
+
 | 
						|
+#define RGB_ALLOWED_COLORIMETRY (BIT(HDMI_EXTENDED_COLORIMETRY_BT2020) |\
 | 
						|
+				 BIT(HDMI_EXTENDED_COLORIMETRY_OPRGB))
 | 
						|
+#define YCC_ALLOWED_COLORIMETRY (BIT(HDMI_EXTENDED_COLORIMETRY_BT2020) |\
 | 
						|
+				 BIT(HDMI_EXTENDED_COLORIMETRY_BT2020_CONST_LUM) |\
 | 
						|
+				 BIT(HDMI_EXTENDED_COLORIMETRY_OPYCC_601) |\
 | 
						|
+				 BIT(HDMI_EXTENDED_COLORIMETRY_S_YCC_601) |\
 | 
						|
+				 BIT(HDMI_EXTENDED_COLORIMETRY_XV_YCC_709) |\
 | 
						|
+				 BIT(HDMI_EXTENDED_COLORIMETRY_XV_YCC_601))
 | 
						|
+static int hdmi_avi_info_set(struct cdns_mhdp_device *mhdp,
 | 
						|
+				struct drm_display_mode *mode)
 | 
						|
+{
 | 
						|
+	struct hdmi_avi_infoframe frame;
 | 
						|
+//	struct drm_display_info *di = &mhdp->connector.base.display_info;
 | 
						|
+//	enum hdmi_extended_colorimetry ext_col;
 | 
						|
+//	u32 sink_col, allowed_col;
 | 
						|
+	int format = mhdp->video_info.color_fmt;
 | 
						|
+	u8 buf[32];
 | 
						|
+	int ret;
 | 
						|
+
 | 
						|
+	/* Initialise info frame from DRM mode */
 | 
						|
+	drm_hdmi_avi_infoframe_from_display_mode(&frame, &mhdp->connector.base, mode);
 | 
						|
+
 | 
						|
+#if 0 //TODO to DCSS
 | 
						|
+	/* Set up colorimetry */
 | 
						|
+	allowed_col = format == PXL_RGB ? RGB_ALLOWED_COLORIMETRY :
 | 
						|
+						  YCC_ALLOWED_COLORIMETRY;
 | 
						|
+
 | 
						|
+	sink_col = di->hdmi.colorimetry & allowed_col;
 | 
						|
+
 | 
						|
+	if (sink_col & BIT(HDMI_EXTENDED_COLORIMETRY_BT2020))
 | 
						|
+		ext_col = HDMI_EXTENDED_COLORIMETRY_BT2020;
 | 
						|
+	else if (sink_col & BIT(HDMI_EXTENDED_COLORIMETRY_BT2020_CONST_LUM))
 | 
						|
+		ext_col = HDMI_EXTENDED_COLORIMETRY_BT2020_CONST_LUM;
 | 
						|
+	else if (sink_col & BIT(HDMI_EXTENDED_COLORIMETRY_OPRGB))
 | 
						|
+		ext_col = HDMI_EXTENDED_COLORIMETRY_OPRGB;
 | 
						|
+	else if (sink_col & BIT(HDMI_EXTENDED_COLORIMETRY_XV_YCC_709))
 | 
						|
+		ext_col = HDMI_EXTENDED_COLORIMETRY_XV_YCC_709;
 | 
						|
+	else if (sink_col & BIT(HDMI_EXTENDED_COLORIMETRY_OPYCC_601))
 | 
						|
+		ext_col = HDMI_EXTENDED_COLORIMETRY_OPYCC_601;
 | 
						|
+	else if (sink_col & BIT(HDMI_EXTENDED_COLORIMETRY_S_YCC_601))
 | 
						|
+		ext_col = HDMI_EXTENDED_COLORIMETRY_S_YCC_601;
 | 
						|
+	else if (sink_col & BIT(HDMI_EXTENDED_COLORIMETRY_XV_YCC_601))
 | 
						|
+		ext_col = HDMI_EXTENDED_COLORIMETRY_XV_YCC_601;
 | 
						|
+	else
 | 
						|
+		ext_col = 0;
 | 
						|
+
 | 
						|
+	frame.colorimetry = sink_col ? HDMI_COLORIMETRY_EXTENDED :
 | 
						|
+					  HDMI_COLORIMETRY_NONE;
 | 
						|
+	frame.extended_colorimetry = ext_col;
 | 
						|
+#endif
 | 
						|
+
 | 
						|
+	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;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	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;
 | 
						|
+	hdmi_info_frame_set(mhdp, 0, sizeof(buf), buf, HDMI_INFOFRAME_TYPE_AVI);
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int 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_WARN("Unable to init vendor infoframe: %d\n", ret);
 | 
						|
+		return -1;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	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 -1;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	buf[0] = 0;
 | 
						|
+	hdmi_info_frame_set(mhdp, 3, sizeof(buf), buf, HDMI_INFOFRAME_TYPE_VENDOR);
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+void cdns_hdmi_mode_set(struct cdns_mhdp_device *mhdp)
 | 
						|
+{
 | 
						|
+	struct drm_display_mode *mode = &mhdp->mode;
 | 
						|
+	int ret;
 | 
						|
+
 | 
						|
+	ret = hdmi_sink_config(mhdp);
 | 
						|
+	if (ret < 0) {
 | 
						|
+		DRM_ERROR("%s failed\n", __func__);
 | 
						|
+		return;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	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 */
 | 
						|
+	ret = hdmi_vendor_info_set(mhdp, mode);
 | 
						|
+	if (ret < 0)
 | 
						|
+		DRM_WARN("Unable to configure Vendor infoframe\n");
 | 
						|
+
 | 
						|
+	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;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* wait HDMI PHY pixel clock stable */
 | 
						|
+	msleep(50);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static enum drm_connector_status
 | 
						|
+cdns_hdmi_connector_detect(struct drm_connector *connector, bool force)
 | 
						|
+{
 | 
						|
+	struct imx_mhdp_device *hdmi =
 | 
						|
+				container_of(connector, struct imx_mhdp_device, mhdp.connector.base);
 | 
						|
+
 | 
						|
+	u8 hpd = 0xf;
 | 
						|
+
 | 
						|
+	hpd = cdns_mhdp_read_hpd(&hdmi->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 imx_mhdp_device *hdmi = container_of(connector, struct imx_mhdp_device,
 | 
						|
+					     mhdp.connector.base);
 | 
						|
+	int num_modes = 0;
 | 
						|
+	struct edid *edid;
 | 
						|
+
 | 
						|
+	edid = drm_do_get_edid(&hdmi->mhdp.connector.base,
 | 
						|
+				   cdns_hdmi_get_edid_block, &hdmi->mhdp);
 | 
						|
+	if (edid) {
 | 
						|
+		dev_info(hdmi->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_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,
 | 
						|
+};
 | 
						|
+
 | 
						|
+static int cdns_hdmi_bridge_attach(struct drm_bridge *bridge)
 | 
						|
+{
 | 
						|
+	struct imx_mhdp_device *hdmi = bridge->driver_private;
 | 
						|
+	struct drm_encoder *encoder = bridge->encoder;
 | 
						|
+	struct drm_connector *connector = &hdmi->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);
 | 
						|
+
 | 
						|
+	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)
 | 
						|
+{
 | 
						|
+	enum drm_mode_status mode_status = MODE_OK;
 | 
						|
+
 | 
						|
+	/* 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 148.5MHz */
 | 
						|
+	if (mode->clock > 148500)
 | 
						|
+		return MODE_CLOCK_HIGH;
 | 
						|
+
 | 
						|
+	/* 4096x2160 is not supported */
 | 
						|
+	if (mode->hdisplay > 3840 || mode->vdisplay > 2160)
 | 
						|
+		return MODE_BAD_HVALUE;
 | 
						|
+
 | 
						|
+	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 imx_mhdp_device *hdmi = bridge->driver_private;
 | 
						|
+	struct drm_display_info *display_info = &hdmi->mhdp.connector.base.display_info;
 | 
						|
+	struct video_info *video = &hdmi->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);
 | 
						|
+
 | 
						|
+	mutex_lock(&hdmi->lock);
 | 
						|
+
 | 
						|
+	DRM_INFO("Mode: %dx%dp%d\n", mode->hdisplay, mode->vdisplay, mode->clock); 
 | 
						|
+
 | 
						|
+	memcpy(&hdmi->mhdp.mode, mode, sizeof(struct drm_display_mode));
 | 
						|
+
 | 
						|
+	hdmi->dual_mode = video_is_dual_mode(mode);
 | 
						|
+
 | 
						|
+	hdmi_lanes_config(&hdmi->mhdp);
 | 
						|
+
 | 
						|
+	hdp_plat_call(hdmi, pclock_change);
 | 
						|
+
 | 
						|
+	hdp_plat_call(hdmi, phy_init);
 | 
						|
+
 | 
						|
+	cdns_hdmi_mode_set(&hdmi->mhdp);
 | 
						|
+
 | 
						|
+	mutex_unlock(&hdmi->lock);
 | 
						|
+}
 | 
						|
+
 | 
						|
+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,
 | 
						|
+};
 | 
						|
+
 | 
						|
+static void hotplug_work_func(struct work_struct *work)
 | 
						|
+{
 | 
						|
+	struct imx_mhdp_device *hdmi = container_of(work,
 | 
						|
+					   struct imx_mhdp_device, hotplug_work.work);
 | 
						|
+	struct drm_connector *connector = &hdmi->mhdp.connector.base;
 | 
						|
+
 | 
						|
+	drm_helper_hpd_irq_event(connector->dev);
 | 
						|
+
 | 
						|
+	if (connector->status == connector_status_connected) {
 | 
						|
+		/* Cable Connected */
 | 
						|
+		DRM_INFO("HDMI Cable Plug In\n");
 | 
						|
+		enable_irq(hdmi->irq[IRQ_OUT]);
 | 
						|
+	} else if (connector->status == connector_status_disconnected) {
 | 
						|
+		/* Cable Disconnedted  */
 | 
						|
+		DRM_INFO("HDMI Cable Plug Out\n");
 | 
						|
+		enable_irq(hdmi->irq[IRQ_IN]);
 | 
						|
+	}
 | 
						|
+}
 | 
						|
+
 | 
						|
+static irqreturn_t cdns_hdmi_irq_thread(int irq, void *data)
 | 
						|
+{
 | 
						|
+	struct imx_mhdp_device *hdmi = data;
 | 
						|
+
 | 
						|
+	disable_irq_nosync(irq);
 | 
						|
+
 | 
						|
+	mod_delayed_work(system_wq, &hdmi->hotplug_work,
 | 
						|
+			msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS));
 | 
						|
+
 | 
						|
+	return IRQ_HANDLED;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static struct imx_mhdp_device *
 | 
						|
+__cdns_hdmi_probe(struct platform_device *pdev,
 | 
						|
+			const struct cdn_plat_data *plat_data)
 | 
						|
+{
 | 
						|
+	struct device *dev = &pdev->dev;
 | 
						|
+	struct device_node *np = dev->of_node;
 | 
						|
+	struct platform_device_info pdevinfo;
 | 
						|
+	struct imx_mhdp_device *hdmi;
 | 
						|
+	struct resource *iores = NULL;
 | 
						|
+	int ret;
 | 
						|
+
 | 
						|
+	hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
 | 
						|
+	if (!hdmi)
 | 
						|
+		return ERR_PTR(-ENOMEM);
 | 
						|
+
 | 
						|
+	hdmi->plat_data = plat_data;
 | 
						|
+	hdmi->mhdp.dev = dev;
 | 
						|
+
 | 
						|
+	mutex_init(&hdmi->lock);
 | 
						|
+	mutex_init(&hdmi->audio_mutex);
 | 
						|
+	spin_lock_init(&hdmi->audio_lock);
 | 
						|
+
 | 
						|
+	INIT_DELAYED_WORK(&hdmi->hotplug_work, hotplug_work_func);
 | 
						|
+
 | 
						|
+	iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 | 
						|
+	hdmi->mhdp.regs = devm_ioremap(dev, iores->start, resource_size(iores));
 | 
						|
+	if (IS_ERR(hdmi->mhdp.regs)) {
 | 
						|
+		ret = PTR_ERR(hdmi->mhdp.regs);
 | 
						|
+		goto err_out;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* csr register base */
 | 
						|
+	hdmi->regmap_csr = syscon_regmap_lookup_by_phandle(np, "csr");
 | 
						|
+	if (IS_ERR(hdmi->regmap_csr)) {
 | 
						|
+		dev_info(dev, "No csr regmap\n");
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	hdmi->irq[IRQ_IN] = platform_get_irq_byname(pdev, "plug_in");
 | 
						|
+	if (hdmi->irq[IRQ_IN] < 0) {
 | 
						|
+		dev_info(&pdev->dev, "No plug_in irq number\n");
 | 
						|
+		return ERR_PTR(-EPROBE_DEFER);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	hdmi->irq[IRQ_OUT] = platform_get_irq_byname(pdev, "plug_out");
 | 
						|
+	if (hdmi->irq[IRQ_OUT] < 0) {
 | 
						|
+		dev_info(&pdev->dev, "No plug_out irq number\n");
 | 
						|
+		return ERR_PTR(-EPROBE_DEFER);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* Initialize dual_mode to false */
 | 
						|
+	hdmi->dual_mode = false;
 | 
						|
+
 | 
						|
+	/* Initialize FW */
 | 
						|
+	hdp_plat_call(hdmi, fw_init);
 | 
						|
+
 | 
						|
+	/* HDMI FW alive check */
 | 
						|
+	ret = cdns_mhdp_check_alive(&hdmi->mhdp);
 | 
						|
+	if (ret == false) {
 | 
						|
+		DRM_ERROR("NO HDMI FW running\n");
 | 
						|
+		return ERR_PTR(-ENXIO);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* Enable Hotplug Detect thread */
 | 
						|
+	irq_set_status_flags(hdmi->irq[IRQ_IN], IRQ_NOAUTOEN);
 | 
						|
+	ret = devm_request_threaded_irq(dev, hdmi->irq[IRQ_IN],
 | 
						|
+					NULL, cdns_hdmi_irq_thread,
 | 
						|
+					IRQF_ONESHOT, dev_name(dev),
 | 
						|
+					hdmi);
 | 
						|
+	if (ret) {
 | 
						|
+		dev_err(&pdev->dev, "can't claim irq %d\n",
 | 
						|
+						hdmi->irq[IRQ_IN]);
 | 
						|
+		goto err_out;
 | 
						|
+	}
 | 
						|
+	
 | 
						|
+	irq_set_status_flags(hdmi->irq[IRQ_OUT], IRQ_NOAUTOEN);
 | 
						|
+	ret = devm_request_threaded_irq(dev, hdmi->irq[IRQ_OUT],
 | 
						|
+					NULL, cdns_hdmi_irq_thread,
 | 
						|
+					IRQF_ONESHOT, dev_name(dev),
 | 
						|
+					hdmi);
 | 
						|
+	if (ret) {
 | 
						|
+		dev_err(&pdev->dev, "can't claim irq %d\n",
 | 
						|
+						hdmi->irq[IRQ_OUT]);
 | 
						|
+		goto err_out;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (cdns_mhdp_read_hpd(&hdmi->mhdp))
 | 
						|
+		enable_irq(hdmi->irq[IRQ_OUT]);
 | 
						|
+	else
 | 
						|
+		enable_irq(hdmi->irq[IRQ_IN]);
 | 
						|
+
 | 
						|
+	hdmi->mhdp.bridge.base.driver_private = hdmi;
 | 
						|
+	hdmi->mhdp.bridge.base.funcs = &cdns_hdmi_bridge_funcs;
 | 
						|
+#ifdef CONFIG_OF
 | 
						|
+	hdmi->mhdp.bridge.base.of_node = pdev->dev.of_node;
 | 
						|
+#endif
 | 
						|
+
 | 
						|
+	memset(&pdevinfo, 0, sizeof(pdevinfo));
 | 
						|
+	pdevinfo.parent = dev;
 | 
						|
+	pdevinfo.id = PLATFORM_DEVID_AUTO;
 | 
						|
+
 | 
						|
+	platform_set_drvdata(pdev, hdmi);
 | 
						|
+
 | 
						|
+	return hdmi;
 | 
						|
+
 | 
						|
+err_out:
 | 
						|
+
 | 
						|
+	return ERR_PTR(ret);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void __cdns_hdmi_remove(struct imx_mhdp_device *hdmi)
 | 
						|
+{
 | 
						|
+}
 | 
						|
+
 | 
						|
+/* -----------------------------------------------------------------------------
 | 
						|
+ * Probe/remove API, used from platforms based on the DRM bridge API.
 | 
						|
+ */
 | 
						|
+int cdns_hdmi_probe(struct platform_device *pdev,
 | 
						|
+		  const struct cdn_plat_data *plat_data)
 | 
						|
+{
 | 
						|
+	struct imx_mhdp_device *hdmi;
 | 
						|
+
 | 
						|
+	hdmi = __cdns_hdmi_probe(pdev, plat_data);
 | 
						|
+	if (IS_ERR(hdmi))
 | 
						|
+		return PTR_ERR(hdmi);
 | 
						|
+
 | 
						|
+	drm_bridge_add(&hdmi->mhdp.bridge.base);
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+EXPORT_SYMBOL_GPL(cdns_hdmi_probe);
 | 
						|
+
 | 
						|
+void cdns_hdmi_remove(struct platform_device *pdev)
 | 
						|
+{
 | 
						|
+	struct imx_mhdp_device *hdmi = platform_get_drvdata(pdev);
 | 
						|
+
 | 
						|
+	drm_bridge_remove(&hdmi->mhdp.bridge.base);
 | 
						|
+
 | 
						|
+	__cdns_hdmi_remove(hdmi);
 | 
						|
+}
 | 
						|
+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,
 | 
						|
+		 const struct cdn_plat_data *plat_data)
 | 
						|
+{
 | 
						|
+	struct imx_mhdp_device *hdmi;
 | 
						|
+	int ret;
 | 
						|
+
 | 
						|
+	hdmi = __cdns_hdmi_probe(pdev, plat_data);
 | 
						|
+	if (IS_ERR(hdmi))
 | 
						|
+		return PTR_ERR(hdmi);
 | 
						|
+
 | 
						|
+	ret = drm_bridge_attach(encoder, &hdmi->mhdp.bridge.base, NULL);
 | 
						|
+	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 imx_mhdp_device *hdmi = dev_get_drvdata(dev);
 | 
						|
+
 | 
						|
+	__cdns_hdmi_remove(hdmi);
 | 
						|
+}
 | 
						|
+EXPORT_SYMBOL_GPL(cdns_hdmi_unbind);
 | 
						|
+
 | 
						|
+MODULE_AUTHOR("Sandor Yu <sandor.yu@nxp.com>");
 | 
						|
+MODULE_DESCRIPTION("Cadence HDMI transmitter driver");
 | 
						|
+MODULE_LICENSE("GPL");
 | 
						|
+MODULE_ALIAS("platform:cdn-hdmi");
 | 
						|
--- /dev/null
 | 
						|
+++ b/include/drm/bridge/cdns-mhdp-imx.h
 | 
						|
@@ -0,0 +1,121 @@
 | 
						|
+/*
 | 
						|
+ * 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 <drm/bridge/cdns-mhdp-common.h>
 | 
						|
+
 | 
						|
+#define IRQ_IN    0
 | 
						|
+#define IRQ_OUT   1
 | 
						|
+#define IRQ_NUM   2
 | 
						|
+
 | 
						|
+#define hdp_plat_call(hdp, operation)			\
 | 
						|
+	(!(hdp) ? -ENODEV : (((hdp)->plat_data && (hdp)->plat_data->operation) ?	\
 | 
						|
+	 (hdp)->plat_data->operation(hdp) : ENOIOCTLCMD))
 | 
						|
+
 | 
						|
+#define HDP_DUAL_MODE_MIN_PCLK_RATE	300000	/* KHz */
 | 
						|
+#define HDP_SINGLE_MODE_MAX_WIDTH	1920
 | 
						|
+
 | 
						|
+static inline bool video_is_dual_mode(const struct drm_display_mode *mode)
 | 
						|
+{
 | 
						|
+	return (mode->clock > HDP_DUAL_MODE_MIN_PCLK_RATE ||
 | 
						|
+		mode->hdisplay > HDP_SINGLE_MODE_MAX_WIDTH) ? true : false;
 | 
						|
+}
 | 
						|
+
 | 
						|
+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 cdn_plat_data {
 | 
						|
+	/* Vendor PHY support */
 | 
						|
+	int (*phy_init)(struct imx_mhdp_device *hdmi);
 | 
						|
+	int (*bind)(struct platform_device *pdev,
 | 
						|
+			struct drm_encoder *encoder,
 | 
						|
+		 	const struct cdn_plat_data *plat_data);
 | 
						|
+	void (*unbind)(struct device *dev);
 | 
						|
+	int (*fw_init)(struct imx_mhdp_device *hdp);
 | 
						|
+	void (*pclock_change)(struct imx_mhdp_device *hdp);
 | 
						|
+	char is_dp;
 | 
						|
+};
 | 
						|
+
 | 
						|
+struct imx_mhdp_device {
 | 
						|
+	struct cdns_mhdp_device mhdp;
 | 
						|
+
 | 
						|
+	struct mutex lock;
 | 
						|
+	struct mutex audio_mutex;
 | 
						|
+	spinlock_t audio_lock;
 | 
						|
+	bool connected;
 | 
						|
+	bool active;
 | 
						|
+	bool suspended;
 | 
						|
+	struct imx_hdp_clks clks;
 | 
						|
+
 | 
						|
+	const struct cdn_plat_data *plat_data;
 | 
						|
+
 | 
						|
+	int irq[IRQ_NUM];
 | 
						|
+	struct delayed_work hotplug_work;
 | 
						|
+	//void __iomem *regmap_csr;
 | 
						|
+	struct regmap *regmap_csr;
 | 
						|
+	u32 csr_pxl_mux_reg;
 | 
						|
+	u32 csr_ctrl0_reg;
 | 
						|
+	u32 csr_ctrl0_sec;
 | 
						|
+
 | 
						|
+	struct audio_info audio_info;
 | 
						|
+	bool sink_has_audio;
 | 
						|
+	u32 dual_mode;
 | 
						|
+
 | 
						|
+	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;
 | 
						|
+
 | 
						|
+	u32 phy_init;
 | 
						|
+};
 | 
						|
+
 | 
						|
+int cdns_hdmi_probe(struct platform_device *pdev,
 | 
						|
+		  const struct cdn_plat_data *plat_data);
 | 
						|
+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,
 | 
						|
+		 const struct cdn_plat_data *plat_data);
 | 
						|
+void cdns_hdmi_set_sample_rate(struct imx_mhdp_device *hdmi, unsigned int rate);
 | 
						|
+void cdns_hdmi_audio_enable(struct imx_mhdp_device *hdmi);
 | 
						|
+void cdns_hdmi_audio_disable(struct imx_mhdp_device *hdmi);
 | 
						|
+int cdns_dp_probe(struct platform_device *pdev,
 | 
						|
+		  const struct cdn_plat_data *plat_data);
 | 
						|
+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,
 | 
						|
+		 const struct cdn_plat_data *plat_data);
 | 
						|
+
 | 
						|
+#endif /* CDNS_MHDP_IMX_H_ */
 |