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