dri-devel.lists.freedesktop.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/2] drm/bridge: add initial support for TI FPDLINK bridge drivers
@ 2019-03-13 18:34 Vince Kim
  2019-03-13 18:34 ` [PATCH 2/2] dt-bindings: display: bridge: TI FPD-Link III Serializer/Deserializer Vince Kim
  0 siblings, 1 reply; 3+ messages in thread
From: Vince Kim @ 2019-03-13 18:34 UTC (permalink / raw)
  Cc: Vince Kim, David Airlie, Daniel Vetter, Rob Herring,
	Mark Rutland, Andrzej Hajda, Laurent Pinchart, dri-devel,
	devicetree, linux-kernel

ti-fpdlink/ti-fpdlink.c:

  - A wrapper bridge driver for TI FPD LINK serializer and deserializer.

  - This driver takes care of initilizing FPD link connection between
  serializer and deserializer.

  - of_drm_find_bridge() looks up matching serializer/deserializer bridges
  from given serializer/deserializer device node.

ti-fpdlink/ti-ub927.c:

  - drm bridge driver for TI DS90UB927 FPD-Link III Serializer

ti-fpdlink/ti-ub949.c:

  - drm bridge driver for TI DS90UB949 FPD-Link III Serializer

ti-fpdlink/ti-ub948.c:

  - drm bridge driver for TI DS90UB948 FPD-Link III Deserializer

Signed-off-by: Vince Kim <vince.k.kim@gmail.com>
---
 drivers/gpu/drm/bridge/Kconfig                |  52 +--
 drivers/gpu/drm/bridge/Makefile               |   6 +-
 drivers/gpu/drm/bridge/ti-fpdlink/Kconfig     |  32 ++
 drivers/gpu/drm/bridge/ti-fpdlink/Makefile    |   5 +
 .../gpu/drm/bridge/ti-fpdlink/ti-ds90ub927.c  | 421 +++++++++++++++++
 .../gpu/drm/bridge/ti-fpdlink/ti-ds90ub948.c  | 333 ++++++++++++++
 .../gpu/drm/bridge/ti-fpdlink/ti-ds90ub949.c  | 435 ++++++++++++++++++
 .../gpu/drm/bridge/ti-fpdlink/ti-fpdlink.c    | 396 ++++++++++++++++
 .../gpu/drm/bridge/ti-fpdlink/ti-fpdlink.h    |  70 +++
 9 files changed, 1696 insertions(+), 54 deletions(-)
 create mode 100644 drivers/gpu/drm/bridge/ti-fpdlink/Kconfig
 create mode 100644 drivers/gpu/drm/bridge/ti-fpdlink/Makefile
 create mode 100644 drivers/gpu/drm/bridge/ti-fpdlink/ti-ds90ub927.c
 create mode 100644 drivers/gpu/drm/bridge/ti-fpdlink/ti-ds90ub948.c
 create mode 100644 drivers/gpu/drm/bridge/ti-fpdlink/ti-ds90ub949.c
 create mode 100644 drivers/gpu/drm/bridge/ti-fpdlink/ti-fpdlink.c
 create mode 100644 drivers/gpu/drm/bridge/ti-fpdlink/ti-fpdlink.h

diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index 8840f396a7b6..4441f860f04b 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -25,24 +25,12 @@ config DRM_ANALOGIX_ANX78XX
 	  the HDMI output of an application processor to MyDP
 	  or DisplayPort.
 
-config DRM_CDNS_DSI
-	tristate "Cadence DPI/DSI bridge"
-	select DRM_KMS_HELPER
-	select DRM_MIPI_DSI
-	select DRM_PANEL_BRIDGE
-	select GENERIC_PHY_MIPI_DPHY
-	depends on OF
-	help
-	  Support Cadence DPI to DSI bridge. This is an internal
-	  bridge and is meant to be directly embedded in a SoC.
-
 config DRM_DUMB_VGA_DAC
 	tristate "Dumb VGA DAC Bridge support"
 	depends on OF
 	select DRM_KMS_HELPER
 	help
-	  Support for non-programmable RGB to VGA DAC bridges, such as ADI
-	  ADV7123, TI THS8134 and THS8135 or passive resistor ladder DACs.
+	  Support for RGB to VGA DAC based bridges
 
 config DRM_LVDS_ENCODER
 	tristate "Transparent parallel to LVDS encoder support"
@@ -85,9 +73,6 @@ config DRM_SIL_SII8620
 	tristate "Silicon Image SII8620 HDMI/MHL bridge"
 	depends on OF
 	select DRM_KMS_HELPER
-	imply EXTCON
-	select INPUT
-	select RC_CORE
 	help
 	  Silicon Image SII8620 HDMI/MHL bridge chip driver.
 
@@ -96,32 +81,9 @@ config DRM_SII902X
 	depends on OF
 	select DRM_KMS_HELPER
 	select REGMAP_I2C
-	select I2C_MUX
 	---help---
 	  Silicon Image sii902x bridge chip driver.
 
-config DRM_SII9234
-	tristate "Silicon Image SII9234 HDMI/MHL bridge"
-	depends on OF
-	---help---
-	  Say Y here if you want support for the MHL interface.
-	  It is an I2C driver, that detects connection of MHL bridge
-	  and starts encapsulation of HDMI signal.
-
-config DRM_THINE_THC63LVD1024
-	tristate "Thine THC63LVD1024 LVDS decoder bridge"
-	depends on OF
-	---help---
-	  Thine THC63LVD1024 LVDS/parallel converter driver.
-
-config DRM_TOSHIBA_TC358764
-	tristate "TC358764 DSI/LVDS bridge"
-	depends on DRM && DRM_PANEL
-	depends on OF
-	select DRM_MIPI_DSI
-	help
-	  Toshiba TC358764 DSI/LVDS bridge driver.
-
 config DRM_TOSHIBA_TC358767
 	tristate "Toshiba TC358767 eDP bridge"
 	depends on OF
@@ -138,20 +100,12 @@ config DRM_TI_TFP410
 	---help---
 	  Texas Instruments TFP410 DVI/HDMI Transmitter driver
 
-config DRM_TI_SN65DSI86
-	tristate "TI SN65DSI86 DSI to eDP bridge"
-	depends on OF
-	select DRM_KMS_HELPER
-	select REGMAP_I2C
-	select DRM_PANEL
-	select DRM_MIPI_DSI
-	help
-	  Texas Instruments SN65DSI86 DSI to eDP Bridge driver
-
 source "drivers/gpu/drm/bridge/analogix/Kconfig"
 
 source "drivers/gpu/drm/bridge/adv7511/Kconfig"
 
 source "drivers/gpu/drm/bridge/synopsys/Kconfig"
 
+source "drivers/gpu/drm/bridge/ti-fpdlink/Kconfig"
+
 endmenu
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index 4934fcf5a6f8..9edd4c89f152 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -1,6 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0
 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_LVDS_ENCODER) += lvds-encoder.o
 obj-$(CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW) += megachips-stdpxxxx-ge-b850v3-fw.o
@@ -8,12 +7,9 @@ obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o
 obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o
 obj-$(CONFIG_DRM_SIL_SII8620) += sil-sii8620.o
 obj-$(CONFIG_DRM_SII902X) += sii902x.o
-obj-$(CONFIG_DRM_SII9234) += sii9234.o
-obj-$(CONFIG_DRM_THINE_THC63LVD1024) += thc63lvd1024.o
-obj-$(CONFIG_DRM_TOSHIBA_TC358764) += tc358764.o
 obj-$(CONFIG_DRM_TOSHIBA_TC358767) += tc358767.o
 obj-$(CONFIG_DRM_ANALOGIX_DP) += analogix/
 obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511/
-obj-$(CONFIG_DRM_TI_SN65DSI86) += ti-sn65dsi86.o
 obj-$(CONFIG_DRM_TI_TFP410) += ti-tfp410.o
+obj-$(CONFIG_DRM_TI_FPDLINK) += ti-fpdlink/
 obj-y += synopsys/
diff --git a/drivers/gpu/drm/bridge/ti-fpdlink/Kconfig b/drivers/gpu/drm/bridge/ti-fpdlink/Kconfig
new file mode 100644
index 000000000000..4e81e6454cfe
--- /dev/null
+++ b/drivers/gpu/drm/bridge/ti-fpdlink/Kconfig
@@ -0,0 +1,32 @@
+
+config DRM_TI_FPDLINK
+	tristate "TI FPD-Link III bridge"
+	depends on OF
+	select DRM_KMS_HELPER
+	help
+          Support wrapper bridge driver for Texas Instruments FPD-Link
+          Serializer and Deserializer chip.
+
+config DRM_TI_UB949
+	tristate "TI FPD-Link III UB949 Serializer"
+	depends on DRM_TI_FPDLINK
+	select DRM_KMS_HELPER
+	help
+          Support Texas Instruments FPD-Link III DS90UB949
+          Serializer which converts HDMI input to FPD-link output.
+
+config DRM_TI_UB948
+	tristate "TI FPD-Link III UB948 Deserializer"
+	depends on DRM_TI_FPDLINK
+	select DRM_KMS_HELPER
+	help
+          Supports Texas Instruments FPD-Link III DS90UB948
+          Deserializer which converts FPD-link input to LVDS output.
+
+config DRM_TI_UB927
+	tristate "TI FPD-Link III UB927 Serializer"
+	depends on DRM_TI_FPDLINK
+	select DRM_KMS_HELPER
+	help
+          Support Texas Instruments FPD-Link III DS90UB927
+          Serializer which converts LVDS input to FPD-link output.
diff --git a/drivers/gpu/drm/bridge/ti-fpdlink/Makefile b/drivers/gpu/drm/bridge/ti-fpdlink/Makefile
new file mode 100644
index 000000000000..a176a388f872
--- /dev/null
+++ b/drivers/gpu/drm/bridge/ti-fpdlink/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_DRM_TI_FPDLINK) += ti-fpdlink.o
+obj-$(CONFIG_DRM_TI_UB949) += ti-ds90ub949.o
+obj-$(CONFIG_DRM_TI_UB948) += ti-ds90ub948.o
+obj-$(CONFIG_DRM_TI_UB927) += ti-ds90ub927.o
diff --git a/drivers/gpu/drm/bridge/ti-fpdlink/ti-ds90ub927.c b/drivers/gpu/drm/bridge/ti-fpdlink/ti-ds90ub927.c
new file mode 100644
index 000000000000..88173f8b18ae
--- /dev/null
+++ b/drivers/gpu/drm/bridge/ti-fpdlink/ti-ds90ub927.c
@@ -0,0 +1,421 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * TI FPD-LinkIII DS90UB927 driver
+ *
+ * Copyright (C) 2019 Lucid Motors Inc.
+ *
+ * Contact: Vince Kim <vince.k.kim@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/of_gpio.h>
+#include <drm/drmP.h>
+#include "ti-fpdlink.h"
+
+#define UB927_RESET_REG				0x01
+#define UB927_RESET_DGTL_RST1			(1<<1)
+
+#define UB927_GS_REG				0x0c
+#define UB927_GS_BIST_CRC_ERROR			(1<<3)
+#define UB927_GS_PCLK_DETECT			(1<<2)
+#define UB927_GS_DES_CRC_ERROR			(1<<1)
+#define UB927_GS_LINK_DETECT			(1<<0)
+
+#define UB927_I2C_CTRL_REG			0x17
+#define UB927_I2C_CTRL_PASS_ALL			(1<<7)
+
+#define MAX_I2C_RETRY 10
+
+
+static inline struct fpdlink_dev *
+drm_bridge_to_ub927(struct drm_bridge *bridge)
+{
+	return container_of(bridge, struct fpdlink_dev, bridge);
+}
+
+static inline int
+regmap_write_fpdlink(struct regmap *map, unsigned int reg, unsigned int val)
+{
+	int count = MAX_I2C_RETRY;
+	int ret = -1;
+
+	while (count-- && (ret = regmap_write(map, reg, val)))
+		usleep_range(1000, 1100);
+
+	return ret;
+}
+
+static const struct regmap_config ub927_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.cache_type = REGCACHE_NONE,
+	.max_register = 0xff,
+};
+
+static int ub927_enable(struct fpdlink_dev *ub927)
+{
+	if (gpio_is_valid(ub927->gpio_power_en)) {
+		gpio_set_value(ub927->gpio_power_en, 1);
+	} else {
+		if (ub927->gpio_power_en > 0) {
+			dev_err(ub927->dev, "invalid gpio for gpio_disp_en %s\n",
+				ub927->gpio_name);
+			return -ENXIO;
+		}
+	}
+	msleep(200);
+	return 0;
+}
+
+static void ub927_disable(struct fpdlink_dev *ub927)
+{
+	regmap_write_fpdlink(ub927->regmap, UB927_RESET_REG,
+			UB927_RESET_DGTL_RST1);
+}
+
+static int ub927_reset(struct fpdlink_dev *ub927)
+{
+	int ret;
+
+	ret = regmap_write_fpdlink(ub927->regmap, UB927_RESET_REG,
+				   UB927_RESET_DGTL_RST1);
+	usleep_range(1000, 1500);
+	return ret;
+}
+
+static int ub927_enable_i2c_passthrough(struct fpdlink_dev *ub927)
+{
+	int ret;
+	unsigned int val;
+
+	ret = regmap_read(ub927->regmap, UB927_I2C_CTRL_REG, &val);
+	if (ret < 0) {
+		dev_err(ub927->dev, "failed on regmap_read at %s()\n",
+				__func__);
+		return ret;
+	}
+
+	ret = regmap_write_fpdlink(ub927->regmap, UB927_I2C_CTRL_REG,
+				   (val | UB927_I2C_CTRL_PASS_ALL));
+	if (ret < 0) {
+		dev_err(ub927->dev, "failed onregmap_write_fpdlink at %s()\n",
+			__func__);
+		return ret;
+	}
+
+	msleep(100);
+	return ret;
+}
+
+static int ub927_reg_write(struct fpdlink_dev *ub927, u8 reg, u8 val)
+{
+	return regmap_write_fpdlink(ub927->regmap, reg, val);
+}
+
+static int ub927_update_config(struct fpdlink_dev *ub927)
+{
+	int i = 0;
+	int size;
+	u8 *config_array;
+	int ret = 0;
+
+	size = ub927->config_array_size;
+	config_array = ub927->config_array;
+
+	if (!config_array)
+		return 0;
+
+	while (i < size) {
+		ret = regmap_write_fpdlink(ub927->regmap, config_array[i],
+					   config_array[i+1]);
+		if (ret) {
+			dev_err(ub927->dev,
+				"%s: failed to update register\n", __func__);
+			return -EINVAL;
+		}
+		i += 2;
+	}
+	return 0;
+}
+
+static bool ub927_link_detect(struct fpdlink_dev *ub927)
+{
+	unsigned int val;
+	int count, ret;
+
+	mutex_lock(&ub927->lock);
+	ub927->link_status = not_detected;
+
+	for (count = 0; count < MAX_I2C_RETRY; count++) {
+		usleep_range(10000, 15000);
+		ret = regmap_read(ub927->regmap, UB927_GS_REG, &val);
+		if (!ret && (val & UB927_GS_LINK_DETECT)) {
+			dev_info(ub927->dev,
+				 "LVDS: %d Serializer FPD link ready!\n",
+				 ub927->index);
+			ub927->link_status = detected;
+			ret = true;
+			goto done;
+		}
+	}
+
+	dev_info(ub927->dev, "LVDS: %d Serializer FPD link failed!!\n",
+			ub927->index);
+
+done:
+	mutex_unlock(&ub927->lock);
+
+	if (ret < 0) {
+		dev_info(ub927->dev,
+				"failed on regmap_read at %s()\n", __func__);
+	}
+	return (ub927->link_status ? true : false);
+}
+
+static bool ub927_pixel_clk_detect(struct fpdlink_dev *ub927)
+{
+	unsigned int val;
+	int count, ret;
+
+	mutex_lock(&ub927->lock);
+
+	for (count = 0; count < MAX_I2C_RETRY; count++) {
+		ret = regmap_read(ub927->regmap, UB927_GS_REG, &val);
+		if (!ret && (val & UB927_GS_PCLK_DETECT)) {
+			dev_info(ub927->dev,
+					"LVDS: %d, Serializer LVDS CLK OK!!\n",
+					ub927->index);
+			ub927->pixel_clk_status = detected;
+			ret = true;
+			goto done;
+		}
+	}
+	dev_info(ub927->dev,
+			"LVDS:%d LVDS CLK is not detected at Serializer !!\n",
+			ub927->index);
+	ub927->pixel_clk_status = not_detected;
+
+done:
+	mutex_unlock(&ub927->lock);
+	if (ret < 0) {
+		dev_info(ub927->dev,
+				"failed on regmap_read at %s()\n", __func__);
+	}
+	return (ub927->pixel_clk_status ? true : false);
+
+}
+
+static int parse_config_val(struct fpdlink_dev *ub927, struct device_node *np)
+{
+	const u32 *config_property;
+	int len;
+	u8 *config_array;
+	int config_array_size;
+	int i = 0;
+
+	config_property = of_get_property(np, "reg_config", &len);
+	config_array_size = len/sizeof(u32);
+
+	/*config value must be pair of register offset and value */
+	if (!config_property || config_array_size <= 1 || config_array_size%2)
+		return -EINVAL;
+
+	config_array = kcalloc(config_array_size, sizeof(u8), GFP_KERNEL);
+	if (!config_array_size)
+		return -ENOMEM;
+
+	for (i = 0; i < config_array_size; i++)
+		config_array[i] = (u8)be32_to_cpu(config_property[i]);
+
+	ub927->config_array_size = config_array_size;
+	ub927->config_array = config_array;
+	return 0;
+}
+
+static const struct fpdlink_dev_funcs ub927_fpdlink_dev_funcs = {
+	.enable				= ub927_enable,
+	.reset				= ub927_reset,
+	.enable_i2c_passthrough		= ub927_enable_i2c_passthrough,
+	.reg_write			= ub927_reg_write,
+	.link_detect			= ub927_link_detect,
+	.pixel_clk_detect		= ub927_pixel_clk_detect,
+	.config				= ub927_update_config,
+};
+
+static void ub927_shutdown(struct i2c_client *client)
+{
+	struct fpdlink_dev *ub927 = i2c_get_clientdata(client);
+
+	ub927_disable(ub927);
+}
+
+static ssize_t link_status_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct fpdlink_dev *ub927 = dev_get_drvdata(dev);
+	int ret = 0;
+	int count, status;
+	unsigned int val;
+
+	ret = mutex_lock_interruptible(&ub927->lock);
+	if (ret)
+		return ret;
+
+	ub927->link_status = not_detected;
+	for (count = 0; count < MAX_I2C_RETRY; count++) {
+		usleep_range(10000, 15000);
+		ret = regmap_read(ub927->regmap, UB927_GS_REG, &val);
+		if (!ret && (val & UB927_GS_LINK_DETECT)) {
+			ub927->link_status = detected;
+			break;
+		}
+		usleep_range(10000, 15000);
+	}
+	status = ub927->link_status;
+	mutex_unlock(&ub927->lock);
+
+	if (ret < 0) {
+		dev_info(ub927->dev, "failed on regmap_read at %s()\n",
+				__func__);
+	}
+
+	return snprintf(buf, PAGE_SIZE, "%s\n",
+			status ? "connected":"disconnected");
+}
+
+static ssize_t pixel_clk_status_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct fpdlink_dev *ub927 = dev_get_drvdata(dev);
+	int count, status;
+	int ret = 0;
+	unsigned int val;
+
+	ret = mutex_lock_interruptible(&ub927->lock);
+	if (ret)
+		return ret;
+
+	ub927->pixel_clk_status = not_detected;
+	for (count = 0; count < MAX_I2C_RETRY; count++) {
+		ret = regmap_read(ub927->regmap, UB927_GS_REG, &val);
+		if (!ret && (val & UB927_GS_PCLK_DETECT)) {
+			ub927->pixel_clk_status = detected;
+			break;
+		}
+		usleep_range(10000, 15000);
+	}
+
+	status = ub927->pixel_clk_status;
+	mutex_unlock(&ub927->lock);
+
+	if (ret < 0) {
+		dev_info(ub927->dev, "failed on regmap_read at %s()\n",
+				__func__);
+	}
+	return snprintf(buf, PAGE_SIZE, "%s\n", status ? "on":"off");
+}
+
+static DEVICE_ATTR_RO(link_status);
+static DEVICE_ATTR_RO(pixel_clk_status);
+
+
+static int ub927_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
+{
+	struct device *dev = &client->dev;
+	struct fpdlink_dev *ub927;
+	struct device_node *node_ptr;
+
+	int ret = 0;
+
+	ub927 = devm_kzalloc(dev, sizeof(*ub927), GFP_KERNEL);
+	if (!ub927)
+		return -ENOMEM;
+
+	ub927->dev = dev;
+	node_ptr = dev->of_node;
+
+	ub927->gpio_power_en = of_get_named_gpio(node_ptr, "power-en-pin", 0);
+	if (ub927->gpio_power_en > 0) {
+		sprintf(ub927->gpio_name, "ub927 %d", ub927->index);
+		if (!gpio_is_valid(ub927->gpio_power_en)) {
+			dev_err(dev, "invalid gpio for gpio_power_en %s\n",
+					ub927->gpio_name);
+			ret = -ENXIO;
+			goto done;
+		}
+	}
+
+	ub927->client = client;
+	ub927->regmap = devm_regmap_init_i2c(client, &ub927_regmap_config);
+	if (IS_ERR(ub927->regmap)) {
+		dev_err(dev, "failed to allocate register map\n");
+		ret = PTR_ERR(ub927->regmap);
+		goto done;
+	}
+
+	ub927->bridge.of_node = node_ptr;
+	ub927_disable(ub927);
+
+	mutex_init(&ub927->lock);
+	ub927->funcs = &ub927_fpdlink_dev_funcs;
+	parse_config_val(ub927, node_ptr);
+	i2c_set_clientdata(client, ub927);
+
+	drm_bridge_add(&ub927->bridge);
+
+	device_create_file(dev, &dev_attr_link_status);
+	device_create_file(dev, &dev_attr_pixel_clk_status);
+
+done:
+	if (ret < 0)
+		devm_kfree(&client->dev, ub927);
+	return ret;
+}
+
+static int ub927_remove(struct i2c_client *client)
+{
+	struct fpdlink_dev *ub927 =  i2c_get_clientdata(client);
+
+	drm_bridge_remove(&ub927->bridge);
+	return 0;
+}
+
+static const struct i2c_device_id ub927_id[] = {
+	{ "ds90ub927", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ub927_id);
+
+static const struct of_device_id ub927_match[] = {
+	{ .compatible = "ti,ds90ub927" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, ub927_match);
+
+static struct i2c_driver ti_ub927_driver = {
+	.id_table	= ub927_id,
+	.probe		= ub927_probe,
+	.remove		= ub927_remove,
+	.shutdown	= ub927_shutdown,
+	.driver		= {
+	.name		= "ti-ub927",
+	.of_match_table	= ub927_match,
+	},
+};
+module_i2c_driver(ti_ub927_driver);
+
+MODULE_AUTHOR("Vince Kim <vince.k.kim@gmail.com>");
+MODULE_DESCRIPTION("TI UB927 Serializer driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/bridge/ti-fpdlink/ti-ds90ub948.c b/drivers/gpu/drm/bridge/ti-fpdlink/ti-ds90ub948.c
new file mode 100644
index 000000000000..f42130a19533
--- /dev/null
+++ b/drivers/gpu/drm/bridge/ti-fpdlink/ti-ds90ub948.c
@@ -0,0 +1,333 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * TI FPD-LinkIII DS90UB948 driver
+ *
+ * Copyright (C) 2019 Lucid Motors Inc.
+ *
+ * Contact: Vince Kim <vince.k.kim@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/of_gpio.h>
+#include <drm/drmP.h>
+#include "ti-fpdlink.h"
+
+#define UB948_RESET_REG				0x01
+#define UB948_RESET_DGTL_RST0			(1<<2)
+
+#define UB948_GS_REG				0x1c
+#define UB948_GS_LOCK_DETECT			(1<<0)
+
+#define UB948_DUAL_CTL1_REG			0x5b
+
+#define UB948_DUAL_STS_REG			0x5a
+#define UB948_DUAL_STS_FPD3_LINK_READY		(1<<7)
+
+#define MAX_I2C_RETRY 10
+
+static inline struct fpdlink_dev *
+drm_bridge_to_ub948(struct drm_bridge *bridge)
+{
+	return container_of(bridge, struct fpdlink_dev, bridge);
+}
+
+static inline int regmap_write_fpdlink(struct regmap *map, unsigned int reg,
+					unsigned int val)
+{
+	int count = MAX_I2C_RETRY;
+	int ret = -1;
+
+	while (count-- && (ret = regmap_write(map, reg, val)))
+		usleep_range(1000, 1100);
+
+	return ret;
+}
+
+static const struct regmap_config ub948_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.cache_type = REGCACHE_NONE,
+	.max_register = 0xff,
+};
+
+static int ub948_enable(struct fpdlink_dev *ub948)
+{
+	if (gpio_is_valid(ub948->gpio_power_en)) {
+		gpio_set_value(ub948->gpio_power_en, 1);
+	} else {
+		if (ub948->gpio_power_en > 0) {
+			dev_err(ub948->dev,
+				"invalid gpio for gpio_disp_en %s\n",
+				ub948->gpio_name);
+			return -ENXIO;
+		}
+	}
+	msleep(200);
+	return 0;
+}
+
+static void ub948_disable(struct fpdlink_dev *ub948)
+{
+	if (gpio_is_valid(ub948->gpio_power_en))
+		gpio_set_value(ub948->gpio_power_en, 0);
+}
+
+static int ub948_reset(struct fpdlink_dev *ub948)
+{
+	int ret;
+
+	ret = regmap_write_fpdlink(ub948->regmap,
+			UB948_RESET_REG, UB948_RESET_DGTL_RST0);
+	msleep(100);
+	if (ret == 0)
+		ub948->detected = true;
+	return ret;
+}
+
+static int ub948_reg_write(struct fpdlink_dev *ub948, u8 reg, u8 val)
+{
+	return regmap_write_fpdlink(ub948->regmap, reg, val);
+}
+
+static int ub948_update_config(struct fpdlink_dev *ub948)
+{
+	int i = 0;
+	int size;
+	u8 *config_array;
+	int ret = 0;
+
+	size = ub948->config_array_size;
+	config_array = ub948->config_array;
+
+	if (!config_array)
+		return 0;
+
+	while (i < size) {
+		ret = regmap_write_fpdlink(ub948->regmap, config_array[i],
+				config_array[i+1]);
+		if (ret) {
+			dev_err(ub948->dev,
+				"%s: failed to update register\n", __func__);
+			return -EINVAL;
+		}
+		i += 2;
+	}
+	return 0;
+}
+
+static bool ub948_link_detect(struct fpdlink_dev *ub948)
+{
+	unsigned int val;
+	int count, ret;
+
+	if (ub948->detected == false)
+		return false;
+
+	mutex_lock(&ub948->lock);
+	ub948->link_status = not_detected;
+	for (count = 0; count < MAX_I2C_RETRY; count++) {
+		ret = regmap_read(ub948->regmap, UB948_GS_REG, &val);
+
+		if (!ret && (val & UB948_GS_LOCK_DETECT)) {
+			dev_info(ub948->dev,
+				"HDMI: %d DeSerializer FPD link ready!\n",
+				ub948->index);
+			ub948->link_status = detected;
+			ret = true;
+			goto done;
+		}
+		usleep_range(10000, 11000);
+	}
+	dev_info(ub948->dev, "HDMI: %d DeSerializer FPD link failed!!\n",
+			ub948->index);
+
+done:
+	mutex_unlock(&ub948->lock);
+
+	if (ret < 0)
+		dev_info(ub948->dev, "failed on regmap_read at %s()\n",
+				__func__);
+
+	return ub948->link_status ? true : false;
+}
+
+static int parse_config_val(struct fpdlink_dev *ub948,
+		struct device_node *node_ptr)
+{
+	const u32 *config_property;
+	int len;
+	u8 *config_array;
+	int config_array_size;
+	int i = 0;
+
+	config_property = of_get_property(node_ptr, "reg_config", &len);
+	config_array_size = len/sizeof(u32);
+
+	/*config value must be pair of register offset and value */
+	if (!config_property || config_array_size <= 1 || config_array_size%2)
+		return -EINVAL;
+
+	config_array = kcalloc(config_array_size, sizeof(u8), GFP_KERNEL);
+	if (!config_array_size)
+		return -ENOMEM;
+
+	for (i = 0; i < config_array_size; i++)
+		config_array[i] = (u8)be32_to_cpu(config_property[i]);
+
+	ub948->config_array_size = config_array_size;
+	ub948->config_array = config_array;
+	return 0;
+}
+
+static const struct fpdlink_dev_funcs ub948_fpdlink_dev_funcs = {
+	.enable				= ub948_enable,
+	.reset				= ub948_reset,
+	.reg_write			= ub948_reg_write,
+	.link_detect			= ub948_link_detect,
+	.config				= ub948_update_config,
+};
+
+static void ub948_shutdown(struct i2c_client *client)
+{
+	struct fpdlink_dev *ub948 = i2c_get_clientdata(client);
+
+	ub948_disable(ub948);
+}
+
+static ssize_t lock_status_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct fpdlink_dev *ub948 = dev_get_drvdata(dev);
+	int ret = 0;
+	int status;
+	unsigned int val;
+	int count;
+
+	if (ub948->detected == false)
+		return snprintf(buf, PAGE_SIZE, "%s\n", "off");
+
+	ret = mutex_lock_interruptible(&ub948->lock);
+	if (ret)
+		return ret;
+
+	ub948->link_status = not_detected;
+	for (count = 0; count < MAX_I2C_RETRY; count++) {
+		ret = regmap_read(ub948->regmap, UB948_GS_REG, &val);
+		if (!ret && (val & UB948_GS_LOCK_DETECT)) {
+			ub948->link_status = detected;
+			break;
+		}
+		usleep_range(10000, 11000);
+	}
+	status = ub948->link_status;
+	mutex_unlock(&ub948->lock);
+
+	if (ret < 0) {
+		dev_info(ub948->dev, "failed on regmap_read at %s()\n",
+				__func__);
+	}
+
+	return snprintf(buf, PAGE_SIZE, "%s\n", status ? "on":"off");
+}
+static DEVICE_ATTR_RO(lock_status);
+
+static int ub948_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
+{
+	struct device *dev = &client->dev;
+	struct fpdlink_dev *ub948;
+	struct device_node *node_ptr;
+
+	int ret = 0;
+
+	ub948 = devm_kzalloc(dev, sizeof(*ub948), GFP_KERNEL);
+	if (!ub948)
+		return -ENOMEM;
+
+	ub948->dev = dev;
+	node_ptr = dev->of_node;
+
+	ub948->gpio_power_en = of_get_named_gpio(node_ptr, "power-en-pin", 0);
+	if (ub948->gpio_power_en > 0) {
+		sprintf(ub948->gpio_name, "ub948 %d", ub948->index);
+		if (!gpio_is_valid(ub948->gpio_power_en)) {
+			dev_err(dev, "invalid gpio for gpio_power_en %s\n",
+					ub948->gpio_name);
+			ret = -ENXIO;
+			goto done;
+		}
+	}
+
+	ub948->client = client;
+	ub948->regmap = devm_regmap_init_i2c(client, &ub948_regmap_config);
+	if (IS_ERR(ub948->regmap)) {
+		dev_err(dev, "failed to allocate register map\n");
+		ret = PTR_ERR(ub948->regmap);
+		goto done;
+	}
+
+	mutex_init(&ub948->lock);
+	ub948->bridge.of_node = node_ptr;
+	ub948_disable(ub948);
+
+	ub948->funcs = &ub948_fpdlink_dev_funcs;
+	parse_config_val(ub948, node_ptr);
+	i2c_set_clientdata(client, ub948);
+
+	drm_bridge_add(&ub948->bridge);
+	device_create_file(dev, &dev_attr_lock_status);
+	/*
+	 * we don't know if deserializer is attached until serializer is ready.
+	 */
+	ub948->detected = false;
+
+done:
+	return ret;
+}
+
+static int ub948_remove(struct i2c_client *client)
+{
+	struct fpdlink_dev *ub948 =  i2c_get_clientdata(client);
+
+	drm_bridge_remove(&ub948->bridge);
+	return 0;
+}
+
+static const struct i2c_device_id ub948_id[] = {
+	{ "ds90ub948", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ub948_id);
+
+static const struct of_device_id ub948_match[] = {
+	{ .compatible = "ti,ds90ub948" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, ub948_match);
+
+static struct i2c_driver ti_ub948_driver = {
+	.id_table	= ub948_id,
+	.probe		= ub948_probe,
+	.remove		= ub948_remove,
+	.shutdown	= ub948_shutdown,
+	.driver		= {
+	.name		= "ti-ub948",
+	.of_match_table	= ub948_match,
+	},
+};
+module_i2c_driver(ti_ub948_driver);
+
+MODULE_AUTHOR("Vince Kim <vince.k.kim@gmail.com>");
+MODULE_DESCRIPTION("TI UB948 Serializer driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/bridge/ti-fpdlink/ti-ds90ub949.c b/drivers/gpu/drm/bridge/ti-fpdlink/ti-ds90ub949.c
new file mode 100644
index 000000000000..bb99d296fbb8
--- /dev/null
+++ b/drivers/gpu/drm/bridge/ti-fpdlink/ti-ds90ub949.c
@@ -0,0 +1,435 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * TI FPD-LinkIII DS90UB949 driver
+ *
+ * Copyright (C) 2019 Lucid Motors Inc.
+ *
+ * Contact: Vince Kim <vince.k.kim@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/of_gpio.h>
+#include <drm/drmP.h>
+#include "ti-fpdlink.h"
+
+#define UB949_RESET_REG				0x01
+#define UB949_RESET_HDMI_RST			(1<<4)
+#define UB949_RESET_DGTL_RST1			(1<<1)
+
+#define UB949_GS_REG				0x0c
+#define UB949_GS_BIST_CRC_ERROR			(1<<3)
+#define UB949_GS_TMDS_CLK_DETECT		(1<<2)
+#define UB949_GS_DES_CRC_ERROR			(1<<1)
+#define UB949_GS_LINK_DETECT			(1<<0)
+
+#define UB949_I2C_CTRL_REG			0x17
+#define UB949_I2C_CTRL_PASS_ALL			(1<<7)
+
+#define UB949_DUAL_STS_REG			0x5a
+#define UB949_DUAL_STS_FPD3_LINK_READY		(1<<7)
+#define UB949_DUAL_STS_FPD3_TX_STS		(1<<6)
+
+#define UB949_DUAL_CTL1_REG			0x5b
+#define UB949_DUAL_CTL2_REG			0x5c
+#define UB949_HDMI_FREQ				0x5f
+
+#define MAX_I2C_RETRY 10
+
+
+static inline struct fpdlink_dev *
+drm_bridge_to_ub949(struct drm_bridge *bridge)
+{
+	return container_of(bridge, struct fpdlink_dev, bridge);
+}
+
+static inline int regmap_write_fpdlink(struct regmap *map, unsigned int reg,
+					unsigned int val)
+{
+	int count = MAX_I2C_RETRY;
+	int ret = -1;
+
+	while (count-- && (ret = regmap_write(map, reg, val)))
+		usleep_range(1000, 1100);
+
+	return ret;
+}
+
+static const struct regmap_config ub949_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.cache_type = REGCACHE_NONE,
+	.max_register = 0xff,
+};
+
+static int ub949_enable(struct fpdlink_dev *ub949)
+{
+	if (gpio_is_valid(ub949->gpio_power_en)) {
+		gpio_set_value(ub949->gpio_power_en, 1);
+	} else {
+		if (ub949->gpio_power_en > 0) {
+			dev_err(ub949->dev,
+				"invalid gpio for gpio_disp_en %s\n",
+				ub949->gpio_name);
+			return -ENXIO;
+		}
+	}
+
+	msleep(200);
+	return 0;
+}
+
+static void ub949_disable(struct fpdlink_dev *ub949)
+{
+	regmap_write_fpdlink(ub949->regmap, UB949_RESET_REG,
+			(UB949_RESET_HDMI_RST|UB949_RESET_DGTL_RST1));
+}
+
+static int ub949_reset(struct fpdlink_dev *ub949)
+{
+	int ret;
+
+	ret = regmap_write_fpdlink(ub949->regmap, UB949_RESET_REG,
+			(UB949_RESET_HDMI_RST|UB949_RESET_DGTL_RST1));
+	usleep_range(10000, 11000);
+
+	return ret;
+}
+
+static int ub949_enable_i2c_passthrough(struct fpdlink_dev *ub949)
+{
+	int ret;
+	unsigned int val;
+
+	ret = regmap_read(ub949->regmap, UB949_I2C_CTRL_REG, &val);
+	if (ret < 0) {
+		dev_err(ub949->dev, "failed on regmap_read at %s()\n",
+				__func__);
+		return ret;
+	}
+
+	ret = regmap_write_fpdlink(ub949->regmap, UB949_I2C_CTRL_REG,
+			(val | UB949_I2C_CTRL_PASS_ALL));
+	msleep(100);
+
+	return ret;
+}
+
+static int ub949_reg_write(struct fpdlink_dev *ub949, u8 reg, u8 val)
+{
+	return regmap_write_fpdlink(ub949->regmap, reg, val);
+}
+
+static int ub949_update_config(struct fpdlink_dev *ub949)
+{
+	int i = 0;
+	int size;
+	u8 *config_array;
+	int ret = 0;
+
+	size = ub949->config_array_size;
+	config_array = ub949->config_array;
+
+	if (!config_array)
+		return 0;
+
+	while (i < size) {
+		ret = regmap_write_fpdlink(ub949->regmap, config_array[i],
+				config_array[i+1]);
+		if (ret) {
+			dev_err(ub949->dev,
+				"%s: failed to update register\n", __func__);
+			return -EINVAL;
+		}
+		i += 2;
+	}
+
+	return 0;
+}
+
+static bool ub949_link_detect(struct fpdlink_dev *ub949)
+{
+	unsigned int val, val2;
+	int count, ret;
+
+	mutex_lock(&ub949->lock);
+	ub949->link_status = not_detected;
+
+	for (count = 0; count < MAX_I2C_RETRY; count++) {
+		usleep_range(10000, 11000);
+		ret = regmap_read(ub949->regmap, UB949_GS_REG, &val);
+		ret += regmap_read(ub949->regmap, UB949_DUAL_STS_REG, &val2);
+		if (!ret && (val & UB949_GS_LINK_DETECT) &&
+				(val2 & UB949_DUAL_STS_FPD3_LINK_READY)) {
+			dev_info(ub949->dev,
+					"HDMI: %d Serializer FPD link ready!\n",
+					ub949->index);
+			ub949->link_status = detected;
+			ret = true;
+			goto done;
+		}
+	}
+	dev_info(ub949->dev,
+			"HDMI:%d Serializer FPD link failed!!\n", ub949->index);
+
+done:
+	mutex_unlock(&ub949->lock);
+
+	if (ret < 0) {
+		dev_info(ub949->dev,
+				"failed on regmap_read at %s()\n", __func__);
+	}
+
+	return (ub949->link_status ? true : false);
+}
+
+static bool ub949_pixel_clk_detect(struct fpdlink_dev *ub949)
+{
+	unsigned int val;
+	int count, ret;
+
+	mutex_lock(&ub949->lock);
+
+	for (count = 0; count < 1; count++) {
+		ret = regmap_read(ub949->regmap, UB949_HDMI_FREQ, &val);
+		if (!ret && val) {
+			dev_info(ub949->dev,
+					"HDMI: %d,  Serializer HDMI CLK OK!!\n",
+					ub949->index);
+			ub949->pixel_clk_status = detected;
+			ret = true;
+			goto done;
+		}
+	}
+	dev_info(ub949->dev,
+			"HDMI:%d HDMI CLK is not detected at Serializer !!\n",
+			ub949->index);
+	ub949->pixel_clk_status = not_detected;
+
+done:
+	mutex_unlock(&ub949->lock);
+
+	if (ret < 0)
+		dev_info(ub949->dev,
+				"failed on regmap_read at %s()\n", __func__);
+
+	return ub949->pixel_clk_status ? true : false;
+
+}
+
+static int parse_config_val(struct fpdlink_dev *ub949,
+		struct device_node *node_ptr)
+{
+	const u32 *config_property;
+	int len;
+	u8 *config_array;
+	int config_array_size;
+	int i = 0;
+
+	config_property = of_get_property(node_ptr, "reg_config", &len);
+	config_array_size = len/sizeof(u32);
+
+	/*config value must be a pair of register offset and value */
+	if (!config_property || config_array_size <= 1 || config_array_size%2)
+		return -EINVAL;
+
+	config_array = kcalloc(config_array_size, sizeof(u8), GFP_KERNEL);
+	if (!config_array_size)
+		return -ENOMEM;
+
+	for (i = 0; i < config_array_size; i++)
+		config_array[i] = (u8)be32_to_cpu(config_property[i]);
+
+	ub949->config_array_size = config_array_size;
+	ub949->config_array = config_array;
+	return 0;
+}
+
+static const struct fpdlink_dev_funcs ub949_fpdlink_dev_funcs = {
+	.enable				= ub949_enable,
+	.reset				= ub949_reset,
+	.enable_i2c_passthrough		= ub949_enable_i2c_passthrough,
+	.reg_write			= ub949_reg_write,
+	.link_detect			= ub949_link_detect,
+	.pixel_clk_detect		= ub949_pixel_clk_detect,
+	.config				= ub949_update_config,
+};
+
+static void ub949_shutdown(struct i2c_client *client)
+{
+	struct fpdlink_dev *ub949 = i2c_get_clientdata(client);
+
+	ub949_disable(ub949);
+}
+
+static ssize_t link_status_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct fpdlink_dev *ub949 = dev_get_drvdata(dev);
+	int status, ret;
+	unsigned int val, val2;
+	int count;
+
+	ret = mutex_lock_interruptible(&ub949->lock);
+	if (ret)
+		return ret;
+
+	ub949->link_status = not_detected;
+
+	for (count = 0; count < MAX_I2C_RETRY; count++) {
+		ret = regmap_read(ub949->regmap, UB949_GS_REG, &val);
+		ret += regmap_read(ub949->regmap, UB949_DUAL_STS_REG, &val2);
+		if (!ret && (val & UB949_GS_LINK_DETECT)
+			 && (val2 & UB949_DUAL_STS_FPD3_LINK_READY)) {
+			ub949->link_status = detected;
+			break;
+		}
+		usleep_range(10000, 11000);
+	}
+	status = ub949->link_status;
+	mutex_unlock(&ub949->lock);
+
+	if (ret < 0)
+		dev_err(ub949->dev,
+				"failed on regmap_read at %s()\n", __func__);
+
+	return snprintf(buf, PAGE_SIZE, "%s\n",
+			status ? "connected":"disconnected");
+}
+
+static ssize_t pixel_clk_status_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct fpdlink_dev *ub949 = dev_get_drvdata(dev);
+	int status, ret;
+	unsigned int val, val2;
+	int count;
+
+	ret = mutex_lock_interruptible(&ub949->lock);
+	if (ret)
+		return ret;
+
+	ub949->pixel_clk_status = not_detected;
+	for (count = 0; count < MAX_I2C_RETRY; count++) {
+		ret = regmap_read(ub949->regmap, UB949_HDMI_FREQ, &val);
+		ret += regmap_read(ub949->regmap, UB949_GS_REG, &val2);
+		if (!ret && val && (val2 & UB949_GS_TMDS_CLK_DETECT)) {
+			ub949->pixel_clk_status = detected;
+			break;
+		}
+		usleep_range(10000, 11000);
+	}
+	status = ub949->pixel_clk_status;
+	mutex_unlock(&ub949->lock);
+
+	if (ret < 0)
+		dev_err(ub949->dev,
+				"failed on regmap_read at %s()\n", __func__);
+
+	return snprintf(buf, PAGE_SIZE, "%s\n", status ? "on":"off");
+}
+
+static DEVICE_ATTR_RO(link_status);
+static DEVICE_ATTR_RO(pixel_clk_status);
+
+static int ub949_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
+{
+	struct device *dev = &client->dev;
+	struct fpdlink_dev *ub949;
+	struct device_node *node_ptr;
+
+	int ret = 0;
+
+	ub949 = devm_kzalloc(dev, sizeof(*ub949), GFP_KERNEL);
+	if (!ub949)
+		return -ENOMEM;
+
+	ub949->dev = dev;
+	node_ptr = dev->of_node;
+
+	ub949->gpio_power_en = of_get_named_gpio(node_ptr, "power-en-pin", 0);
+	if (ub949->gpio_power_en > 0) {
+		sprintf(ub949->gpio_name, "ub949 %d", ub949->index);
+		if (!gpio_is_valid(ub949->gpio_power_en)) {
+			dev_err(dev, "invalid gpio for gpio_power_en %s\n",
+					ub949->gpio_name);
+			ret = -ENXIO;
+			goto done;
+		}
+	}
+
+	ub949->client = client;
+	ub949->regmap = devm_regmap_init_i2c(client, &ub949_regmap_config);
+	if (IS_ERR(ub949->regmap)) {
+		dev_err(dev, "failed to allocate register map\n");
+		ret = PTR_ERR(ub949->regmap);
+		goto done;
+	}
+
+	mutex_init(&ub949->lock);
+
+	ub949->bridge.of_node = node_ptr;
+	ub949_disable(ub949);
+
+	ub949->funcs = &ub949_fpdlink_dev_funcs;
+	parse_config_val(ub949, node_ptr);
+	i2c_set_clientdata(client, ub949);
+
+	drm_bridge_add(&ub949->bridge);
+
+	device_create_file(dev, &dev_attr_link_status);
+	device_create_file(dev, &dev_attr_pixel_clk_status);
+
+done:
+	if (ret < 0)
+		devm_kfree(&client->dev, ub949);
+
+	return ret;
+}
+
+static int ub949_remove(struct i2c_client *client)
+{
+	struct fpdlink_dev *ub949 =  i2c_get_clientdata(client);
+
+	drm_bridge_remove(&ub949->bridge);
+	return 0;
+}
+
+static const struct i2c_device_id ub949_id[] = {
+	{ "ds90ub949", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ub949_id);
+
+static const struct of_device_id ub949_match[] = {
+	{ .compatible = "ti,ds90ub949" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, ub949_match);
+
+static struct i2c_driver ti_ub949_driver = {
+	.id_table	= ub949_id,
+	.probe		= ub949_probe,
+	.remove		= ub949_remove,
+	.shutdown	= ub949_shutdown,
+	.driver		= {
+		.name		= "ti-ub949",
+		.of_match_table	= ub949_match,
+	},
+};
+module_i2c_driver(ti_ub949_driver);
+
+MODULE_AUTHOR("Vince Kim <vince.k.kim@gmail.com>");
+MODULE_DESCRIPTION("TI UB949 Serializer driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/bridge/ti-fpdlink/ti-fpdlink.c b/drivers/gpu/drm/bridge/ti-fpdlink/ti-fpdlink.c
new file mode 100644
index 000000000000..fa4cd5806f19
--- /dev/null
+++ b/drivers/gpu/drm/bridge/ti-fpdlink/ti-fpdlink.c
@@ -0,0 +1,396 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * TI FPD-LinkIII interface bridge driver
+ *
+ * Copyright (C) 2019 Lucid Motors Inc.
+ *
+ * Contact: Vince Kim <vince.k.kim@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_probe_helper.h>
+#include <linux/of_gpio.h>
+#include "ti-fpdlink.h"
+
+struct fpdlink {
+	struct drm_bridge	bridge;
+	struct drm_connector	connector;
+
+	struct fpdlink_dev	*serializer;
+	struct fpdlink_dev	*deserializer;
+	struct device_node	*fpdlink_node;
+	struct mutex lock;
+
+	unsigned int index;
+	enum clk_status pixel_clk_status;
+	enum clk_status link_status;
+	bool enabled;
+};
+
+
+static inline struct fpdlink *
+drm_bridge_to_fpdlink(struct drm_bridge *bridge)
+{
+	return container_of(bridge, struct fpdlink, bridge);
+}
+
+static inline struct fpdlink *
+drm_connector_to_fpdlink(struct drm_connector *connector)
+{
+	return container_of(connector, struct fpdlink, connector);
+}
+
+static int fpdlink_dev_enable(struct fpdlink_dev *dev)
+{
+	int ret;
+
+	if (!dev)
+		return -EINVAL;
+	if (!dev->funcs)
+		return -EINVAL;
+	if (!dev->funcs->enable)
+		return -EINVAL;
+
+	ret = dev->funcs->enable(dev);
+
+	return ret;
+}
+
+static int fpdlink_dev_reset(struct fpdlink_dev *dev)
+{
+	int ret;
+
+	if (!dev)
+		return -EINVAL;
+	if (!dev->funcs)
+		return -EINVAL;
+	if (!dev->funcs->reset)
+		return -EINVAL;
+
+	ret = dev->funcs->reset(dev);
+	return ret;
+}
+
+static int fpdlink_dev_enable_i2c_passthrough(struct fpdlink_dev *dev)
+{
+	int ret;
+
+	if (!dev)
+		return -EINVAL;
+	if (!dev->funcs)
+		return -EINVAL;
+	if (!dev->funcs->enable_i2c_passthrough)
+		return -EINVAL;
+
+	ret = dev->funcs->enable_i2c_passthrough(dev);
+	return ret;
+}
+
+static bool fpdlink_dev_link_detect(struct fpdlink_dev *dev)
+{
+	bool ret;
+
+	if (!dev)
+		return -EINVAL;
+	if (!dev->funcs)
+		return -EINVAL;
+	if (!dev->funcs->link_detect)
+		return -EINVAL;
+
+	ret = dev->funcs->link_detect(dev);
+	return ret;
+}
+
+static bool fpdlink_dev_pixel_clk_detect(struct fpdlink_dev *dev)
+{
+	bool ret;
+
+	if (!dev)
+		return -EINVAL;
+	if (!dev->funcs)
+		return -EINVAL;
+	if (!dev->funcs->pixel_clk_detect)
+		return -EINVAL;
+
+	ret = dev->funcs->pixel_clk_detect(dev);
+	return ret;
+}
+
+static int fpdlink_dev_config(struct fpdlink_dev *dev)
+{
+	int ret;
+
+	if (!dev)
+		return -EINVAL;
+	if (!dev->funcs)
+		return -EINVAL;
+	if (!dev->funcs->config)
+		return -EINVAL;
+
+	ret = dev->funcs->config(dev);
+	return ret;
+}
+
+static enum drm_connector_status
+fpdlink_connector_detect(struct drm_connector *connector, bool force)
+{
+	struct fpdlink *fpdlink_bridge = drm_connector_to_fpdlink(connector);
+
+	if (fpdlink_bridge->link_status == detected)
+		return connector_status_connected;
+
+	return connector_status_disconnected;
+}
+
+static const struct drm_connector_funcs fpdlink_con_funcs = {
+	.detect			= fpdlink_connector_detect,
+	.fill_modes		= drm_helper_probe_single_connector_modes,
+	.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 void fpdlink_pre_enable(struct drm_bridge *bridge)
+{
+	struct fpdlink *fpdlink_bridge = drm_bridge_to_fpdlink(bridge);
+
+	struct fpdlink_dev *serializer = fpdlink_bridge->serializer;
+	struct fpdlink_dev *deserializer = fpdlink_bridge->deserializer;
+	int ret = 0;
+
+	mutex_lock(&fpdlink_bridge->lock);
+
+	if (fpdlink_bridge->link_status == detected)
+		goto done;
+
+	fpdlink_dev_enable(deserializer);
+	fpdlink_dev_enable(serializer);
+	fpdlink_dev_reset(serializer);
+	fpdlink_dev_enable_i2c_passthrough(serializer);
+	ret = fpdlink_dev_reset(deserializer);
+	if (ret != 0) {
+		fpdlink_bridge->connector.status =
+				connector_status_disconnected;
+		DRM_INFO("Not able to detect Deserializer at %pOF\n",
+				fpdlink_bridge->fpdlink_node);
+		goto done;
+	}
+
+	ret = fpdlink_dev_link_detect(serializer);
+	if (ret != true) {
+		fpdlink_bridge->connector.status =
+				connector_status_disconnected;
+		goto done;
+	}
+
+	fpdlink_bridge->link_status = detected;
+	fpdlink_bridge->pixel_clk_status = not_detected;
+	fpdlink_bridge->connector.status = connector_status_connected;
+done:
+	mutex_unlock(&fpdlink_bridge->lock);
+}
+
+static void fpdlink_enable(struct drm_bridge *bridge)
+{
+	struct fpdlink *fpdlink_bridge = drm_bridge_to_fpdlink(bridge);
+	int ret = 0;
+	struct fpdlink_dev *serializer = fpdlink_bridge->serializer;
+	struct fpdlink_dev *deserializer = fpdlink_bridge->deserializer;
+
+	mutex_lock(&fpdlink_bridge->lock);
+
+	if (fpdlink_bridge->link_status == not_detected) {
+		fpdlink_bridge->connector.status =
+				connector_status_disconnected;
+		fpdlink_bridge->pixel_clk_status = not_detected;
+		goto done;
+	}
+
+	if (fpdlink_bridge->enabled)
+		goto done;
+
+	ret = fpdlink_dev_pixel_clk_detect(serializer);
+
+	if (ret != true) {
+		fpdlink_bridge->connector.status =
+				connector_status_disconnected;
+		fpdlink_bridge->pixel_clk_status = not_detected;
+		pr_err("pixel_clk_status not detected\n");
+		goto done;
+	}
+
+	fpdlink_bridge->pixel_clk_status = detected;
+	fpdlink_bridge->connector.status = connector_status_connected;
+	fpdlink_dev_config(serializer);
+	fpdlink_dev_config(deserializer);
+	fpdlink_bridge->enabled = 1;
+
+done:
+	mutex_unlock(&fpdlink_bridge->lock);
+}
+
+static int fpdlink_get_modes(struct drm_connector *connector)
+{
+	struct fpdlink *fpdlink_bridge = drm_connector_to_fpdlink(connector);
+
+	if (fpdlink_bridge->enabled == 0)
+		fpdlink_enable(&fpdlink_bridge->bridge);
+
+	return 0;
+}
+static const struct drm_connector_helper_funcs fpdlink_con_helper_funcs = {
+	.get_modes	= fpdlink_get_modes,
+};
+
+
+static int fpdlink_attach(struct drm_bridge *bridge)
+{
+	struct fpdlink *fpdlink_bridge = drm_bridge_to_fpdlink(bridge);
+	struct device_node *fpdlink_node = bridge->of_node;
+	struct device_node *serializer_node;
+	struct device_node *deserializer_node;
+	struct drm_bridge *serializer_bridge = NULL;
+	struct drm_bridge *deserializer_bridge = NULL;
+	struct drm_connector *connector;
+	int ret;
+
+	if (!bridge->encoder) {
+		DRM_ERROR("Missing encoder\n");
+		return -ENODEV;
+	}
+	connector = dev_get_drvdata(bridge->dev->dev);
+	fpdlink_node = fpdlink_bridge->fpdlink_node;
+
+	drm_connector_helper_add(&fpdlink_bridge->connector,
+				 &fpdlink_con_helper_funcs);
+
+	ret = drm_connector_init(bridge->dev, &fpdlink_bridge->connector,
+				 &fpdlink_con_funcs, DRM_MODE_CONNECTOR_VGA);
+	if (ret) {
+		DRM_ERROR("Failed to initialize connector\n");
+		return ret;
+	}
+
+	drm_connector_attach_encoder(&fpdlink_bridge->connector,
+					  bridge->encoder);
+
+	serializer_node = of_parse_phandle(fpdlink_node,
+					   "fpdlink-serializer-i2c-handle", 0);
+	if (!serializer_node) {
+		DRM_INFO("failed to find fpdlink-serializer-i2c-handle node at %pOF\n",
+				fpdlink_node);
+		return -ENODEV;
+	}
+
+	serializer_bridge = of_drm_find_bridge(serializer_node);
+
+
+	if (!serializer_bridge) {
+		DRM_INFO("failed to find serializer bridge for %pOF\n",
+				serializer_node);
+		return -ENODEV;
+	}
+
+	fpdlink_bridge->serializer =
+				drm_bridge_to_fpdlink_dev(serializer_bridge);
+	fpdlink_bridge->serializer->index = bridge->encoder->index;
+
+	deserializer_node = of_parse_phandle(fpdlink_node,
+			"fpdlink-deserializer-i2c-handle", 0);
+	if (!deserializer_node) {
+		DRM_INFO("failed to find fpdlink-deserializer-i2c-handle node at %pOF\n",
+				fpdlink_node);
+		return -ENODEV;
+	}
+
+	deserializer_bridge = of_drm_find_bridge(deserializer_node);
+	if (!deserializer_bridge) {
+		DRM_INFO("failed to find deserializer bridge for %pOF\n",
+				deserializer_node);
+		return -ENODEV;
+	}
+
+	fpdlink_bridge->deserializer =
+				drm_bridge_to_fpdlink_dev(deserializer_bridge);
+	fpdlink_bridge->deserializer->index = bridge->encoder->index;
+	fpdlink_bridge->index = bridge->encoder->index;
+	fpdlink_bridge->link_status = not_detected;
+	fpdlink_bridge->pixel_clk_status = not_detected;
+	return ret;
+}
+
+static const struct drm_bridge_funcs fpdlink_bridge_funcs = {
+	.attach		= fpdlink_attach,
+	.pre_enable	= fpdlink_pre_enable,
+	.enable		= fpdlink_enable,
+};
+
+
+
+static int fpdlink_probe(struct platform_device *pdev)
+{
+	struct fpdlink *fpdlink_bridge;
+	int gpio_pin;
+	struct device_node *np;
+
+	fpdlink_bridge = devm_kzalloc(&pdev->dev, sizeof(*fpdlink_bridge),
+					GFP_KERNEL);
+	if (!fpdlink_bridge)
+		return -ENOMEM;
+
+	mutex_init(&fpdlink_bridge->lock);
+
+
+	platform_set_drvdata(pdev, fpdlink_bridge);
+
+	fpdlink_bridge->bridge.funcs = &fpdlink_bridge_funcs;
+	np = pdev->dev.of_node;
+	fpdlink_bridge->bridge.of_node = np;
+	fpdlink_bridge->fpdlink_node = np;
+
+	gpio_pin = of_get_named_gpio(np, "disp-en-pin", 0);
+
+	drm_bridge_add(&fpdlink_bridge->bridge);
+	return 0;
+}
+
+static int fpdlink_remove(struct platform_device *pdev)
+{
+	struct fpdlink *fpdlink_bridge = platform_get_drvdata(pdev);
+
+	drm_bridge_remove(&fpdlink_bridge->bridge);
+	return 0;
+}
+
+static const struct of_device_id fpdlink_match[] = {
+	{ .compatible = "ti,fpdlink" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, fpdlink_match);
+
+static struct platform_driver ti_fpdlink_driver = {
+	.probe	= fpdlink_probe,
+	.remove	= fpdlink_remove,
+	.driver		= {
+		.name		= "ti-fpdlink",
+		.of_match_table	= fpdlink_match,
+	},
+};
+module_platform_driver(ti_fpdlink_driver);
+
+MODULE_AUTHOR("Vince Kim <vince.k.kim@gmail.com>");
+MODULE_DESCRIPTION("TI FPDlink driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/bridge/ti-fpdlink/ti-fpdlink.h b/drivers/gpu/drm/bridge/ti-fpdlink/ti-fpdlink.h
new file mode 100644
index 000000000000..073d4b4601f3
--- /dev/null
+++ b/drivers/gpu/drm/bridge/ti-fpdlink/ti-fpdlink.h
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * TI FPD-LinkIII interface bridge driver header file
+ *
+ * Copyright (C) 2019 Lucid Motors Inc.
+ *
+ * Contact: Vince Kim <vince.k.kim@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __TI_FPDLINK_H__
+#define __TI_FPDLINK_H__
+#include <linux/list.h>
+#include <linux/ctype.h>
+#include <drm/drm_mode_object.h>
+#include <drm/drm_modes.h>
+
+struct fpdlink_dev;
+
+enum clk_status {
+	not_detected = 0,
+	detected = 1,
+};
+
+struct fpdlink_dev_funcs {
+	int (*enable)(struct fpdlink_dev *dev);
+	void (*disable)(struct fpdlink_dev *dev);
+	int (*reset)(struct fpdlink_dev *dev);
+	int (*enable_i2c_passthrough)(struct fpdlink_dev *dev);
+	bool (*link_detect)(struct fpdlink_dev *dev);
+	bool (*pixel_clk_detect)(struct fpdlink_dev *dev);
+	int (*config)(struct fpdlink_dev *dev);
+	int (*reg_write)(struct fpdlink_dev *dev, u8 reg, u8 val);
+};
+
+struct fpdlink_dev {
+	struct drm_bridge	bridge;
+	struct drm_connector	connector;
+	struct device		*dev;
+	struct i2c_client	*client;
+	struct regmap		*regmap;
+	struct mutex		lock;
+
+	const struct fpdlink_dev_funcs *funcs;
+
+	char gpio_name[20];
+	unsigned int index;
+	bool	detected;
+	int gpio_power_en;
+	enum clk_status pixel_clk_status;
+	enum clk_status link_status;
+	int config_array_size;
+	u8 *config_array;
+};
+
+static inline struct fpdlink_dev *
+drm_bridge_to_fpdlink_dev(struct drm_bridge *bridge)
+{
+	return container_of(bridge, struct fpdlink_dev, bridge);
+}
+#endif
-- 
2.17.1

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

* [PATCH 2/2] dt-bindings: display: bridge: TI FPD-Link III Serializer/Deserializer
  2019-03-13 18:34 [PATCH 1/2] drm/bridge: add initial support for TI FPDLINK bridge drivers Vince Kim
@ 2019-03-13 18:34 ` Vince Kim
  2019-03-13 18:49   ` Laurent Pinchart
  0 siblings, 1 reply; 3+ messages in thread
From: Vince Kim @ 2019-03-13 18:34 UTC (permalink / raw)
  Cc: Vince Kim, David Airlie, Daniel Vetter, Rob Herring,
	Mark Rutland, Andrzej Hajda, Laurent Pinchart, dri-devel,
	devicetree, linux-kernel

Add documments of device tree bindings for TI FPD-Link III Serializer
and Deserializer chips;  DS90UB927, DS90UB949, DS90UB948

Signed-off-by: Vince Kim <vince.k.kim@gmail.com>
---
 .../bindings/display/bridge/ti,ds90ub9xx.txt  | 39 +++++++++++++++++++
 .../bindings/display/bridge/ti,fpdlink.txt    | 25 ++++++++++++
 2 files changed, 64 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/bridge/ti,ds90ub9xx.txt
 create mode 100644 Documentation/devicetree/bindings/display/bridge/ti,fpdlink.txt

diff --git a/Documentation/devicetree/bindings/display/bridge/ti,ds90ub9xx.txt b/Documentation/devicetree/bindings/display/bridge/ti,ds90ub9xx.txt
new file mode 100644
index 000000000000..351d9cb79ebd
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/bridge/ti,ds90ub9xx.txt
@@ -0,0 +1,39 @@
+Binding for TI FPD-Link III Bridge Serializers; TI DS90UB927, DS90UB949
+TI FPD-Link III Bridge Deserializer; DS90UB948
+
+Required properties:
+- compatible: shall be one of
+		"ti,ds90ub927"
+		"ti,ds90ub948"
+		"ti,ds90ub949"
+- reg: i2c device address
+
+
+Optional properties:
+- power-en-pin: GPIO pin for power enable
+- reg_config: register address and value to be written during initialization
+
+
+Example:
+--------
+I2c-master-node {
+...
+	fpdlink_serializer_i2c3: serializer@0c {
+		status = "okay";
+		compatible = "ti,ds90ub949";
+		reg = <0x0c>;
+	/* Enabling passthrough for GPIO0 on serializer for touchscreen IRQ */
+		reg_config = <0x0d 0x25>;
+	};
+
+	fpdlink_deserializer_i2c3: deserializer@2c {
+		status = "okay";
+		compatible = "ti,ds90ub948";
+		power-en-pin =  <&gpio9 8  GPIO_ACTIVE_HIGH>;
+		reg = <0x2c>;
+		/* 0x90 for DISP enable, 0x9 is for Touch enable on GPIO1 and 2*/
+		reg_config = <0x1e 0x99>;
+	};
+...
+}
+
diff --git a/Documentation/devicetree/bindings/display/bridge/ti,fpdlink.txt b/Documentation/devicetree/bindings/display/bridge/ti,fpdlink.txt
new file mode 100644
index 000000000000..2f2e1a269722
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/bridge/ti,fpdlink.txt
@@ -0,0 +1,25 @@
+Binding for TI FPD-Link III Bridge driver
+
+Wrapper DRM Bridge driver for TI FPD-Link III Serializer and Deserializer
+
+Required properties:
+- compatible: "ti,fpdlink"
+- fpdlink-serializer-i2c-handle: i2c node for TI FPD-Link III serializer driver
+- fpdlink-deserializer-i2c-handle: i2c node for TI FPD-Link III deserializer driver
+
+
+Example:
+--------
+fpdlink_lvds: fpdlink_bridge@0 {
+	status = "okay";
+	compatible = "ti,fpdlink";
+	fpdlink-serializer-i2c-handle = <&fpdlink_serializer_i2c0>;
+	fpdlink-deserializer-i2c-handle = <&fpdlink_deserializer_i2c0>;
+};
+
+consumer: {
+...
+	fpdlink-bridge-handle = <&fpdlink_lvds>;
+...
+}
+
-- 
2.17.1

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

* Re: [PATCH 2/2] dt-bindings: display: bridge: TI FPD-Link III Serializer/Deserializer
  2019-03-13 18:34 ` [PATCH 2/2] dt-bindings: display: bridge: TI FPD-Link III Serializer/Deserializer Vince Kim
@ 2019-03-13 18:49   ` Laurent Pinchart
  0 siblings, 0 replies; 3+ messages in thread
From: Laurent Pinchart @ 2019-03-13 18:49 UTC (permalink / raw)
  To: Vince Kim
  Cc: David Airlie, Daniel Vetter, Rob Herring, Mark Rutland,
	Andrzej Hajda, dri-devel, devicetree, linux-kernel

Hi Vince,

Thank you for the patch.

Are you aware of the "[PATCH 0/7] mfd/pinctrl: add initial support of TI
DS90Ux9xx ICs" patch series
(https://lore.kernel.org/lkml/20181012060314.GU4939@dell/T/) ?

On Wed, Mar 13, 2019 at 11:34:58AM -0700, Vince Kim wrote:
> Add documments of device tree bindings for TI FPD-Link III Serializer
> and Deserializer chips;  DS90UB927, DS90UB949, DS90UB948
> 
> Signed-off-by: Vince Kim <vince.k.kim@gmail.com>
> ---
>  .../bindings/display/bridge/ti,ds90ub9xx.txt  | 39 +++++++++++++++++++
>  .../bindings/display/bridge/ti,fpdlink.txt    | 25 ++++++++++++
>  2 files changed, 64 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/display/bridge/ti,ds90ub9xx.txt
>  create mode 100644 Documentation/devicetree/bindings/display/bridge/ti,fpdlink.txt
> 
> diff --git a/Documentation/devicetree/bindings/display/bridge/ti,ds90ub9xx.txt b/Documentation/devicetree/bindings/display/bridge/ti,ds90ub9xx.txt
> new file mode 100644
> index 000000000000..351d9cb79ebd
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/bridge/ti,ds90ub9xx.txt
> @@ -0,0 +1,39 @@
> +Binding for TI FPD-Link III Bridge Serializers; TI DS90UB927, DS90UB949
> +TI FPD-Link III Bridge Deserializer; DS90UB948
> +
> +Required properties:
> +- compatible: shall be one of
> +		"ti,ds90ub927"
> +		"ti,ds90ub948"
> +		"ti,ds90ub949"
> +- reg: i2c device address
> +
> +
> +Optional properties:
> +- power-en-pin: GPIO pin for power enable
> +- reg_config: register address and value to be written during initialization

That property is a software policy, not a system description. It doesn't
belong in DT.

> +
> +
> +Example:
> +--------
> +I2c-master-node {
> +...
> +	fpdlink_serializer_i2c3: serializer@0c {
> +		status = "okay";
> +		compatible = "ti,ds90ub949";
> +		reg = <0x0c>;
> +	/* Enabling passthrough for GPIO0 on serializer for touchscreen IRQ */
> +		reg_config = <0x0d 0x25>;
> +	};
> +
> +	fpdlink_deserializer_i2c3: deserializer@2c {
> +		status = "okay";
> +		compatible = "ti,ds90ub948";
> +		power-en-pin =  <&gpio9 8  GPIO_ACTIVE_HIGH>;
> +		reg = <0x2c>;
> +		/* 0x90 for DISP enable, 0x9 is for Touch enable on GPIO1 and 2*/
> +		reg_config = <0x1e 0x99>;
> +	};

The connection between the serializer and deserializer, as well as
between those two chips and the other part of the display (or camera)
pipeline should be described using ports (see the OF graph bindings).

> +...
> +}
> +
> diff --git a/Documentation/devicetree/bindings/display/bridge/ti,fpdlink.txt b/Documentation/devicetree/bindings/display/bridge/ti,fpdlink.txt
> new file mode 100644
> index 000000000000..2f2e1a269722
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/bridge/ti,fpdlink.txt
> @@ -0,0 +1,25 @@
> +Binding for TI FPD-Link III Bridge driver
> +
> +Wrapper DRM Bridge driver for TI FPD-Link III Serializer and Deserializer
> +
> +Required properties:
> +- compatible: "ti,fpdlink"
> +- fpdlink-serializer-i2c-handle: i2c node for TI FPD-Link III serializer driver
> +- fpdlink-deserializer-i2c-handle: i2c node for TI FPD-Link III deserializer driver
> +
> +
> +Example:
> +--------
> +fpdlink_lvds: fpdlink_bridge@0 {
> +	status = "okay";
> +	compatible = "ti,fpdlink";
> +	fpdlink-serializer-i2c-handle = <&fpdlink_serializer_i2c0>;
> +	fpdlink-deserializer-i2c-handle = <&fpdlink_deserializer_i2c0>;
> +};

This doesn't correspond to hardware, you shouldn't have such a node in
DT. Instead your drivers should operate properly using the two nodes
above, corresponding to the serializer and deserializer.

> +
> +consumer: {
> +...
> +	fpdlink-bridge-handle = <&fpdlink_lvds>;

This is very device-specific and would require the consumer to be aware
of fpdlink. You should instead use OF graph to describe connections.

> +...
> +}
> +

-- 
Regards,

Laurent Pinchart

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

end of thread, other threads:[~2019-03-13 18:49 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-03-13 18:34 [PATCH 1/2] drm/bridge: add initial support for TI FPDLINK bridge drivers Vince Kim
2019-03-13 18:34 ` [PATCH 2/2] dt-bindings: display: bridge: TI FPD-Link III Serializer/Deserializer Vince Kim
2019-03-13 18:49   ` Laurent Pinchart

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).