xref: /OK3568_Linux_fs/u-boot/drivers/video/drm/rk618_lvds.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * (C) Copyright 2008-2018 Fuzhou Rockchip Electronics Co., Ltd
4  */
5 
6 #include <common.h>
7 #include <dm.h>
8 #include <dm/device-internal.h>
9 #include <errno.h>
10 #include <i2c.h>
11 #include <video_bridge.h>
12 #include <linux/media-bus-format.h>
13 
14 #include "rockchip_display.h"
15 #include "rockchip_bridge.h"
16 #include "rockchip_panel.h"
17 #include "rockchip_connector.h"
18 
19 #include "rk618.h"
20 
21 enum {
22 	LVDS_8BIT_MODE_FORMAT_1,
23 	LVDS_8BIT_MODE_FORMAT_2,
24 	LVDS_8BIT_MODE_FORMAT_3,
25 	LVDS_6BIT_MODE,
26 };
27 
28 struct rk618_lvds_priv {
29 	struct udevice *dev;
30 	struct rk618 *parent;
31 	bool dual_channel;
32 };
33 
lvds_write(struct rk618_lvds_priv * priv,u16 reg,u32 val)34 static int lvds_write(struct rk618_lvds_priv *priv, u16 reg, u32 val)
35 {
36 	return rk618_i2c_write(priv->parent, reg, val);
37 }
38 
rk618_lvds_bridge_enable(struct rockchip_bridge * bridge)39 static void rk618_lvds_bridge_enable(struct rockchip_bridge *bridge)
40 {
41 	struct rk618_lvds_priv *priv = dev_get_priv(bridge->dev);
42 	struct rockchip_panel *panel = bridge->conn->panel;
43 	u32 value, format;
44 
45 	rk618_frc_dclk_invert(priv->parent);
46 
47 	switch (panel->bus_format) {
48 	case MEDIA_BUS_FMT_RGB666_1X7X3_JEIDA:	/* jeida-18 */
49 		format = LVDS_6BIT_MODE;
50 		break;
51 	case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:	/* jeida-24 */
52 		format = LVDS_8BIT_MODE_FORMAT_2;
53 		break;
54 	case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:	/* vesa-18 */
55 		format = LVDS_8BIT_MODE_FORMAT_3;
56 		break;
57 	case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:	/* vesa-24 */
58 	default:
59 		format = LVDS_8BIT_MODE_FORMAT_1;
60 		break;
61 	}
62 
63 	value = LVDS_CON_CHA0TTL_DISABLE | LVDS_CON_CHA1TTL_DISABLE |
64 		LVDS_CON_CHA0_POWER_UP | LVDS_CON_CBG_POWER_UP |
65 		LVDS_CON_PLL_POWER_UP | LVDS_CON_SELECT(format);
66 
67 	if (priv->dual_channel)
68 		value |= LVDS_CON_CHA1_POWER_UP | LVDS_DCLK_INV |
69 			 LVDS_CON_CHASEL_DOUBLE_CHANNEL;
70 	else
71 		value |= LVDS_CON_CHA1_POWER_DOWN |
72 			 LVDS_CON_CHASEL_SINGLE_CHANNEL;
73 
74 	lvds_write(priv, RK618_LVDS_CON, value);
75 }
76 
rk618_lvds_bridge_disable(struct rockchip_bridge * bridge)77 static void rk618_lvds_bridge_disable(struct rockchip_bridge *bridge)
78 {
79 	struct rk618_lvds_priv *priv = dev_get_priv(bridge->dev);
80 
81 	lvds_write(priv, RK618_LVDS_CON,
82 		   LVDS_CON_CHA0_POWER_DOWN | LVDS_CON_CHA1_POWER_DOWN |
83 		   LVDS_CON_CBG_POWER_DOWN | LVDS_CON_PLL_POWER_DOWN);
84 }
85 
86 static const struct rockchip_bridge_funcs rk618_lvds_bridge_funcs = {
87 	.enable = rk618_lvds_bridge_enable,
88 	.disable = rk618_lvds_bridge_disable,
89 };
90 
rk618_lvds_probe(struct udevice * dev)91 static int rk618_lvds_probe(struct udevice *dev)
92 {
93 	struct rk618_lvds_priv *priv = dev_get_priv(dev);
94 	struct rockchip_bridge *bridge =
95 		(struct rockchip_bridge *)dev_get_driver_data(dev);
96 	int ret;
97 
98 	priv->dev = dev;
99 	priv->parent = dev_get_priv(dev->parent);
100 	priv->dual_channel = dev_read_bool(dev, "dual-channel");
101 
102 	ret = device_probe(dev->parent);
103 	if (ret)
104 		return ret;
105 
106 	bridge->dev = dev;
107 
108 	return 0;
109 }
110 
111 static struct rockchip_bridge rk618_lvds_driver_data = {
112 	.funcs = &rk618_lvds_bridge_funcs,
113 };
114 
115 static const struct udevice_id rk618_lvds_ids[] = {
116 	{
117 		.compatible = "rockchip,rk618-lvds",
118 		.data = (ulong)&rk618_lvds_driver_data,
119 	},
120 	{ }
121 };
122 
123 U_BOOT_DRIVER(rk618_lvds) = {
124 	.name = "rk618_lvds",
125 	.id = UCLASS_VIDEO_BRIDGE,
126 	.of_match = rk618_lvds_ids,
127 	.probe = rk618_lvds_probe,
128 	.priv_auto_alloc_size = sizeof(struct rk618_lvds_priv),
129 };
130