diff mbox

[v4,2/2] drm/rockchip: Add support for Rockchip Soc RGB output interface

Message ID 1529997340-156879-3-git-send-email-hjc@rock-chips.com (mailing list archive)
State New, archived
Headers show

Commit Message

黄家钗 June 26, 2018, 7:15 a.m. UTC
Some Rockchip CRTCs, like rv1108 and px30, can directly output parallel
and serial RGB data to panel or conversion chip, so we add this driver
to probe encoder and connector.

Signed-off-by: Sandy Huang <hjc@rock-chips.com>
Reviewed-by: Sean Paul <seanpaul@chromium.org>
Reviewed-by: Mark Yao <mark.yao@rock-chips.com>
Link: https://patchwork.freedesktop.org/patch/msgid/1509522851-128181-1-git-send-email-hjc@rock-chips.com
---

Changes in v4:
    1. add support px30;
Changes in v3:
    1. update for rgb-mode move to panel node.
Changes in v2:
    1. add error log when probe failed;
    2. update name_to_output_mode() according to sean's suggest;	
    3. Fix uninitialized use of ret.

 drivers/gpu/drm/rockchip/Kconfig            |  11 +
 drivers/gpu/drm/rockchip/Makefile           |   1 +
 drivers/gpu/drm/rockchip/rockchip_drm_drv.c |   2 +
 drivers/gpu/drm/rockchip/rockchip_drm_drv.h |   1 +
 drivers/gpu/drm/rockchip/rockchip_rgb.c     | 355 ++++++++++++++++++++++++++++
 5 files changed, 370 insertions(+)
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_rgb.c

Comments

kernel test robot June 26, 2018, 11:41 a.m. UTC | #1
Hi Sandy,

I love your patch! Yet something to improve:

[auto build test ERROR on rockchip/for-next]
[also build test ERROR on v4.18-rc2 next-20180626]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Sandy-Huang/Add-support-rockchip-RGB-output-interface/20180626-154044
base:   https://git.kernel.org/pub/scm/linux/kernel/git/mmind/linux-rockchip.git for-next
config: arm-allmodconfig (attached as .config)
compiler: arm-linux-gnueabi-gcc (Debian 7.2.0-11) 7.2.0
reproduce:
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        GCC_VERSION=7.2.0 make.cross ARCH=arm 

All errors (new ones prefixed by >>):

   drivers/gpu//drm/rockchip/rockchip_rgb.c: In function 'rockchip_rgb_encoder_enable':
>> drivers/gpu//drm/rockchip/rockchip_rgb.c:98:36: error: dereferencing pointer to incomplete type 'struct dev_pin_info'
     if (rgb->pins && !IS_ERR(rgb->pins->default_state))
                                       ^~
>> drivers/gpu//drm/rockchip/rockchip_rgb.c:99:3: error: implicit declaration of function 'pinctrl_select_state'; did you mean 'pci_store_saved_state'? [-Werror=implicit-function-declaration]
      pinctrl_select_state(rgb->pins->p, rgb->pins->default_state);
      ^~~~~~~~~~~~~~~~~~~~
      pci_store_saved_state
   drivers/gpu//drm/rockchip/rockchip_rgb.c: In function 'rockchip_rgb_bind':
>> drivers/gpu//drm/rockchip/rockchip_rgb.c:245:12: error: 'struct drm_connector' has no member named 'port'
      connector->port = dev->of_node;
               ^~
   drivers/gpu//drm/rockchip/rockchip_rgb.c: In function 'rockchip_rgb_probe':
>> drivers/gpu//drm/rockchip/rockchip_rgb.c:318:17: error: implicit declaration of function 'devm_pinctrl_get'; did you mean 'drm_connector_get'? [-Werror=implicit-function-declaration]
     rgb->pins->p = devm_pinctrl_get(rgb->dev);
                    ^~~~~~~~~~~~~~~~
                    drm_connector_get
>> drivers/gpu//drm/rockchip/rockchip_rgb.c:325:4: error: implicit declaration of function 'pinctrl_lookup_state'; did you mean 'inc_node_state'? [-Werror=implicit-function-declaration]
       pinctrl_lookup_state(rgb->pins->p, "lcdc");
       ^~~~~~~~~~~~~~~~~~~~
       inc_node_state
   cc1: some warnings being treated as errors

vim +98 drivers/gpu//drm/rockchip/rockchip_rgb.c

    91	
    92	static void rockchip_rgb_encoder_enable(struct drm_encoder *encoder)
    93	{
    94		struct rockchip_rgb *rgb = encoder_to_rgb(encoder);
    95	
    96		drm_panel_prepare(rgb->panel);
    97		/* iomux to LCD data/sync mode */
  > 98		if (rgb->pins && !IS_ERR(rgb->pins->default_state))
  > 99			pinctrl_select_state(rgb->pins->p, rgb->pins->default_state);
   100	
   101		drm_panel_enable(rgb->panel);
   102	}
   103	
   104	static void rockchip_rgb_encoder_disable(struct drm_encoder *encoder)
   105	{
   106		struct rockchip_rgb *rgb = encoder_to_rgb(encoder);
   107	
   108		drm_panel_disable(rgb->panel);
   109		drm_panel_unprepare(rgb->panel);
   110	}
   111	
   112	static int
   113	rockchip_rgb_encoder_atomic_check(struct drm_encoder *encoder,
   114					   struct drm_crtc_state *crtc_state,
   115					   struct drm_connector_state *conn_state)
   116	{
   117		struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
   118		struct rockchip_rgb *rgb = encoder_to_rgb(encoder);
   119	
   120		s->output_mode = rgb->output_mode;
   121		s->output_type = DRM_MODE_CONNECTOR_LVDS;
   122	
   123		return 0;
   124	}
   125	
   126	static const
   127	struct drm_encoder_helper_funcs rockchip_rgb_encoder_helper_funcs = {
   128		.enable = rockchip_rgb_encoder_enable,
   129		.disable = rockchip_rgb_encoder_disable,
   130		.atomic_check = rockchip_rgb_encoder_atomic_check,
   131	};
   132	
   133	static const struct drm_encoder_funcs rockchip_rgb_encoder_funcs = {
   134		.destroy = drm_encoder_cleanup,
   135	};
   136	
   137	static const struct of_device_id rockchip_rgb_dt_ids[] = {
   138		{
   139			.compatible = "rockchip,px30-rgb",
   140		},
   141		{
   142			.compatible = "rockchip,rv1108-rgb",
   143		},
   144		{}
   145	};
   146	
   147	MODULE_DEVICE_TABLE(of, rockchip_rgb_dt_ids);
   148	
   149	static int rockchip_rgb_bind(struct device *dev, struct device *master,
   150				     void *data)
   151	{
   152		struct rockchip_rgb *rgb = dev_get_drvdata(dev);
   153		struct drm_device *drm_dev = data;
   154		struct drm_encoder *encoder;
   155		struct drm_connector *connector;
   156		struct device_node *remote = NULL;
   157		struct device_node  *port, *endpoint;
   158		u32 endpoint_id;
   159		const char *name;
   160		int ret = 0, child_count = 0;
   161	
   162		rgb->drm_dev = drm_dev;
   163		port = of_graph_get_port_by_id(dev->of_node, 1);
   164		if (!port) {
   165			DRM_DEV_ERROR(dev,
   166				      "can't found port point, please init rgb panel port!\n");
   167			return -EINVAL;
   168		}
   169		for_each_child_of_node(port, endpoint) {
   170			child_count++;
   171			if (of_property_read_u32(endpoint, "reg", &endpoint_id))
   172				endpoint_id = 0;
   173			ret = drm_of_find_panel_or_bridge(dev->of_node, 1, endpoint_id,
   174							  &rgb->panel, &rgb->bridge);
   175			if (!ret)
   176				break;
   177		}
   178		if (!child_count) {
   179			DRM_DEV_ERROR(dev, "rgb port does not have any children\n");
   180			ret = -EINVAL;
   181			goto err_put_port;
   182		} else if (ret) {
   183			DRM_DEV_ERROR(dev, "failed to find panel and bridge node\n");
   184			ret = -EPROBE_DEFER;
   185			goto err_put_port;
   186		}
   187		if (rgb->panel)
   188			remote = rgb->panel->dev->of_node;
   189		else
   190			remote = rgb->bridge->of_node;
   191		if (of_property_read_string(remote, "rgb-mode", &name))
   192			/* default set it as output mode P888 */
   193			rgb->output_mode = ROCKCHIP_OUT_MODE_P888;
   194		else
   195			rgb->output_mode = name_to_output_mode(name);
   196		if (rgb->output_mode < 0) {
   197			DRM_DEV_ERROR(dev, "invalid rockchip,rgb-mode [%s]\n", name);
   198			ret = rgb->output_mode;
   199			goto err_put_remote;
   200		}
   201	
   202		encoder = &rgb->encoder;
   203		encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
   204								     dev->of_node);
   205	
   206		ret = drm_encoder_init(drm_dev, encoder, &rockchip_rgb_encoder_funcs,
   207				       DRM_MODE_ENCODER_NONE, NULL);
   208		if (ret < 0) {
   209			DRM_DEV_ERROR(drm_dev->dev,
   210				      "failed to initialize encoder: %d\n", ret);
   211			goto err_put_remote;
   212		}
   213	
   214		drm_encoder_helper_add(encoder, &rockchip_rgb_encoder_helper_funcs);
   215	
   216		if (rgb->panel) {
   217			connector = &rgb->connector;
   218			connector->dpms = DRM_MODE_DPMS_OFF;
   219			ret = drm_connector_init(drm_dev, connector,
   220						 &rockchip_rgb_connector_funcs,
   221						 DRM_MODE_CONNECTOR_Unknown);
   222			if (ret < 0) {
   223				DRM_DEV_ERROR(drm_dev->dev,
   224					      "failed to initialize connector: %d\n",
   225					      ret);
   226				goto err_free_encoder;
   227			}
   228	
   229			drm_connector_helper_add(connector,
   230						 &rockchip_rgb_connector_helper_funcs);
   231	
   232			ret = drm_mode_connector_attach_encoder(connector, encoder);
   233			if (ret < 0) {
   234				DRM_DEV_ERROR(drm_dev->dev,
   235					      "failed to attach encoder: %d\n", ret);
   236				goto err_free_connector;
   237			}
   238	
   239			ret = drm_panel_attach(rgb->panel, connector);
   240			if (ret < 0) {
   241				DRM_DEV_ERROR(drm_dev->dev,
   242					      "failed to attach panel: %d\n", ret);
   243				goto err_free_connector;
   244			}
 > 245			connector->port = dev->of_node;
   246		} else {
   247			rgb->bridge->encoder = encoder;
   248			ret = drm_bridge_attach(encoder, rgb->bridge, NULL);
   249			if (ret) {
   250				DRM_DEV_ERROR(drm_dev->dev,
   251					      "failed to attach bridge: %d\n", ret);
   252				goto err_free_encoder;
   253			}
   254			encoder->bridge = rgb->bridge;
   255		}
   256	
   257		of_node_put(remote);
   258		of_node_put(port);
   259	
   260		return 0;
   261	
   262	err_free_connector:
   263		drm_connector_cleanup(connector);
   264	err_free_encoder:
   265		drm_encoder_cleanup(encoder);
   266	err_put_remote:
   267		of_node_put(remote);
   268	err_put_port:
   269		of_node_put(port);
   270	
   271		return ret;
   272	}
   273	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
diff mbox

Patch

diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index 0ccc762..e88eb71 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -8,6 +8,7 @@  config DRM_ROCKCHIP
 	select DRM_ANALOGIX_DP if ROCKCHIP_ANALOGIX_DP
 	select DRM_DW_HDMI if ROCKCHIP_DW_HDMI
 	select DRM_MIPI_DSI if ROCKCHIP_DW_MIPI_DSI
+	select DRM_RGB if ROCKCHIP_RGB
 	select SND_SOC_HDMI_CODEC if ROCKCHIP_CDN_DP && SND_SOC
 	help
 	  Choose this option if you have a Rockchip soc chipset.
@@ -66,4 +67,14 @@  config ROCKCHIP_LVDS
 	  Rockchip rk3288 SoC has LVDS TX Controller can be used, and it
 	  support LVDS, rgb, dual LVDS output mode. say Y to enable its
 	  driver.
+
+config ROCKCHIP_RGB
+	bool "Rockchip RGB support"
+	depends on DRM_ROCKCHIP
+	depends on PINCTRL
+	help
+	  Choose this option to enable support for Rockchip RGB output.
+	  Some Rockchip CRTCs, like rv1108, can directly output parallel
+	  and serial RGB format to panel or connect to a conversion chip.
+	  say Y to enable its driver.
 endif
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index a314e21..868263f 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -14,5 +14,6 @@  rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
 rockchipdrm-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o
 rockchipdrm-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
 rockchipdrm-$(CONFIG_ROCKCHIP_LVDS) += rockchip_lvds.o
+rockchipdrm-$(CONFIG_ROCKCHIP_RGB) += rockchip_rgb.o
 
 obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
index f814d37..7f81afa 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
@@ -474,6 +474,8 @@  static int __init rockchip_drm_init(void)
 				CONFIG_ROCKCHIP_LVDS);
 	ADD_ROCKCHIP_SUB_DRIVER(rockchip_dp_driver,
 				CONFIG_ROCKCHIP_ANALOGIX_DP);
+	ADD_ROCKCHIP_SUB_DRIVER(rockchip_rgb_driver,
+				CONFIG_ROCKCHIP_RGB);
 	ADD_ROCKCHIP_SUB_DRIVER(cdn_dp_driver, CONFIG_ROCKCHIP_CDN_DP);
 	ADD_ROCKCHIP_SUB_DRIVER(dw_hdmi_rockchip_pltfm_driver,
 				CONFIG_ROCKCHIP_DW_HDMI);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
index 3a6ebfc..929b457 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -71,5 +71,6 @@  extern struct platform_driver dw_mipi_dsi_driver;
 extern struct platform_driver inno_hdmi_driver;
 extern struct platform_driver rockchip_dp_driver;
 extern struct platform_driver rockchip_lvds_driver;
+extern struct platform_driver rockchip_rgb_driver;
 extern struct platform_driver vop_platform_driver;
 #endif /* _ROCKCHIP_DRM_DRV_H_ */
diff --git a/drivers/gpu/drm/rockchip/rockchip_rgb.c b/drivers/gpu/drm/rockchip/rockchip_rgb.c
new file mode 100644
index 0000000..a1345ca
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_rgb.c
@@ -0,0 +1,355 @@ 
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:
+ *      Sandy Huang <hjc@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_dp_helper.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_of.h>
+
+#include <linux/component.h>
+#include <linux/of_graph.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_vop.h"
+
+#define connector_to_rgb(c) container_of(c, struct rockchip_rgb, connector)
+#define encoder_to_rgb(c) container_of(c, struct rockchip_rgb, encoder)
+
+struct rockchip_rgb {
+	struct device *dev;
+	struct drm_device *drm_dev;
+	struct drm_panel *panel;
+	struct drm_bridge *bridge;
+	struct drm_connector connector;
+	struct drm_encoder encoder;
+	struct dev_pin_info *pins;
+	int output_mode;
+};
+
+static inline int name_to_output_mode(const char *s)
+{
+	static const struct {
+		const char *name;
+		int format;
+	} formats[] = {
+		{ "p888", ROCKCHIP_OUT_MODE_P888 },
+		{ "p666", ROCKCHIP_OUT_MODE_P666 },
+		{ "p565", ROCKCHIP_OUT_MODE_P565 }
+	};
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(formats); i++)
+		if (!strncmp(s, formats[i].name, strlen(formats[i].name)))
+			return formats[i].format;
+
+	return -EINVAL;
+}
+
+static const struct drm_connector_funcs rockchip_rgb_connector_funcs = {
+	.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 int rockchip_rgb_connector_get_modes(struct drm_connector *connector)
+{
+	struct rockchip_rgb *rgb = connector_to_rgb(connector);
+	struct drm_panel *panel = rgb->panel;
+
+	return drm_panel_get_modes(panel);
+}
+
+static struct drm_encoder *
+rockchip_rgb_connector_best_encoder(struct drm_connector *connector)
+{
+	struct rockchip_rgb *rgb = connector_to_rgb(connector);
+
+	return &rgb->encoder;
+}
+
+static const
+struct drm_connector_helper_funcs rockchip_rgb_connector_helper_funcs = {
+	.get_modes = rockchip_rgb_connector_get_modes,
+	.best_encoder = rockchip_rgb_connector_best_encoder,
+};
+
+static void rockchip_rgb_encoder_enable(struct drm_encoder *encoder)
+{
+	struct rockchip_rgb *rgb = encoder_to_rgb(encoder);
+
+	drm_panel_prepare(rgb->panel);
+	/* iomux to LCD data/sync mode */
+	if (rgb->pins && !IS_ERR(rgb->pins->default_state))
+		pinctrl_select_state(rgb->pins->p, rgb->pins->default_state);
+
+	drm_panel_enable(rgb->panel);
+}
+
+static void rockchip_rgb_encoder_disable(struct drm_encoder *encoder)
+{
+	struct rockchip_rgb *rgb = encoder_to_rgb(encoder);
+
+	drm_panel_disable(rgb->panel);
+	drm_panel_unprepare(rgb->panel);
+}
+
+static int
+rockchip_rgb_encoder_atomic_check(struct drm_encoder *encoder,
+				   struct drm_crtc_state *crtc_state,
+				   struct drm_connector_state *conn_state)
+{
+	struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
+	struct rockchip_rgb *rgb = encoder_to_rgb(encoder);
+
+	s->output_mode = rgb->output_mode;
+	s->output_type = DRM_MODE_CONNECTOR_LVDS;
+
+	return 0;
+}
+
+static const
+struct drm_encoder_helper_funcs rockchip_rgb_encoder_helper_funcs = {
+	.enable = rockchip_rgb_encoder_enable,
+	.disable = rockchip_rgb_encoder_disable,
+	.atomic_check = rockchip_rgb_encoder_atomic_check,
+};
+
+static const struct drm_encoder_funcs rockchip_rgb_encoder_funcs = {
+	.destroy = drm_encoder_cleanup,
+};
+
+static const struct of_device_id rockchip_rgb_dt_ids[] = {
+	{
+		.compatible = "rockchip,px30-rgb",
+	},
+	{
+		.compatible = "rockchip,rv1108-rgb",
+	},
+	{}
+};
+
+MODULE_DEVICE_TABLE(of, rockchip_rgb_dt_ids);
+
+static int rockchip_rgb_bind(struct device *dev, struct device *master,
+			     void *data)
+{
+	struct rockchip_rgb *rgb = dev_get_drvdata(dev);
+	struct drm_device *drm_dev = data;
+	struct drm_encoder *encoder;
+	struct drm_connector *connector;
+	struct device_node *remote = NULL;
+	struct device_node  *port, *endpoint;
+	u32 endpoint_id;
+	const char *name;
+	int ret = 0, child_count = 0;
+
+	rgb->drm_dev = drm_dev;
+	port = of_graph_get_port_by_id(dev->of_node, 1);
+	if (!port) {
+		DRM_DEV_ERROR(dev,
+			      "can't found port point, please init rgb panel port!\n");
+		return -EINVAL;
+	}
+	for_each_child_of_node(port, endpoint) {
+		child_count++;
+		if (of_property_read_u32(endpoint, "reg", &endpoint_id))
+			endpoint_id = 0;
+		ret = drm_of_find_panel_or_bridge(dev->of_node, 1, endpoint_id,
+						  &rgb->panel, &rgb->bridge);
+		if (!ret)
+			break;
+	}
+	if (!child_count) {
+		DRM_DEV_ERROR(dev, "rgb port does not have any children\n");
+		ret = -EINVAL;
+		goto err_put_port;
+	} else if (ret) {
+		DRM_DEV_ERROR(dev, "failed to find panel and bridge node\n");
+		ret = -EPROBE_DEFER;
+		goto err_put_port;
+	}
+	if (rgb->panel)
+		remote = rgb->panel->dev->of_node;
+	else
+		remote = rgb->bridge->of_node;
+	if (of_property_read_string(remote, "rgb-mode", &name))
+		/* default set it as output mode P888 */
+		rgb->output_mode = ROCKCHIP_OUT_MODE_P888;
+	else
+		rgb->output_mode = name_to_output_mode(name);
+	if (rgb->output_mode < 0) {
+		DRM_DEV_ERROR(dev, "invalid rockchip,rgb-mode [%s]\n", name);
+		ret = rgb->output_mode;
+		goto err_put_remote;
+	}
+
+	encoder = &rgb->encoder;
+	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
+							     dev->of_node);
+
+	ret = drm_encoder_init(drm_dev, encoder, &rockchip_rgb_encoder_funcs,
+			       DRM_MODE_ENCODER_NONE, NULL);
+	if (ret < 0) {
+		DRM_DEV_ERROR(drm_dev->dev,
+			      "failed to initialize encoder: %d\n", ret);
+		goto err_put_remote;
+	}
+
+	drm_encoder_helper_add(encoder, &rockchip_rgb_encoder_helper_funcs);
+
+	if (rgb->panel) {
+		connector = &rgb->connector;
+		connector->dpms = DRM_MODE_DPMS_OFF;
+		ret = drm_connector_init(drm_dev, connector,
+					 &rockchip_rgb_connector_funcs,
+					 DRM_MODE_CONNECTOR_Unknown);
+		if (ret < 0) {
+			DRM_DEV_ERROR(drm_dev->dev,
+				      "failed to initialize connector: %d\n",
+				      ret);
+			goto err_free_encoder;
+		}
+
+		drm_connector_helper_add(connector,
+					 &rockchip_rgb_connector_helper_funcs);
+
+		ret = drm_mode_connector_attach_encoder(connector, encoder);
+		if (ret < 0) {
+			DRM_DEV_ERROR(drm_dev->dev,
+				      "failed to attach encoder: %d\n", ret);
+			goto err_free_connector;
+		}
+
+		ret = drm_panel_attach(rgb->panel, connector);
+		if (ret < 0) {
+			DRM_DEV_ERROR(drm_dev->dev,
+				      "failed to attach panel: %d\n", ret);
+			goto err_free_connector;
+		}
+		connector->port = dev->of_node;
+	} else {
+		rgb->bridge->encoder = encoder;
+		ret = drm_bridge_attach(encoder, rgb->bridge, NULL);
+		if (ret) {
+			DRM_DEV_ERROR(drm_dev->dev,
+				      "failed to attach bridge: %d\n", ret);
+			goto err_free_encoder;
+		}
+		encoder->bridge = rgb->bridge;
+	}
+
+	of_node_put(remote);
+	of_node_put(port);
+
+	return 0;
+
+err_free_connector:
+	drm_connector_cleanup(connector);
+err_free_encoder:
+	drm_encoder_cleanup(encoder);
+err_put_remote:
+	of_node_put(remote);
+err_put_port:
+	of_node_put(port);
+
+	return ret;
+}
+
+static void rockchip_rgb_unbind(struct device *dev, struct device *master,
+				void *data)
+{
+	struct rockchip_rgb *rgb = dev_get_drvdata(dev);
+
+	rockchip_rgb_encoder_disable(&rgb->encoder);
+	if (rgb->panel) {
+		drm_panel_detach(rgb->panel);
+		drm_connector_cleanup(&rgb->connector);
+	}
+	drm_encoder_cleanup(&rgb->encoder);
+}
+
+static const struct component_ops rockchip_rgb_component_ops = {
+	.bind = rockchip_rgb_bind,
+	.unbind = rockchip_rgb_unbind,
+};
+
+static int rockchip_rgb_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct rockchip_rgb *rgb;
+	const struct of_device_id *match;
+	int ret;
+
+	if (!dev->of_node) {
+		DRM_DEV_ERROR(dev, "dev->of_node is null\n");
+		return -ENODEV;
+	}
+
+	rgb = devm_kzalloc(&pdev->dev, sizeof(*rgb), GFP_KERNEL);
+	if (!rgb)
+		return -ENOMEM;
+
+	rgb->dev = dev;
+	match = of_match_node(rockchip_rgb_dt_ids, dev->of_node);
+	if (!match) {
+		DRM_DEV_ERROR(dev, "match node failed\n");
+		return -ENODEV;
+	}
+
+	rgb->pins = devm_kzalloc(rgb->dev, sizeof(*rgb->pins), GFP_KERNEL);
+	if (!rgb->pins)
+		return -ENOMEM;
+	rgb->pins->p = devm_pinctrl_get(rgb->dev);
+	if (IS_ERR(rgb->pins->p)) {
+		DRM_DEV_ERROR(dev, "no pinctrl handle\n");
+		devm_kfree(rgb->dev, rgb->pins);
+		rgb->pins = NULL;
+	} else {
+		rgb->pins->default_state =
+			pinctrl_lookup_state(rgb->pins->p, "lcdc");
+		if (IS_ERR(rgb->pins->default_state)) {
+			DRM_DEV_ERROR(dev, "no default pinctrl state\n");
+			devm_kfree(rgb->dev, rgb->pins);
+			rgb->pins = NULL;
+		}
+	}
+
+	dev_set_drvdata(dev, rgb);
+	ret = component_add(&pdev->dev, &rockchip_rgb_component_ops);
+	if (ret < 0)
+		DRM_DEV_ERROR(dev, "failed to add component\n");
+
+	return ret;
+}
+
+static int rockchip_rgb_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &rockchip_rgb_component_ops);
+
+	return 0;
+}
+
+struct platform_driver rockchip_rgb_driver = {
+	.probe = rockchip_rgb_probe,
+	.remove = rockchip_rgb_remove,
+	.driver = {
+		   .name = "rockchip-rgb",
+		   .of_match_table = of_match_ptr(rockchip_rgb_dt_ids),
+	},
+};