1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * Copyright 2016 Rockchip Inc.
4 */
5
6 #include <common.h>
7 #include <display.h>
8 #include <dm.h>
9 #include <edid.h>
10 #include <panel.h>
11 #include <regmap.h>
12 #include <syscon.h>
13 #include <asm/gpio.h>
14 #include <asm/io.h>
15 #include <asm/arch/clock.h>
16 #include <asm/arch/lvds_rk3288.h>
17 #include <asm/arch/grf_rk3288.h>
18 #include <dt-bindings/clock/rk3288-cru.h>
19 #include <dt-bindings/video/rk3288.h>
20
21 DECLARE_GLOBAL_DATA_PTR;
22
23 /**
24 * struct rk_lvds_priv - private rockchip lvds display driver info
25 *
26 * @reg: LVDS register address
27 * @grf: GRF register
28 * @panel: Panel device that is used in driver
29 *
30 * @output: Output mode, decided single or double channel,
31 * LVDS or LVTLL
32 * @format: Data format that RGB data will packing as
33 */
34 struct rk_lvds_priv {
35 void __iomem *regs;
36 struct rk3288_grf *grf;
37 struct udevice *panel;
38
39 int output;
40 int format;
41 };
42
lvds_writel(struct rk_lvds_priv * lvds,u32 offset,u32 val)43 static inline void lvds_writel(struct rk_lvds_priv *lvds, u32 offset, u32 val)
44 {
45 writel(val, lvds->regs + offset);
46
47 writel(val, lvds->regs + offset + 0x100);
48 }
49
rk_lvds_enable(struct udevice * dev,int panel_bpp,const struct display_timing * edid)50 int rk_lvds_enable(struct udevice *dev, int panel_bpp,
51 const struct display_timing *edid)
52 {
53 struct rk_lvds_priv *priv = dev_get_priv(dev);
54 struct display_plat *uc_plat = dev_get_uclass_platdata(dev);
55 int ret = 0;
56 unsigned int val = 0;
57
58 ret = panel_enable_backlight(priv->panel);
59 if (ret) {
60 debug("%s: backlight error: %d\n", __func__, ret);
61 return ret;
62 }
63
64 /* Select the video source */
65 if (uc_plat->source_id)
66 val = RK3288_LVDS_SOC_CON6_SEL_VOP_LIT |
67 (RK3288_LVDS_SOC_CON6_SEL_VOP_LIT << 16);
68 else
69 val = RK3288_LVDS_SOC_CON6_SEL_VOP_LIT << 16;
70 rk_setreg(&priv->grf->soc_con6, val);
71
72 /* Select data transfer format */
73 val = priv->format;
74 if (priv->output == LVDS_OUTPUT_DUAL)
75 val |= LVDS_DUAL | LVDS_CH0_EN | LVDS_CH1_EN;
76 else if (priv->output == LVDS_OUTPUT_SINGLE)
77 val |= LVDS_CH0_EN;
78 else if (priv->output == LVDS_OUTPUT_RGB)
79 val |= LVDS_TTL_EN | LVDS_CH0_EN | LVDS_CH1_EN;
80 val |= (0xffff << 16);
81 rk_setreg(&priv->grf->soc_con7, val);
82
83 /* Enable LVDS PHY */
84 if (priv->output == LVDS_OUTPUT_RGB) {
85 lvds_writel(priv, RK3288_LVDS_CH0_REG0,
86 RK3288_LVDS_CH0_REG0_TTL_EN |
87 RK3288_LVDS_CH0_REG0_LANECK_EN |
88 RK3288_LVDS_CH0_REG0_LANE4_EN |
89 RK3288_LVDS_CH0_REG0_LANE3_EN |
90 RK3288_LVDS_CH0_REG0_LANE2_EN |
91 RK3288_LVDS_CH0_REG0_LANE1_EN |
92 RK3288_LVDS_CH0_REG0_LANE0_EN);
93 lvds_writel(priv, RK3288_LVDS_CH0_REG2,
94 RK3288_LVDS_PLL_FBDIV_REG2(0x46));
95
96 lvds_writel(priv, RK3288_LVDS_CH0_REG3,
97 RK3288_LVDS_PLL_FBDIV_REG3(0x46));
98 lvds_writel(priv, RK3288_LVDS_CH0_REG4,
99 RK3288_LVDS_CH0_REG4_LANECK_TTL_MODE |
100 RK3288_LVDS_CH0_REG4_LANE4_TTL_MODE |
101 RK3288_LVDS_CH0_REG4_LANE3_TTL_MODE |
102 RK3288_LVDS_CH0_REG4_LANE2_TTL_MODE |
103 RK3288_LVDS_CH0_REG4_LANE1_TTL_MODE |
104 RK3288_LVDS_CH0_REG4_LANE0_TTL_MODE);
105 lvds_writel(priv, RK3288_LVDS_CH0_REG5,
106 RK3288_LVDS_CH0_REG5_LANECK_TTL_DATA |
107 RK3288_LVDS_CH0_REG5_LANE4_TTL_DATA |
108 RK3288_LVDS_CH0_REG5_LANE3_TTL_DATA |
109 RK3288_LVDS_CH0_REG5_LANE2_TTL_DATA |
110 RK3288_LVDS_CH0_REG5_LANE1_TTL_DATA |
111 RK3288_LVDS_CH0_REG5_LANE0_TTL_DATA);
112 lvds_writel(priv, RK3288_LVDS_CH0_REGD,
113 RK3288_LVDS_PLL_PREDIV_REGD(0x0a));
114 lvds_writel(priv, RK3288_LVDS_CH0_REG20,
115 RK3288_LVDS_CH0_REG20_LSB);
116 } else {
117 lvds_writel(priv, RK3288_LVDS_CH0_REG0,
118 RK3288_LVDS_CH0_REG0_LVDS_EN |
119 RK3288_LVDS_CH0_REG0_LANECK_EN |
120 RK3288_LVDS_CH0_REG0_LANE4_EN |
121 RK3288_LVDS_CH0_REG0_LANE3_EN |
122 RK3288_LVDS_CH0_REG0_LANE2_EN |
123 RK3288_LVDS_CH0_REG0_LANE1_EN |
124 RK3288_LVDS_CH0_REG0_LANE0_EN);
125 lvds_writel(priv, RK3288_LVDS_CH0_REG1,
126 RK3288_LVDS_CH0_REG1_LANECK_BIAS |
127 RK3288_LVDS_CH0_REG1_LANE4_BIAS |
128 RK3288_LVDS_CH0_REG1_LANE3_BIAS |
129 RK3288_LVDS_CH0_REG1_LANE2_BIAS |
130 RK3288_LVDS_CH0_REG1_LANE1_BIAS |
131 RK3288_LVDS_CH0_REG1_LANE0_BIAS);
132 lvds_writel(priv, RK3288_LVDS_CH0_REG2,
133 RK3288_LVDS_CH0_REG2_RESERVE_ON |
134 RK3288_LVDS_CH0_REG2_LANECK_LVDS_MODE |
135 RK3288_LVDS_CH0_REG2_LANE4_LVDS_MODE |
136 RK3288_LVDS_CH0_REG2_LANE3_LVDS_MODE |
137 RK3288_LVDS_CH0_REG2_LANE2_LVDS_MODE |
138 RK3288_LVDS_CH0_REG2_LANE1_LVDS_MODE |
139 RK3288_LVDS_CH0_REG2_LANE0_LVDS_MODE |
140 RK3288_LVDS_PLL_FBDIV_REG2(0x46));
141 lvds_writel(priv, RK3288_LVDS_CH0_REG3,
142 RK3288_LVDS_PLL_FBDIV_REG3(0x46));
143 lvds_writel(priv, RK3288_LVDS_CH0_REG4, 0x00);
144 lvds_writel(priv, RK3288_LVDS_CH0_REG5, 0x00);
145 lvds_writel(priv, RK3288_LVDS_CH0_REGD,
146 RK3288_LVDS_PLL_PREDIV_REGD(0x0a));
147 lvds_writel(priv, RK3288_LVDS_CH0_REG20,
148 RK3288_LVDS_CH0_REG20_LSB);
149 }
150
151 /* Power on */
152 writel(RK3288_LVDS_CFG_REGC_PLL_ENABLE,
153 priv->regs + RK3288_LVDS_CFG_REGC);
154
155 writel(RK3288_LVDS_CFG_REG21_TX_ENABLE,
156 priv->regs + RK3288_LVDS_CFG_REG21);
157
158 return 0;
159 }
160
rk_lvds_read_timing(struct udevice * dev,struct display_timing * timing)161 int rk_lvds_read_timing(struct udevice *dev, struct display_timing *timing)
162 {
163 if (fdtdec_decode_display_timing
164 (gd->fdt_blob, dev_of_offset(dev), 0, timing)) {
165 debug("%s: Failed to decode display timing\n", __func__);
166 return -EINVAL;
167 }
168
169 return 0;
170 }
171
rk_lvds_ofdata_to_platdata(struct udevice * dev)172 static int rk_lvds_ofdata_to_platdata(struct udevice *dev)
173 {
174 struct rk_lvds_priv *priv = dev_get_priv(dev);
175 const void *blob = gd->fdt_blob;
176 int node = dev_of_offset(dev);
177 int ret;
178 priv->regs = (void *)devfdt_get_addr(dev);
179 priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF);
180
181 ret = fdtdec_get_int(blob, node, "rockchip,output", -1);
182 if (ret != -1) {
183 priv->output = ret;
184 debug("LVDS output : %d\n", ret);
185 } else {
186 /* default set it as output rgb */
187 priv->output = LVDS_OUTPUT_RGB;
188 }
189
190 ret = fdtdec_get_int(blob, node, "rockchip,data-mapping", -1);
191 if (ret != -1) {
192 priv->format = ret;
193 debug("LVDS data-mapping : %d\n", ret);
194 } else {
195 /* default set it as format jeida */
196 priv->format = LVDS_FORMAT_JEIDA;
197 }
198
199 ret = fdtdec_get_int(blob, node, "rockchip,data-width", -1);
200 if (ret != -1) {
201 debug("LVDS data-width : %d\n", ret);
202 if (ret == 24) {
203 priv->format |= LVDS_24BIT;
204 } else if (ret == 18) {
205 priv->format |= LVDS_18BIT;
206 } else {
207 debug("rockchip-lvds unsupport data-width[%d]\n", ret);
208 ret = -EINVAL;
209 return ret;
210 }
211 } else {
212 priv->format |= LVDS_24BIT;
213 }
214
215 return 0;
216 }
217
rk_lvds_probe(struct udevice * dev)218 int rk_lvds_probe(struct udevice *dev)
219 {
220 struct rk_lvds_priv *priv = dev_get_priv(dev);
221 int ret;
222
223 ret = uclass_get_device_by_phandle(UCLASS_PANEL, dev, "rockchip,panel",
224 &priv->panel);
225 if (ret) {
226 debug("%s: Cannot find panel for '%s' (ret=%d)\n", __func__,
227 dev->name, ret);
228 return ret;
229 }
230
231 return 0;
232 }
233
234 static const struct dm_display_ops lvds_rockchip_ops = {
235 .read_timing = rk_lvds_read_timing,
236 .enable = rk_lvds_enable,
237 };
238
239 static const struct udevice_id rockchip_lvds_ids[] = {
240 {.compatible = "rockchip,rk3288-lvds"},
241 {}
242 };
243
244 U_BOOT_DRIVER(lvds_rockchip) = {
245 .name = "lvds_rockchip",
246 .id = UCLASS_DISPLAY,
247 .of_match = rockchip_lvds_ids,
248 .ops = &lvds_rockchip_ops,
249 .ofdata_to_platdata = rk_lvds_ofdata_to_platdata,
250 .probe = rk_lvds_probe,
251 .priv_auto_alloc_size = sizeof(struct rk_lvds_priv),
252 };
253