dri-devel.lists.freedesktop.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v6 0/4] IT6505 cover letter
@ 2020-01-20  2:44 allen
  2020-01-20  2:44 ` [PATCH v6 2/4] Revert "drm/tegra: Move drm_dp_link helpers to Tegra DRM" allen
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: allen @ 2020-01-20  2:44 UTC (permalink / raw)
  Cc: open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	Jernej Skrabec, Jau-Chih Tseng, Allen Chen, Jonas Karlman,
	open list, open list:DRM DRIVERS, Mauro Carvalho Chehab,
	moderated list:ARM/Mediatek SoC support, Laurent Pinchart,
	Pi-Hsun Shih, open list:DRM DRIVERS FOR NVIDIA TEGRA, Shawn Guo,
	Sam Ravnborg, moderated list:ARM/Mediatek SoC support,
	Icenowy Zheng

The IT6505 is a high-performance DisplayPort 1.1a transmitter, fully compliant
with DisplayPort 1.1a, HDCP 1.3 specifications. The IT6505 supports color depth
of up to 36 bits (12 bits/color) and ensures robust transmission of high-quality
uncompressed video content, along with uncompressed and compressed digital audio
content.

This series contains document bindings, revert commit, add vendor prefix,
Kconfig to control the function enable or not.

Allen Chen (1):
  WIP: drm/bridge: add it6505 driver

allen (3):
  dt-bindings: Add vendor prefix for ITE Tech. Inc.
  Revert "drm/tegra: Move drm_dp_link helpers to Tegra DRM"
  WIP: dt-bindings: Add binding for IT6505.

 .../bindings/display/bridge/ite,it6505.yaml        |   89 +
 .../devicetree/bindings/vendor-prefixes.yaml       |    2 +
 drivers/gpu/drm/bridge/Kconfig                     |    7 +
 drivers/gpu/drm/bridge/Makefile                    |    1 +
 drivers/gpu/drm/bridge/ite-it6505.c                | 2698 ++++++++++++++++++++
 drivers/gpu/drm/drm_dp_helper.c                    |  128 +
 drivers/gpu/drm/tegra/Makefile                     |    1 -
 drivers/gpu/drm/tegra/dp.c                         |  876 -------
 drivers/gpu/drm/tegra/dp.h                         |  177 --
 drivers/gpu/drm/tegra/dpaux.c                      |    1 -
 drivers/gpu/drm/tegra/sor.c                        |    1 -
 include/drm/drm_dp_helper.h                        |   16 +
 12 files changed, 2941 insertions(+), 1056 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/display/bridge/ite,it6505.yaml
 create mode 100644 drivers/gpu/drm/bridge/ite-it6505.c
 delete mode 100644 drivers/gpu/drm/tegra/dp.c
 delete mode 100644 drivers/gpu/drm/tegra/dp.h

-- 
1.9.1

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v6 2/4] Revert "drm/tegra: Move drm_dp_link helpers to Tegra DRM"
  2020-01-20  2:44 [PATCH v6 0/4] IT6505 cover letter allen
@ 2020-01-20  2:44 ` allen
  2020-01-20  7:09   ` Thierry Reding
  2020-01-20  2:44 ` [PATCH v6 3/4] dt-bindings: Add binding for IT6505 allen
  2020-01-20  2:44 ` [PATCH v6 4/4] drm/bridge: add it6505 driver allen
  2 siblings, 1 reply; 6+ messages in thread
From: allen @ 2020-01-20  2:44 UTC (permalink / raw)
  Cc: Jau-Chih Tseng, David Airlie, Allen Chen, open list,
	Jonathan Hunter, Thierry Reding, open list:DRM DRIVERS,
	Pi-Hsun Shih, open list:DRM DRIVERS FOR NVIDIA TEGRA, Sean Paul

IT6505 driver ite-it6505.c file using drm_dp_link helpers, so revert.
This reverts commit 9a42c7c647a9ad0f7ebb147a52eda3dcb7c84292.

Signed-off-by: Allen Chen <allen.chen@ite.com.tw>
---
 drivers/gpu/drm/drm_dp_helper.c | 128 ++++++
 drivers/gpu/drm/tegra/Makefile  |   1 -
 drivers/gpu/drm/tegra/dp.c      | 876 ----------------------------------------
 drivers/gpu/drm/tegra/dp.h      | 177 --------
 drivers/gpu/drm/tegra/dpaux.c   |   1 -
 drivers/gpu/drm/tegra/sor.c     |   1 -
 include/drm/drm_dp_helper.h     |  16 +
 7 files changed, 144 insertions(+), 1056 deletions(-)
 delete mode 100644 drivers/gpu/drm/tegra/dp.c
 delete mode 100644 drivers/gpu/drm/tegra/dp.h

diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c
index 2c7870a..f567141 100644
--- a/drivers/gpu/drm/drm_dp_helper.c
+++ b/drivers/gpu/drm/drm_dp_helper.c
@@ -352,6 +352,134 @@ int drm_dp_dpcd_read_link_status(struct drm_dp_aux *aux,
 EXPORT_SYMBOL(drm_dp_dpcd_read_link_status);
 
 /**
+ * drm_dp_link_probe() - probe a DisplayPort link for capabilities
+ * @aux: DisplayPort AUX channel
+ * @link: pointer to structure in which to return link capabilities
+ *
+ * The structure filled in by this function can usually be passed directly
+ * into drm_dp_link_power_up() and drm_dp_link_configure() to power up and
+ * configure the link based on the link's capabilities.
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link)
+{
+	u8 values[3];
+	int err;
+
+	memset(link, 0, sizeof(*link));
+
+	err = drm_dp_dpcd_read(aux, DP_DPCD_REV, values, sizeof(values));
+	if (err < 0)
+		return err;
+
+	link->revision = values[0];
+	link->rate = drm_dp_bw_code_to_link_rate(values[1]);
+	link->num_lanes = values[2] & DP_MAX_LANE_COUNT_MASK;
+
+	if (values[2] & DP_ENHANCED_FRAME_CAP)
+		link->capabilities |= DP_LINK_CAP_ENHANCED_FRAMING;
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_dp_link_probe);
+
+/**
+ * drm_dp_link_power_up() - power up a DisplayPort link
+ * @aux: DisplayPort AUX channel
+ * @link: pointer to a structure containing the link configuration
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int drm_dp_link_power_up(struct drm_dp_aux *aux, struct drm_dp_link *link)
+{
+	u8 value;
+	int err;
+
+	/* DP_SET_POWER register is only available on DPCD v1.1 and later */
+	if (link->revision < 0x11)
+		return 0;
+
+	err = drm_dp_dpcd_readb(aux, DP_SET_POWER, &value);
+	if (err < 0)
+		return err;
+
+	value &= ~DP_SET_POWER_MASK;
+	value |= DP_SET_POWER_D0;
+
+	err = drm_dp_dpcd_writeb(aux, DP_SET_POWER, value);
+	if (err < 0)
+		return err;
+
+	/*
+	 * According to the DP 1.1 specification, a "Sink Device must exit the
+	 * power saving state within 1 ms" (Section 2.5.3.1, Table 5-52, "Sink
+	 * Control Field" (register 0x600).
+	 */
+	usleep_range(1000, 2000);
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_dp_link_power_up);
+
+/**
+ * drm_dp_link_power_down() - power down a DisplayPort link
+ * @aux: DisplayPort AUX channel
+ * @link: pointer to a structure containing the link configuration
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int drm_dp_link_power_down(struct drm_dp_aux *aux, struct drm_dp_link *link)
+{
+	u8 value;
+	int err;
+
+	/* DP_SET_POWER register is only available on DPCD v1.1 and later */
+	if (link->revision < 0x11)
+		return 0;
+
+	err = drm_dp_dpcd_readb(aux, DP_SET_POWER, &value);
+	if (err < 0)
+		return err;
+
+	value &= ~DP_SET_POWER_MASK;
+	value |= DP_SET_POWER_D3;
+
+	err = drm_dp_dpcd_writeb(aux, DP_SET_POWER, value);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_dp_link_power_down);
+
+/**
+ * drm_dp_link_configure() - configure a DisplayPort link
+ * @aux: DisplayPort AUX channel
+ * @link: pointer to a structure containing the link configuration
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link)
+{
+	u8 values[2];
+	int err;
+
+	values[0] = drm_dp_link_rate_to_bw_code(link->rate);
+	values[1] = link->num_lanes;
+
+	if (link->capabilities & DP_LINK_CAP_ENHANCED_FRAMING)
+		values[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
+
+	err = drm_dp_dpcd_write(aux, DP_LINK_BW_SET, values, sizeof(values));
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_dp_link_configure);
+
+/**
  * drm_dp_downstream_max_clock() - extract branch device max
  *                                 pixel rate for legacy VGA
  *                                 converter or max TMDS clock
diff --git a/drivers/gpu/drm/tegra/Makefile b/drivers/gpu/drm/tegra/Makefile
index d6cf202..33c463e 100644
--- a/drivers/gpu/drm/tegra/Makefile
+++ b/drivers/gpu/drm/tegra/Makefile
@@ -5,7 +5,6 @@ tegra-drm-y := \
 	drm.o \
 	gem.o \
 	fb.o \
-	dp.o \
 	hub.o \
 	plane.o \
 	dc.o \
diff --git a/drivers/gpu/drm/tegra/dp.c b/drivers/gpu/drm/tegra/dp.c
deleted file mode 100644
index 70dfb7d..00000000
--- a/drivers/gpu/drm/tegra/dp.c
+++ /dev/null
@@ -1,876 +0,0 @@
-// SPDX-License-Identifier: MIT
-/*
- * Copyright (C) 2013-2019 NVIDIA Corporation
- * Copyright (C) 2015 Rob Clark
- */
-
-#include <drm/drm_crtc.h>
-#include <drm/drm_dp_helper.h>
-#include <drm/drm_print.h>
-
-#include "dp.h"
-
-static const u8 drm_dp_edp_revisions[] = { 0x11, 0x12, 0x13, 0x14 };
-
-static void drm_dp_link_caps_reset(struct drm_dp_link_caps *caps)
-{
-	caps->enhanced_framing = false;
-	caps->tps3_supported = false;
-	caps->fast_training = false;
-	caps->channel_coding = false;
-	caps->alternate_scrambler_reset = false;
-}
-
-void drm_dp_link_caps_copy(struct drm_dp_link_caps *dest,
-			   const struct drm_dp_link_caps *src)
-{
-	dest->enhanced_framing = src->enhanced_framing;
-	dest->tps3_supported = src->tps3_supported;
-	dest->fast_training = src->fast_training;
-	dest->channel_coding = src->channel_coding;
-	dest->alternate_scrambler_reset = src->alternate_scrambler_reset;
-}
-
-static void drm_dp_link_reset(struct drm_dp_link *link)
-{
-	unsigned int i;
-
-	if (!link)
-		return;
-
-	link->revision = 0;
-	link->max_rate = 0;
-	link->max_lanes = 0;
-
-	drm_dp_link_caps_reset(&link->caps);
-	link->aux_rd_interval.cr = 0;
-	link->aux_rd_interval.ce = 0;
-	link->edp = 0;
-
-	link->rate = 0;
-	link->lanes = 0;
-
-	for (i = 0; i < DP_MAX_SUPPORTED_RATES; i++)
-		link->rates[i] = 0;
-
-	link->num_rates = 0;
-}
-
-/**
- * drm_dp_link_add_rate() - add a rate to the list of supported rates
- * @link: the link to add the rate to
- * @rate: the rate to add
- *
- * Add a link rate to the list of supported link rates.
- *
- * Returns:
- * 0 on success or one of the following negative error codes on failure:
- * - ENOSPC if the maximum number of supported rates has been reached
- * - EEXISTS if the link already supports this rate
- *
- * See also:
- * drm_dp_link_remove_rate()
- */
-int drm_dp_link_add_rate(struct drm_dp_link *link, unsigned long rate)
-{
-	unsigned int i, pivot;
-
-	if (link->num_rates == DP_MAX_SUPPORTED_RATES)
-		return -ENOSPC;
-
-	for (pivot = 0; pivot < link->num_rates; pivot++)
-		if (rate <= link->rates[pivot])
-			break;
-
-	if (pivot != link->num_rates && rate == link->rates[pivot])
-		return -EEXIST;
-
-	for (i = link->num_rates; i > pivot; i--)
-		link->rates[i] = link->rates[i - 1];
-
-	link->rates[pivot] = rate;
-	link->num_rates++;
-
-	return 0;
-}
-
-/**
- * drm_dp_link_remove_rate() - remove a rate from the list of supported rates
- * @link: the link from which to remove the rate
- * @rate: the rate to remove
- *
- * Removes a link rate from the list of supported link rates.
- *
- * Returns:
- * 0 on success or one of the following negative error codes on failure:
- * - EINVAL if the specified rate is not among the supported rates
- *
- * See also:
- * drm_dp_link_add_rate()
- */
-int drm_dp_link_remove_rate(struct drm_dp_link *link, unsigned long rate)
-{
-	unsigned int i;
-
-	for (i = 0; i < link->num_rates; i++)
-		if (rate == link->rates[i])
-			break;
-
-	if (i == link->num_rates)
-		return -EINVAL;
-
-	link->num_rates--;
-
-	while (i < link->num_rates) {
-		link->rates[i] = link->rates[i + 1];
-		i++;
-	}
-
-	return 0;
-}
-
-/**
- * drm_dp_link_update_rates() - normalize the supported link rates array
- * @link: the link for which to normalize the supported link rates
- *
- * Users should call this function after they've manually modified the array
- * of supported link rates. This function removes any stale entries, compacts
- * the array and updates the supported link rate count. Note that calling the
- * drm_dp_link_remove_rate() function already does this janitorial work.
- *
- * See also:
- * drm_dp_link_add_rate(), drm_dp_link_remove_rate()
- */
-void drm_dp_link_update_rates(struct drm_dp_link *link)
-{
-	unsigned int i, count = 0;
-
-	for (i = 0; i < link->num_rates; i++) {
-		if (link->rates[i] != 0)
-			link->rates[count++] = link->rates[i];
-	}
-
-	for (i = count; i < link->num_rates; i++)
-		link->rates[i] = 0;
-
-	link->num_rates = count;
-}
-
-/**
- * drm_dp_link_probe() - probe a DisplayPort link for capabilities
- * @aux: DisplayPort AUX channel
- * @link: pointer to structure in which to return link capabilities
- *
- * The structure filled in by this function can usually be passed directly
- * into drm_dp_link_power_up() and drm_dp_link_configure() to power up and
- * configure the link based on the link's capabilities.
- *
- * Returns 0 on success or a negative error code on failure.
- */
-int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link)
-{
-	u8 dpcd[DP_RECEIVER_CAP_SIZE], value;
-	unsigned int rd_interval;
-	int err;
-
-	drm_dp_link_reset(link);
-
-	err = drm_dp_dpcd_read(aux, DP_DPCD_REV, dpcd, sizeof(dpcd));
-	if (err < 0)
-		return err;
-
-	link->revision = dpcd[DP_DPCD_REV];
-	link->max_rate = drm_dp_max_link_rate(dpcd);
-	link->max_lanes = drm_dp_max_lane_count(dpcd);
-
-	link->caps.enhanced_framing = drm_dp_enhanced_frame_cap(dpcd);
-	link->caps.tps3_supported = drm_dp_tps3_supported(dpcd);
-	link->caps.fast_training = drm_dp_fast_training_cap(dpcd);
-	link->caps.channel_coding = drm_dp_channel_coding_supported(dpcd);
-
-	if (drm_dp_alternate_scrambler_reset_cap(dpcd)) {
-		link->caps.alternate_scrambler_reset = true;
-
-		err = drm_dp_dpcd_readb(aux, DP_EDP_DPCD_REV, &value);
-		if (err < 0)
-			return err;
-
-		if (value >= ARRAY_SIZE(drm_dp_edp_revisions))
-			DRM_ERROR("unsupported eDP version: %02x\n", value);
-		else
-			link->edp = drm_dp_edp_revisions[value];
-	}
-
-	/*
-	 * The DPCD stores the AUX read interval in units of 4 ms. There are
-	 * two special cases:
-	 *
-	 *   1) if the TRAINING_AUX_RD_INTERVAL field is 0, the clock recovery
-	 *      and channel equalization should use 100 us or 400 us AUX read
-	 *      intervals, respectively
-	 *
-	 *   2) for DP v1.4 and above, clock recovery should always use 100 us
-	 *      AUX read intervals
-	 */
-	rd_interval = dpcd[DP_TRAINING_AUX_RD_INTERVAL] &
-			   DP_TRAINING_AUX_RD_MASK;
-
-	if (rd_interval > 4) {
-		DRM_DEBUG_KMS("AUX interval %u out of range (max. 4)\n",
-			      rd_interval);
-		rd_interval = 4;
-	}
-
-	rd_interval *= 4 * USEC_PER_MSEC;
-
-	if (rd_interval == 0 || link->revision >= DP_DPCD_REV_14)
-		link->aux_rd_interval.cr = 100;
-
-	if (rd_interval == 0)
-		link->aux_rd_interval.ce = 400;
-
-	link->rate = link->max_rate;
-	link->lanes = link->max_lanes;
-
-	/* Parse SUPPORTED_LINK_RATES from eDP 1.4 */
-	if (link->edp >= 0x14) {
-		u8 supported_rates[DP_MAX_SUPPORTED_RATES * 2];
-		unsigned int i;
-		u16 rate;
-
-		err = drm_dp_dpcd_read(aux, DP_SUPPORTED_LINK_RATES,
-				       supported_rates,
-				       sizeof(supported_rates));
-		if (err < 0)
-			return err;
-
-		for (i = 0; i < DP_MAX_SUPPORTED_RATES; i++) {
-			rate = supported_rates[i * 2 + 1] << 8 |
-			       supported_rates[i * 2 + 0];
-
-			drm_dp_link_add_rate(link, rate * 200);
-		}
-	}
-
-	return 0;
-}
-
-/**
- * drm_dp_link_power_up() - power up a DisplayPort link
- * @aux: DisplayPort AUX channel
- * @link: pointer to a structure containing the link configuration
- *
- * Returns 0 on success or a negative error code on failure.
- */
-int drm_dp_link_power_up(struct drm_dp_aux *aux, struct drm_dp_link *link)
-{
-	u8 value;
-	int err;
-
-	/* DP_SET_POWER register is only available on DPCD v1.1 and later */
-	if (link->revision < 0x11)
-		return 0;
-
-	err = drm_dp_dpcd_readb(aux, DP_SET_POWER, &value);
-	if (err < 0)
-		return err;
-
-	value &= ~DP_SET_POWER_MASK;
-	value |= DP_SET_POWER_D0;
-
-	err = drm_dp_dpcd_writeb(aux, DP_SET_POWER, value);
-	if (err < 0)
-		return err;
-
-	/*
-	 * According to the DP 1.1 specification, a "Sink Device must exit the
-	 * power saving state within 1 ms" (Section 2.5.3.1, Table 5-52, "Sink
-	 * Control Field" (register 0x600).
-	 */
-	usleep_range(1000, 2000);
-
-	return 0;
-}
-
-/**
- * drm_dp_link_power_down() - power down a DisplayPort link
- * @aux: DisplayPort AUX channel
- * @link: pointer to a structure containing the link configuration
- *
- * Returns 0 on success or a negative error code on failure.
- */
-int drm_dp_link_power_down(struct drm_dp_aux *aux, struct drm_dp_link *link)
-{
-	u8 value;
-	int err;
-
-	/* DP_SET_POWER register is only available on DPCD v1.1 and later */
-	if (link->revision < 0x11)
-		return 0;
-
-	err = drm_dp_dpcd_readb(aux, DP_SET_POWER, &value);
-	if (err < 0)
-		return err;
-
-	value &= ~DP_SET_POWER_MASK;
-	value |= DP_SET_POWER_D3;
-
-	err = drm_dp_dpcd_writeb(aux, DP_SET_POWER, value);
-	if (err < 0)
-		return err;
-
-	return 0;
-}
-
-/**
- * drm_dp_link_configure() - configure a DisplayPort link
- * @aux: DisplayPort AUX channel
- * @link: pointer to a structure containing the link configuration
- *
- * Returns 0 on success or a negative error code on failure.
- */
-int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link)
-{
-	u8 values[2], value;
-	int err;
-
-	if (link->ops && link->ops->configure) {
-		err = link->ops->configure(link);
-		if (err < 0) {
-			DRM_ERROR("failed to configure DP link: %d\n", err);
-			return err;
-		}
-	}
-
-	values[0] = drm_dp_link_rate_to_bw_code(link->rate);
-	values[1] = link->lanes;
-
-	if (link->caps.enhanced_framing)
-		values[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
-
-	err = drm_dp_dpcd_write(aux, DP_LINK_BW_SET, values, sizeof(values));
-	if (err < 0)
-		return err;
-
-	if (link->caps.channel_coding)
-		value = DP_SET_ANSI_8B10B;
-	else
-		value = 0;
-
-	err = drm_dp_dpcd_writeb(aux, DP_MAIN_LINK_CHANNEL_CODING_SET, value);
-	if (err < 0)
-		return err;
-
-	if (link->caps.alternate_scrambler_reset) {
-		err = drm_dp_dpcd_writeb(aux, DP_EDP_CONFIGURATION_SET,
-					 DP_ALTERNATE_SCRAMBLER_RESET_ENABLE);
-		if (err < 0)
-			return err;
-	}
-
-	return 0;
-}
-
-/**
- * drm_dp_link_choose() - choose the lowest possible configuration for a mode
- * @link: DRM DP link object
- * @mode: DRM display mode
- * @info: DRM display information
- *
- * According to the eDP specification, a source should select a configuration
- * with the lowest number of lanes and the lowest possible link rate that can
- * match the bitrate requirements of a video mode. However it must ensure not
- * to exceed the capabilities of the sink.
- *
- * Returns: 0 on success or a negative error code on failure.
- */
-int drm_dp_link_choose(struct drm_dp_link *link,
-		       const struct drm_display_mode *mode,
-		       const struct drm_display_info *info)
-{
-	/* available link symbol clock rates */
-	static const unsigned int rates[3] = { 162000, 270000, 540000 };
-	/* available number of lanes */
-	static const unsigned int lanes[3] = { 1, 2, 4 };
-	unsigned long requirement, capacity;
-	unsigned int rate = link->max_rate;
-	unsigned int i, j;
-
-	/* bandwidth requirement */
-	requirement = mode->clock * info->bpc * 3;
-
-	for (i = 0; i < ARRAY_SIZE(lanes) && lanes[i] <= link->max_lanes; i++) {
-		for (j = 0; j < ARRAY_SIZE(rates) && rates[j] <= rate; j++) {
-			/*
-			 * Capacity for this combination of lanes and rate,
-			 * factoring in the ANSI 8B/10B encoding.
-			 *
-			 * Link rates in the DRM DP helpers are really link
-			 * symbol frequencies, so a tenth of the actual rate
-			 * of the link.
-			 */
-			capacity = lanes[i] * (rates[j] * 10) * 8 / 10;
-
-			if (capacity >= requirement) {
-				DRM_DEBUG_KMS("using %u lanes at %u kHz (%lu/%lu kbps)\n",
-					      lanes[i], rates[j], requirement,
-					      capacity);
-				link->lanes = lanes[i];
-				link->rate = rates[j];
-				return 0;
-			}
-		}
-	}
-
-	return -ERANGE;
-}
-
-/**
- * DOC: Link training
- *
- * These functions contain common logic and helpers to implement DisplayPort
- * link training.
- */
-
-/**
- * drm_dp_link_train_init() - initialize DisplayPort link training state
- * @train: DisplayPort link training state
- */
-void drm_dp_link_train_init(struct drm_dp_link_train *train)
-{
-	struct drm_dp_link_train_set *request = &train->request;
-	struct drm_dp_link_train_set *adjust = &train->adjust;
-	unsigned int i;
-
-	for (i = 0; i < 4; i++) {
-		request->voltage_swing[i] = 0;
-		adjust->voltage_swing[i] = 0;
-
-		request->pre_emphasis[i] = 0;
-		adjust->pre_emphasis[i] = 0;
-
-		request->post_cursor[i] = 0;
-		adjust->post_cursor[i] = 0;
-	}
-
-	train->pattern = DP_TRAINING_PATTERN_DISABLE;
-	train->clock_recovered = false;
-	train->channel_equalized = false;
-}
-
-static bool drm_dp_link_train_valid(const struct drm_dp_link_train *train)
-{
-	return train->clock_recovered && train->channel_equalized;
-}
-
-static int drm_dp_link_apply_training(struct drm_dp_link *link)
-{
-	struct drm_dp_link_train_set *request = &link->train.request;
-	unsigned int lanes = link->lanes, *vs, *pe, *pc, i;
-	struct drm_dp_aux *aux = link->aux;
-	u8 values[4], pattern = 0;
-	int err;
-
-	err = link->ops->apply_training(link);
-	if (err < 0) {
-		DRM_ERROR("failed to apply link training: %d\n", err);
-		return err;
-	}
-
-	vs = request->voltage_swing;
-	pe = request->pre_emphasis;
-	pc = request->post_cursor;
-
-	/* write currently selected voltage-swing and pre-emphasis levels */
-	for (i = 0; i < lanes; i++)
-		values[i] = DP_TRAIN_VOLTAGE_SWING_LEVEL(vs[i]) |
-			    DP_TRAIN_PRE_EMPHASIS_LEVEL(pe[i]);
-
-	err = drm_dp_dpcd_write(aux, DP_TRAINING_LANE0_SET, values, lanes);
-	if (err < 0) {
-		DRM_ERROR("failed to set training parameters: %d\n", err);
-		return err;
-	}
-
-	/* write currently selected post-cursor level (if supported) */
-	if (link->revision >= 0x12 && link->rate == 540000) {
-		values[0] = values[1] = 0;
-
-		for (i = 0; i < lanes; i++)
-			values[i / 2] |= DP_LANE_POST_CURSOR(i, pc[i]);
-
-		err = drm_dp_dpcd_write(aux, DP_TRAINING_LANE0_1_SET2, values,
-					DIV_ROUND_UP(lanes, 2));
-		if (err < 0) {
-			DRM_ERROR("failed to set post-cursor: %d\n", err);
-			return err;
-		}
-	}
-
-	/* write link pattern */
-	if (link->train.pattern != DP_TRAINING_PATTERN_DISABLE)
-		pattern |= DP_LINK_SCRAMBLING_DISABLE;
-
-	pattern |= link->train.pattern;
-
-	err = drm_dp_dpcd_writeb(aux, DP_TRAINING_PATTERN_SET, pattern);
-	if (err < 0) {
-		DRM_ERROR("failed to set training pattern: %d\n", err);
-		return err;
-	}
-
-	return 0;
-}
-
-static void drm_dp_link_train_wait(struct drm_dp_link *link)
-{
-	unsigned long min = 0;
-
-	switch (link->train.pattern) {
-	case DP_TRAINING_PATTERN_1:
-		min = link->aux_rd_interval.cr;
-		break;
-
-	case DP_TRAINING_PATTERN_2:
-	case DP_TRAINING_PATTERN_3:
-		min = link->aux_rd_interval.ce;
-		break;
-
-	default:
-		break;
-	}
-
-	if (min > 0)
-		usleep_range(min, 2 * min);
-}
-
-static void drm_dp_link_get_adjustments(struct drm_dp_link *link,
-					u8 status[DP_LINK_STATUS_SIZE])
-{
-	struct drm_dp_link_train_set *adjust = &link->train.adjust;
-	unsigned int i;
-
-	for (i = 0; i < link->lanes; i++) {
-		adjust->voltage_swing[i] =
-			drm_dp_get_adjust_request_voltage(status, i) >>
-				DP_TRAIN_VOLTAGE_SWING_SHIFT;
-
-		adjust->pre_emphasis[i] =
-			drm_dp_get_adjust_request_pre_emphasis(status, i) >>
-				DP_TRAIN_PRE_EMPHASIS_SHIFT;
-
-		adjust->post_cursor[i] =
-			drm_dp_get_adjust_request_post_cursor(status, i);
-	}
-}
-
-static void drm_dp_link_train_adjust(struct drm_dp_link_train *train)
-{
-	struct drm_dp_link_train_set *request = &train->request;
-	struct drm_dp_link_train_set *adjust = &train->adjust;
-	unsigned int i;
-
-	for (i = 0; i < 4; i++)
-		if (request->voltage_swing[i] != adjust->voltage_swing[i])
-			request->voltage_swing[i] = adjust->voltage_swing[i];
-
-	for (i = 0; i < 4; i++)
-		if (request->pre_emphasis[i] != adjust->pre_emphasis[i])
-			request->pre_emphasis[i] = adjust->pre_emphasis[i];
-
-	for (i = 0; i < 4; i++)
-		if (request->post_cursor[i] != adjust->post_cursor[i])
-			request->post_cursor[i] = adjust->post_cursor[i];
-}
-
-static int drm_dp_link_recover_clock(struct drm_dp_link *link)
-{
-	u8 status[DP_LINK_STATUS_SIZE];
-	int err;
-
-	err = drm_dp_link_apply_training(link);
-	if (err < 0)
-		return err;
-
-	drm_dp_link_train_wait(link);
-
-	err = drm_dp_dpcd_read_link_status(link->aux, status);
-	if (err < 0) {
-		DRM_ERROR("failed to read link status: %d\n", err);
-		return err;
-	}
-
-	if (!drm_dp_clock_recovery_ok(status, link->lanes))
-		drm_dp_link_get_adjustments(link, status);
-	else
-		link->train.clock_recovered = true;
-
-	return 0;
-}
-
-static int drm_dp_link_clock_recovery(struct drm_dp_link *link)
-{
-	unsigned int repeat;
-	int err;
-
-	/* start clock recovery using training pattern 1 */
-	link->train.pattern = DP_TRAINING_PATTERN_1;
-
-	for (repeat = 1; repeat < 5; repeat++) {
-		err = drm_dp_link_recover_clock(link);
-		if (err < 0) {
-			DRM_ERROR("failed to recover clock: %d\n", err);
-			return err;
-		}
-
-		if (link->train.clock_recovered)
-			break;
-
-		drm_dp_link_train_adjust(&link->train);
-	}
-
-	return 0;
-}
-
-static int drm_dp_link_equalize_channel(struct drm_dp_link *link)
-{
-	struct drm_dp_aux *aux = link->aux;
-	u8 status[DP_LINK_STATUS_SIZE];
-	int err;
-
-	err = drm_dp_link_apply_training(link);
-	if (err < 0)
-		return err;
-
-	drm_dp_link_train_wait(link);
-
-	err = drm_dp_dpcd_read_link_status(aux, status);
-	if (err < 0) {
-		DRM_ERROR("failed to read link status: %d\n", err);
-		return err;
-	}
-
-	if (!drm_dp_clock_recovery_ok(status, link->lanes)) {
-		DRM_ERROR("clock recovery lost while equalizing channel\n");
-		link->train.clock_recovered = false;
-		return 0;
-	}
-
-	if (!drm_dp_channel_eq_ok(status, link->lanes))
-		drm_dp_link_get_adjustments(link, status);
-	else
-		link->train.channel_equalized = true;
-
-	return 0;
-}
-
-static int drm_dp_link_channel_equalization(struct drm_dp_link *link)
-{
-	unsigned int repeat;
-	int err;
-
-	/* start channel equalization using pattern 2 or 3 */
-	if (link->caps.tps3_supported)
-		link->train.pattern = DP_TRAINING_PATTERN_3;
-	else
-		link->train.pattern = DP_TRAINING_PATTERN_2;
-
-	for (repeat = 1; repeat < 5; repeat++) {
-		err = drm_dp_link_equalize_channel(link);
-		if (err < 0) {
-			DRM_ERROR("failed to equalize channel: %d\n", err);
-			return err;
-		}
-
-		if (link->train.channel_equalized)
-			break;
-
-		drm_dp_link_train_adjust(&link->train);
-	}
-
-	return 0;
-}
-
-static int drm_dp_link_downgrade(struct drm_dp_link *link)
-{
-	switch (link->rate) {
-	case 162000:
-		return -EINVAL;
-
-	case 270000:
-		link->rate = 162000;
-		break;
-
-	case 540000:
-		link->rate = 270000;
-		return 0;
-	}
-
-	return 0;
-}
-
-static void drm_dp_link_train_disable(struct drm_dp_link *link)
-{
-	int err;
-
-	link->train.pattern = DP_TRAINING_PATTERN_DISABLE;
-
-	err = drm_dp_link_apply_training(link);
-	if (err < 0)
-		DRM_ERROR("failed to disable link training: %d\n", err);
-}
-
-static int drm_dp_link_train_full(struct drm_dp_link *link)
-{
-	int err;
-
-retry:
-	DRM_DEBUG_KMS("full-training link: %u lane%s at %u MHz\n",
-		      link->lanes, (link->lanes > 1) ? "s" : "",
-		      link->rate / 100);
-
-	err = drm_dp_link_configure(link->aux, link);
-	if (err < 0) {
-		DRM_ERROR("failed to configure DP link: %d\n", err);
-		return err;
-	}
-
-	err = drm_dp_link_clock_recovery(link);
-	if (err < 0) {
-		DRM_ERROR("clock recovery failed: %d\n", err);
-		goto out;
-	}
-
-	if (!link->train.clock_recovered) {
-		DRM_ERROR("clock recovery failed, downgrading link\n");
-
-		err = drm_dp_link_downgrade(link);
-		if (err < 0)
-			goto out;
-
-		goto retry;
-	}
-
-	DRM_DEBUG_KMS("clock recovery succeeded\n");
-
-	err = drm_dp_link_channel_equalization(link);
-	if (err < 0) {
-		DRM_ERROR("channel equalization failed: %d\n", err);
-		goto out;
-	}
-
-	if (!link->train.channel_equalized) {
-		DRM_ERROR("channel equalization failed, downgrading link\n");
-
-		err = drm_dp_link_downgrade(link);
-		if (err < 0)
-			goto out;
-
-		goto retry;
-	}
-
-	DRM_DEBUG_KMS("channel equalization succeeded\n");
-
-out:
-	drm_dp_link_train_disable(link);
-	return err;
-}
-
-static int drm_dp_link_train_fast(struct drm_dp_link *link)
-{
-	u8 status[DP_LINK_STATUS_SIZE];
-	int err;
-
-	DRM_DEBUG_KMS("fast-training link: %u lane%s at %u MHz\n",
-		      link->lanes, (link->lanes > 1) ? "s" : "",
-		      link->rate / 100);
-
-	err = drm_dp_link_configure(link->aux, link);
-	if (err < 0) {
-		DRM_ERROR("failed to configure DP link: %d\n", err);
-		return err;
-	}
-
-	/* transmit training pattern 1 for 500 microseconds */
-	link->train.pattern = DP_TRAINING_PATTERN_1;
-
-	err = drm_dp_link_apply_training(link);
-	if (err < 0)
-		goto out;
-
-	usleep_range(500, 1000);
-
-	/* transmit training pattern 2 or 3 for 500 microseconds */
-	if (link->caps.tps3_supported)
-		link->train.pattern = DP_TRAINING_PATTERN_3;
-	else
-		link->train.pattern = DP_TRAINING_PATTERN_2;
-
-	err = drm_dp_link_apply_training(link);
-	if (err < 0)
-		goto out;
-
-	usleep_range(500, 1000);
-
-	err = drm_dp_dpcd_read_link_status(link->aux, status);
-	if (err < 0) {
-		DRM_ERROR("failed to read link status: %d\n", err);
-		goto out;
-	}
-
-	if (!drm_dp_clock_recovery_ok(status, link->lanes)) {
-		DRM_ERROR("clock recovery failed\n");
-		err = -EIO;
-	}
-
-	if (!drm_dp_channel_eq_ok(status, link->lanes)) {
-		DRM_ERROR("channel equalization failed\n");
-		err = -EIO;
-	}
-
-out:
-	drm_dp_link_train_disable(link);
-	return err;
-}
-
-/**
- * drm_dp_link_train() - perform DisplayPort link training
- * @link: a DP link object
- *
- * Uses the context stored in the DP link object to perform link training. It
- * is expected that drivers will call drm_dp_link_probe() to obtain the link
- * capabilities before performing link training.
- *
- * If the sink supports fast link training (no AUX CH handshake) and valid
- * training settings are available, this function will try to perform fast
- * link training and fall back to full link training on failure.
- *
- * Returns: 0 on success or a negative error code on failure.
- */
-int drm_dp_link_train(struct drm_dp_link *link)
-{
-	int err;
-
-	drm_dp_link_train_init(&link->train);
-
-	if (link->caps.fast_training) {
-		if (drm_dp_link_train_valid(&link->train)) {
-			err = drm_dp_link_train_fast(link);
-			if (err < 0)
-				DRM_ERROR("fast link training failed: %d\n",
-					  err);
-			else
-				return 0;
-		} else {
-			DRM_DEBUG_KMS("training parameters not available\n");
-		}
-	} else {
-		DRM_DEBUG_KMS("fast link training not supported\n");
-	}
-
-	err = drm_dp_link_train_full(link);
-	if (err < 0)
-		DRM_ERROR("full link training failed: %d\n", err);
-
-	return err;
-}
diff --git a/drivers/gpu/drm/tegra/dp.h b/drivers/gpu/drm/tegra/dp.h
deleted file mode 100644
index cb12ed0..00000000
--- a/drivers/gpu/drm/tegra/dp.h
+++ /dev/null
@@ -1,177 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/*
- * Copyright (C) 2013-2019 NVIDIA Corporation.
- * Copyright (C) 2015 Rob Clark
- */
-
-#ifndef DRM_TEGRA_DP_H
-#define DRM_TEGRA_DP_H 1
-
-#include <linux/types.h>
-
-struct drm_display_info;
-struct drm_display_mode;
-struct drm_dp_aux;
-struct drm_dp_link;
-
-/**
- * struct drm_dp_link_caps - DP link capabilities
- */
-struct drm_dp_link_caps {
-	/**
-	 * @enhanced_framing:
-	 *
-	 * enhanced framing capability (mandatory as of DP 1.2)
-	 */
-	bool enhanced_framing;
-
-	/**
-	 * tps3_supported:
-	 *
-	 * training pattern sequence 3 supported for equalization
-	 */
-	bool tps3_supported;
-
-	/**
-	 * @fast_training:
-	 *
-	 * AUX CH handshake not required for link training
-	 */
-	bool fast_training;
-
-	/**
-	 * @channel_coding:
-	 *
-	 * ANSI 8B/10B channel coding capability
-	 */
-	bool channel_coding;
-
-	/**
-	 * @alternate_scrambler_reset:
-	 *
-	 * eDP alternate scrambler reset capability
-	 */
-	bool alternate_scrambler_reset;
-};
-
-void drm_dp_link_caps_copy(struct drm_dp_link_caps *dest,
-			   const struct drm_dp_link_caps *src);
-
-/**
- * struct drm_dp_link_ops - DP link operations
- */
-struct drm_dp_link_ops {
-	/**
-	 * @apply_training:
-	 */
-	int (*apply_training)(struct drm_dp_link *link);
-
-	/**
-	 * @configure:
-	 */
-	int (*configure)(struct drm_dp_link *link);
-};
-
-#define DP_TRAIN_VOLTAGE_SWING_LEVEL(x) ((x) << 0)
-#define DP_TRAIN_PRE_EMPHASIS_LEVEL(x) ((x) << 3)
-#define DP_LANE_POST_CURSOR(i, x) (((x) & 0x3) << (((i) & 1) << 2))
-
-/**
- * struct drm_dp_link_train_set - link training settings
- * @voltage_swing: per-lane voltage swing
- * @pre_emphasis: per-lane pre-emphasis
- * @post_cursor: per-lane post-cursor
- */
-struct drm_dp_link_train_set {
-	unsigned int voltage_swing[4];
-	unsigned int pre_emphasis[4];
-	unsigned int post_cursor[4];
-};
-
-/**
- * struct drm_dp_link_train - link training state information
- * @request: currently requested settings
- * @adjust: adjustments requested by sink
- * @pattern: currently requested training pattern
- * @clock_recovered: flag to track if clock recovery has completed
- * @channel_equalized: flag to track if channel equalization has completed
- */
-struct drm_dp_link_train {
-	struct drm_dp_link_train_set request;
-	struct drm_dp_link_train_set adjust;
-
-	unsigned int pattern;
-
-	bool clock_recovered;
-	bool channel_equalized;
-};
-
-/**
- * struct drm_dp_link - DP link capabilities and configuration
- * @revision: DP specification revision supported on the link
- * @max_rate: maximum clock rate supported on the link
- * @max_lanes: maximum number of lanes supported on the link
- * @caps: capabilities supported on the link (see &drm_dp_link_caps)
- * @aux_rd_interval: AUX read interval to use for training (in microseconds)
- * @edp: eDP revision (0x11: eDP 1.1, 0x12: eDP 1.2, ...)
- * @rate: currently configured link rate
- * @lanes: currently configured number of lanes
- * @rates: additional supported link rates in kHz (eDP 1.4)
- * @num_rates: number of additional supported link rates (eDP 1.4)
- */
-struct drm_dp_link {
-	unsigned char revision;
-	unsigned int max_rate;
-	unsigned int max_lanes;
-
-	struct drm_dp_link_caps caps;
-
-	/**
-	 * @cr: clock recovery read interval
-	 * @ce: channel equalization read interval
-	 */
-	struct {
-		unsigned int cr;
-		unsigned int ce;
-	} aux_rd_interval;
-
-	unsigned char edp;
-
-	unsigned int rate;
-	unsigned int lanes;
-
-	unsigned long rates[DP_MAX_SUPPORTED_RATES];
-	unsigned int num_rates;
-
-	/**
-	 * @ops: DP link operations
-	 */
-	const struct drm_dp_link_ops *ops;
-
-	/**
-	 * @aux: DP AUX channel
-	 */
-	struct drm_dp_aux *aux;
-
-	/**
-	 * @train: DP link training state
-	 */
-	struct drm_dp_link_train train;
-};
-
-int drm_dp_link_add_rate(struct drm_dp_link *link, unsigned long rate);
-int drm_dp_link_remove_rate(struct drm_dp_link *link, unsigned long rate);
-void drm_dp_link_update_rates(struct drm_dp_link *link);
-
-int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link);
-int drm_dp_link_power_up(struct drm_dp_aux *aux, struct drm_dp_link *link);
-int drm_dp_link_power_down(struct drm_dp_aux *aux, struct drm_dp_link *link);
-int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link);
-int drm_dp_link_choose(struct drm_dp_link *link,
-		       const struct drm_display_mode *mode,
-		       const struct drm_display_info *info);
-
-void drm_dp_link_train_init(struct drm_dp_link_train *train);
-int drm_dp_link_train(struct drm_dp_link *link);
-
-#endif
diff --git a/drivers/gpu/drm/tegra/dpaux.c b/drivers/gpu/drm/tegra/dpaux.c
index 622cdf1..066177a 100644
--- a/drivers/gpu/drm/tegra/dpaux.c
+++ b/drivers/gpu/drm/tegra/dpaux.c
@@ -23,7 +23,6 @@
 #include <drm/drm_dp_helper.h>
 #include <drm/drm_panel.h>
 
-#include "dp.h"
 #include "dpaux.h"
 #include "drm.h"
 #include "trace.h"
diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c
index 615cb31..29c3ae8 100644
--- a/drivers/gpu/drm/tegra/sor.c
+++ b/drivers/gpu/drm/tegra/sor.c
@@ -25,7 +25,6 @@
 #include <drm/drm_scdc_helper.h>
 
 #include "dc.h"
-#include "dp.h"
 #include "drm.h"
 #include "hda.h"
 #include "sor.h"
diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h
index 51ecb51..04f6c0b 100644
--- a/include/drm/drm_dp_helper.h
+++ b/include/drm/drm_dp_helper.h
@@ -1455,6 +1455,22 @@ static inline ssize_t drm_dp_dpcd_writeb(struct drm_dp_aux *aux,
 int drm_dp_dpcd_read_link_status(struct drm_dp_aux *aux,
 				 u8 status[DP_LINK_STATUS_SIZE]);
 
+/*
+ * DisplayPort link
+ */
+#define DP_LINK_CAP_ENHANCED_FRAMING (1 << 0)
+
+struct drm_dp_link {
+	unsigned char revision;
+	unsigned int rate;
+	unsigned int num_lanes;
+	unsigned long capabilities;
+};
+
+int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link);
+int drm_dp_link_power_up(struct drm_dp_aux *aux, struct drm_dp_link *link);
+int drm_dp_link_power_down(struct drm_dp_aux *aux, struct drm_dp_link *link);
+int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link);
 int drm_dp_downstream_max_clock(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
 				const u8 port_cap[4]);
 int drm_dp_downstream_max_bpc(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
-- 
1.9.1

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH v6 3/4] dt-bindings: Add binding for IT6505.
  2020-01-20  2:44 [PATCH v6 0/4] IT6505 cover letter allen
  2020-01-20  2:44 ` [PATCH v6 2/4] Revert "drm/tegra: Move drm_dp_link helpers to Tegra DRM" allen
@ 2020-01-20  2:44 ` allen
  2020-01-21 18:00   ` Rob Herring
  2020-01-20  2:44 ` [PATCH v6 4/4] drm/bridge: add it6505 driver allen
  2 siblings, 1 reply; 6+ messages in thread
From: allen @ 2020-01-20  2:44 UTC (permalink / raw)
  Cc: Mark Rutland,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	Pi-Hsun Shih, Jau-Chih Tseng, David Airlie, Allen Chen,
	open list, open list:DRM DRIVERS, Rob Herring, Sam Ravnborg

Add a DT binding documentation for IT6505.

Acked-by: Sam Ravnborg <sam@ravnborg.org>
Signed-off-by: Allen Chen <allen.chen@ite.com.tw>
Signed-off-by: Pi-Hsun Shih <pihsun@chromium.org>
---
 .../bindings/display/bridge/ite,it6505.yaml        | 89 ++++++++++++++++++++++
 1 file changed, 89 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/bridge/ite,it6505.yaml

diff --git a/Documentation/devicetree/bindings/display/bridge/ite,it6505.yaml b/Documentation/devicetree/bindings/display/bridge/ite,it6505.yaml
new file mode 100644
index 00000000..5c152ca8
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/bridge/ite,it6505.yaml
@@ -0,0 +1,89 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/bridge/ite,it6505.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ITE it6505 Device Tree Bindings
+
+maintainers:
+  - Allen Chen <allen.chen@ite.com.tw>
+
+description: |
+  The IT6505 is a high-performance DisplayPort 1.1a transmitter,
+  fully compliant with DisplayPort 1.1a, HDCP 1.3 specifications.
+  The IT6505 supports color depth of up to 36 bits (12 bits/color)
+  and ensures robust transmission of high-quality uncompressed video
+  content, along with uncompressed and compressed digital audio content.
+
+  Aside from the various video output formats supported, the IT6505
+  also encodes and transmits up to 8 channels of I2S digital audio,
+  with sampling rate up to 192kHz and sample size up to 24 bits.
+  In addition, an S/PDIF input port takes in compressed audio of up to
+  192kHz frame rate.
+
+  Each IT6505 chip comes preprogrammed with an unique HDCP key,
+  in compliance with the HDCP 1.3 standard so as to provide secure
+  transmission of high-definition content. Users of the IT6505 need not
+  purchase any HDCP keys or ROMs.
+
+properties:
+  compatible:
+    const: ite,it6505
+
+  reg:
+    maxItems: 1
+    description: i2c address of the bridge
+
+  ovdd-supply:
+    maxItems: 1
+    description: I/O voltage
+
+  pwr18-supply:
+    maxItems: 1
+    description: core voltage
+
+  interrupts:
+    maxItems: 1
+    description: interrupt specifier of INT pin
+
+  reset-gpios:
+    maxItems: 1
+    description: gpio specifier of RESET pin
+
+  extcon:
+    maxItems: 1
+    description: extcon specifier for the Power Delivery
+
+  port:
+    type: object
+    description: A port node pointing to DPI host port node
+
+required:
+  - compatible
+  - reg
+  - ovdd-supply
+  - pwr18-supply
+  - interrupts
+  - reset-gpios
+  - extcon
+
+examples:
+  - |
+    dp-bridge@5c {
+        compatible = "ite,it6505";
+        interrupts = <152 IRQ_TYPE_EDGE_RISING 152 0>;
+        reg = <0x5c>;
+        pinctrl-names = "default";
+        pinctrl-0 = <&it6505_pins>;
+        ovdd-supply = <&mt6358_vsim1_reg>;
+        pwr18-supply = <&it6505_pp18_reg>;
+        reset-gpios = <&pio 179 1>;
+        extcon = <&usbc_extcon>;
+
+        port {
+            it6505_in: endpoint {
+                remote-endpoint = <&dpi_out>;
+            };
+        };
+    };
-- 
1.9.1

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH v6 4/4] drm/bridge: add it6505 driver
  2020-01-20  2:44 [PATCH v6 0/4] IT6505 cover letter allen
  2020-01-20  2:44 ` [PATCH v6 2/4] Revert "drm/tegra: Move drm_dp_link helpers to Tegra DRM" allen
  2020-01-20  2:44 ` [PATCH v6 3/4] dt-bindings: Add binding for IT6505 allen
@ 2020-01-20  2:44 ` allen
  2 siblings, 0 replies; 6+ messages in thread
From: allen @ 2020-01-20  2:44 UTC (permalink / raw)
  Cc: Jernej Skrabec, Jitao Shi, Jau-Chih Tseng, Yilun Lin, Allen Chen,
	David Airlie, Neil Armstrong, open list, open list:DRM DRIVERS,
	Jonas Karlman, moderated list:ARM/Mediatek SoC support,
	Laurent Pinchart, Pi-Hsun Shih, Matthias Brugger,
	moderated list:ARM/Mediatek SoC support

From: Allen Chen <allen.chen@ite.com.tw>

This adds support for the iTE IT6505.
This device can convert DPI signal to DP output.

Signed-off-by: Jitao Shi <jitao.shi@mediatek.com>
Signed-off-by: Yilun Lin <yllin@google.com>
Signed-off-by: Allen Chen <allen.chen@ite.com.tw>
Signed-off-by: Pi-Hsun Shih <pihsun@chromium.org>
---
 drivers/gpu/drm/bridge/Kconfig      |    7 +
 drivers/gpu/drm/bridge/Makefile     |    1 +
 drivers/gpu/drm/bridge/ite-it6505.c | 2698 +++++++++++++++++++++++++++++++++++
 3 files changed, 2706 insertions(+)
 create mode 100644 drivers/gpu/drm/bridge/ite-it6505.c

diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index 3436297..c8cfaf7 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -45,6 +45,13 @@ config DRM_DUMB_VGA_DAC
 	  Support for non-programmable RGB to VGA DAC bridges, such as ADI
 	  ADV7123, TI THS8134 and THS8135 or passive resistor ladder DACs.
 
+config DRM_ITE_IT6505
+	tristate "ITE IT6505 DP bridge"
+	depends on OF
+	select DRM_KMS_HELPER
+	help
+	  ITE IT6505 DP bridge chip driver.
+
 config DRM_LVDS_ENCODER
 	tristate "Transparent parallel to LVDS encoder support"
 	depends on OF
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index 4934fcf..f5abca5 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -2,6 +2,7 @@
 obj-$(CONFIG_DRM_ANALOGIX_ANX78XX) += analogix-anx78xx.o
 obj-$(CONFIG_DRM_CDNS_DSI) += cdns-dsi.o
 obj-$(CONFIG_DRM_DUMB_VGA_DAC) += dumb-vga-dac.o
+obj-$(CONFIG_DRM_ITE_IT6505) += ite-it6505.o
 obj-$(CONFIG_DRM_LVDS_ENCODER) += lvds-encoder.o
 obj-$(CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW) += megachips-stdpxxxx-ge-b850v3-fw.o
 obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o
diff --git a/drivers/gpu/drm/bridge/ite-it6505.c b/drivers/gpu/drm/bridge/ite-it6505.c
new file mode 100644
index 00000000..a244c1c
--- /dev/null
+++ b/drivers/gpu/drm/bridge/ite-it6505.c
@@ -0,0 +1,2698 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ */
+#include <linux/bits.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/extcon.h>
+#include <linux/fs.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/types.h>
+
+#include <crypto/hash.h>
+#include <crypto/sha.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_dp_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
+
+#include <sound/hdmi-codec.h>
+
+/* Vendor option */
+#define AUDIO_SELECT I2S
+#define AUDIO_TYPE LPCM
+#define AUDIO_SAMPLE_RATE SAMPLE_RATE_48K
+#define AUDIO_CHANNEL_COUNT 2
+
+/*
+ * 0: Standard I2S
+ * 1: 32bit I2S
+ */
+#define I2S_INPUT_FORMAT 1
+
+/*
+ * 0: Left-justified
+ * 1: Right-justified
+ */
+#define I2S_JUSTIFIED 0
+
+/*
+ * 0: Data delay 1T correspond to WS
+ * 1: No data delay correspond to WS
+ */
+#define I2S_DATA_DELAY 0
+
+/*
+ * 0: Left channel
+ * 1: Right channel
+ */
+#define I2S_WS_CHANNEL 0
+
+/*
+ * 0: MSB shift first
+ * 1: LSB shift first
+ */
+#define I2S_DATA_SEQUENCE 0
+
+/*
+ * IT6505 maximum link rate
+ * RBR : 1.62 Gbps/lane
+ * HBR : 2.7  Gbps/lane
+ * HBR2: 5.4  Gbps/lane
+ * HBR3: 8.1  Gbps/lane
+ */
+#define MAX_LINK_RATE HBR
+
+/* IT6505 maximum lane count */
+#define MAX_LANE_COUNT 4
+
+#define TRAINING_LINK_RATE HBR
+#define TRAINING_LANE_COUNT 4
+#define ENABLE_DP_LANE_SWAP 0
+#define AUX_WAIT_TIMEOUT_MS 15
+#define PIXEL_CLK_DELAY 1
+#define PIXEL_CLK_INVERSE 0
+#define ADJUST_PHASE_THRESHOLD 80000
+#define MAX_PIXEL_CLK 95000
+#define DEFAULT_DRV_HOLD 0
+#define DEFAULT_PWR_ON 0
+#define AUX_FIFO_MAX_SIZE 0x10
+
+/*
+ * Vendor option afe settings for different platforms
+ * 0: for bitland 10e, quanta zde
+ * 1: for google kukui p1/p2, huaqin krane
+ */
+#define AFE_SETTING 1
+
+static u8 afe_setting_table[2][3] = {
+	{0, 0, 0},
+	{0x93, 0x2A, 0x85}
+};
+
+enum it6505_sys_state {
+	SYS_UNPLUG = 0,
+	SYS_HPD,
+	SYS_AUTOTRAIN,
+	SYS_WAIT,
+	SYS_TRAINFAIL,
+	SYS_HDCP,
+	SYS_NOROP,
+	SYS_UNKNOWN,
+};
+
+enum it6505_audio_select {
+	I2S = 0,
+	SPDIF,
+};
+
+enum it6505_audio_sample_rate {
+	SAMPLE_RATE_24K = 0x6,
+	SAMPLE_RATE_32K = 0x3,
+	SAMPLE_RATE_48K = 0x2,
+	SAMPLE_RATE_96K = 0xA,
+	SAMPLE_RATE_192K = 0xE,
+	SAMPLE_RATE_44_1K = 0x0,
+	SAMPLE_RATE_88_2K = 0x8,
+	SAMPLE_RATE_176_4K = 0xC,
+	SAMPLE_RATE_UNKNOWN = 0xFF,
+};
+
+enum it6505_audio_type {
+	LPCM = 0,
+	NLPCM,
+	DSS,
+};
+
+enum it6505_audio_word_length {
+	WORD_LENGTH_16BIT = 0,
+	WORD_LENGTH_18BIT,
+	WORD_LENGTH_20BIT,
+	WORD_LENGTH_24BIT,
+};
+
+/*
+ * Audio Sample Word Length
+ * WORD_LENGTH_16BIT
+ * WORD_LENGTH_18BIT
+ * WORD_LENGTH_20BIT
+ * WORD_LENGTH_24BIT
+ */
+#define AUDIO_WORD_LENGTH WORD_LENGTH_24BIT
+
+enum it6505_link_rate {
+	RBR,
+	HBR,
+	HBR2,
+	HBR3,
+};
+
+struct it6505_audio_sample_rate_map {
+	enum it6505_audio_sample_rate rate;
+	int sample_rate_value;
+};
+
+struct it6505_platform_data {
+	struct regulator *pwr18;
+	struct regulator *ovdd;
+	struct gpio_desc *gpiod_hpd;
+	struct gpio_desc *gpiod_reset;
+};
+
+struct it6505 {
+	struct drm_dp_aux aux;
+	struct drm_bridge bridge;
+	struct i2c_client *client;
+	struct edid *edid;
+	struct drm_connector connector;
+	struct drm_dp_link link;
+	struct it6505_platform_data pdata;
+	struct mutex lock;
+	struct mutex mode_lock;
+	struct regmap *regmap;
+	struct drm_display_mode video_info;
+
+	struct notifier_block event_nb;
+	struct extcon_dev *extcon;
+	struct work_struct extcon_wq;
+	enum it6505_sys_state state;
+	bool hbr;
+	u8 lane_count;
+	bool enable_ssc;
+	bool lane_swap_disabled;
+	bool lane_swap;
+
+	enum it6505_audio_select audio_select;
+	enum it6505_audio_sample_rate audio_sample_rate;
+	enum it6505_audio_type audio_type;
+	enum it6505_audio_word_length audio_word_length;
+	u8 audio_channel_count;
+	u8 i2s_input_format;
+	u8 i2s_justified;
+	u8 i2s_data_delay;
+	u8 i2s_ws_channel;
+	u8 i2s_data_sequence;
+	bool hdcp_flag;
+	bool enable_hdcp;
+	bool enable_audio;
+	u8 auto_train_count;
+	u8 train_fail_hpd;
+	bool cp_capable;
+	u8 sha1_input[64];
+	u8 av[5][4];
+	u8 bv[5][4];
+	bool powered;
+	u8 dpcd_sink_count;
+	/* it6505 driver hold option */
+	bool enable_drv_hold;
+};
+
+static const struct regmap_range it6505_bridge_volatile_ranges[] = {
+	{ .range_min = 0, .range_max = 0xFF },
+};
+
+static const struct regmap_access_table it6505_bridge_volatile_table = {
+	.yes_ranges = it6505_bridge_volatile_ranges,
+	.n_yes_ranges = ARRAY_SIZE(it6505_bridge_volatile_ranges),
+};
+
+static const struct regmap_config it6505_bridge_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.volatile_table = &it6505_bridge_volatile_table,
+	.cache_type = REGCACHE_NONE,
+};
+
+static int dptx_read(struct it6505 *it6505, unsigned int reg_addr)
+{
+	unsigned int value;
+	int err;
+	struct device *dev = &it6505->client->dev;
+
+	err = regmap_read(it6505->regmap, reg_addr, &value);
+	if (err < 0) {
+		DRM_DEV_ERROR(dev, "read failed reg[0x%x] err: %d", reg_addr,
+			      err);
+		return err;
+	}
+
+	return value;
+}
+
+static void dptx_debug_dump(struct it6505 *it6505)
+{
+	unsigned int i, j;
+	u8 regs[16];
+	struct device *dev = &it6505->client->dev;
+
+	for (i = 0; i <= 0xff; i += 16) {
+		for (j = 0; j < 16; j++)
+			regs[j] = dptx_read(it6505, i + j);
+
+		DRM_DEV_DEBUG_DRIVER(dev, "[0x%02x] = %16ph", i, regs);
+	}
+}
+
+static int dptx_write(struct it6505 *it6505, unsigned int reg_addr,
+		      unsigned int reg_val)
+{
+	int err;
+	struct device *dev = &it6505->client->dev;
+
+	err = regmap_write(it6505->regmap, reg_addr, reg_val);
+
+	if (err < 0) {
+		DRM_DEV_ERROR(dev, "write failed reg[0x%x] = 0x%x err = %d",
+			      reg_addr, reg_val, err);
+		return err;
+	}
+
+	return 0;
+}
+
+static int dptx_set_bits(struct it6505 *it6505, unsigned int reg,
+			 unsigned int mask, unsigned int value)
+{
+	int err;
+	struct device *dev = &it6505->client->dev;
+
+	err = regmap_update_bits(it6505->regmap, reg, mask, value);
+	if (err < 0) {
+		DRM_DEV_ERROR(
+			dev, "write reg[0x%x] = 0x%x mask = 0x%x failed err %d",
+			reg, value, mask, err);
+		return err;
+	}
+
+	return 0;
+}
+
+static unsigned int dpcd_read(struct it6505 *it6505, unsigned long offset)
+{
+	u8 value;
+	int ret;
+	struct device *dev = &it6505->client->dev;
+
+	ret = drm_dp_dpcd_readb(&it6505->aux, offset, &value);
+	if (ret < 0) {
+		DRM_DEV_ERROR(dev, "DPCD read failed [0x%lx] ret: %d", offset,
+			      ret);
+		return ret;
+	}
+	return value;
+}
+
+static int dpcd_write(struct it6505 *it6505, unsigned long offset,
+		      unsigned long datain)
+{
+	int ret;
+	struct device *dev = &it6505->client->dev;
+
+	ret = drm_dp_dpcd_writeb(&it6505->aux, offset, datain);
+	if (ret < 0) {
+		DRM_DEV_ERROR(dev, "DPCD write failed [0x%lx] ret: %d", offset,
+			      ret);
+		return ret;
+	}
+	return 0;
+}
+
+static void dptx_debug_print(struct it6505 *it6505, unsigned int reg,
+			     const char *prefix)
+{
+	int val;
+	struct device *dev = &it6505->client->dev;
+
+	if (likely(!(drm_debug & DRM_UT_DRIVER)))
+		return;
+
+	val = dptx_read(it6505, reg);
+	if (val < 0)
+		DRM_DEV_DEBUG_DRIVER(dev, "%s reg[%02x] read error (%d)",
+				     prefix, reg, val);
+	else
+		DRM_DEV_DEBUG_DRIVER(dev, "%s reg[%02x] = 0x%02x", prefix, reg,
+				     val);
+}
+
+static void dpcd_debug_print(struct it6505 *it6505, unsigned int reg,
+			     const char *prefix)
+{
+	unsigned int val;
+	struct device *dev = &it6505->client->dev;
+
+	if (likely(!(drm_debug & DRM_UT_DRIVER)))
+		return;
+
+	val = dpcd_read(it6505, reg);
+	if (val < 0)
+		DRM_DEV_DEBUG_DRIVER(dev, "%s reg%02x read error", prefix, reg);
+	else
+		DRM_DEV_DEBUG_DRIVER(dev, "%s DPCD%02x = 0x%02x", prefix, reg,
+				     val);
+}
+
+static inline struct it6505 *connector_to_it6505(struct drm_connector *c)
+{
+	return container_of(c, struct it6505, connector);
+}
+
+static inline struct it6505 *bridge_to_it6505(struct drm_bridge *bridge)
+{
+	return container_of(bridge, struct it6505, bridge);
+}
+
+static void it6505_init_fsm(struct it6505 *it6505)
+{
+	it6505->audio_select = AUDIO_SELECT;
+	it6505->audio_sample_rate = AUDIO_SAMPLE_RATE;
+	it6505->audio_channel_count = AUDIO_CHANNEL_COUNT;
+	it6505->audio_type = AUDIO_TYPE;
+	it6505->i2s_input_format = I2S_INPUT_FORMAT;
+	it6505->i2s_justified = I2S_JUSTIFIED;
+	it6505->i2s_data_delay = I2S_DATA_DELAY;
+	it6505->i2s_ws_channel = I2S_WS_CHANNEL;
+	it6505->i2s_data_sequence = I2S_DATA_SEQUENCE;
+	it6505->audio_word_length = AUDIO_WORD_LENGTH;
+	it6505->enable_audio = false;
+	it6505->enable_hdcp =  true;
+	it6505->lane_count = min(MAX_LANE_COUNT, TRAINING_LANE_COUNT);
+	it6505->hbr = min(MAX_LINK_RATE, TRAINING_LINK_RATE) ? true : false;
+	it6505->enable_ssc = true;
+	it6505->lane_swap = ENABLE_DP_LANE_SWAP ? true : false;
+}
+
+static void it6505_lane_termination_on(struct it6505 *it6505)
+{
+	if (dptx_read(it6505, 0xCF) == 0xF0) {
+		dptx_set_bits(it6505, 0x5D, 0x80, 0x00);
+		dptx_set_bits(it6505, 0x5E, 0x02, 0x02);
+	}
+
+	if (dptx_read(it6505, 0xCF) == 0x70) {
+		if (it6505->lane_swap) {
+			switch (it6505->lane_count) {
+			case 1:
+			case 2:
+				dptx_set_bits(it6505, 0x5D, 0x0C, 0x08);
+				break;
+			default:
+				dptx_set_bits(it6505, 0x5D, 0x0C, 0x0C);
+				break;
+			}
+		} else {
+			switch (it6505->lane_count) {
+			case 1:
+			case 2:
+				dptx_set_bits(it6505, 0x5D, 0x0C, 0x04);
+				break;
+			default:
+				dptx_set_bits(it6505, 0x5D, 0x0C, 0x0C);
+				break;
+			}
+		}
+	}
+}
+
+static void it6505_lane_termination_off(struct it6505 *it6505)
+{
+	if (dptx_read(it6505, 0xCF) == 0xF0) {
+		dptx_set_bits(it6505, 0x5D, 0x80, 0x80);
+		dptx_set_bits(it6505, 0x5E, 0x02, 0x00);
+		dptx_set_bits(it6505, 0x5C, 0xF0, 0x00);
+	}
+
+	if (dptx_read(it6505, 0xCF) == 0x70)
+		dptx_set_bits(it6505, 0x5D, 0x0C, 0x00);
+}
+
+static void it6505_lane_power_on(struct it6505 *it6505)
+{
+	dptx_set_bits(it6505, 0x5C, 0xF1, ((BIT(it6505->lane_count) - 1) <<
+	(it6505->lane_swap ? (8 - it6505->lane_count) : 4)) | 0x01);
+}
+
+static void it6505_lane_power_off(struct it6505 *it6505)
+{
+	dptx_set_bits(it6505, 0x5C, 0xF0, 0x00);
+}
+
+static void it6505_lane_off(struct it6505 *it6505)
+{
+	it6505_lane_power_off(it6505);
+	it6505_lane_termination_off(it6505);
+}
+
+static void it6505_aux_termination_on(struct it6505 *it6505)
+{
+	if (dptx_read(it6505, 0xCF) == 0xF0)
+		it6505_lane_termination_on(it6505);
+
+	if (dptx_read(it6505, 0xCF) == 0x70)
+		dptx_set_bits(it6505, 0x5D, 0x80, 0x80);
+}
+
+static void it6505_aux_power_on(struct it6505 *it6505)
+{
+	dptx_set_bits(it6505, 0x5E, 0x02, 0x02);
+}
+
+static void it6505_aux_on(struct it6505 *it6505)
+{
+	it6505_aux_power_on(it6505);
+	it6505_aux_termination_on(it6505);
+}
+
+static void it6505_aux_reset(struct it6505 *it6505)
+{
+	dptx_set_bits(it6505, 0x05, 0x08, 0x08);
+	dptx_set_bits(it6505, 0x05, 0x08, 0x00);
+}
+
+static bool dptx_get_sink_hpd(struct it6505 *it6505)
+{
+	int reg0d = dptx_read(it6505, 0x0D);
+
+	if (reg0d < 0)
+		return false;
+
+	return (reg0d & BIT(1)) == BIT(1);
+}
+
+static void dptx_select_bank(struct it6505 *it6505, unsigned int bank_id)
+{
+	dptx_set_bits(it6505, 0x0F, 0x01, bank_id & BIT(0));
+}
+
+static void it6505_int_mask_on(struct it6505 *it6505)
+{
+	dptx_write(it6505, 0x09, 0x1F);
+	dptx_write(it6505, 0x0A, 0x07);
+	dptx_write(it6505, 0x0B, 0x90);
+}
+
+static void it6505_int_mask_off(struct it6505 *it6505)
+{
+	dptx_write(it6505, 0x09, 0x00);
+	dptx_write(it6505, 0x0A, 0x00);
+	dptx_write(it6505, 0x0B, 0x00);
+}
+
+static void dptx_init(struct it6505 *it6505)
+{
+	dptx_write(it6505, 0x05, 0x3B);
+	usleep_range(1000, 2000);
+	dptx_write(it6505, 0x05, 0x1F);
+	usleep_range(1000, 1500);
+}
+
+static void it6505_init_and_mask_on(struct it6505 *it6505)
+{
+	dptx_init(it6505);
+	it6505_int_mask_on(it6505);
+}
+
+static int it6505_poweron(struct it6505 *it6505)
+{
+	struct it6505_platform_data *pdata = &it6505->pdata;
+	int err;
+	struct device *dev = &it6505->client->dev;
+
+	if (it6505->powered) {
+		DRM_DEV_DEBUG_DRIVER(dev, "it6505 already powered on");
+		return 0;
+	}
+
+	DRM_DEV_DEBUG_DRIVER(dev, "it6505 start to power on");
+
+	err = regulator_enable(pdata->pwr18);
+
+	if (err)
+		return err;
+
+	/* time interval between IVDD and OVDD at least be 1ms */
+	usleep_range(1000, 2000);
+	err = regulator_enable(pdata->ovdd);
+
+	if (err) {
+		regulator_disable(pdata->pwr18);
+		return err;
+	}
+	/* time interval between OVDD and SYSRSTN at least be 10ms */
+	usleep_range(10000, 20000);
+	gpiod_set_value_cansleep(pdata->gpiod_reset, 0);
+	usleep_range(1000, 2000);
+	gpiod_set_value_cansleep(pdata->gpiod_reset, 1);
+	usleep_range(10000, 20000);
+
+	it6505_init_and_mask_on(it6505);
+	it6505->powered = true;
+	return 0;
+}
+
+static int it6505_poweroff(struct it6505 *it6505)
+{
+	struct it6505_platform_data *pdata = &it6505->pdata;
+	int err;
+	struct device *dev = &it6505->client->dev;
+
+	if (!it6505->powered) {
+		DRM_DEV_DEBUG_DRIVER(dev, "power had been already off");
+		return 0;
+	}
+	gpiod_set_value_cansleep(pdata->gpiod_reset, 0);
+	err = regulator_disable(pdata->pwr18);
+
+	if (err)
+		return err;
+
+	err = regulator_disable(pdata->ovdd);
+
+	if (err)
+		return err;
+
+	kfree(it6505->edid);
+	it6505->edid = NULL;
+	it6505->powered = false;
+	return 0;
+}
+
+static int dptx_read_word(struct it6505 *it6505, unsigned int reg)
+{
+	int val0, val1;
+
+	val0 = dptx_read(it6505, reg);
+	if (val0 < 0)
+		return val0;
+
+	val1 = dpcd_read(it6505, reg + 1);
+	if (val1 < 0)
+		return val1;
+
+	return (val1 << 8) | val0;
+}
+
+static void show_video_info(struct it6505 *it6505)
+{
+	int hsync_pol, vsync_pol, interlaced;
+	int htotal, hdes, hdew, hfph, hsyncw;
+	int vtotal, vdes, vdew, vfph, vsyncw;
+	int rddata, i;
+	int pclk, sum;
+
+	usleep_range(10000, 15000);
+	dptx_select_bank(it6505, 0);
+	rddata = dptx_read(it6505, 0xA0);
+	hsync_pol = rddata & BIT(0);
+	vsync_pol = (rddata & BIT(2)) >> 2;
+	interlaced = (rddata & BIT(4)) >> 4;
+
+	htotal = dptx_read_word(it6505, 0xA1) & 0x1FFF;
+	hdes = dptx_read_word(it6505, 0xA3) & 0x1FFF;
+	hdew = dptx_read_word(it6505, 0xA5) & 0x1FFF;
+	hfph = dptx_read_word(it6505, 0xA7) & 0x1FFF;
+	hsyncw = dptx_read_word(it6505, 0xA9) & 0x1FFF;
+
+	vtotal = dptx_read_word(it6505, 0xAB) & 0xFFF;
+	vdes = dptx_read_word(it6505, 0xAD) & 0xFFF;
+	vdew = dptx_read_word(it6505, 0xAF) & 0xFFF;
+	vfph = dptx_read_word(it6505, 0xB1) & 0xFFF;
+	vsyncw = dptx_read_word(it6505, 0xB3) & 0xFFF;
+
+	sum = 0;
+	for (i = 0; i < 100; i++) {
+		dptx_set_bits(it6505, 0x12, 0x80, 0x80);
+		usleep_range(10000, 15000);
+		dptx_set_bits(it6505, 0x12, 0x80, 0x00);
+		rddata = dptx_read_word(it6505, 0x13) & 0xFFF;
+
+		sum += rddata;
+	}
+
+	sum /= 100;
+	pclk = 13500 * 2048 / sum;
+	it6505->video_info.clock = pclk;
+	it6505->video_info.hdisplay = hdew;
+	it6505->video_info.hsync_start = hdew + hfph;
+	it6505->video_info.hsync_end = hdew + hfph + hsyncw;
+	it6505->video_info.htotal = htotal;
+	it6505->video_info.vdisplay = vdew;
+	it6505->video_info.vsync_start = vdew + vfph;
+	it6505->video_info.vsync_end = vdew + vfph + vsyncw;
+	it6505->video_info.vtotal = vtotal;
+	it6505->video_info.vrefresh = pclk / htotal / vtotal;
+
+	DRM_DEV_DEBUG_DRIVER(&it6505->client->dev, DRM_MODE_FMT,
+			     DRM_MODE_ARG(&it6505->video_info));
+}
+
+static const struct it6505_audio_sample_rate_map audio_sample_rate_map[] = {
+	{SAMPLE_RATE_24K, 24000},
+	{SAMPLE_RATE_32K, 32000},
+	{SAMPLE_RATE_48K, 48000},
+	{SAMPLE_RATE_96K, 96000},
+	{SAMPLE_RATE_192K, 192000},
+	{SAMPLE_RATE_44_1K, 44100},
+	{SAMPLE_RATE_88_2K, 88200},
+	{SAMPLE_RATE_176_4K, 176400},
+	{SAMPLE_RATE_UNKNOWN, 0},
+};
+
+static void show_audio_m_count(struct it6505 *it6505)
+{
+	unsigned int audio_n, vclk, aclk, audio_m_cal, audio_m_count, i;
+	struct device *dev = &it6505->client->dev;
+
+	vclk = it6505->hbr ? 2700000 : 1620000;
+
+	for (i = 0; audio_sample_rate_map[i].rate != SAMPLE_RATE_UNKNOWN; i++) {
+		if (it6505->audio_sample_rate ==
+		    audio_sample_rate_map[i].rate) {
+			aclk = audio_sample_rate_map[i].sample_rate_value / 100;
+			break;
+		}
+	}
+
+	if (audio_sample_rate_map[i].rate == SAMPLE_RATE_UNKNOWN)
+		aclk = audio_sample_rate_map[i].sample_rate_value;
+
+	audio_n = (dptx_read(it6505, 0xE0) << 16) +
+		  (dptx_read(it6505, 0xDF) << 8) + dptx_read(it6505, 0xDE);
+	audio_m_cal = audio_n * aclk / vclk * 512;
+	audio_m_count = (dptx_read(it6505, 0xE6) << 16) +
+		  (dptx_read(it6505, 0xE5) << 8) + dptx_read(it6505, 0xE4);
+	DRM_DEV_DEBUG_DRIVER(dev, "audio N:0x%06x", audio_n);
+	DRM_DEV_DEBUG_DRIVER(dev, "audio M cal:0x%06x", audio_m_cal);
+	DRM_DEV_DEBUG_DRIVER(dev, "audio M count:0x%06x", audio_m_count);
+}
+
+static void it6505_audio_disable(struct it6505 *it6505)
+{
+	dptx_set_bits(it6505, 0xD3, 0x20, 0x20);
+	dptx_set_bits(it6505, 0xB8, 0x0F, 0x00);
+	dptx_set_bits(it6505, 0xE8, 0x22, 0x00);
+	dptx_set_bits(it6505, 0x05, 0x02, 0x02);
+	it6505->enable_audio = false;
+}
+
+static const char *const state_string[] = {
+	[SYS_UNPLUG] = "SYS_UNPLUG",
+	[SYS_HPD] = "SYS_HPD",
+	[SYS_AUTOTRAIN] = "SYS_AUTOTRAIN",
+	[SYS_WAIT] = "SYS_WAIT",
+	[SYS_TRAINFAIL] = "SYS_TRAINFAIL",
+	[SYS_HDCP] = "SYS_HDCP",
+	[SYS_NOROP] = "SYS_NOROP",
+	[SYS_UNKNOWN] = "SYS_UNKNOWN",
+};
+
+static void dptx_sys_chg(struct it6505 *it6505, enum it6505_sys_state newstate)
+{
+	int i;
+	struct device *dev = &it6505->client->dev;
+
+	if (newstate != SYS_UNPLUG) {
+		if (!dptx_get_sink_hpd(it6505))
+			newstate = SYS_UNPLUG;
+	}
+	if (it6505->state == newstate)
+		return;
+
+	DRM_DEV_DEBUG_DRIVER(dev, "sys_state change: %s -> %s",
+			     state_string[it6505->state],
+			     state_string[newstate]);
+	it6505->state = newstate;
+
+	switch (it6505->state) {
+	case SYS_UNPLUG:
+		kfree(it6505->edid);
+		it6505->edid = NULL;
+		DRM_DEV_DEBUG_DRIVER(dev, "Free it6505 EDID memory");
+
+		if (it6505->enable_audio)
+			it6505_audio_disable(it6505);
+
+		if (it6505->powered) {
+			it6505_init_and_mask_on(it6505);
+			it6505_lane_off(it6505);
+		}
+		break;
+	case SYS_HPD:
+		it6505_aux_on(it6505);
+		break;
+	case SYS_AUTOTRAIN:
+		break;
+	case SYS_WAIT:
+		break;
+	case SYS_HDCP:
+		break;
+	case SYS_NOROP:
+		for (i = 0; i < 3; i++)
+			show_video_info(it6505);
+		break;
+	case SYS_TRAINFAIL:
+		/* it6505 goes to idle */
+		break;
+	default:
+		break;
+	}
+}
+
+static bool it6505_aux_op_finished(struct it6505 *it6505)
+{
+	int reg2b = dptx_read(it6505, 0x2B);
+
+	if (reg2b < 0)
+		return false;
+
+	return (reg2b & BIT(5)) == 0;
+}
+
+static int dptx_aux_wait(struct it6505 *it6505)
+{
+	int status;
+	unsigned long timeout;
+	struct device *dev = &it6505->client->dev;
+
+	timeout = jiffies + msecs_to_jiffies(AUX_WAIT_TIMEOUT_MS) + 1;
+
+	while (!it6505_aux_op_finished(it6505)) {
+		if (time_after(jiffies, timeout)) {
+			DRM_DEV_ERROR(dev, "Timed out waiting AUX to finish");
+			return -ETIMEDOUT;
+		}
+		usleep_range(1000, 2000);
+	}
+
+	status = dptx_read(it6505, 0x9F);
+	if (status < 0) {
+		DRM_DEV_ERROR(dev, "Failed to read AUX channel: %d", status);
+		return status;
+	}
+
+	if (status & 0x03) {
+		DRM_DEV_ERROR(dev, "Failed to wait for AUX (status: 0x%x)",
+			      status);
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+enum aux_cmd_type {
+	CMD_AUX_NATIVE_READ = 0x0,
+	CMD_AUX_NATIVE_WRITE = 0x5,
+	CMD_AUX_I2C_EDID_READ = 0xB,
+};
+
+enum aux_cmd_reply {
+	REPLY_ACK,
+	REPLY_NACK,
+	REPLY_DEFER,
+};
+
+static ssize_t it6505_aux_do_transfer(struct it6505 *it6505,
+				      enum aux_cmd_type cmd,
+				      unsigned int address, u8 *buffer,
+				      size_t size, enum aux_cmd_reply *reply)
+{
+	int i;
+	int status;
+
+	if (cmd == CMD_AUX_I2C_EDID_READ) {
+		/* DP AUX EDID FIFO has maximum length of 16 bytes. */
+		size = min_t(size_t, size, AUX_FIFO_MAX_SIZE);
+		/* Enable AUX FIFO read back and clear FIFO */
+		dptx_write(it6505, 0x23, 0xC3);
+		dptx_write(it6505, 0x23, 0xC2);
+	} else {
+		/* The DP AUX transmit buffer has 4 bytes. */
+		size = min_t(size_t, size, 4);
+		dptx_write(it6505, 0x23, 0x42);
+	}
+
+	/* Start Address[7:0] */
+	dptx_write(it6505, 0x24, (address >> 0) & 0xFF);
+	/* Start Address[15:8] */
+	dptx_write(it6505, 0x25, (address >> 8) & 0xFF);
+	/* WriteNum[3:0]+StartAdr[19:16] */
+	dptx_write(it6505, 0x26, ((address >> 16) & 0x0F) | ((size - 1) << 4));
+
+	if (cmd == CMD_AUX_NATIVE_WRITE)
+		regmap_bulk_write(it6505->regmap, 0x27, buffer, size);
+
+	/* Aux Fire */
+	dptx_write(it6505, 0x2B, cmd);
+
+	status = dptx_aux_wait(it6505);
+	if (status < 0)
+		return status;
+
+	status = dptx_read(it6505, 0x9F);
+	if (status < 0)
+		return status;
+
+	switch ((status >> 6) & 0x3) {
+	case 0:
+		*reply = REPLY_ACK;
+		break;
+	case 1:
+		*reply = REPLY_DEFER;
+		return 0;
+	case 2:
+		*reply = REPLY_NACK;
+		return 0;
+	case 3:
+		return -ETIMEDOUT;
+	}
+
+	if (cmd == CMD_AUX_NATIVE_WRITE)
+		goto out;
+
+	if (cmd == CMD_AUX_I2C_EDID_READ) {
+		for (i = 0; i < size; i++) {
+			status = dptx_read(it6505, 0x2F);
+			if (status < 0)
+				return status;
+			buffer[i] = status;
+		}
+	} else {
+		for (i = 0; i < size; i++) {
+			status = dptx_read(it6505, 0x2C + i);
+			if (status < 0)
+				return status;
+			buffer[size - 1 - i] = status;
+		}
+	}
+
+out:
+	dptx_write(it6505, 0x23, 0x41);
+	dptx_write(it6505, 0x23, 0x40);
+	return size;
+}
+
+static ssize_t it6505_aux_transfer(struct drm_dp_aux *aux,
+				   struct drm_dp_aux_msg *msg)
+{
+	struct it6505 *it6505 = container_of(aux, struct it6505, aux);
+	u8 cmd;
+	bool is_i2c = !(msg->request & DP_AUX_NATIVE_WRITE);
+	int ret;
+	enum aux_cmd_reply reply;
+
+	/* IT6505 doesn't support arbitrary I2C read / write. */
+	if (is_i2c)
+		return -EINVAL;
+
+	switch (msg->request) {
+	case DP_AUX_NATIVE_READ:
+		cmd = CMD_AUX_NATIVE_READ;
+		break;
+	case DP_AUX_NATIVE_WRITE:
+		cmd = CMD_AUX_NATIVE_WRITE;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = it6505_aux_do_transfer(it6505, cmd, msg->address, msg->buffer,
+				     msg->size, &reply);
+	if (ret < 0)
+		return ret;
+
+	switch (reply) {
+	case REPLY_ACK:
+		msg->reply = DP_AUX_NATIVE_REPLY_ACK;
+		break;
+	case REPLY_NACK:
+		msg->reply = DP_AUX_NATIVE_REPLY_NACK;
+		break;
+	case REPLY_DEFER:
+		msg->reply = DP_AUX_NATIVE_REPLY_DEFER;
+		break;
+	}
+
+	return ret;
+}
+
+static int dptx_get_edidblock(void *data, u8 *buf, unsigned int blockno,
+			      size_t len)
+{
+	struct it6505 *it6505 = data;
+	int offset, ret, aux_retry = 100;
+	struct device *dev = &it6505->client->dev;
+	enum aux_cmd_reply reply;
+
+	it6505_aux_reset(it6505);
+	DRM_DEV_DEBUG_DRIVER(dev, "blocknum = %d", blockno);
+
+	for (offset = 0; offset < EDID_LENGTH;) {
+		ret = it6505_aux_do_transfer(it6505, CMD_AUX_I2C_EDID_READ,
+					     blockno * EDID_LENGTH + offset,
+					     buf + offset, 8, &reply);
+		if (ret < 0)
+			return ret;
+
+		switch (reply) {
+		case REPLY_ACK:
+			DRM_DEV_DEBUG_DRIVER(dev, "[0x%02x]: %8ph", offset,
+					     buf + offset);
+			offset += 8;
+			aux_retry = 80;
+			break;
+		case REPLY_NACK:
+			return -EIO;
+		case REPLY_DEFER:
+			msleep(20);
+			if (!(--aux_retry))
+				return -EIO;
+		}
+	}
+
+	return 0;
+}
+
+static void it6505_disable_hdcp(struct it6505 *it6505)
+{
+	dptx_set_bits(it6505, 0x38, 0x0B, 0x00);
+	dptx_set_bits(it6505, 0x05, 0x10, 0x10);
+}
+
+
+static int it6505_get_modes(struct drm_connector *connector)
+{
+	struct it6505 *it6505 = connector_to_it6505(connector);
+	int err, num_modes = 0, i;
+	struct device *dev = &it6505->client->dev;
+
+	it6505->train_fail_hpd = 3;
+	DRM_DEV_DEBUG_DRIVER(dev, "sink_count:%d", it6505->dpcd_sink_count);
+
+	if (it6505->edid)
+		return drm_add_edid_modes(connector, it6505->edid);
+	mutex_lock(&it6505->mode_lock);
+
+	for (i = 0; i < 3; i++) {
+		dptx_debug_print(it6505, 0x9F, "aux status");
+		it6505_aux_reset(it6505);
+		it6505_disable_hdcp(it6505);
+		it6505->edid =
+		drm_do_get_edid(&it6505->connector, dptx_get_edidblock, it6505);
+
+		if (it6505->edid)
+			break;
+		dptx_debug_dump(it6505);
+	}
+	if (!it6505->edid) {
+		DRM_DEV_ERROR(dev, "Failed to read EDID");
+		goto unlock;
+	}
+
+	err = drm_connector_update_edid_property(connector, it6505->edid);
+	if (err) {
+		DRM_DEV_ERROR(dev, "Failed to update EDID property: %d", err);
+		goto unlock;
+	}
+
+	num_modes = drm_add_edid_modes(connector, it6505->edid);
+
+unlock:
+	mutex_unlock(&it6505->mode_lock);
+
+	return num_modes;
+}
+
+static const struct drm_connector_helper_funcs it6505_connector_helper_funcs = {
+	.get_modes = it6505_get_modes,
+};
+
+static enum drm_connector_status it6505_detect(struct drm_connector *connector,
+					       bool force)
+{
+	struct it6505 *it6505 = connector_to_it6505(connector);
+	enum drm_connector_status status;
+	u8 dpcd600;
+
+	if (!it6505->powered)
+		goto it6505_no_powered;
+
+	it6505_aux_on(it6505);
+	dpcd600 = dpcd_read(it6505, DP_SET_POWER);
+	dpcd_debug_print(it6505, DP_SET_POWER, "");
+
+	if (!dptx_get_sink_hpd(it6505) ||
+	    (dpcd600 & DP_SET_POWER_MASK) != DP_SET_POWER_D0) {
+it6505_no_powered:
+		it6505->dpcd_sink_count = 0;
+	}
+
+	if (it6505->dpcd_sink_count)
+		status = connector_status_connected;
+	else
+		status = connector_status_disconnected;
+
+	DRM_DEV_DEBUG_DRIVER(&it6505->client->dev, "sink_count:%d status:%d",
+			     it6505->dpcd_sink_count, status);
+
+	return status;
+}
+
+static const struct drm_connector_funcs it6505_connector_funcs = {
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.detect = it6505_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 int it6505_extcon_notifier(struct notifier_block *self,
+				  unsigned long event, void *ptr)
+{
+	struct it6505 *it6505 = container_of(self, struct it6505, event_nb);
+
+	schedule_work(&it6505->extcon_wq);
+	return NOTIFY_DONE;
+}
+
+static void it6505_extcon_work(struct work_struct *work)
+{
+	struct it6505 *it6505 = container_of(work, struct it6505, extcon_wq);
+	int state = extcon_get_state(it6505->extcon, EXTCON_DISP_DP);
+	unsigned int pwroffretry = 0;
+	struct device *dev = &it6505->client->dev;
+
+	if (it6505->enable_drv_hold)
+		return;
+	mutex_lock(&it6505->lock);
+	DRM_DEV_DEBUG_DRIVER(dev, "EXTCON_DISP_DP = 0x%02x", state);
+	if (state > 0) {
+		DRM_DEV_DEBUG_DRIVER(dev, "start to power on");
+		msleep(1000);
+		it6505_poweron(it6505);
+		if (!dptx_get_sink_hpd(it6505))
+			it6505_lane_off(it6505);
+		it6505_aux_on(it6505);
+	} else {
+		DRM_DEV_DEBUG_DRIVER(dev, "start to power off");
+		while (it6505_poweroff(it6505) && pwroffretry++ < 5) {
+			DRM_DEV_DEBUG_DRIVER(dev, "power off fail %d times",
+					     pwroffretry);
+		}
+		drm_helper_hpd_irq_event(it6505->connector.dev);
+		DRM_DEV_DEBUG_DRIVER(dev, "power off it6505 success!");
+	}
+	mutex_unlock(&it6505->lock);
+}
+
+static int it6505_use_notifier_module(struct it6505 *it6505)
+{
+	int ret;
+	struct device *dev = &it6505->client->dev;
+
+	it6505->event_nb.notifier_call = it6505_extcon_notifier;
+	INIT_WORK(&it6505->extcon_wq, it6505_extcon_work);
+	ret = devm_extcon_register_notifier(&it6505->client->dev,
+					    it6505->extcon, EXTCON_DISP_DP,
+					    &it6505->event_nb);
+	if (ret) {
+		DRM_DEV_ERROR(dev, "failed to register notifier for DP");
+		return ret;
+	}
+	schedule_work(&it6505->extcon_wq);
+
+	return 0;
+}
+
+static void it6505_remove_notifier_module(struct it6505 *it6505)
+{
+	if (it6505->extcon) {
+		devm_extcon_unregister_notifier(&it6505->client->dev,
+						it6505->extcon,
+						EXTCON_DISP_DP,
+						&it6505->event_nb);
+		flush_work(&it6505->extcon_wq);
+	}
+}
+
+static int it6505_bridge_attach(struct drm_bridge *bridge)
+{
+	struct it6505 *it6505 = bridge_to_it6505(bridge);
+	struct device *dev;
+	int err;
+
+	dev = &it6505->client->dev;
+	if (!bridge->encoder) {
+		DRM_DEV_ERROR(dev, "Parent encoder object not found");
+		return -ENODEV;
+	}
+
+	err = drm_connector_init(bridge->dev, &it6505->connector,
+				 &it6505_connector_funcs,
+				 DRM_MODE_CONNECTOR_DisplayPort);
+	if (err < 0) {
+		DRM_DEV_ERROR(dev, "Failed to initialize connector: %d", err);
+		return err;
+	}
+
+	drm_connector_helper_add(&it6505->connector,
+				 &it6505_connector_helper_funcs);
+
+	it6505->connector.polled = DRM_CONNECTOR_POLL_HPD;
+
+	err = drm_connector_attach_encoder(&it6505->connector, bridge->encoder);
+	if (err < 0) {
+		DRM_DEV_ERROR(dev, "Failed to link up connector to encoder: %d",
+			      err);
+		goto cleanup_connector;
+	}
+
+	err = drm_connector_register(&it6505->connector);
+	if (err < 0) {
+		DRM_DEV_ERROR(dev, "Failed to register connector: %d", err);
+		goto cleanup_connector;
+	}
+
+	err = it6505_use_notifier_module(it6505);
+	if (err < 0) {
+		drm_connector_unregister(&it6505->connector);
+		goto unregister_connector;
+	}
+
+	return 0;
+
+unregister_connector:
+	drm_connector_unregister(&it6505->connector);
+cleanup_connector:
+	drm_connector_cleanup(&it6505->connector);
+	return err;
+}
+
+static void it6505_bridge_detach(struct drm_bridge *bridge)
+{
+	struct it6505 *it6505 = bridge_to_it6505(bridge);
+
+	it6505_remove_notifier_module(it6505);
+	drm_connector_unregister(&it6505->connector);
+	drm_connector_cleanup(&it6505->connector);
+}
+
+static enum drm_mode_status
+it6505_bridge_mode_valid(struct drm_bridge *bridge,
+			 const struct drm_display_mode *mode)
+{
+	struct it6505 *it6505 = bridge_to_it6505(bridge);
+	struct drm_display_mode *video = &it6505->video_info;
+
+	if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+		return MODE_NO_INTERLACE;
+
+	/* Max 1200p at 5.4 Ghz, one lane */
+	if (mode->clock > MAX_PIXEL_CLK)
+		return MODE_CLOCK_HIGH;
+	video->clock = mode->clock;
+
+	return MODE_OK;
+}
+
+static int it6505_send_video_infoframe(struct it6505 *it6505,
+				       struct hdmi_avi_infoframe *frame)
+{
+	u8 buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AVI_INFOFRAME_SIZE];
+	int err;
+	struct device *dev = &it6505->client->dev;
+
+	err = hdmi_avi_infoframe_pack(frame, buffer, sizeof(buffer));
+	if (err < 0) {
+		DRM_DEV_ERROR(dev, "Failed to pack AVI infoframe: %d\n", err);
+		return err;
+	}
+
+	err = dptx_set_bits(it6505, 0xE8, 0x01, 0x00);
+	if (err)
+		return err;
+
+	err = regmap_bulk_write(it6505->regmap, 0xE9,
+				buffer + HDMI_INFOFRAME_HEADER_SIZE,
+				frame->length);
+	if (err)
+		return err;
+
+	err = dptx_set_bits(it6505, 0xE8, 0x01, 0x01);
+	if (err)
+		return err;
+
+	return 0;
+}
+
+static void it6505_bridge_mode_set(struct drm_bridge *bridge,
+				   const struct drm_display_mode *mode,
+				   const struct drm_display_mode *adjusted_mode)
+{
+	struct it6505 *it6505 = bridge_to_it6505(bridge);
+	struct hdmi_avi_infoframe frame;
+	int err;
+	struct device *dev = &it6505->client->dev;
+
+	mutex_lock(&it6505->mode_lock);
+	err = drm_hdmi_avi_infoframe_from_display_mode(&frame,
+						       &it6505->connector,
+						       adjusted_mode);
+	if (err) {
+		DRM_DEV_ERROR(dev, "Failed to setup AVI infoframe: %d", err);
+		goto unlock;
+	}
+
+	strlcpy(it6505->video_info.name, adjusted_mode->name,
+		DRM_DISPLAY_MODE_LEN);
+	it6505->video_info.type = adjusted_mode->type;
+	it6505->video_info.flags = adjusted_mode->flags;
+	err = it6505_send_video_infoframe(it6505, &frame);
+	if (err)
+		DRM_DEV_ERROR(dev, "Failed to send AVI infoframe: %d", err);
+
+unlock:
+	mutex_unlock(&it6505->mode_lock);
+}
+
+static bool it6505_audio_input(struct it6505 *it6505)
+{
+	int regbe = dptx_read(it6505, 0xBE);
+
+	return regbe != 0xFF;
+}
+
+static void dptx_set_audio_format(struct it6505 *it6505)
+{
+	unsigned int audio_source_count;
+
+	/* I2S MODE */
+	dptx_write(it6505, 0xB9,
+	   (it6505->audio_word_length << 5) | (it6505->i2s_data_sequence << 4) |
+		       (it6505->i2s_ws_channel << 3) |
+		       (it6505->i2s_data_delay << 2) |
+		       (it6505->i2s_justified << 1) | it6505->i2s_input_format);
+	if (it6505->audio_select == SPDIF) {
+		dptx_write(it6505, 0xBA, 0x00);
+		/* 0x30 = 128*FS */
+		dptx_set_bits(it6505, 0x11, 0xF0, 0x30);
+	} else {
+		dptx_write(it6505, 0xBA, 0xE4);
+	}
+
+	dptx_write(it6505, 0xBB, 0x20);
+	dptx_write(it6505, 0xBC, 0x00);
+	audio_source_count = BIT(DIV_ROUND_UP(it6505->audio_channel_count, 2))
+				 - 1;
+
+
+	audio_source_count |= it6505->audio_select << 4;
+
+	dptx_write(it6505, 0xB8, audio_source_count);
+}
+
+static void dptx_set_audio_channel_status(struct it6505 *it6505)
+{
+	enum it6505_audio_sample_rate sample_rate = it6505->audio_sample_rate;
+	u8 audio_word_length_map[] = {0x02, 0x04, 0x03, 0x0B};
+
+	/* Channel Status */
+	dptx_write(it6505, 0xBF, it6505->audio_type << 1);
+	dptx_write(it6505, 0xC0, 0x00);
+	dptx_write(it6505, 0xC1, 0x00);
+	dptx_write(it6505, 0xC2, sample_rate);
+	dptx_write(it6505, 0xC3, (~sample_rate << 4) |
+		   audio_word_length_map[it6505->audio_word_length]);
+}
+
+static void dptx_set_audio_infoframe(struct it6505 *it6505)
+{
+	struct device *dev = &it6505->client->dev;
+	u8 audio_info_ca[] = {0x00, 0x00, 0x01, 0x03, 0x07, 0x0B, 0x0F, 0x1F};
+
+	dptx_write(it6505, 0xF7, it6505->audio_channel_count - 1);
+	dptx_set_bits(it6505, 0xF8, 0x03, 0x00);
+
+	if (it6505->audio_channel_count > 0 && it6505->audio_channel_count <= 8)
+		dptx_write(it6505, 0xF9,
+			   audio_info_ca[it6505->audio_channel_count - 1]);
+	else
+		DRM_DEV_ERROR(dev, "audio channel number error: %u",
+			      it6505->audio_channel_count);
+	/* Enable Audio InfoFrame */
+	dptx_set_bits(it6505, 0xE8, 0x22, 0x22);
+}
+
+static void it6505_set_audio(struct it6505 *it6505)
+{
+	struct device *dev = &it6505->client->dev;
+	int regbe;
+
+	it6505_audio_disable(it6505);
+	/* Audio Clock Domain Reset */
+	dptx_set_bits(it6505, 0x05, 0x02, 0x02);
+	/* Audio mute */
+	dptx_set_bits(it6505, 0xD3, 0x20, 0x20);
+	/* Release Audio Clock Domain Reset */
+	dptx_set_bits(it6505, 0x05, 0x02, 0x00);
+
+	if (it6505_audio_input(it6505)) {
+		regbe = dptx_read(it6505, 0xBE);
+		DRM_DEV_DEBUG_DRIVER(dev, "audio input fs: %d.%d kHz",
+				     6750 / regbe, 67500 % regbe);
+	} else {
+		DRM_DEV_DEBUG_DRIVER(dev, "no audio input");
+	}
+
+	dptx_set_audio_format(it6505);
+	dptx_set_audio_channel_status(it6505);
+	dptx_set_audio_infoframe(it6505);
+
+	/* Enable Enhanced Audio TimeStmp Mode */
+	dptx_set_bits(it6505, 0xD4, 0x04, 0x04);
+	/* Disable Full Audio Packet */
+	dptx_set_bits(it6505, 0xBB, 0x10, 0x00);
+
+	dptx_write(it6505, 0xDE, 0x00);
+	dptx_write(it6505, 0xDF, 0x80);
+	dptx_write(it6505, 0xE0, 0x00);
+	dptx_set_bits(it6505, 0xB8, 0x80, 0x80);
+	dptx_set_bits(it6505, 0xB8, 0x80, 0x00);
+	dptx_set_bits(it6505, 0xD3, 0x20, 0x00);
+	it6505->enable_audio = true;
+}
+
+static int __maybe_unused it6505_audio_set_hw_params(struct it6505 *it6505,
+					struct hdmi_codec_params *params)
+{
+	unsigned int channel_count = params->cea.channels, i = 0;
+	struct device *dev = &it6505->client->dev;
+
+	DRM_DEV_DEBUG_DRIVER(dev, "setting %d Hz, %d bit, %d channels\n",
+			     params->sample_rate, params->sample_width,
+			     channel_count);
+
+	if (!it6505->bridge.encoder)
+		return -ENODEV;
+
+	while (audio_sample_rate_map[i].sample_rate_value &&
+	       params->sample_rate !=
+			audio_sample_rate_map[i].sample_rate_value) {
+		i++;
+	}
+	if (!audio_sample_rate_map[i].sample_rate_value) {
+		DRM_DEV_DEBUG_DRIVER(dev, "sample rate: %d Hz not support",
+				     params->sample_rate);
+		return -EINVAL;
+	}
+	it6505->audio_sample_rate = audio_sample_rate_map[i].rate;
+
+	switch (params->sample_width) {
+	case 16:
+		it6505->audio_word_length = WORD_LENGTH_16BIT;
+		break;
+	case 18:
+		it6505->audio_word_length = WORD_LENGTH_18BIT;
+		break;
+	case 20:
+		it6505->audio_word_length = WORD_LENGTH_20BIT;
+		break;
+	case 24:
+	case 32:
+		it6505->audio_word_length = WORD_LENGTH_24BIT;
+		break;
+	default:
+		DRM_DEV_DEBUG_DRIVER(dev, "wordlength: %d bit not support",
+				     params->sample_width);
+		return -EINVAL;
+	}
+
+	if (channel_count == 0 || channel_count % 2) {
+		DRM_DEV_DEBUG_DRIVER(dev, "channel number: %d not support",
+				     channel_count);
+		return -EINVAL;
+	}
+	it6505->audio_channel_count = channel_count;
+
+	return 0;
+}
+
+/*
+ * DPCD Read
+ */
+
+static int it6505_get_dpcd(struct it6505 *it6505, int offset, u8 *dpcd, int num)
+{
+	int i, ret;
+	struct device *dev = &it6505->client->dev;
+
+	for (i = 0; i < num; i += 4) {
+		ret = drm_dp_dpcd_read(&it6505->aux, offset + i, dpcd + i,
+				       min(num - i, 4));
+		if (ret < 0)
+			return ret;
+	}
+
+	DRM_DEV_DEBUG_DRIVER(dev, "DPCD[0x%x] = %*ph", offset, num, dpcd);
+	return 0;
+}
+
+static void it6505_parse_dpcd(struct it6505 *it6505)
+{
+	u8 dpcd[DP_RECEIVER_CAP_SIZE];
+	struct device *dev = &it6505->client->dev;
+	int bcaps;
+	struct drm_dp_link *link = &it6505->link;
+
+	it6505_get_dpcd(it6505, DP_DPCD_REV, dpcd, ARRAY_SIZE(dpcd));
+
+	DRM_DEV_DEBUG_DRIVER(dev, "#########DPCD Rev.: %d.%d###########",
+			     link->revision >> 4, link->revision & 0x0F);
+
+	DRM_DEV_DEBUG_DRIVER(dev, "Sink maximum link rate: %d.%d Gbps per lane",
+			     link->rate / 100000, link->rate / 1000 % 100);
+
+	switch (link->rate) {
+	case 162000:
+		it6505->hbr =
+		min3(MAX_LINK_RATE, TRAINING_LINK_RATE, RBR) ? true : false;
+		if (it6505->hbr)
+			DRM_DEV_DEBUG_DRIVER(
+				dev, "Not support HBR Mode, will train RBR");
+		else
+			DRM_DEV_DEBUG_DRIVER(dev, "Training RBR");
+		break;
+
+	case 270000:
+		it6505->hbr =
+		min3(MAX_LINK_RATE, TRAINING_LINK_RATE, HBR) ? true : false;
+		if (!it6505->hbr)
+			DRM_DEV_DEBUG_DRIVER(
+				dev, "Support HBR mode, will train RBR");
+		else
+			DRM_DEV_DEBUG_DRIVER(dev, "Training HBR");
+		break;
+
+	case 540000:
+		it6505->hbr =
+		min3(MAX_LINK_RATE, TRAINING_LINK_RATE, HBR2) ? true : false;
+		if (it6505->hbr)
+			DRM_DEV_DEBUG_DRIVER(
+				dev, "Support HBR2 mode, will train HBR");
+		else
+			DRM_DEV_DEBUG_DRIVER(dev, "Training RBR");
+		break;
+
+	case 810000:
+		it6505->hbr =
+		min3(MAX_LINK_RATE, TRAINING_LINK_RATE, HBR3) ? true : false;
+		if (it6505->hbr)
+			DRM_DEV_DEBUG_DRIVER(
+				dev, "Support HBR3 mode, will train HBR");
+		else
+			DRM_DEV_DEBUG_DRIVER(dev, "Training RBR");
+		break;
+	default:
+		DRM_DEV_ERROR(dev, "Unknown Maximum Link Rate: %u",
+			      link->rate);
+		break;
+	}
+
+	if (link->num_lanes == 1 || link->num_lanes == 2 ||
+	    link->num_lanes == 4) {
+		DRM_DEV_DEBUG_DRIVER(
+			dev, "Lane Count: %u lane", link->num_lanes);
+		it6505->lane_count = min3(MAX_LANE_COUNT, TRAINING_LANE_COUNT,
+					  (int)link->num_lanes);
+		DRM_DEV_DEBUG_DRIVER(dev,
+				     "Training %u lane", it6505->lane_count);
+	} else {
+		DRM_DEV_ERROR(dev, "Lane Count Error: %u", link->num_lanes);
+	}
+
+	if (link->capabilities & DP_LINK_CAP_ENHANCED_FRAMING)
+		DRM_DEV_DEBUG_DRIVER(dev, "Support Enhanced Framing");
+	else
+		DRM_DEV_DEBUG_DRIVER(dev,
+				     "Can not support Enhanced Framing Mode");
+
+	if (dpcd[DP_MAX_DOWNSPREAD] & DP_MAX_DOWNSPREAD_0_5) {
+		DRM_DEV_DEBUG_DRIVER(dev,
+				     "Maximum Down-Spread: 0.5, support SSC!");
+	} else {
+		DRM_DEV_DEBUG_DRIVER(dev,
+				     "Maximum Down-Spread: 0, No support SSC!");
+		it6505->enable_ssc = false;
+	}
+
+	if (link->revision >= 0x11 &&
+	    dpcd[DP_MAX_DOWNSPREAD] & DP_NO_AUX_HANDSHAKE_LINK_TRAINING)
+		DRM_DEV_DEBUG_DRIVER(dev, "Support No AUX Training");
+	else
+		DRM_DEV_DEBUG_DRIVER(dev, "Can not support No AUX Training");
+
+	bcaps = dpcd_read(it6505, DP_AUX_HDCP_BCAPS);
+	if (bcaps & DP_BCAPS_HDCP_CAPABLE) {
+		DRM_DEV_DEBUG_DRIVER(dev, "Sink support HDCP!");
+		it6505->cp_capable = true;
+		DRM_DEV_DEBUG_DRIVER(dev, "Downstream is %s!",
+		bcaps & DP_BCAPS_REPEATER_PRESENT ? "repeater" : "receiver");
+	} else {
+		DRM_DEV_DEBUG_DRIVER(dev, "Sink not support HDCP!");
+		it6505->cp_capable = false;
+		it6505->enable_hdcp = false;
+	}
+}
+
+static void it6505_enable_hdcp(struct it6505 *it6505)
+{
+	u8 bksvs[5], c;
+	struct device *dev = &it6505->client->dev;
+
+	/* Disable CP_Desired */
+	dptx_set_bits(it6505, 0x38, 0x0B, 0x00);
+	dptx_set_bits(it6505, 0x05, 0x10, 0x10);
+
+	usleep_range(1000, 1500);
+	c = dpcd_read(it6505, DP_AUX_HDCP_BCAPS);
+	DRM_DEV_DEBUG_DRIVER(dev, "DPCD[0x68028]: 0x%x\n", c);
+	if (!c)
+		return;
+
+	dptx_set_bits(it6505, 0x05, 0x10, 0x00);
+	/* Disable CP_Desired */
+	dptx_set_bits(it6505, 0x38, 0x01, 0x00);
+	/* Use R0' 100ms waiting */
+	dptx_set_bits(it6505, 0x38, 0x08, 0x00);
+	/* clear the repeater List Chk Done and fail bit */
+	dptx_set_bits(it6505, 0x39, 0x30, 0x00);
+
+	it6505_get_dpcd(it6505, DP_AUX_HDCP_BKSV, bksvs, ARRAY_SIZE(bksvs));
+
+	DRM_DEV_DEBUG_DRIVER(dev, "Sink BKSV = %5ph", bksvs);
+
+	/* Select An Generator */
+	dptx_set_bits(it6505, 0x3A, 0x01, 0x01);
+	/* Enable An Generator */
+	dptx_set_bits(it6505, 0x3A, 0x02, 0x02);
+	/* delay1ms(10);*/
+	usleep_range(10000, 15000);
+	/* Stop An Generator */
+	dptx_set_bits(it6505, 0x3A, 0x02, 0x00);
+
+	dptx_set_bits(it6505, 0x38, 0x01, 0x01);
+	dptx_set_bits(it6505, 0x39, 0x01, 0x01);
+}
+
+static void it6505_lanespeed_setup(struct it6505 *it6505)
+{
+	if (!it6505->hbr) {
+		dptx_set_bits(it6505, 0x16, 0x01, 0x01);
+		dptx_set_bits(it6505, 0x5C, 0x02, 0x00);
+	} else {
+		dptx_set_bits(it6505, 0x16, 0x01, 0x00);
+		dptx_set_bits(it6505, 0x5C, 0x02, 0x02);
+	}
+}
+
+static void it6505_lane_swap(struct it6505 *it6505)
+{
+	int err;
+	union extcon_property_value property;
+	struct device *dev = &it6505->client->dev;
+
+	if (!it6505->lane_swap_disabled) {
+		err = extcon_get_property(it6505->extcon, EXTCON_DISP_DP,
+					  EXTCON_PROP_USB_TYPEC_POLARITY,
+					  &property);
+		if (err) {
+			DRM_DEV_ERROR(dev, "get property fail!");
+			return;
+		}
+		it6505->lane_swap = property.intval;
+	}
+
+	dptx_set_bits(it6505, 0x16, 0x08, it6505->lane_swap ? 0x08 : 0x00);
+	dptx_set_bits(it6505, 0x16, 0x06, (it6505->lane_count - 1) << 1);
+	DRM_DEV_DEBUG_DRIVER(dev, "it6505 lane_swap = 0x%x", it6505->lane_swap);
+	it6505_lane_power_on(it6505);
+	it6505_lane_termination_on(it6505);
+}
+
+static void it6505_lane_config(struct it6505 *it6505)
+{
+	it6505_lanespeed_setup(it6505);
+	it6505_lane_swap(it6505);
+}
+
+static void it6505_set_ssc(struct it6505 *it6505)
+{
+	dptx_set_bits(it6505, 0x16, 0x10, it6505->enable_ssc << 4);
+	if (it6505->enable_ssc) {
+		dptx_select_bank(it6505, 1);
+		dptx_write(it6505, 0x88, 0x9E);
+		dptx_write(it6505, 0x89, 0x1C);
+		dptx_write(it6505, 0x8A, 0x42);
+		dptx_select_bank(it6505, 0);
+		dptx_write(it6505, 0x58, 0x07);
+		dptx_write(it6505, 0x59, 0x29);
+		dptx_write(it6505, 0x5A, 0x03);
+		/* Stamp Interrupt Step */
+		dptx_set_bits(it6505, 0xD4, 0x30, 0x10);
+		dpcd_write(it6505, DP_DOWNSPREAD_CTRL, DP_SPREAD_AMP_0_5);
+	} else {
+		dpcd_write(it6505, DP_DOWNSPREAD_CTRL, 0x00);
+		dptx_set_bits(it6505, 0xD4, 0x30, 0x00);
+	}
+}
+
+static void it6505_pixel_clk_phase_adjustment(struct it6505 *it6505)
+{
+	struct drm_display_mode *vid = &it6505->video_info;
+
+	dptx_set_bits(it6505, 0x10, 0x03,
+		vid->clock < ADJUST_PHASE_THRESHOLD ? PIXEL_CLK_DELAY : 0);
+	dptx_set_bits(it6505, 0x12, 0x10, PIXEL_CLK_INVERSE << 4);
+}
+
+static void it6505_set_afe_driving(struct it6505 *it6505)
+{
+	struct device *dev = &it6505->client->dev;
+	unsigned int afe_setting;
+
+	afe_setting = AFE_SETTING;
+	if (afe_setting >= ARRAY_SIZE(afe_setting_table)) {
+		DRM_DEV_ERROR(dev, "afe setting value error and use default");
+		afe_setting = 0;
+	}
+	if (afe_setting) {
+		dptx_select_bank(it6505, 1);
+		dptx_write(it6505, 0x7E, afe_setting_table[afe_setting][0]);
+		dptx_write(it6505, 0x7F, afe_setting_table[afe_setting][1]);
+		dptx_write(it6505, 0x81, afe_setting_table[afe_setting][2]);
+		dptx_select_bank(it6505, 0);
+	}
+}
+
+static void dptx_output(struct it6505 *it6505)
+{
+	/* change bank 0 */
+	dptx_select_bank(it6505, 0);
+	/* config CSC mapping table into RGB-to-YCbCr */
+	dptx_write(it6505, 0x64, 0x10);
+	dptx_write(it6505, 0x65, 0x80);
+	dptx_write(it6505, 0x66, 0x10);
+	dptx_write(it6505, 0x67, 0x4F);
+	dptx_write(it6505, 0x68, 0x09);
+	dptx_write(it6505, 0x69, 0xBA);
+	dptx_write(it6505, 0x6A, 0x3B);
+	dptx_write(it6505, 0x6B, 0x4B);
+	dptx_write(it6505, 0x6C, 0x3E);
+	dptx_write(it6505, 0x6D, 0x4F);
+	dptx_write(it6505, 0x6E, 0x09);
+	dptx_write(it6505, 0x6F, 0x56);
+	dptx_write(it6505, 0x70, 0x0E);
+	dptx_write(it6505, 0x71, 0x00);
+	dptx_write(it6505, 0x72, 0x00);
+	dptx_write(it6505, 0x73, 0x4F);
+	dptx_write(it6505, 0x74, 0x09);
+	dptx_write(it6505, 0x75, 0x00);
+	dptx_write(it6505, 0x76, 0x00);
+	dptx_write(it6505, 0x77, 0xE7);
+	dptx_write(it6505, 0x78, 0x10);
+	/* initial HPD Timer event setting */
+	dptx_write(it6505, 0xE8, 0x00);
+	dptx_set_bits(it6505, 0xCE, 0x70, 0x60);
+	dptx_write(it6505, 0xCA, 0x4D);
+	dptx_write(it6505, 0xC9, 0xF5);
+	dptx_write(it6505, 0x5C, 0x02);
+
+	drm_dp_link_power_up(&it6505->aux, &it6505->link);
+	dptx_set_bits(it6505, 0x59, 0x01, 0x01);
+	dptx_set_bits(it6505, 0x5A, 0x05, 0x01);
+	dptx_write(it6505, 0x12, 0x01);
+	dptx_write(it6505, 0xCB, 0x17);
+	dptx_write(it6505, 0x11, 0x09);
+	dptx_write(it6505, 0x20, 0x28);
+	dptx_set_bits(it6505, 0x23, 0x30, 0x00);
+	dptx_set_bits(it6505, 0x3A, 0x04, 0x04);
+	dptx_set_bits(it6505, 0x15, 0x01, 0x01);
+	dptx_write(it6505, 0x0C, 0x08);
+
+	dptx_set_bits(it6505, 0x5F, 0x20, 0x00);
+	it6505_lane_config(it6505);
+
+	it6505_set_ssc(it6505);
+
+	if (it6505->link.capabilities & DP_LINK_CAP_ENHANCED_FRAMING) {
+		dptx_write(it6505, 0xD3, 0x33);
+		dpcd_write(it6505, DP_LANE_COUNT_SET,
+			    DP_LANE_COUNT_ENHANCED_FRAME_EN);
+	} else {
+		dptx_write(it6505, 0xD3, 0x32);
+	}
+
+	/* it6505 displayport phy and logic reset */
+	dptx_set_bits(it6505, 0x15, 0x02, 0x02);
+	dptx_set_bits(it6505, 0x15, 0x02, 0x00);
+	dptx_set_bits(it6505, 0x05, 0x03, 0x02);
+	dptx_set_bits(it6505, 0x05, 0x03, 0x00);
+
+	/* reg60[2] = InDDR */
+	dptx_write(it6505, 0x60, 0x44);
+	/* M444B24 format */
+	dptx_write(it6505, 0x62, 0x01);
+	/* select RGB Bypass CSC */
+	dptx_write(it6505, 0x63, 0x00);
+
+	it6505_pixel_clk_phase_adjustment(it6505);
+	dptx_set_bits(it6505, 0x61, 0x01, 0x01);
+	dptx_write(it6505, 0x06, 0xFF);
+	dptx_write(it6505, 0x07, 0xFF);
+	dptx_write(it6505, 0x08, 0xFF);
+
+	dptx_set_bits(it6505, 0xD3, 0x30, 0x00);
+	dptx_set_bits(it6505, 0xD4, 0x41, 0x41);
+	dptx_set_bits(it6505, 0xE8, 0x11, 0x11);
+
+	it6505_set_afe_driving(it6505);
+	/* reset auto DP link training */
+	dptx_write(it6505, 0x17, 0x04);
+	/* start auto DP link training */
+	dptx_write(it6505, 0x17, 0x01);
+}
+
+static void dptx_process_sys_wait(struct it6505 *it6505)
+{
+	int reg0e;
+	struct device *dev = &it6505->client->dev;
+
+	reg0e = dptx_read(it6505, 0x0E);
+	DRM_DEV_DEBUG_DRIVER(dev, "SYS_WAIT state reg0e=0x%02x", reg0e);
+	dptx_debug_print(it6505, 0x9F, "");
+
+	if (reg0e & BIT(4)) {
+		DRM_DEV_DEBUG_DRIVER(dev, "Auto Link Training Success...");
+		DRM_DEV_DEBUG_DRIVER(dev, "Link State: 0x%x", reg0e & 0x1F);
+		if (it6505_audio_input(it6505)) {
+			DRM_DEV_DEBUG_DRIVER(dev, "Enable audio!");
+			it6505_set_audio(it6505);
+		}
+
+		if (it6505->hdcp_flag) {
+			DRM_DEV_DEBUG_DRIVER(dev, "Enable HDCP");
+			dptx_sys_chg(it6505, SYS_HDCP);
+		} else {
+			DRM_DEV_DEBUG_DRIVER(dev, "Disable HDCP");
+			dptx_sys_chg(it6505, SYS_NOROP);
+		}
+	} else {
+		DRM_DEV_DEBUG_DRIVER(dev, "Auto Link Training fail step %d",
+				     it6505->auto_train_count);
+		dptx_debug_print(it6505, 0x0D, "system state");
+		if (it6505->auto_train_count > 0) {
+			it6505->auto_train_count--;
+			dptx_sys_chg(it6505, SYS_AUTOTRAIN);
+		} else {
+			DRM_DEV_DEBUG_DRIVER(dev, "Auto Training Fail 5 times");
+			DRM_DEV_DEBUG_DRIVER(dev, "Sys change to SYS_HPD");
+			dpcd_write(it6505, DP_TRAINING_PATTERN_SET, 0x00);
+			if (it6505->train_fail_hpd > 0) {
+				it6505->train_fail_hpd--;
+				dptx_sys_chg(it6505, SYS_HPD);
+			} else {
+				dptx_sys_chg(it6505, SYS_TRAINFAIL);
+			}
+		}
+	}
+}
+
+static void dptx_sys_fsm(struct it6505 *it6505)
+{
+	unsigned int i;
+	int reg0e;
+	struct device *dev = &it6505->client->dev;
+
+	if (it6505->state != SYS_UNPLUG && !dptx_get_sink_hpd(it6505))
+		dptx_sys_chg(it6505, SYS_UNPLUG);
+
+	DRM_DEV_DEBUG_DRIVER(dev, "state: %s", state_string[it6505->state]);
+	switch (it6505->state) {
+	case SYS_UNPLUG:
+		drm_helper_hpd_irq_event(it6505->connector.dev);
+		break;
+
+	case SYS_HPD:
+		dptx_debug_print(it6505, 0x9F, "aux status");
+		drm_dp_link_probe(&it6505->aux, &it6505->link);
+		drm_dp_link_power_up(&it6505->aux, &it6505->link);
+		dpcd_debug_print(it6505, DP_SET_POWER, "");
+		it6505->dpcd_sink_count
+			= dpcd_read(it6505, DP_SINK_COUNT);
+		it6505->dpcd_sink_count &= 0x3F;
+		DRM_DEV_DEBUG_DRIVER(dev, "sink_count:%d",
+				     it6505->dpcd_sink_count);
+		drm_helper_hpd_irq_event(it6505->connector.dev);
+
+		if (it6505->dpcd_sink_count) {
+			for (i = 0; i < 4000; i++) {
+				usleep_range(1000, 2000);
+				if (it6505->edid)
+					break;
+			}
+		}
+
+		it6505_init_fsm(it6505);
+		/* GETDPCD */
+		it6505_parse_dpcd(it6505);
+
+		/*
+		 * training fail 5 times,
+		 * then change to HPD to restart
+		 */
+		it6505->auto_train_count = 5;
+		DRM_DEV_DEBUG_DRIVER(dev, "will Train %s, %d lanes",
+				     it6505->hbr ? "HBR" : "RBR",
+				     it6505->lane_count);
+		dptx_sys_chg(it6505, SYS_AUTOTRAIN);
+		break;
+
+	case SYS_AUTOTRAIN:
+		it6505_disable_hdcp(it6505);
+		dptx_output(it6505);
+
+		/*
+		 * waiting for training down flag
+		 * because we don't know
+		 * how long this step will be completed
+		 * so use step 1ms to wait
+		 */
+		for (i = 0; i < 200; i++) {
+			usleep_range(1000, 2000);
+			reg0e = dptx_read(it6505, 0x0E);
+			if (reg0e & BIT(4))
+				break;
+		}
+
+		dptx_sys_chg(it6505, SYS_WAIT);
+		break;
+
+	case SYS_WAIT:
+		dptx_process_sys_wait(it6505);
+		break;
+
+	case SYS_HDCP:
+		msleep(2400);
+		it6505_enable_hdcp(it6505);
+		break;
+
+	case SYS_NOROP:
+		break;
+
+	case SYS_TRAINFAIL:
+		break;
+
+	default:
+		DRM_DEV_ERROR(dev, "sys_state change to unknown_state: %d",
+			      it6505->state);
+		break;
+	}
+}
+
+static int sha1_digest(struct it6505 *it6505, u8 *sha1_input, unsigned int size,
+		       u8 *output_av)
+{
+	struct shash_desc *desc;
+	struct crypto_shash *tfm;
+	int err;
+	struct device *dev = &it6505->client->dev;
+
+	tfm = crypto_alloc_shash("sha1", 0, 0);
+	if (IS_ERR(tfm)) {
+		DRM_DEV_ERROR(dev, "crypto_alloc_shash sha1 failed");
+		return PTR_ERR(tfm);
+	}
+	desc = kzalloc(sizeof(*desc) + crypto_shash_descsize(tfm), GFP_KERNEL);
+	if (!desc) {
+		crypto_free_shash(tfm);
+		return -ENOMEM;
+	}
+
+	desc->tfm = tfm;
+	err = crypto_shash_digest(desc, sha1_input, size, output_av);
+	if (err)
+		DRM_DEV_ERROR(dev, "crypto_shash_digest sha1 failed");
+
+	crypto_free_shash(tfm);
+	kfree(desc);
+	return err;
+}
+
+static int it6505_makeup_sha1_input(struct it6505 *it6505)
+{
+	int msgcnt = 0, i;
+	u8 am0[8], binfo[2];
+	struct device *dev = &it6505->client->dev;
+
+	dptx_set_bits(it6505, 0x3A, 0x20, 0x20);
+	for (i = 0; i < 8; i++)
+		am0[i] = dptx_read(it6505, 0x4C + i);
+
+	DRM_DEV_DEBUG_DRIVER(dev, "read am0 = %8ph", am0);
+	dptx_set_bits(it6505, 0x3A, 0x20, 0x00);
+
+	it6505_get_dpcd(it6505, DP_AUX_HDCP_BINFO, binfo, ARRAY_SIZE(binfo));
+	DRM_DEV_DEBUG_DRIVER(dev, "Attached devices: %02x", binfo[0] & 0x7F);
+
+	DRM_DEV_DEBUG_DRIVER(dev, "%s 127 devices are attached",
+			     (binfo[0] & BIT(7)) ? "over" : "under");
+	DRM_DEV_DEBUG_DRIVER(dev, "depth, attached levels: %02x",
+			     binfo[1] & 0x07);
+	DRM_DEV_DEBUG_DRIVER(dev, "%s than seven levels cascaded",
+			     (binfo[1] & BIT(3)) ? "more" : "less");
+
+	for (i = 0; i < (binfo[0] & 0x7F); i++) {
+		it6505_get_dpcd(it6505, DP_AUX_HDCP_KSV_FIFO + (i % 3) * 5,
+				it6505->sha1_input + msgcnt, 5);
+		msgcnt += 5;
+
+		DRM_DEV_DEBUG_DRIVER(dev, "KSV List %d device : %5ph", i,
+				     it6505->sha1_input + i * 5);
+	}
+	it6505->sha1_input[msgcnt++] = binfo[0];
+	it6505->sha1_input[msgcnt++] = binfo[1];
+	for (i = 0; i < 8; i++)
+		it6505->sha1_input[msgcnt++] = am0[i];
+
+	DRM_DEV_DEBUG_DRIVER(dev, "SHA Message Count = %d", msgcnt);
+	return msgcnt;
+}
+
+static void it6505_check_sha1_result(struct it6505 *it6505)
+{
+	unsigned int i, sha_pass;
+	struct device *dev = &it6505->client->dev;
+
+	DRM_DEV_DEBUG_DRIVER(dev, "SHA calculate complete");
+	for (i = 0; i < 5; i++)
+		DRM_DEV_DEBUG_DRIVER(dev, "av %d: %4ph", i, it6505->av[i]);
+
+	sha_pass = 1;
+	for (i = 0; i < 5; i++) {
+		it6505_get_dpcd(it6505, DP_AUX_HDCP_V_PRIME(i), it6505->bv[i],
+				4);
+		DRM_DEV_DEBUG_DRIVER(dev, "bv %d: %4ph", i, it6505->bv[i]);
+		if ((it6505->bv[i][3] != it6505->av[i][0]) ||
+		    (it6505->bv[i][2] != it6505->av[i][1]) ||
+		    (it6505->bv[i][1] != it6505->av[i][2]) ||
+		    (it6505->bv[i][0] != it6505->av[i][3])) {
+			sha_pass = 0;
+		}
+	}
+	if (sha_pass) {
+		DRM_DEV_DEBUG_DRIVER(dev, "SHA check result pass!");
+		dptx_set_bits(it6505, 0x39, BIT(4), BIT(4));
+	} else {
+		DRM_DEV_DEBUG_DRIVER(dev, "SHA check result fail");
+		dptx_set_bits(it6505, 0x39, BIT(5), BIT(5));
+	}
+}
+
+static void to_fsm_state(struct it6505 *it6505, enum it6505_sys_state state)
+{
+	while (it6505->state != state && it6505->state != SYS_TRAINFAIL) {
+		dptx_sys_fsm(it6505);
+		if (it6505->state == SYS_UNPLUG)
+			return;
+	}
+}
+
+static void go_on_fsm(struct it6505 *it6505);
+
+static u8 dp_link_status(const u8 link_status[DP_LINK_STATUS_SIZE], int r)
+{
+	return link_status[r - DP_LANE0_1_STATUS];
+}
+
+static void hpd_irq(struct it6505 *it6505)
+{
+	u8 dpcd_sink_count, dpcd_irq_vector;
+	u8 link_status[DP_LINK_STATUS_SIZE];
+	unsigned int bstatus;
+	struct device *dev = &it6505->client->dev;
+
+	dpcd_sink_count = dpcd_read(it6505, DP_SINK_COUNT);
+	dpcd_irq_vector = dpcd_read(it6505, DP_DEVICE_SERVICE_IRQ_VECTOR);
+	drm_dp_dpcd_read_link_status(&it6505->aux, link_status);
+
+	DRM_DEV_DEBUG_DRIVER(dev, "dpcd_sink_count = 0x%x", dpcd_sink_count);
+	DRM_DEV_DEBUG_DRIVER(dev, "dpcd_irq_vector = 0x%x", dpcd_irq_vector);
+	DRM_DEV_DEBUG_DRIVER(dev, "link_status = %*ph",
+			     (int)ARRAY_SIZE(link_status), link_status);
+	DRM_DEV_DEBUG_DRIVER(dev, "dpcd_sink_count:%d",
+			     it6505->dpcd_sink_count);
+	if (!it6505->dpcd_sink_count) {
+		it6505_init_and_mask_on(it6505);
+		return;
+	}
+
+	if (dpcd_irq_vector & DP_CP_IRQ) {
+		bstatus = dpcd_read(it6505, DP_AUX_HDCP_BSTATUS);
+		dptx_set_bits(it6505, 0x39, 0x02, 0x02);
+		DRM_DEV_DEBUG_DRIVER(dev, "Receive CP_IRQ, bstatus = 0x%02x",
+				     bstatus);
+		dptx_debug_print(it6505, 0x55, "");
+
+		if (bstatus & DP_BSTATUS_READY)
+			DRM_DEV_DEBUG_DRIVER(dev, "HDCP KSV list ready");
+
+		if (bstatus & DP_BSTATUS_LINK_FAILURE) {
+			DRM_DEV_DEBUG_DRIVER(
+				dev, "Link Integrity Fail, restart HDCP");
+			return;
+		}
+		if (bstatus & DP_BSTATUS_R0_PRIME_READY)
+			DRM_DEV_DEBUG_DRIVER(dev, "HDCP R0' ready");
+	}
+
+	if (dp_link_status(link_status, DP_LANE_ALIGN_STATUS_UPDATED) &
+	    DP_LINK_STATUS_UPDATED) {
+		if (!drm_dp_channel_eq_ok(link_status,
+					  it6505->lane_count)) {
+			DRM_DEV_DEBUG_DRIVER(dev, "Link Re-Training");
+			dptx_set_bits(it6505, 0xD3, 0x30, 0x30);
+			dptx_set_bits(it6505, 0xE8, 0x33, 0x00);
+			msleep(500);
+			dptx_sys_chg(it6505, SYS_HPD);
+			go_on_fsm(it6505);
+		}
+	}
+}
+
+static void go_on_fsm(struct it6505 *it6505)
+{
+	struct device *dev = &it6505->client->dev;
+
+	if (it6505->dpcd_sink_count && it6505->cp_capable) {
+		DRM_DEV_DEBUG_DRIVER(dev, "Support HDCP, cp_capable: %d",
+				     it6505->cp_capable);
+		if (it6505->enable_hdcp) {
+			DRM_DEV_DEBUG_DRIVER(dev, "Enable HDCP");
+			it6505->hdcp_flag = true;
+			to_fsm_state(it6505, SYS_HDCP);
+			dptx_sys_fsm(it6505);
+
+		} else {
+			DRM_DEV_DEBUG_DRIVER(dev, "Disable HDCP");
+			it6505->hdcp_flag = false;
+			to_fsm_state(it6505, SYS_NOROP);
+		}
+	} else {
+		DRM_DEV_DEBUG_DRIVER(dev, "Not support HDCP");
+		it6505->hdcp_flag = false;
+		to_fsm_state(it6505, SYS_NOROP);
+	}
+}
+
+static void it6505_check_reg06(struct it6505 *it6505, unsigned int reg06)
+{
+	unsigned int rddata;
+	struct device *dev = &it6505->client->dev;
+
+	if (reg06 & BIT(0)) {
+		/* hpd pin state change */
+		DRM_DEV_DEBUG_DRIVER(dev, "HPD Change Interrupt");
+		if (dptx_get_sink_hpd(it6505)) {
+			dptx_sys_chg(it6505, SYS_HPD);
+			dptx_sys_fsm(it6505);
+
+			if (it6505->edid) {
+				rddata = dptx_read(it6505, 0x0D);
+				if (rddata & BIT(2)) {
+					go_on_fsm(it6505);
+				} else {
+					dptx_write(it6505, 0x05, 0x00);
+					dptx_set_bits(it6505, 0x61, 0x02, 0x02);
+					dptx_set_bits(it6505, 0x61, 0x02, 0x00);
+				}
+			} else {
+				go_on_fsm(it6505);
+			}
+		} else {
+			dptx_sys_chg(it6505, SYS_UNPLUG);
+			dptx_sys_fsm(it6505);
+			return;
+		}
+	}
+
+	if (it6505->cp_capable) {
+		if (reg06 & BIT(4)) {
+			DRM_DEV_DEBUG_DRIVER(dev,
+					     "HDCP encryption Done Interrupt");
+			dptx_sys_chg(it6505, SYS_NOROP);
+		}
+
+		if (reg06 & BIT(3)) {
+			DRM_DEV_DEBUG_DRIVER(
+				dev, "HDCP encryption Fail Interrupt, retry");
+			it6505_init_and_mask_on(it6505);
+		}
+	}
+
+	if (reg06 & BIT(1)) {
+		DRM_DEV_DEBUG_DRIVER(dev, "HPD IRQ Interrupt");
+		hpd_irq(it6505);
+	}
+
+	if (reg06 & BIT(2)) {
+		rddata = dptx_read(it6505, 0x0D);
+
+		if (rddata & BIT(2)) {
+			DRM_DEV_DEBUG_DRIVER(dev, "Video Stable On Interrupt");
+			dptx_sys_chg(it6505, SYS_AUTOTRAIN);
+			go_on_fsm(it6505);
+		} else {
+			DRM_DEV_DEBUG_DRIVER(dev, "Video Stable Off Interrupt");
+		}
+	}
+}
+
+static void it6505_check_reg07(struct it6505 *it6505, unsigned int reg07)
+{
+	struct device *dev = &it6505->client->dev;
+	unsigned int len, i;
+	unsigned int bstatus;
+
+	if (it6505->state == SYS_UNPLUG)
+		return;
+	if (reg07 & BIT(0)) {
+		DRM_DEV_DEBUG_DRIVER(dev, "AUX PC Request Fail Interrupt");
+		dptx_debug_print(it6505, 0x9F, "AUX status:");
+	}
+
+	if (reg07 & BIT(1)) {
+		DRM_DEV_DEBUG_DRIVER(dev, "HDCP event Interrupt");
+		for (i = 0; i < 500; i++) {
+			bstatus = dpcd_read(it6505, DP_AUX_HDCP_BSTATUS);
+			usleep_range(2000, 3000);
+			if (bstatus & DP_BSTATUS_READY)
+				break;
+		}
+
+		/*
+		 * Read Bstatus to determine what happened
+		 */
+		DRM_DEV_DEBUG_DRIVER(dev, "Bstatus: 0x%02x", bstatus);
+		dptx_debug_print(it6505, 0x3B, "ar0_low");
+		dptx_debug_print(it6505, 0x3C, "ar0_high");
+		dptx_debug_print(it6505, 0x45, "br0_low");
+		dptx_debug_print(it6505, 0x46, "br0_high");
+
+		if (bstatus & DP_BSTATUS_READY) {
+			DRM_DEV_DEBUG_DRIVER(dev, "HDCP KSV list ready");
+			len = it6505_makeup_sha1_input(it6505);
+			sha1_digest(it6505, it6505->sha1_input, len,
+				    (u8 *)it6505->av);
+			it6505_check_sha1_result(it6505);
+		}
+	}
+	if (it6505->enable_audio && (reg07 & BIT(2))) {
+		DRM_DEV_DEBUG_DRIVER(dev, "Audio FIFO OverFlow Interrupt");
+		dptx_debug_print(it6505, 0xBE, "");
+		dptx_set_bits(it6505, 0xD3, 0x20, 0x20);
+		dptx_set_bits(it6505, 0xE8, 0x22, 0x00);
+		dptx_set_bits(it6505, 0xB8, 0x80, 0x80);
+		dptx_set_bits(it6505, 0xB8, 0x80, 0x00);
+		it6505_set_audio(it6505);
+	}
+	if (it6505->enable_audio && (reg07 & BIT(5)))
+		DRM_DEV_DEBUG_DRIVER(dev, "Audio infoframe packet done");
+}
+
+static void it6505_check_reg08(struct it6505 *it6505, unsigned int reg08)
+{
+	struct device *dev = &it6505->client->dev;
+
+	if (it6505->state == SYS_UNPLUG)
+		return;
+	if (it6505->enable_audio && (reg08 & BIT(1)))
+		DRM_DEV_DEBUG_DRIVER(dev, "Audio TimeStamp Packet Done!");
+
+	if (it6505->enable_audio && (reg08 & BIT(3))) {
+		DRM_DEV_DEBUG_DRIVER(dev, "Audio M Error Interrupt ...");
+		show_audio_m_count(it6505);
+	}
+	if (reg08 & BIT(4)) {
+		DRM_DEV_DEBUG_DRIVER(dev, "Link Training Fail Interrupt");
+		/* restart training */
+		dptx_sys_chg(it6505, SYS_AUTOTRAIN);
+		go_on_fsm(it6505);
+	}
+
+	if (reg08 & BIT(7)) {
+		DRM_DEV_DEBUG_DRIVER(dev, "IO Latch FIFO OverFlow Interrupt");
+		dptx_set_bits(it6505, 0x61, 0x02, 0x02);
+		dptx_set_bits(it6505, 0x61, 0x02, 0x00);
+	}
+}
+
+static void it6505_dptx_irq(struct it6505 *it6505)
+{
+	int reg06, reg07, reg08;
+	struct device *dev = &it6505->client->dev;
+
+	reg06 = dptx_read(it6505, 0x06);
+	reg07 = dptx_read(it6505, 0x07);
+	reg08 = dptx_read(it6505, 0x08);
+
+	dptx_write(it6505, 0x06, reg06);
+	dptx_write(it6505, 0x07, reg07);
+	dptx_write(it6505, 0x08, reg08);
+
+	DRM_DEV_DEBUG_DRIVER(dev, "reg06 = 0x%02x", reg06);
+	DRM_DEV_DEBUG_DRIVER(dev, "reg07 = 0x%02x", reg07);
+	DRM_DEV_DEBUG_DRIVER(dev, "reg08 = 0x%02x", reg08);
+	dptx_debug_print(it6505, 0x0D, "");
+
+	if (reg06 != 0)
+		it6505_check_reg06(it6505, reg06);
+
+	if (reg07 != 0)
+		it6505_check_reg07(it6505, reg07);
+
+	if (reg08 != 0)
+		it6505_check_reg08(it6505, reg08);
+}
+
+static void it6505_bridge_enable(struct drm_bridge *bridge)
+{
+	struct it6505 *it6505 = bridge_to_it6505(bridge);
+
+	if (!it6505->enable_drv_hold) {
+		it6505_int_mask_on(it6505);
+		dptx_sys_chg(it6505, SYS_HPD);
+	}
+}
+
+static void it6505_bridge_disable(struct drm_bridge *bridge)
+{
+	struct it6505 *it6505 = bridge_to_it6505(bridge);
+
+	it6505_poweroff(it6505);
+	dptx_sys_chg(it6505, SYS_UNPLUG);
+}
+
+static const struct drm_bridge_funcs it6505_bridge_funcs = {
+	.attach = it6505_bridge_attach,
+	.detach = it6505_bridge_detach,
+	.mode_valid = it6505_bridge_mode_valid,
+	.mode_set = it6505_bridge_mode_set,
+	.enable = it6505_bridge_enable,
+	.disable = it6505_bridge_disable,
+};
+
+static void it6505_clear_int(struct it6505 *it6505)
+{
+	dptx_write(it6505, 0x06, 0xFF);
+	dptx_write(it6505, 0x07, 0xFF);
+	dptx_write(it6505, 0x08, 0xFF);
+}
+
+static irqreturn_t it6505_intp_threaded_handler(int unused, void *data)
+{
+	struct it6505 *it6505 = data;
+	struct device *dev = &it6505->client->dev;
+
+	msleep(150);
+	mutex_lock(&it6505->lock);
+
+	if (it6505->enable_drv_hold == 0 && it6505->powered) {
+		DRM_DEV_DEBUG_DRIVER(dev, "into it6505_dptx_irq");
+		it6505_dptx_irq(it6505);
+	}
+
+	mutex_unlock(&it6505->lock);
+	return IRQ_HANDLED;
+}
+
+static int it6505_init_pdata(struct it6505 *it6505)
+{
+	struct it6505_platform_data *pdata = &it6505->pdata;
+	struct device *dev = &it6505->client->dev;
+
+	/* 1.0V digital core power regulator  */
+	pdata->pwr18 = devm_regulator_get(dev, "pwr18");
+	if (IS_ERR(pdata->pwr18)) {
+		DRM_DEV_ERROR(dev, "pwr18 regulator not found");
+		return PTR_ERR(pdata->pwr18);
+	}
+
+	pdata->ovdd = devm_regulator_get(dev, "ovdd");
+	if (IS_ERR(pdata->ovdd)) {
+		DRM_DEV_ERROR(dev, "ovdd regulator not found");
+		return PTR_ERR(pdata->ovdd);
+	}
+
+	/* GPIO for HPD */
+	pdata->gpiod_hpd = devm_gpiod_get(dev, "hpd", GPIOD_IN);
+	if (IS_ERR(pdata->gpiod_hpd))
+		return PTR_ERR(pdata->gpiod_hpd);
+
+	/* GPIO for chip reset */
+	pdata->gpiod_reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
+
+	return PTR_ERR_OR_ZERO(pdata->gpiod_reset);
+}
+
+static int it6505_bridge_resume(struct device *dev)
+{
+	struct it6505 *it6505 = dev_get_drvdata(dev);
+
+	return it6505_poweron(it6505);
+}
+
+static int it6505_bridge_suspend(struct device *dev)
+{
+	struct it6505 *it6505 = dev_get_drvdata(dev);
+
+	return it6505_poweroff(it6505);
+}
+
+static SIMPLE_DEV_PM_OPS(it6505_bridge_pm_ops, it6505_bridge_suspend,
+			 it6505_bridge_resume);
+
+static ssize_t enable_drv_hold_show(struct device *dev,
+				    struct device_attribute *attr, char *buf)
+{
+	struct it6505 *it6505 = dev_get_drvdata(dev);
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", it6505->enable_drv_hold);
+}
+
+static ssize_t enable_drv_hold_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	struct it6505 *it6505 = dev_get_drvdata(dev);
+	unsigned int drv_hold;
+
+	if (kstrtoint(buf, 10, &drv_hold) < 0)
+		return -EINVAL;
+
+	it6505->enable_drv_hold = drv_hold ? true : false;
+
+	if (it6505->enable_drv_hold) {
+		it6505_int_mask_off(it6505);
+	} else {
+		it6505_clear_int(it6505);
+		it6505_int_mask_on(it6505);
+	}
+	return count;
+}
+
+static ssize_t print_timing_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	struct it6505 *it6505 = dev_get_drvdata(dev);
+	struct drm_display_mode *vid = &it6505->video_info;
+	char *str = buf, *end = buf + PAGE_SIZE;
+
+	str += scnprintf(str, end - str, "---video timing---\n");
+	str += scnprintf(str, end - str, "PCLK:%d.%03dMHz\n", vid->clock / 1000,
+			 vid->clock % 1000);
+	str += scnprintf(str, end - str, "HTotal:%d\n", vid->htotal);
+	str += scnprintf(str, end - str, "HActive:%d\n", vid->hdisplay);
+	str += scnprintf(str, end - str, "HFrontPorch:%d\n",
+			 vid->hsync_start - vid->hdisplay);
+	str += scnprintf(str, end - str, "HSyncWidth:%d\n",
+			 vid->hsync_end - vid->hsync_start);
+	str += scnprintf(str, end - str, "HBackPorch:%d\n",
+			 vid->htotal - vid->hsync_end);
+	str += scnprintf(str, end - str, "VTotal:%d\n", vid->vtotal);
+	str += scnprintf(str, end - str, "VActive:%d\n", vid->vdisplay);
+	str += scnprintf(str, end - str, "VFrontPorch:%d\n",
+			 vid->vsync_start - vid->vdisplay);
+	str += scnprintf(str, end - str, "VSyncWidth:%d\n",
+			 vid->vsync_end - vid->vsync_start);
+	str += scnprintf(str, end - str, "VBackPorch:%d\n",
+			 vid->vtotal - vid->vsync_end);
+
+	return str - buf;
+}
+
+static ssize_t sha_debug_show(struct device *dev, struct device_attribute *attr,
+			      char *buf)
+{
+	int i = 0;
+	char *str = buf, *end = buf + PAGE_SIZE;
+	struct it6505 *it6505 = dev_get_drvdata(dev);
+
+	str += scnprintf(str, end - str, "sha input:\n");
+	for (i = 0; i < ARRAY_SIZE(it6505->sha1_input); i += 16)
+		str += scnprintf(str, end - str, "%16ph\n",
+				 it6505->sha1_input + i);
+
+	str += scnprintf(str, end - str, "av:\n");
+	for (i = 0; i < ARRAY_SIZE(it6505->av); i++)
+		str += scnprintf(str, end - str, "%4ph\n", it6505->av[i]);
+
+	str += scnprintf(str, end - str, "bv:\n");
+	for (i = 0; i < ARRAY_SIZE(it6505->bv); i++)
+		str += scnprintf(str, end - str, "%4ph\n", it6505->bv[i]);
+
+	return end - str;
+}
+
+static ssize_t enable_hdcp_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct it6505 *it6505 = dev_get_drvdata(dev);
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", it6505->enable_hdcp);
+}
+
+static ssize_t enable_hdcp_store(struct device *dev,
+				 struct device_attribute *attr,
+				 const char *buf, size_t count)
+{
+	struct it6505 *it6505 = dev_get_drvdata(dev);
+	unsigned int reg3f, hdcp;
+
+	if (kstrtoint(buf, 10, &hdcp) < 0)
+		return -EINVAL;
+
+	if (!it6505->powered || it6505->state == SYS_UNPLUG) {
+		DRM_DEV_DEBUG_DRIVER(dev,
+				     "power down or unplug, can not fire HDCP");
+		return -EINVAL;
+	}
+	it6505->enable_hdcp = hdcp ? true : false;
+
+	if (it6505->enable_hdcp) {
+		if (it6505->cp_capable) {
+			dptx_sys_chg(it6505, SYS_HDCP);
+			dptx_sys_fsm(it6505);
+		} else {
+			DRM_DEV_ERROR(dev, "sink not support HDCP");
+		}
+	} else {
+		dptx_set_bits(it6505, 0x05, 0x10, 0x10);
+		dptx_set_bits(it6505, 0x05, 0x10, 0x00);
+		reg3f = dptx_read(it6505, 0x3F);
+		hdcp = (reg3f & BIT(7)) >> 7;
+		DRM_DEV_DEBUG_DRIVER(dev, "%s to disable hdcp",
+				     hdcp ? "failed" : "succeeded");
+	}
+	return count;
+}
+
+static ssize_t force_pwronoff_store(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t count)
+{
+	struct it6505 *it6505 = dev_get_drvdata(dev);
+	int pwr;
+
+	if (kstrtoint(buf, 10, &pwr) < 0)
+		return -EINVAL;
+	if (pwr)
+		it6505_poweron(it6505);
+	else
+		it6505_poweroff(it6505);
+	return count;
+}
+
+static ssize_t pwr_state_show(struct device *dev,
+			      struct device_attribute *attr, char *buf)
+{
+	struct it6505 *it6505 = dev_get_drvdata(dev);
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", it6505->powered);
+}
+
+static DEVICE_ATTR_RO(print_timing);
+static DEVICE_ATTR_RO(pwr_state);
+static DEVICE_ATTR_RO(sha_debug);
+static DEVICE_ATTR_WO(force_pwronoff);
+static DEVICE_ATTR_RW(enable_drv_hold);
+static DEVICE_ATTR_RW(enable_hdcp);
+
+static const struct attribute *it6505_attrs[] = {
+	&dev_attr_enable_drv_hold.attr,
+	&dev_attr_print_timing.attr,
+	&dev_attr_sha_debug.attr,
+	&dev_attr_enable_hdcp.attr,
+	&dev_attr_force_pwronoff.attr,
+	&dev_attr_pwr_state.attr,
+	NULL,
+};
+
+static int it6505_i2c_probe(struct i2c_client *client,
+			    const struct i2c_device_id *id)
+{
+	struct it6505 *it6505;
+	struct it6505_platform_data *pdata;
+	struct device *dev = &client->dev;
+	struct extcon_dev *extcon;
+	int err, intp_irq;
+
+	it6505 = devm_kzalloc(&client->dev, sizeof(*it6505), GFP_KERNEL);
+	if (!it6505)
+		return -ENOMEM;
+
+	mutex_init(&it6505->lock);
+	mutex_init(&it6505->mode_lock);
+
+	pdata = &it6505->pdata;
+
+	it6505->bridge.of_node = client->dev.of_node;
+	it6505->client = client;
+	i2c_set_clientdata(client, it6505);
+
+	/* get extcon device from DTS */
+	extcon = extcon_get_edev_by_phandle(dev, 0);
+	if (PTR_ERR(extcon) == -EPROBE_DEFER)
+		return -EPROBE_DEFER;
+	if (IS_ERR(extcon)) {
+		DRM_DEV_ERROR(dev, "can not get extcon device!");
+		return -EINVAL;
+	}
+
+	it6505->extcon = extcon;
+
+	it6505->lane_swap_disabled =
+		device_property_read_bool(dev, "no-laneswap");
+
+	err = it6505_init_pdata(it6505);
+	if (err) {
+		DRM_DEV_ERROR(dev, "Failed to initialize pdata: %d", err);
+		return err;
+	}
+
+	it6505->regmap =
+		devm_regmap_init_i2c(client, &it6505_bridge_regmap_config);
+	if (IS_ERR(it6505->regmap)) {
+		DRM_DEV_ERROR(dev, "regmap i2c init failed");
+		return PTR_ERR(it6505->regmap);
+	}
+
+	intp_irq = client->irq;
+
+	if (!intp_irq) {
+		DRM_DEV_ERROR(dev, "Failed to get CABLE_DET and INTP IRQ");
+		return -ENODEV;
+	}
+
+	err = devm_request_threaded_irq(&client->dev, intp_irq, NULL,
+					it6505_intp_threaded_handler,
+					IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+					"it6505-intp", it6505);
+	if (err) {
+		DRM_DEV_ERROR(dev, "Failed to request INTP threaded IRQ: %d",
+			      err);
+		return err;
+	}
+
+	/* Register aux channel */
+	it6505->aux.name = "DP-AUX";
+	it6505->aux.dev = dev;
+	it6505->aux.transfer = it6505_aux_transfer;
+	err = drm_dp_aux_register(&it6505->aux);
+	if (err < 0) {
+		DRM_DEV_ERROR(dev, "Failed to register aux: %d", err);
+		return err;
+	}
+
+	it6505->enable_drv_hold = DEFAULT_DRV_HOLD;
+	it6505->enable_hdcp = true;
+	it6505->enable_audio = false;
+	it6505->powered = false;
+
+	if (DEFAULT_PWR_ON)
+		it6505_poweron(it6505);
+	err = sysfs_create_files(&client->dev.kobj, it6505_attrs);
+	if (err) {
+		drm_dp_aux_unregister(&it6505->aux);
+		return err;
+	}
+
+	it6505->bridge.funcs = &it6505_bridge_funcs;
+	drm_bridge_add(&it6505->bridge);
+	return 0;
+}
+
+static int it6505_remove(struct i2c_client *client)
+{
+	struct it6505 *it6505 = i2c_get_clientdata(client);
+
+	drm_connector_unregister(&it6505->connector);
+	drm_connector_cleanup(&it6505->connector);
+	drm_bridge_remove(&it6505->bridge);
+	sysfs_remove_files(&client->dev.kobj, it6505_attrs);
+	drm_dp_aux_unregister(&it6505->aux);
+	it6505_poweroff(it6505);
+	return 0;
+}
+
+static const struct i2c_device_id it6505_id[] = {
+	{ "it6505", 0 },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(i2c, it6505_id);
+
+static const struct of_device_id it6505_of_match[] = {
+	{ .compatible = "ite,it6505" },
+	{ }
+};
+
+static struct i2c_driver it6505_i2c_driver = {
+	.driver = {
+		.name = "it6505_dptx",
+		.of_match_table = it6505_of_match,
+		.pm = &it6505_bridge_pm_ops,
+	},
+	.probe = it6505_i2c_probe,
+	.remove = it6505_remove,
+	.id_table = it6505_id,
+};
+
+module_i2c_driver(it6505_i2c_driver);
+
+MODULE_AUTHOR("Jitao Shi <jitao.shi@mediatek.com>");
+MODULE_DESCRIPTION("IT6505 DisplayPort Transmitter driver");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH v6 2/4] Revert "drm/tegra: Move drm_dp_link helpers to Tegra DRM"
  2020-01-20  2:44 ` [PATCH v6 2/4] Revert "drm/tegra: Move drm_dp_link helpers to Tegra DRM" allen
@ 2020-01-20  7:09   ` Thierry Reding
  0 siblings, 0 replies; 6+ messages in thread
From: Thierry Reding @ 2020-01-20  7:09 UTC (permalink / raw)
  To: allen
  Cc: Jau-Chih Tseng, David Airlie, open list, Jonathan Hunter,
	open list:DRM DRIVERS, Pi-Hsun Shih,
	open list:DRM DRIVERS FOR NVIDIA TEGRA, Sean Paul


[-- Attachment #1.1: Type: text/plain, Size: 2055 bytes --]

On Mon, Jan 20, 2020 at 10:44:32AM +0800, allen wrote:
> IT6505 driver ite-it6505.c file using drm_dp_link helpers, so revert.
> This reverts commit 9a42c7c647a9ad0f7ebb147a52eda3dcb7c84292.
> 
> Signed-off-by: Allen Chen <allen.chen@ite.com.tw>
> ---
>  drivers/gpu/drm/drm_dp_helper.c | 128 ++++++
>  drivers/gpu/drm/tegra/Makefile  |   1 -
>  drivers/gpu/drm/tegra/dp.c      | 876 ----------------------------------------
>  drivers/gpu/drm/tegra/dp.h      | 177 --------
>  drivers/gpu/drm/tegra/dpaux.c   |   1 -
>  drivers/gpu/drm/tegra/sor.c     |   1 -
>  include/drm/drm_dp_helper.h     |  16 +
>  7 files changed, 144 insertions(+), 1056 deletions(-)
>  delete mode 100644 drivers/gpu/drm/tegra/dp.c
>  delete mode 100644 drivers/gpu/drm/tegra/dp.h

Sorry, but no. Please just duplicate whatever information you need from
these helpers into your driver. The decision was made recently to remove
these helpers because they were a premature generalization and unlikely
to be useful very widely. See this commit:

commit 9a42c7c647a9ad0f7ebb147a52eda3dcb7c84292
Author: Thierry Reding <treding@nvidia.com>
Date:   Mon Oct 21 16:34:37 2019 +0200

    drm/tegra: Move drm_dp_link helpers to Tegra DRM

    During the discussion of patches that enhance the drm_dp_link helpers it
    was concluded that these helpers aren't very useful to begin with. After
    all other drivers have been converted not to use these helpers anymore,
    move these helpers into the last remaining user: Tegra DRM.

    If at some point these helpers are deemed more widely useful, they can
    be moved out into the DRM DP helpers again.

    Signed-off-by: Thierry Reding <treding@nvidia.com>
    Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
    Link: https://patchwork.freedesktop.org/patch/msgid/20191021143437.1477719-14-thierry.reding@gmail.com

Your new driver here doesn't change that, in my opinion, so just follow
what we did for other drivers and duplicate the corresponding code into
the driver.

Thierry

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

[-- Attachment #2: Type: text/plain, Size: 160 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v6 3/4] dt-bindings: Add binding for IT6505.
  2020-01-20  2:44 ` [PATCH v6 3/4] dt-bindings: Add binding for IT6505 allen
@ 2020-01-21 18:00   ` Rob Herring
  0 siblings, 0 replies; 6+ messages in thread
From: Rob Herring @ 2020-01-21 18:00 UTC (permalink / raw)
  To: allen
  Cc: Mark Rutland,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	Pi-Hsun Shih, Jau-Chih Tseng, David Airlie, Allen Chen,
	open list, open  list:DRM DRIVERS, Sam Ravnborg

On Mon, 20 Jan 2020 10:44:33 +0800, allen wrote:
> Add a DT binding documentation for IT6505.
> 
> Acked-by: Sam Ravnborg <sam@ravnborg.org>
> Signed-off-by: Allen Chen <allen.chen@ite.com.tw>
> Signed-off-by: Pi-Hsun Shih <pihsun@chromium.org>
> ---
>  .../bindings/display/bridge/ite,it6505.yaml        | 89 ++++++++++++++++++++++
>  1 file changed, 89 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/display/bridge/ite,it6505.yaml
> 

My bot found errors running 'make dt_binding_check' on your patch:

Error: Documentation/devicetree/bindings/display/bridge/ite,it6505.example.dts:19.31-32 syntax error
FATAL ERROR: Unable to parse input tree
scripts/Makefile.lib:300: recipe for target 'Documentation/devicetree/bindings/display/bridge/ite,it6505.example.dt.yaml' failed
make[1]: *** [Documentation/devicetree/bindings/display/bridge/ite,it6505.example.dt.yaml] Error 1
Makefile:1263: recipe for target 'dt_binding_check' failed
make: *** [dt_binding_check] Error 2

See https://patchwork.ozlabs.org/patch/1225618
Please check and re-submit.
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2020-01-21 18:00 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-01-20  2:44 [PATCH v6 0/4] IT6505 cover letter allen
2020-01-20  2:44 ` [PATCH v6 2/4] Revert "drm/tegra: Move drm_dp_link helpers to Tegra DRM" allen
2020-01-20  7:09   ` Thierry Reding
2020-01-20  2:44 ` [PATCH v6 3/4] dt-bindings: Add binding for IT6505 allen
2020-01-21 18:00   ` Rob Herring
2020-01-20  2:44 ` [PATCH v6 4/4] drm/bridge: add it6505 driver allen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).