From 9d77db3c730780c8ef5ccd4b66403ff5675cfe4e Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Mon, 13 May 2024 10:30:14 +0000
Subject: [PATCH] modify sin led gpio

---
 kernel/drivers/gpu/drm/rockchip/rockchip_rgb.c |  769 +++++++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 694 insertions(+), 75 deletions(-)

diff --git a/kernel/drivers/gpu/drm/rockchip/rockchip_rgb.c b/kernel/drivers/gpu/drm/rockchip/rockchip_rgb.c
index 9a7b1a7..a7f2057 100644
--- a/kernel/drivers/gpu/drm/rockchip/rockchip_rgb.c
+++ b/kernel/drivers/gpu/drm/rockchip/rockchip_rgb.c
@@ -1,24 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * 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_device.h>
@@ -27,6 +12,17 @@
 #include <linux/mfd/syscon.h>
 #include <linux/phy/phy.h>
 #include <linux/pinctrl/consumer.h>
+#include <linux/gpio/consumer.h>
+
+#include <video/of_display_timing.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_dp_helper.h>
+#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_probe_helper.h>
+
 #include <uapi/linux/videodev2.h>
 
 #include "rockchip_drm_drv.h"
@@ -41,6 +37,11 @@
 #define RK1808_GRF_PD_VO_CON1		0x0444
 #define RK1808_RGB_DATA_SYNC_BYPASS(v)	HIWORD_UPDATE(v, 3, 3)
 
+#define RV1106_VENC_GRF_VOP_IO_WRAPPER	0x1000c
+#define RV1106_IO_BYPASS_SEL(v)		HIWORD_UPDATE(v, 0, 1)
+#define RV1106_VOGRF_VOP_PIPE_BYPASS	0x60034
+#define RV1106_VOP_PIPE_BYPASS(v)	HIWORD_UPDATE(v, 0, 1)
+
 #define RV1126_GRF_IOFUNC_CON3		0x1026c
 #define RV1126_LCDC_IO_BYPASS(v)	HIWORD_UPDATE(v, 0, 0)
 
@@ -53,6 +54,9 @@
 #define RK3288_LVDS_CON_CLKINV(x)	HIWORD_UPDATE(x,  8,  8)
 #define RK3288_LVDS_CON_TTL_EN(x)	HIWORD_UPDATE(x,  6,  6)
 
+#define RK3562_GRF_IOC_VO_IO_CON	0x10500
+#define RK3562_RGB_DATA_BYPASS(v)	HIWORD_UPDATE(v, 6, 6)
+
 #define RK3568_GRF_VO_CON1		0X0364
 #define RK3568_RGB_DATA_BYPASS(v)	HIWORD_UPDATE(v, 6, 6)
 
@@ -63,9 +67,72 @@
 	void (*disable)(struct rockchip_rgb *rgb);
 };
 
+struct rockchip_rgb_data {
+	u32 rgb_max_dclk_rate;
+	u32 mcu_max_dclk_rate;
+	const struct rockchip_rgb_funcs *funcs;
+};
+
+struct mcu_cmd_header {
+	u8 data_type;
+	u8 delay;
+	u8 payload_length;
+} __packed;
+
+struct mcu_cmd_desc {
+	struct mcu_cmd_header header;
+	u8 *payload;
+};
+
+struct mcu_cmd_seq {
+	struct mcu_cmd_desc *cmds;
+	unsigned int cmd_cnt;
+};
+
+struct rockchip_mcu_panel_desc {
+	struct drm_display_mode *mode;
+	struct mcu_cmd_seq *init_seq;
+	struct mcu_cmd_seq *exit_seq;
+
+	struct {
+		unsigned int width;
+		unsigned int height;
+	} size;
+
+	struct {
+		unsigned int prepare;
+		unsigned int enable;
+		unsigned int disable;
+		unsigned int unprepare;
+		unsigned int reset;
+		unsigned int init;
+	} delay;
+
+	unsigned int bpc;
+	u32 bus_format;
+	u32 bus_flags;
+};
+
+struct rockchip_mcu_panel {
+	struct drm_panel base;
+	struct drm_device *drm_dev;
+	struct rockchip_mcu_panel_desc *desc;
+
+	struct gpio_desc *enable_gpio;
+	struct gpio_desc *reset_gpio;
+
+	struct device_node *np_crtc;
+
+	bool prepared;
+	bool enabled;
+};
+
 struct rockchip_rgb {
 	u8 id;
+	u32 max_dclk_rate;
+	u32 mcu_pix_total;
 	struct device *dev;
+	struct device_node *np_mcu_panel;
 	struct drm_panel *panel;
 	struct drm_bridge *bridge;
 	struct drm_connector connector;
@@ -73,6 +140,7 @@
 	struct phy *phy;
 	struct regmap *grf;
 	bool data_sync_bypass;
+	bool phy_enabled;
 	const struct rockchip_rgb_funcs *funcs;
 	struct rockchip_drm_sub_dev sub_dev;
 };
@@ -85,6 +153,11 @@
 static inline struct rockchip_rgb *encoder_to_rgb(struct drm_encoder *e)
 {
 	return container_of(e, struct rockchip_rgb, encoder);
+}
+
+static inline struct rockchip_mcu_panel *to_rockchip_mcu_panel(struct drm_panel *panel)
+{
+	return container_of(panel, struct rockchip_mcu_panel, base);
 }
 
 static enum drm_connector_status
@@ -112,7 +185,6 @@
 }
 
 static const struct drm_connector_funcs rockchip_rgb_connector_funcs = {
-	.dpms = drm_helper_connector_dpms,
 	.detect = rockchip_rgb_connector_detect,
 	.fill_modes = drm_helper_probe_single_connector_modes,
 	.destroy = drm_connector_cleanup,
@@ -127,7 +199,7 @@
 	struct rockchip_rgb *rgb = connector_to_rgb(connector);
 	struct drm_panel *panel = rgb->panel;
 
-	return drm_panel_get_modes(panel);
+	return drm_panel_get_modes(panel, connector);
 }
 
 static struct drm_encoder *
@@ -147,21 +219,15 @@
 static void rockchip_rgb_encoder_enable(struct drm_encoder *encoder)
 {
 	struct rockchip_rgb *rgb = encoder_to_rgb(encoder);
-	int ret;
 
 	pinctrl_pm_select_default_state(rgb->dev);
 
 	if (rgb->funcs && rgb->funcs->enable)
 		rgb->funcs->enable(rgb);
 
-	if (rgb->phy) {
-		ret = phy_set_mode(rgb->phy, PHY_MODE_VIDEO_TTL);
-		if (ret) {
-			dev_err(rgb->dev, "failed to set phy mode: %d\n", ret);
-			return;
-		}
-
+	if (rgb->phy && !rgb->phy_enabled) {
 		phy_power_on(rgb->phy);
+		rgb->phy_enabled = true;
 	}
 
 	if (rgb->panel) {
@@ -173,28 +239,21 @@
 static void rockchip_rgb_encoder_disable(struct drm_encoder *encoder)
 {
 	struct rockchip_rgb *rgb = encoder_to_rgb(encoder);
-	struct drm_crtc *crtc = encoder->crtc;
-	struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc->state);
 
 	if (rgb->panel) {
 		drm_panel_disable(rgb->panel);
 		drm_panel_unprepare(rgb->panel);
 	}
 
-	if (rgb->phy)
+	if (rgb->phy && rgb->phy_enabled) {
 		phy_power_off(rgb->phy);
+		rgb->phy_enabled = false;
+	}
 
 	if (rgb->funcs && rgb->funcs->disable)
 		rgb->funcs->disable(rgb);
 
 	pinctrl_pm_select_sleep_state(rgb->dev);
-
-	if (s->output_if & VOP_OUTPUT_IF_RGB)
-		s->output_if &= ~VOP_OUTPUT_IF_RGB;
-	else if (s->output_if & VOP_OUTPUT_IF_BT656)
-		s->output_if &= ~VOP_OUTPUT_IF_BT656;
-	else if (s->output_if & VOP_OUTPUT_IF_BT1120)
-		s->output_if &= ~VOP_OUTPUT_IF_BT1120;
 }
 
 static int
@@ -214,46 +273,57 @@
 	switch (s->bus_format) {
 	case MEDIA_BUS_FMT_RGB666_1X18:
 		s->output_mode = ROCKCHIP_OUT_MODE_P666;
-		s->output_if |= VOP_OUTPUT_IF_RGB;
+		s->output_if = VOP_OUTPUT_IF_RGB;
 		break;
 	case MEDIA_BUS_FMT_RGB565_1X16:
 		s->output_mode = ROCKCHIP_OUT_MODE_P565;
-		s->output_if |= VOP_OUTPUT_IF_RGB;
+		s->output_if = VOP_OUTPUT_IF_RGB;
 		break;
-	case MEDIA_BUS_FMT_SRGB888_3X8:
+	case MEDIA_BUS_FMT_RGB565_2X8_LE:
+	case MEDIA_BUS_FMT_BGR565_2X8_LE:
+		s->output_mode = ROCKCHIP_OUT_MODE_S565;
+		s->output_if = VOP_OUTPUT_IF_RGB;
+		break;
+	case MEDIA_BUS_FMT_RGB666_3X6:
+		s->output_mode = ROCKCHIP_OUT_MODE_S666;
+		s->output_if = VOP_OUTPUT_IF_RGB;
+		break;
+	case MEDIA_BUS_FMT_RGB888_3X8:
+	case MEDIA_BUS_FMT_BGR888_3X8:
 		s->output_mode = ROCKCHIP_OUT_MODE_S888;
-		s->output_if |= VOP_OUTPUT_IF_RGB;
+		s->output_if = VOP_OUTPUT_IF_RGB;
 		break;
-	case MEDIA_BUS_FMT_SRGB888_DUMMY_4X8:
+	case MEDIA_BUS_FMT_RGB888_DUMMY_4X8:
+	case MEDIA_BUS_FMT_BGR888_DUMMY_4X8:
 		s->output_mode = ROCKCHIP_OUT_MODE_S888_DUMMY;
-		s->output_if |= VOP_OUTPUT_IF_RGB;
+		s->output_if = VOP_OUTPUT_IF_RGB;
 		break;
 	case MEDIA_BUS_FMT_YUYV8_2X8:
 	case MEDIA_BUS_FMT_YVYU8_2X8:
 	case MEDIA_BUS_FMT_UYVY8_2X8:
 	case MEDIA_BUS_FMT_VYUY8_2X8:
 		s->output_mode = ROCKCHIP_OUT_MODE_BT656;
-		s->output_if |= VOP_OUTPUT_IF_BT656;
+		s->output_if = VOP_OUTPUT_IF_BT656;
 		break;
 	case MEDIA_BUS_FMT_YUYV8_1X16:
 	case MEDIA_BUS_FMT_YVYU8_1X16:
 	case MEDIA_BUS_FMT_UYVY8_1X16:
 	case MEDIA_BUS_FMT_VYUY8_1X16:
 		s->output_mode = ROCKCHIP_OUT_MODE_BT1120;
-		s->output_if |= VOP_OUTPUT_IF_BT1120;
+		s->output_if = VOP_OUTPUT_IF_BT1120;
 		break;
 	case MEDIA_BUS_FMT_RGB888_1X24:
 	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
 	default:
 		s->output_mode = ROCKCHIP_OUT_MODE_P888;
-		s->output_if |= VOP_OUTPUT_IF_RGB;
+		s->output_if = VOP_OUTPUT_IF_RGB;
 		break;
 	}
 
 	s->output_type = DRM_MODE_CONNECTOR_DPI;
 	s->bus_flags = info->bus_flags;
 	s->tv_state = &conn_state->tv;
-	s->eotf = TRADITIONAL_GAMMA_SDR;
+	s->eotf = HDMI_EOTF_TRADITIONAL_GAMMA_SDR;
 	s->color_space = V4L2_COLORSPACE_DEFAULT;
 
 	return 0;
@@ -264,10 +334,65 @@
 {
 	struct rockchip_rgb *rgb = encoder_to_rgb(encoder);
 
+	if (rgb->np_mcu_panel) {
+		struct rockchip_mcu_panel *mcu_panel = to_rockchip_mcu_panel(rgb->panel);
+
+		mcu_panel->prepared = true;
+		mcu_panel->enabled = true;
+
+		return 0;
+	}
+
 	if (rgb->panel)
-		drm_panel_loader_protect(rgb->panel, on);
+		panel_simple_loader_protect(rgb->panel);
+
+	if (on) {
+		phy_init(rgb->phy);
+		if (rgb->phy) {
+			rgb->phy->power_count++;
+			rgb->phy_enabled = true;
+		}
+	} else {
+		phy_exit(rgb->phy);
+		if (rgb->phy) {
+			rgb->phy->power_count--;
+			rgb->phy_enabled = false;
+		}
+	}
 
 	return 0;
+}
+
+static enum drm_mode_status
+rockchip_rgb_encoder_mode_valid(struct drm_encoder *encoder,
+				 const struct drm_display_mode *mode)
+{
+	struct rockchip_rgb *rgb = encoder_to_rgb(encoder);
+	struct device *dev = rgb->dev;
+	struct drm_display_info *info = &rgb->connector.display_info;
+	u32 request_clock = mode->clock;
+	u32 max_clock = rgb->max_dclk_rate;
+	u32 bus_format;
+
+	if (info->num_bus_formats)
+		bus_format = info->bus_formats[0];
+	else
+		bus_format = MEDIA_BUS_FMT_RGB888_1X24;
+
+	if (mode->flags & DRM_MODE_FLAG_DBLCLK)
+		request_clock *= 2;
+
+	if (rgb->np_mcu_panel)
+		request_clock *= rockchip_drm_get_cycles_per_pixel(bus_format) *
+				 (rgb->mcu_pix_total + 1);
+
+	if (max_clock != 0 && request_clock > max_clock) {
+		DRM_DEV_ERROR(dev, "mode [%dx%d] clock %d is higher than max_clock %d\n",
+			      mode->hdisplay, mode->vdisplay, request_clock, max_clock);
+		return MODE_CLOCK_HIGH;
+	}
+
+	return MODE_OK;
 }
 
 static const
@@ -275,12 +400,418 @@
 	.enable = rockchip_rgb_encoder_enable,
 	.disable = rockchip_rgb_encoder_disable,
 	.atomic_check = rockchip_rgb_encoder_atomic_check,
-	.loader_protect = rockchip_rgb_encoder_loader_protect,
+	.mode_valid = rockchip_rgb_encoder_mode_valid,
 };
 
 static const struct drm_encoder_funcs rockchip_rgb_encoder_funcs = {
 	.destroy = drm_encoder_cleanup,
 };
+
+static int rockchip_mcu_panel_parse_cmd_seq(struct device *dev,
+					    const u8 *data, int length,
+					    struct mcu_cmd_seq *seq)
+{
+	struct mcu_cmd_header *header;
+	struct mcu_cmd_desc *desc;
+	char *buf, *d;
+	unsigned int i, cnt, len;
+
+	if (!seq)
+		return -EINVAL;
+
+	buf = devm_kmemdup(dev, data, length, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	d = buf;
+	len = length;
+	cnt = 0;
+	while (len > sizeof(*header)) {
+		header = (struct mcu_cmd_header *)d;
+
+		d += sizeof(*header);
+		len -= sizeof(*header);
+
+		if (header->payload_length > len)
+			return -EINVAL;
+
+		d += header->payload_length;
+		len -= header->payload_length;
+		cnt++;
+	}
+
+	if (len)
+		return -EINVAL;
+
+	seq->cmd_cnt = cnt;
+	seq->cmds = devm_kcalloc(dev, cnt, sizeof(*desc), GFP_KERNEL);
+	if (!seq->cmds)
+		return -ENOMEM;
+
+	d = buf;
+	len = length;
+	for (i = 0; i < cnt; i++) {
+		header = (struct mcu_cmd_header *)d;
+		len -= sizeof(*header);
+		d += sizeof(*header);
+
+		desc = &seq->cmds[i];
+		desc->header = *header;
+		desc->payload = d;
+
+		d += header->payload_length;
+		len -= header->payload_length;
+	}
+
+	return 0;
+}
+
+static int rockchip_mcu_panel_init(struct rockchip_rgb *rgb)
+{
+	struct device *dev = rgb->dev;
+	struct device_node *np_mcu_panel = rgb->np_mcu_panel;
+	struct device_node *port, *endpoint, *np_crtc, *remote, *np_mcu_timing;
+	struct rockchip_mcu_panel *mcu_panel = to_rockchip_mcu_panel(rgb->panel);
+	struct drm_display_mode *mode;
+	const void *data;
+	int len;
+	int ret;
+	u32 bus_flags;
+	u32 val;
+
+	mcu_panel->enable_gpio = devm_fwnode_gpiod_get_index(dev, &np_mcu_panel->fwnode,
+							     "enable", 0, GPIOD_ASIS,
+							     fwnode_get_name(&np_mcu_panel->fwnode));
+	if (IS_ERR(mcu_panel->enable_gpio)) {
+		DRM_DEV_ERROR(dev, "failed to find mcu panel enable GPIO\n");
+		return PTR_ERR(mcu_panel->enable_gpio);
+	}
+
+	mcu_panel->reset_gpio = devm_fwnode_gpiod_get_index(dev, &np_mcu_panel->fwnode,
+							    "reset", 0, GPIOD_ASIS,
+							    fwnode_get_name(&np_mcu_panel->fwnode));
+	if (IS_ERR(mcu_panel->reset_gpio)) {
+		DRM_DEV_ERROR(dev, "failed to find mcu panel reset GPIO\n");
+		return PTR_ERR(mcu_panel->reset_gpio);
+	}
+
+	mcu_panel->desc = devm_kzalloc(dev, sizeof(*mcu_panel->desc), GFP_KERNEL);
+	if (!mcu_panel->desc)
+		return -ENOMEM;
+
+	mode = devm_kzalloc(dev, sizeof(*mode), GFP_KERNEL);
+	if (!mode)
+		return -ENOMEM;
+
+	if (!of_get_drm_display_mode(np_mcu_panel, mode, &bus_flags,
+				     OF_USE_NATIVE_MODE)) {
+		mcu_panel->desc->mode = mode;
+		mcu_panel->desc->bus_flags = bus_flags;
+	} else {
+		DRM_DEV_ERROR(dev, "failed to parse display mode\n");
+		return -EINVAL;
+	}
+
+	of_property_read_u32(np_mcu_panel, "bpc", &mcu_panel->desc->bpc);
+	of_property_read_u32(np_mcu_panel, "bus-format", &mcu_panel->desc->bus_format);
+	of_property_read_u32(np_mcu_panel, "width-mm", &mcu_panel->desc->size.width);
+	of_property_read_u32(np_mcu_panel, "height-mm", &mcu_panel->desc->size.height);
+
+	of_property_read_u32(np_mcu_panel, "prepare-delay-ms", &mcu_panel->desc->delay.prepare);
+	of_property_read_u32(np_mcu_panel, "enable-delay-ms", &mcu_panel->desc->delay.enable);
+	of_property_read_u32(np_mcu_panel, "disable-delay-ms", &mcu_panel->desc->delay.disable);
+	of_property_read_u32(np_mcu_panel, "unprepare-delay-ms",
+			     &mcu_panel->desc->delay.unprepare);
+	of_property_read_u32(np_mcu_panel, "reset-delay-ms", &mcu_panel->desc->delay.reset);
+	of_property_read_u32(np_mcu_panel, "init-delay-ms", &mcu_panel->desc->delay.init);
+
+	data = of_get_property(np_mcu_panel, "panel-init-sequence", &len);
+	if (data) {
+		mcu_panel->desc->init_seq = devm_kzalloc(dev, sizeof(*mcu_panel->desc->init_seq),
+							 GFP_KERNEL);
+		if (!mcu_panel->desc->init_seq)
+			return -ENOMEM;
+
+		ret = rockchip_mcu_panel_parse_cmd_seq(dev, data, len,
+						       mcu_panel->desc->init_seq);
+		if (ret < 0) {
+			DRM_DEV_ERROR(dev, "failed to parse init sequence\n");
+			return ret;
+		}
+	}
+
+	data = of_get_property(np_mcu_panel, "panel-exit-sequence", &len);
+	if (data) {
+		mcu_panel->desc->exit_seq = devm_kzalloc(dev, sizeof(*mcu_panel->desc->exit_seq),
+					      GFP_KERNEL);
+		if (!mcu_panel->desc->exit_seq)
+			return -ENOMEM;
+
+		ret = rockchip_mcu_panel_parse_cmd_seq(dev, data, len,
+						       mcu_panel->desc->exit_seq);
+		if (ret < 0) {
+			DRM_DEV_ERROR(dev, "failed to parse exit sequence\n");
+			return ret;
+		}
+	}
+
+	/*
+	 * Support to find crtc device for both vop and vop3:
+	 * vopl/vopb       -> rgb
+	 * vop2/vop3 -> vp -> rgb
+	 */
+	port = of_graph_get_port_by_id(dev->of_node, 0);
+	if (port) {
+		for_each_child_of_node(port, endpoint) {
+			if (of_device_is_available(endpoint)) {
+				remote = of_graph_get_remote_endpoint(endpoint);
+				if (remote) {
+					np_crtc = of_get_next_parent(remote);
+					mcu_panel->np_crtc = np_crtc;
+
+					of_node_put(np_crtc);
+					break;
+				}
+			}
+		}
+
+		if (!mcu_panel->np_crtc) {
+			DRM_DEV_ERROR(dev, "failed to find available crtc for mcu panel\n");
+			return -EINVAL;
+		}
+
+		np_mcu_timing = of_get_child_by_name(mcu_panel->np_crtc, "mcu-timing");
+		if (!np_mcu_timing) {
+			np_crtc = of_get_parent(mcu_panel->np_crtc);
+			if (np_crtc)
+				np_mcu_timing = of_get_child_by_name(np_crtc, "mcu-timing");
+
+			if (!np_mcu_timing) {
+				DRM_DEV_ERROR(dev, "failed to find timing config for mcu panel\n");
+				of_node_put(np_crtc);
+				return -EINVAL;
+			}
+
+			of_node_put(np_crtc);
+		}
+
+		ret = of_property_read_u32(np_mcu_timing, "mcu-pix-total", &val);
+		if (ret || val == 0) {
+			DRM_DEV_ERROR(dev, "failed to parse mcu_pix_total config\n");
+			of_node_put(np_mcu_timing);
+			return -EINVAL;
+		}
+		rgb->mcu_pix_total = val;
+
+		of_node_put(np_mcu_timing);
+	}
+
+	return 0;
+}
+
+static void rockchip_mcu_panel_sleep(unsigned int msec)
+{
+	if (msec > 20)
+		msleep(msec);
+	else
+		usleep_range(msec * 1000, (msec + 1) * 1000);
+}
+
+static int rockchip_mcu_panel_xfer_mcu_cmd_seq(struct rockchip_mcu_panel *mcu_panel,
+					       struct mcu_cmd_seq *cmds)
+{
+	struct drm_device *drm_dev = mcu_panel->drm_dev;
+	struct drm_panel *panel = &mcu_panel->base;
+	struct device_node *np_crtc = mcu_panel->np_crtc;
+	struct drm_crtc *crtc;
+	struct mcu_cmd_desc *cmd;
+	struct rockchip_drm_private *priv;
+	int i;
+	int pipe = 0;
+	u32 value;
+
+	if (!cmds)
+		return -EINVAL;
+
+	drm_for_each_crtc(crtc, drm_dev) {
+		if (crtc->port == np_crtc)
+			break;
+	}
+
+	pipe = drm_crtc_index(crtc);
+	priv = crtc->dev->dev_private;
+	if (!priv->crtc_funcs[pipe]->crtc_send_mcu_cmd) {
+		DRM_DEV_ERROR(panel->dev, "crtc not supported to send mcu cmds\n");
+		return -EINVAL;
+	}
+
+	priv->crtc_funcs[pipe]->crtc_send_mcu_cmd(crtc, MCU_SETBYPASS, 1);
+	for (i = 0; i < cmds->cmd_cnt; i++) {
+		cmd = &cmds->cmds[i];
+		value = cmd->payload[0];
+		priv->crtc_funcs[pipe]->crtc_send_mcu_cmd(crtc, cmd->header.data_type, value);
+		if (cmd->header.delay)
+			rockchip_mcu_panel_sleep(cmd->header.delay);
+	}
+	priv->crtc_funcs[pipe]->crtc_send_mcu_cmd(crtc, MCU_SETBYPASS, 0);
+
+	return 0;
+}
+
+static int rockchip_mcu_panel_disable(struct drm_panel *panel)
+{
+	struct rockchip_mcu_panel *mcu_panel = to_rockchip_mcu_panel(panel);
+	int ret = 0;
+
+	if (!mcu_panel->enabled)
+		return 0;
+
+	if (mcu_panel->desc->delay.disable)
+		msleep(mcu_panel->desc->delay.disable);
+
+	ret = rockchip_mcu_panel_xfer_mcu_cmd_seq(mcu_panel, mcu_panel->desc->exit_seq);
+	if (ret)
+		DRM_DEV_ERROR(panel->dev, "failed to send exit cmds seq\n");
+
+	mcu_panel->enabled = false;
+
+	return 0;
+}
+
+static int rockchip_mcu_panel_unprepare(struct drm_panel *panel)
+{
+	struct rockchip_mcu_panel *mcu_panel = to_rockchip_mcu_panel(panel);
+
+	if (!mcu_panel->prepared)
+		return 0;
+
+	gpiod_direction_output(mcu_panel->reset_gpio, 1);
+	gpiod_direction_output(mcu_panel->enable_gpio, 0);
+
+	if (mcu_panel->desc->delay.unprepare)
+		msleep(mcu_panel->desc->delay.unprepare);
+
+	mcu_panel->prepared = false;
+
+	return 0;
+}
+
+static int rockchip_mcu_panel_prepare(struct drm_panel *panel)
+{
+	struct rockchip_mcu_panel *mcu_panel = to_rockchip_mcu_panel(panel);
+	unsigned int delay;
+
+	if (mcu_panel->prepared)
+		return 0;
+
+	gpiod_direction_output(mcu_panel->enable_gpio, 1);
+
+	delay = mcu_panel->desc->delay.prepare;
+	if (delay)
+		msleep(delay);
+
+	gpiod_direction_output(mcu_panel->reset_gpio, 1);
+
+	if (mcu_panel->desc->delay.reset)
+		msleep(mcu_panel->desc->delay.reset);
+
+	gpiod_direction_output(mcu_panel->reset_gpio, 0);
+
+	if (mcu_panel->desc->delay.init)
+		msleep(mcu_panel->desc->delay.init);
+
+	mcu_panel->prepared = true;
+
+	return 0;
+}
+
+static int rockchip_mcu_panel_enable(struct drm_panel *panel)
+{
+	struct rockchip_mcu_panel *mcu_panel = to_rockchip_mcu_panel(panel);
+	int ret = 0;
+
+	if (mcu_panel->enabled)
+		return 0;
+
+	ret = rockchip_mcu_panel_xfer_mcu_cmd_seq(mcu_panel, mcu_panel->desc->init_seq);
+	if (ret)
+		DRM_DEV_ERROR(panel->dev, "failed to send init cmds seq\n");
+
+	if (mcu_panel->desc->delay.enable)
+		msleep(mcu_panel->desc->delay.enable);
+
+	mcu_panel->enabled = true;
+
+	return 0;
+}
+
+static int rockchip_mcu_panel_get_modes(struct drm_panel *panel,
+					struct drm_connector *connector)
+{
+	struct rockchip_mcu_panel *mcu_panel = to_rockchip_mcu_panel(panel);
+	struct drm_display_mode *m, *mode;
+
+	if (!mcu_panel->desc)
+		return 0;
+
+	m = mcu_panel->desc->mode;
+	mode = drm_mode_duplicate(connector->dev, m);
+	if (!mode) {
+		DRM_DEV_ERROR(mcu_panel->base.dev, "failed to add mode %ux%u@%u\n",
+			      m->hdisplay, m->vdisplay,
+			      drm_mode_vrefresh(m));
+		return 0;
+	}
+
+	mode->type |= DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+
+	drm_mode_set_name(mode);
+
+	drm_mode_probed_add(connector, mode);
+
+	if (mcu_panel->desc->bpc)
+		connector->display_info.bpc = mcu_panel->desc->bpc;
+	if (mcu_panel->desc->size.width)
+		connector->display_info.width_mm = mcu_panel->desc->size.width;
+	if (mcu_panel->desc->size.height)
+		connector->display_info.height_mm = mcu_panel->desc->size.height;
+	if (mcu_panel->desc->bus_format)
+		drm_display_info_set_bus_formats(&connector->display_info,
+						 &mcu_panel->desc->bus_format, 1);
+	if (mcu_panel->desc->bus_flags)
+		connector->display_info.bus_flags = mcu_panel->desc->bus_flags;
+
+	return 1;
+}
+
+static const struct drm_panel_funcs rockchip_mcu_panel_funcs = {
+	.disable = rockchip_mcu_panel_disable,
+	.unprepare = rockchip_mcu_panel_unprepare,
+	.prepare = rockchip_mcu_panel_prepare,
+	.enable = rockchip_mcu_panel_enable,
+	.get_modes = rockchip_mcu_panel_get_modes,
+};
+
+static struct backlight_device *rockchip_mcu_panel_find_backlight(struct rockchip_rgb *rgb)
+{
+	struct backlight_device *bd = NULL;
+	struct device_node *np_mcu_panel = rgb->np_mcu_panel;
+	struct device_node *np = NULL;
+
+	np = of_parse_phandle(np_mcu_panel, "backlight", 0);
+	if (np) {
+		bd = of_find_backlight_by_node(np);
+		if (IS_ERR_OR_NULL(bd))
+			return NULL;
+
+		of_node_put(np);
+
+		if (!bd->props.brightness)
+			bd->props.brightness = bd->props.max_brightness;
+	}
+
+	return bd;
+}
 
 static int rockchip_rgb_bind(struct device *dev, struct device *master,
 			     void *data)
@@ -291,15 +822,44 @@
 	struct drm_connector *connector;
 	int ret;
 
-	ret = drm_of_find_panel_or_bridge(dev->of_node, 1, -1,
-					  &rgb->panel, &rgb->bridge);
-	if (ret) {
-		DRM_DEV_ERROR(dev, "failed to find panel or bridge: %d\n", ret);
-		return ret;
+	if (rgb->np_mcu_panel) {
+		struct rockchip_mcu_panel *mcu_panel;
+
+		mcu_panel = devm_kzalloc(dev, sizeof(*mcu_panel), GFP_KERNEL);
+		if (!mcu_panel) {
+			return -ENOMEM;
+		}
+		mcu_panel->drm_dev = drm_dev;
+
+		rgb->panel = &mcu_panel->base;
+
+		ret = rockchip_mcu_panel_init(rgb);
+		if (ret < 0) {
+			DRM_DEV_ERROR(dev, "failed to init mcu panel: %d\n", ret);
+			return ret;
+		}
+
+		rgb->panel->backlight = rockchip_mcu_panel_find_backlight(rgb);
+		if (!rgb->panel->backlight) {
+			DRM_DEV_ERROR(dev, "failed to find backlight device");
+			return -EINVAL;
+		}
+
+		drm_panel_init(&mcu_panel->base, dev, &rockchip_mcu_panel_funcs,
+			       DRM_MODE_CONNECTOR_DPI);
+
+		drm_panel_add(&mcu_panel->base);
+	} else {
+		ret = drm_of_find_panel_or_bridge(dev->of_node, 1, -1,
+						  &rgb->panel, &rgb->bridge);
+		if (ret) {
+			DRM_DEV_ERROR(dev, "failed to find panel or bridge: %d\n", ret);
+			return ret;
+		}
 	}
 
-	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
-							     dev->of_node);
+	encoder->possible_crtcs = rockchip_drm_of_find_possible_crtcs(drm_dev,
+								      dev->of_node);
 
 	ret = drm_encoder_init(drm_dev, encoder, &rockchip_rgb_encoder_funcs,
 			       DRM_MODE_ENCODER_DPI, NULL);
@@ -334,25 +894,19 @@
 				      "failed to attach encoder: %d\n", ret);
 			goto err_free_connector;
 		}
-
-		ret = drm_panel_attach(rgb->panel, connector);
-		if (ret < 0) {
-			DRM_DEV_ERROR(dev, "failed to attach panel: %d\n", ret);
-			goto err_free_connector;
-		}
 		rgb->sub_dev.connector = &rgb->connector;
 		rgb->sub_dev.of_node = rgb->dev->of_node;
+		rgb->sub_dev.loader_protect = rockchip_rgb_encoder_loader_protect;
 		drm_object_attach_property(&connector->base, private->connector_id_prop, 0);
 		rockchip_drm_register_sub_dev(&rgb->sub_dev);
 	} else {
 		rgb->bridge->encoder = encoder;
-		ret = drm_bridge_attach(encoder, rgb->bridge, NULL);
+		ret = drm_bridge_attach(encoder, rgb->bridge, NULL, 0);
 		if (ret) {
 			DRM_DEV_ERROR(dev,
 				      "failed to attach bridge: %d\n", ret);
 			goto err_free_encoder;
 		}
-		encoder->bridge = rgb->bridge;
 	}
 
 	return 0;
@@ -371,10 +925,8 @@
 
 	if (rgb->sub_dev.connector)
 		rockchip_drm_unregister_sub_dev(&rgb->sub_dev);
-	if (rgb->panel) {
-		drm_panel_detach(rgb->panel);
+	if (rgb->panel)
 		drm_connector_cleanup(&rgb->connector);
-	}
 
 	drm_encoder_cleanup(&rgb->encoder);
 }
@@ -388,6 +940,8 @@
 {
 	struct device *dev = &pdev->dev;
 	struct rockchip_rgb *rgb;
+	const struct rockchip_rgb_data *rgb_data;
+	struct fwnode_handle *fwnode_mcu_panel;
 	int ret, id;
 
 	rgb = devm_kzalloc(&pdev->dev, sizeof(*rgb), GFP_KERNEL);
@@ -398,13 +952,23 @@
 	if (id < 0)
 		id = 0;
 
+	rgb->data_sync_bypass = of_property_read_bool(dev->of_node, "rockchip,data-sync-bypass");
+
+	fwnode_mcu_panel = device_get_named_child_node(dev, "mcu-panel");
+	if (fwnode_mcu_panel)
+		rgb->np_mcu_panel = to_of_node(fwnode_mcu_panel);
+
+	rgb_data = of_device_get_match_data(dev);
+	if (rgb_data) {
+		rgb->funcs = rgb_data->funcs;
+		if (rgb->np_mcu_panel)
+			rgb->max_dclk_rate = rgb_data->mcu_max_dclk_rate;
+		else
+			rgb->max_dclk_rate = rgb_data->rgb_max_dclk_rate;
+	}
 	rgb->id = id;
 	rgb->dev = dev;
-	rgb->funcs = of_device_get_match_data(dev);
 	platform_set_drvdata(pdev, rgb);
-
-	rgb->data_sync_bypass =
-	    of_property_read_bool(dev->of_node, "rockchip,data-sync-bypass");
 
 	if (dev->parent && dev->parent->of_node) {
 		rgb->grf = syscon_node_to_regmap(dev->parent->of_node);
@@ -445,6 +1009,10 @@
 	.enable = px30_rgb_enable,
 };
 
+static const struct rockchip_rgb_data px30_rgb = {
+	.funcs = &px30_rgb_funcs,
+};
+
 static void rk1808_rgb_enable(struct rockchip_rgb *rgb)
 {
 	regmap_write(rgb->grf, RK1808_GRF_PD_VO_CON1,
@@ -453,6 +1021,10 @@
 
 static const struct rockchip_rgb_funcs rk1808_rgb_funcs = {
 	.enable = rk1808_rgb_enable,
+};
+
+static const struct rockchip_rgb_data rk1808_rgb = {
+	.funcs = &rk1808_rgb_funcs,
 };
 
 static void rk3288_rgb_enable(struct rockchip_rgb *rgb)
@@ -479,6 +1051,24 @@
 	.disable = rk3288_rgb_disable,
 };
 
+static const struct rockchip_rgb_data rk3288_rgb = {
+	.funcs = &rk3288_rgb_funcs,
+};
+
+static void rk3562_rgb_enable(struct rockchip_rgb *rgb)
+{
+	regmap_write(rgb->grf, RK3562_GRF_IOC_VO_IO_CON,
+		     RK3562_RGB_DATA_BYPASS(rgb->data_sync_bypass));
+}
+
+static const struct rockchip_rgb_funcs rk3562_rgb_funcs = {
+	.enable = rk3562_rgb_enable,
+};
+
+static const struct rockchip_rgb_data rk3562_rgb = {
+	.funcs = &rk3562_rgb_funcs,
+};
+
 static void rk3568_rgb_enable(struct rockchip_rgb *rgb)
 {
 	regmap_write(rgb->grf, RK3568_GRF_VO_CON1,
@@ -487,6 +1077,10 @@
 
 static const struct rockchip_rgb_funcs rk3568_rgb_funcs = {
 	.enable = rk3568_rgb_enable,
+};
+
+static const struct rockchip_rgb_data rk3568_rgb = {
+	.funcs = &rk3568_rgb_funcs,
 };
 
 static void rv1126_rgb_enable(struct rockchip_rgb *rgb)
@@ -499,17 +1093,42 @@
 	.enable = rv1126_rgb_enable,
 };
 
+static const struct rockchip_rgb_data rv1126_rgb = {
+	.funcs = &rv1126_rgb_funcs,
+};
+
+static void rv1106_rgb_enable(struct rockchip_rgb *rgb)
+{
+	regmap_write(rgb->grf, RV1106_VENC_GRF_VOP_IO_WRAPPER,
+		     RV1106_IO_BYPASS_SEL(rgb->data_sync_bypass ? 0x3 : 0x0));
+	regmap_write(rgb->grf, RV1106_VOGRF_VOP_PIPE_BYPASS,
+		     RV1106_VOP_PIPE_BYPASS(rgb->data_sync_bypass ? 0x3 : 0x0));
+}
+
+static const struct rockchip_rgb_funcs rv1106_rgb_funcs = {
+	.enable = rv1106_rgb_enable,
+};
+
+static const struct rockchip_rgb_data rv1106_rgb = {
+	.rgb_max_dclk_rate = 74250,
+	.mcu_max_dclk_rate = 150000,
+	.funcs = &rv1106_rgb_funcs,
+};
+
 static const struct of_device_id rockchip_rgb_dt_ids[] = {
-	{ .compatible = "rockchip,px30-rgb", .data = &px30_rgb_funcs },
-	{ .compatible = "rockchip,rk1808-rgb", .data = &rk1808_rgb_funcs },
+	{ .compatible = "rockchip,px30-rgb", .data = &px30_rgb },
+	{ .compatible = "rockchip,rk1808-rgb", .data = &rk1808_rgb },
 	{ .compatible = "rockchip,rk3066-rgb", },
 	{ .compatible = "rockchip,rk3128-rgb", },
-	{ .compatible = "rockchip,rk3288-rgb", .data = &rk3288_rgb_funcs },
+	{ .compatible = "rockchip,rk3288-rgb", .data = &rk3288_rgb },
 	{ .compatible = "rockchip,rk3308-rgb", },
 	{ .compatible = "rockchip,rk3368-rgb", },
-	{ .compatible = "rockchip,rk3568-rgb", .data = &rk3568_rgb_funcs },
+	{ .compatible = "rockchip,rk3562-rgb", .data = &rk3562_rgb },
+	{ .compatible = "rockchip,rk3568-rgb", .data = &rk3568_rgb },
+	{ .compatible = "rockchip,rk3588-rgb", },
+	{ .compatible = "rockchip,rv1106-rgb", .data = &rv1106_rgb},
 	{ .compatible = "rockchip,rv1108-rgb", },
-	{ .compatible = "rockchip,rv1126-rgb", .data = &rv1126_rgb_funcs},
+	{ .compatible = "rockchip,rv1126-rgb", .data = &rv1126_rgb},
 	{}
 };
 MODULE_DEVICE_TABLE(of, rockchip_rgb_dt_ids);

--
Gitblit v1.6.2