1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd |
4 | * Author: |
5 | * Mark Yao <mark.yao@rock-chips.com> |
6 | * Sandy Huang <hjc@rock-chips.com> |
7 | */ |
8 | |
9 | #include <linux/clk.h> |
10 | #include <linux/component.h> |
11 | #include <linux/mfd/syscon.h> |
12 | #include <linux/of_graph.h> |
13 | #include <linux/phy/phy.h> |
14 | #include <linux/pinctrl/devinfo.h> |
15 | #include <linux/platform_device.h> |
16 | #include <linux/pm_runtime.h> |
17 | #include <linux/regmap.h> |
18 | #include <linux/reset.h> |
19 | |
20 | #include <drm/display/drm_dp_helper.h> |
21 | #include <drm/drm_atomic_helper.h> |
22 | #include <drm/drm_bridge.h> |
23 | #include <drm/drm_bridge_connector.h> |
24 | #include <drm/drm_of.h> |
25 | #include <drm/drm_panel.h> |
26 | #include <drm/drm_probe_helper.h> |
27 | #include <drm/drm_simple_kms_helper.h> |
28 | |
29 | #include "rockchip_drm_drv.h" |
30 | #include "rockchip_lvds.h" |
31 | |
32 | #define DISPLAY_OUTPUT_RGB 0 |
33 | #define DISPLAY_OUTPUT_LVDS 1 |
34 | #define DISPLAY_OUTPUT_DUAL_LVDS 2 |
35 | |
36 | struct rockchip_lvds; |
37 | |
38 | /** |
39 | * struct rockchip_lvds_soc_data - rockchip lvds Soc private data |
40 | * @probe: LVDS platform probe function |
41 | * @helper_funcs: LVDS connector helper functions |
42 | */ |
43 | struct rockchip_lvds_soc_data { |
44 | int (*probe)(struct platform_device *pdev, struct rockchip_lvds *lvds); |
45 | const struct drm_encoder_helper_funcs *helper_funcs; |
46 | }; |
47 | |
48 | struct rockchip_lvds { |
49 | struct device *dev; |
50 | void __iomem *regs; |
51 | struct regmap *grf; |
52 | struct clk *pclk; |
53 | struct phy *dphy; |
54 | const struct rockchip_lvds_soc_data *soc_data; |
55 | int output; /* rgb lvds or dual lvds output */ |
56 | int format; /* vesa or jeida format */ |
57 | struct drm_device *drm_dev; |
58 | struct drm_panel *panel; |
59 | struct drm_bridge *bridge; |
60 | struct drm_connector connector; |
61 | struct rockchip_encoder encoder; |
62 | struct dev_pin_info *pins; |
63 | }; |
64 | |
65 | static inline struct rockchip_lvds *connector_to_lvds(struct drm_connector *connector) |
66 | { |
67 | return container_of(connector, struct rockchip_lvds, connector); |
68 | } |
69 | |
70 | static inline struct rockchip_lvds *encoder_to_lvds(struct drm_encoder *encoder) |
71 | { |
72 | struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder); |
73 | |
74 | return container_of(rkencoder, struct rockchip_lvds, encoder); |
75 | } |
76 | |
77 | static inline void rk3288_writel(struct rockchip_lvds *lvds, u32 offset, |
78 | u32 val) |
79 | { |
80 | writel_relaxed(val, lvds->regs + offset); |
81 | if (lvds->output == DISPLAY_OUTPUT_LVDS) |
82 | return; |
83 | writel_relaxed(val, lvds->regs + offset + RK3288_LVDS_CH1_OFFSET); |
84 | } |
85 | |
86 | static inline int rockchip_lvds_name_to_format(const char *s) |
87 | { |
88 | if (strncmp(s, "jeida-18" , 8) == 0) |
89 | return LVDS_JEIDA_18; |
90 | else if (strncmp(s, "jeida-24" , 8) == 0) |
91 | return LVDS_JEIDA_24; |
92 | else if (strncmp(s, "vesa-24" , 7) == 0) |
93 | return LVDS_VESA_24; |
94 | |
95 | return -EINVAL; |
96 | } |
97 | |
98 | static inline int rockchip_lvds_name_to_output(const char *s) |
99 | { |
100 | if (strncmp(s, "rgb" , 3) == 0) |
101 | return DISPLAY_OUTPUT_RGB; |
102 | else if (strncmp(s, "lvds" , 4) == 0) |
103 | return DISPLAY_OUTPUT_LVDS; |
104 | else if (strncmp(s, "duallvds" , 8) == 0) |
105 | return DISPLAY_OUTPUT_DUAL_LVDS; |
106 | |
107 | return -EINVAL; |
108 | } |
109 | |
110 | static const struct drm_connector_funcs rockchip_lvds_connector_funcs = { |
111 | .fill_modes = drm_helper_probe_single_connector_modes, |
112 | .destroy = drm_connector_cleanup, |
113 | .reset = drm_atomic_helper_connector_reset, |
114 | .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, |
115 | .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, |
116 | }; |
117 | |
118 | static int rockchip_lvds_connector_get_modes(struct drm_connector *connector) |
119 | { |
120 | struct rockchip_lvds *lvds = connector_to_lvds(connector); |
121 | struct drm_panel *panel = lvds->panel; |
122 | |
123 | return drm_panel_get_modes(panel, connector); |
124 | } |
125 | |
126 | static const |
127 | struct drm_connector_helper_funcs rockchip_lvds_connector_helper_funcs = { |
128 | .get_modes = rockchip_lvds_connector_get_modes, |
129 | }; |
130 | |
131 | static int |
132 | rockchip_lvds_encoder_atomic_check(struct drm_encoder *encoder, |
133 | struct drm_crtc_state *crtc_state, |
134 | struct drm_connector_state *conn_state) |
135 | { |
136 | struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state); |
137 | |
138 | s->output_mode = ROCKCHIP_OUT_MODE_P888; |
139 | s->output_type = DRM_MODE_CONNECTOR_LVDS; |
140 | |
141 | return 0; |
142 | } |
143 | |
144 | static int rk3288_lvds_poweron(struct rockchip_lvds *lvds) |
145 | { |
146 | int ret; |
147 | u32 val; |
148 | |
149 | ret = clk_enable(clk: lvds->pclk); |
150 | if (ret < 0) { |
151 | DRM_DEV_ERROR(lvds->dev, "failed to enable lvds pclk %d\n" , ret); |
152 | return ret; |
153 | } |
154 | ret = pm_runtime_resume_and_get(dev: lvds->dev); |
155 | if (ret < 0) { |
156 | DRM_DEV_ERROR(lvds->dev, "failed to get pm runtime: %d\n" , ret); |
157 | clk_disable(clk: lvds->pclk); |
158 | return ret; |
159 | } |
160 | val = RK3288_LVDS_CH0_REG0_LANE4_EN | RK3288_LVDS_CH0_REG0_LANE3_EN | |
161 | RK3288_LVDS_CH0_REG0_LANE2_EN | RK3288_LVDS_CH0_REG0_LANE1_EN | |
162 | RK3288_LVDS_CH0_REG0_LANE0_EN; |
163 | if (lvds->output == DISPLAY_OUTPUT_RGB) { |
164 | val |= RK3288_LVDS_CH0_REG0_TTL_EN | |
165 | RK3288_LVDS_CH0_REG0_LANECK_EN; |
166 | rk3288_writel(lvds, RK3288_LVDS_CH0_REG0, val); |
167 | rk3288_writel(lvds, RK3288_LVDS_CH0_REG2, |
168 | RK3288_LVDS_PLL_FBDIV_REG2(0x46)); |
169 | rk3288_writel(lvds, RK3288_LVDS_CH0_REG4, |
170 | RK3288_LVDS_CH0_REG4_LANECK_TTL_MODE | |
171 | RK3288_LVDS_CH0_REG4_LANE4_TTL_MODE | |
172 | RK3288_LVDS_CH0_REG4_LANE3_TTL_MODE | |
173 | RK3288_LVDS_CH0_REG4_LANE2_TTL_MODE | |
174 | RK3288_LVDS_CH0_REG4_LANE1_TTL_MODE | |
175 | RK3288_LVDS_CH0_REG4_LANE0_TTL_MODE); |
176 | rk3288_writel(lvds, RK3288_LVDS_CH0_REG5, |
177 | RK3288_LVDS_CH0_REG5_LANECK_TTL_DATA | |
178 | RK3288_LVDS_CH0_REG5_LANE4_TTL_DATA | |
179 | RK3288_LVDS_CH0_REG5_LANE3_TTL_DATA | |
180 | RK3288_LVDS_CH0_REG5_LANE2_TTL_DATA | |
181 | RK3288_LVDS_CH0_REG5_LANE1_TTL_DATA | |
182 | RK3288_LVDS_CH0_REG5_LANE0_TTL_DATA); |
183 | } else { |
184 | val |= RK3288_LVDS_CH0_REG0_LVDS_EN | |
185 | RK3288_LVDS_CH0_REG0_LANECK_EN; |
186 | rk3288_writel(lvds, RK3288_LVDS_CH0_REG0, val); |
187 | rk3288_writel(lvds, RK3288_LVDS_CH0_REG1, |
188 | RK3288_LVDS_CH0_REG1_LANECK_BIAS | |
189 | RK3288_LVDS_CH0_REG1_LANE4_BIAS | |
190 | RK3288_LVDS_CH0_REG1_LANE3_BIAS | |
191 | RK3288_LVDS_CH0_REG1_LANE2_BIAS | |
192 | RK3288_LVDS_CH0_REG1_LANE1_BIAS | |
193 | RK3288_LVDS_CH0_REG1_LANE0_BIAS); |
194 | rk3288_writel(lvds, RK3288_LVDS_CH0_REG2, |
195 | RK3288_LVDS_CH0_REG2_RESERVE_ON | |
196 | RK3288_LVDS_CH0_REG2_LANECK_LVDS_MODE | |
197 | RK3288_LVDS_CH0_REG2_LANE4_LVDS_MODE | |
198 | RK3288_LVDS_CH0_REG2_LANE3_LVDS_MODE | |
199 | RK3288_LVDS_CH0_REG2_LANE2_LVDS_MODE | |
200 | RK3288_LVDS_CH0_REG2_LANE1_LVDS_MODE | |
201 | RK3288_LVDS_CH0_REG2_LANE0_LVDS_MODE | |
202 | RK3288_LVDS_PLL_FBDIV_REG2(0x46)); |
203 | rk3288_writel(lvds, RK3288_LVDS_CH0_REG4, val: 0x00); |
204 | rk3288_writel(lvds, RK3288_LVDS_CH0_REG5, val: 0x00); |
205 | } |
206 | rk3288_writel(lvds, RK3288_LVDS_CH0_REG3, |
207 | RK3288_LVDS_PLL_FBDIV_REG3(0x46)); |
208 | rk3288_writel(lvds, RK3288_LVDS_CH0_REGD, |
209 | RK3288_LVDS_PLL_PREDIV_REGD(0x0a)); |
210 | rk3288_writel(lvds, RK3288_LVDS_CH0_REG20, |
211 | RK3288_LVDS_CH0_REG20_LSB); |
212 | |
213 | rk3288_writel(lvds, RK3288_LVDS_CFG_REGC, |
214 | RK3288_LVDS_CFG_REGC_PLL_ENABLE); |
215 | rk3288_writel(lvds, RK3288_LVDS_CFG_REG21, |
216 | RK3288_LVDS_CFG_REG21_TX_ENABLE); |
217 | |
218 | return 0; |
219 | } |
220 | |
221 | static void rk3288_lvds_poweroff(struct rockchip_lvds *lvds) |
222 | { |
223 | int ret; |
224 | u32 val; |
225 | |
226 | rk3288_writel(lvds, RK3288_LVDS_CFG_REG21, |
227 | RK3288_LVDS_CFG_REG21_TX_ENABLE); |
228 | rk3288_writel(lvds, RK3288_LVDS_CFG_REGC, |
229 | RK3288_LVDS_CFG_REGC_PLL_ENABLE); |
230 | val = LVDS_DUAL | LVDS_TTL_EN | LVDS_CH0_EN | LVDS_CH1_EN | LVDS_PWRDN; |
231 | val |= val << 16; |
232 | ret = regmap_write(map: lvds->grf, RK3288_LVDS_GRF_SOC_CON7, val); |
233 | if (ret != 0) |
234 | DRM_DEV_ERROR(lvds->dev, "Could not write to GRF: %d\n" , ret); |
235 | |
236 | pm_runtime_put(dev: lvds->dev); |
237 | clk_disable(clk: lvds->pclk); |
238 | } |
239 | |
240 | static int rk3288_lvds_grf_config(struct drm_encoder *encoder, |
241 | struct drm_display_mode *mode) |
242 | { |
243 | struct rockchip_lvds *lvds = encoder_to_lvds(encoder); |
244 | u8 pin_hsync = (mode->flags & DRM_MODE_FLAG_PHSYNC) ? 1 : 0; |
245 | u8 pin_dclk = (mode->flags & DRM_MODE_FLAG_PCSYNC) ? 1 : 0; |
246 | u32 val; |
247 | int ret; |
248 | |
249 | /* iomux to LCD data/sync mode */ |
250 | if (lvds->output == DISPLAY_OUTPUT_RGB) |
251 | if (lvds->pins && !IS_ERR(ptr: lvds->pins->default_state)) |
252 | pinctrl_select_state(p: lvds->pins->p, |
253 | s: lvds->pins->default_state); |
254 | val = lvds->format | LVDS_CH0_EN; |
255 | if (lvds->output == DISPLAY_OUTPUT_RGB) |
256 | val |= LVDS_TTL_EN | LVDS_CH1_EN; |
257 | else if (lvds->output == DISPLAY_OUTPUT_DUAL_LVDS) |
258 | val |= LVDS_DUAL | LVDS_CH1_EN; |
259 | |
260 | if ((mode->htotal - mode->hsync_start) & 0x01) |
261 | val |= LVDS_START_PHASE_RST_1; |
262 | |
263 | val |= (pin_dclk << 8) | (pin_hsync << 9); |
264 | val |= (0xffff << 16); |
265 | ret = regmap_write(map: lvds->grf, RK3288_LVDS_GRF_SOC_CON7, val); |
266 | if (ret) |
267 | DRM_DEV_ERROR(lvds->dev, "Could not write to GRF: %d\n" , ret); |
268 | |
269 | return ret; |
270 | } |
271 | |
272 | static int rk3288_lvds_set_vop_source(struct rockchip_lvds *lvds, |
273 | struct drm_encoder *encoder) |
274 | { |
275 | u32 val; |
276 | int ret; |
277 | |
278 | ret = drm_of_encoder_active_endpoint_id(node: lvds->dev->of_node, encoder); |
279 | if (ret < 0) |
280 | return ret; |
281 | |
282 | val = RK3288_LVDS_SOC_CON6_SEL_VOP_LIT << 16; |
283 | if (ret) |
284 | val |= RK3288_LVDS_SOC_CON6_SEL_VOP_LIT; |
285 | |
286 | ret = regmap_write(map: lvds->grf, RK3288_LVDS_GRF_SOC_CON6, val); |
287 | if (ret < 0) |
288 | return ret; |
289 | |
290 | return 0; |
291 | } |
292 | |
293 | static void rk3288_lvds_encoder_enable(struct drm_encoder *encoder) |
294 | { |
295 | struct rockchip_lvds *lvds = encoder_to_lvds(encoder); |
296 | struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode; |
297 | int ret; |
298 | |
299 | drm_panel_prepare(panel: lvds->panel); |
300 | |
301 | ret = rk3288_lvds_poweron(lvds); |
302 | if (ret < 0) { |
303 | DRM_DEV_ERROR(lvds->dev, "failed to power on LVDS: %d\n" , ret); |
304 | drm_panel_unprepare(panel: lvds->panel); |
305 | return; |
306 | } |
307 | |
308 | ret = rk3288_lvds_grf_config(encoder, mode); |
309 | if (ret) { |
310 | DRM_DEV_ERROR(lvds->dev, "failed to configure LVDS: %d\n" , ret); |
311 | drm_panel_unprepare(panel: lvds->panel); |
312 | return; |
313 | } |
314 | |
315 | ret = rk3288_lvds_set_vop_source(lvds, encoder); |
316 | if (ret) { |
317 | DRM_DEV_ERROR(lvds->dev, "failed to set VOP source: %d\n" , ret); |
318 | drm_panel_unprepare(panel: lvds->panel); |
319 | return; |
320 | } |
321 | |
322 | drm_panel_enable(panel: lvds->panel); |
323 | } |
324 | |
325 | static void rk3288_lvds_encoder_disable(struct drm_encoder *encoder) |
326 | { |
327 | struct rockchip_lvds *lvds = encoder_to_lvds(encoder); |
328 | |
329 | drm_panel_disable(panel: lvds->panel); |
330 | rk3288_lvds_poweroff(lvds); |
331 | drm_panel_unprepare(panel: lvds->panel); |
332 | } |
333 | |
334 | static int px30_lvds_poweron(struct rockchip_lvds *lvds) |
335 | { |
336 | int ret; |
337 | |
338 | ret = pm_runtime_resume_and_get(dev: lvds->dev); |
339 | if (ret < 0) { |
340 | DRM_DEV_ERROR(lvds->dev, "failed to get pm runtime: %d\n" , ret); |
341 | return ret; |
342 | } |
343 | |
344 | /* Enable LVDS mode */ |
345 | ret = regmap_update_bits(map: lvds->grf, PX30_LVDS_GRF_PD_VO_CON1, |
346 | PX30_LVDS_MODE_EN(1) | PX30_LVDS_P2S_EN(1), |
347 | PX30_LVDS_MODE_EN(1) | PX30_LVDS_P2S_EN(1)); |
348 | if (ret) |
349 | pm_runtime_put(dev: lvds->dev); |
350 | |
351 | return ret; |
352 | } |
353 | |
354 | static void px30_lvds_poweroff(struct rockchip_lvds *lvds) |
355 | { |
356 | regmap_update_bits(map: lvds->grf, PX30_LVDS_GRF_PD_VO_CON1, |
357 | PX30_LVDS_MODE_EN(1) | PX30_LVDS_P2S_EN(1), |
358 | PX30_LVDS_MODE_EN(0) | PX30_LVDS_P2S_EN(0)); |
359 | |
360 | pm_runtime_put(dev: lvds->dev); |
361 | } |
362 | |
363 | static int px30_lvds_grf_config(struct drm_encoder *encoder, |
364 | struct drm_display_mode *mode) |
365 | { |
366 | struct rockchip_lvds *lvds = encoder_to_lvds(encoder); |
367 | |
368 | if (lvds->output != DISPLAY_OUTPUT_LVDS) { |
369 | DRM_DEV_ERROR(lvds->dev, "Unsupported display output %d\n" , |
370 | lvds->output); |
371 | return -EINVAL; |
372 | } |
373 | |
374 | /* Set format */ |
375 | return regmap_update_bits(map: lvds->grf, PX30_LVDS_GRF_PD_VO_CON1, |
376 | PX30_LVDS_FORMAT(lvds->format), |
377 | PX30_LVDS_FORMAT(lvds->format)); |
378 | } |
379 | |
380 | static int px30_lvds_set_vop_source(struct rockchip_lvds *lvds, |
381 | struct drm_encoder *encoder) |
382 | { |
383 | int vop; |
384 | |
385 | vop = drm_of_encoder_active_endpoint_id(node: lvds->dev->of_node, encoder); |
386 | if (vop < 0) |
387 | return vop; |
388 | |
389 | return regmap_update_bits(map: lvds->grf, PX30_LVDS_GRF_PD_VO_CON1, |
390 | PX30_LVDS_VOP_SEL(1), |
391 | PX30_LVDS_VOP_SEL(vop)); |
392 | } |
393 | |
394 | static void px30_lvds_encoder_enable(struct drm_encoder *encoder) |
395 | { |
396 | struct rockchip_lvds *lvds = encoder_to_lvds(encoder); |
397 | struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode; |
398 | int ret; |
399 | |
400 | drm_panel_prepare(panel: lvds->panel); |
401 | |
402 | ret = px30_lvds_poweron(lvds); |
403 | if (ret) { |
404 | DRM_DEV_ERROR(lvds->dev, "failed to power on LVDS: %d\n" , ret); |
405 | drm_panel_unprepare(panel: lvds->panel); |
406 | return; |
407 | } |
408 | |
409 | ret = px30_lvds_grf_config(encoder, mode); |
410 | if (ret) { |
411 | DRM_DEV_ERROR(lvds->dev, "failed to configure LVDS: %d\n" , ret); |
412 | drm_panel_unprepare(panel: lvds->panel); |
413 | return; |
414 | } |
415 | |
416 | ret = px30_lvds_set_vop_source(lvds, encoder); |
417 | if (ret) { |
418 | DRM_DEV_ERROR(lvds->dev, "failed to set VOP source: %d\n" , ret); |
419 | drm_panel_unprepare(panel: lvds->panel); |
420 | return; |
421 | } |
422 | |
423 | drm_panel_enable(panel: lvds->panel); |
424 | } |
425 | |
426 | static void px30_lvds_encoder_disable(struct drm_encoder *encoder) |
427 | { |
428 | struct rockchip_lvds *lvds = encoder_to_lvds(encoder); |
429 | |
430 | drm_panel_disable(panel: lvds->panel); |
431 | px30_lvds_poweroff(lvds); |
432 | drm_panel_unprepare(panel: lvds->panel); |
433 | } |
434 | |
435 | static const |
436 | struct drm_encoder_helper_funcs rk3288_lvds_encoder_helper_funcs = { |
437 | .enable = rk3288_lvds_encoder_enable, |
438 | .disable = rk3288_lvds_encoder_disable, |
439 | .atomic_check = rockchip_lvds_encoder_atomic_check, |
440 | }; |
441 | |
442 | static const |
443 | struct drm_encoder_helper_funcs px30_lvds_encoder_helper_funcs = { |
444 | .enable = px30_lvds_encoder_enable, |
445 | .disable = px30_lvds_encoder_disable, |
446 | .atomic_check = rockchip_lvds_encoder_atomic_check, |
447 | }; |
448 | |
449 | static int rk3288_lvds_probe(struct platform_device *pdev, |
450 | struct rockchip_lvds *lvds) |
451 | { |
452 | int ret; |
453 | |
454 | lvds->regs = devm_platform_ioremap_resource(pdev, index: 0); |
455 | if (IS_ERR(ptr: lvds->regs)) |
456 | return PTR_ERR(ptr: lvds->regs); |
457 | |
458 | lvds->pclk = devm_clk_get(dev: lvds->dev, id: "pclk_lvds" ); |
459 | if (IS_ERR(ptr: lvds->pclk)) { |
460 | DRM_DEV_ERROR(lvds->dev, "could not get pclk_lvds\n" ); |
461 | return PTR_ERR(ptr: lvds->pclk); |
462 | } |
463 | |
464 | lvds->pins = devm_kzalloc(dev: lvds->dev, size: sizeof(*lvds->pins), |
465 | GFP_KERNEL); |
466 | if (!lvds->pins) |
467 | return -ENOMEM; |
468 | |
469 | lvds->pins->p = devm_pinctrl_get(dev: lvds->dev); |
470 | if (IS_ERR(ptr: lvds->pins->p)) { |
471 | DRM_DEV_ERROR(lvds->dev, "no pinctrl handle\n" ); |
472 | devm_kfree(dev: lvds->dev, p: lvds->pins); |
473 | lvds->pins = NULL; |
474 | } else { |
475 | lvds->pins->default_state = |
476 | pinctrl_lookup_state(p: lvds->pins->p, name: "lcdc" ); |
477 | if (IS_ERR(ptr: lvds->pins->default_state)) { |
478 | DRM_DEV_ERROR(lvds->dev, "no default pinctrl state\n" ); |
479 | devm_kfree(dev: lvds->dev, p: lvds->pins); |
480 | lvds->pins = NULL; |
481 | } |
482 | } |
483 | |
484 | ret = clk_prepare(clk: lvds->pclk); |
485 | if (ret < 0) { |
486 | DRM_DEV_ERROR(lvds->dev, "failed to prepare pclk_lvds\n" ); |
487 | return ret; |
488 | } |
489 | |
490 | return 0; |
491 | } |
492 | |
493 | static int px30_lvds_probe(struct platform_device *pdev, |
494 | struct rockchip_lvds *lvds) |
495 | { |
496 | int ret; |
497 | |
498 | /* MSB */ |
499 | ret = regmap_update_bits(map: lvds->grf, PX30_LVDS_GRF_PD_VO_CON1, |
500 | PX30_LVDS_MSBSEL(1), |
501 | PX30_LVDS_MSBSEL(1)); |
502 | if (ret) |
503 | return ret; |
504 | |
505 | /* PHY */ |
506 | lvds->dphy = devm_phy_get(dev: &pdev->dev, string: "dphy" ); |
507 | if (IS_ERR(ptr: lvds->dphy)) |
508 | return PTR_ERR(ptr: lvds->dphy); |
509 | |
510 | ret = phy_init(phy: lvds->dphy); |
511 | if (ret) |
512 | return ret; |
513 | |
514 | ret = phy_set_mode(lvds->dphy, PHY_MODE_LVDS); |
515 | if (ret) |
516 | return ret; |
517 | |
518 | return phy_power_on(phy: lvds->dphy); |
519 | } |
520 | |
521 | static const struct rockchip_lvds_soc_data rk3288_lvds_data = { |
522 | .probe = rk3288_lvds_probe, |
523 | .helper_funcs = &rk3288_lvds_encoder_helper_funcs, |
524 | }; |
525 | |
526 | static const struct rockchip_lvds_soc_data px30_lvds_data = { |
527 | .probe = px30_lvds_probe, |
528 | .helper_funcs = &px30_lvds_encoder_helper_funcs, |
529 | }; |
530 | |
531 | static const struct of_device_id rockchip_lvds_dt_ids[] = { |
532 | { |
533 | .compatible = "rockchip,rk3288-lvds" , |
534 | .data = &rk3288_lvds_data |
535 | }, |
536 | { |
537 | .compatible = "rockchip,px30-lvds" , |
538 | .data = &px30_lvds_data |
539 | }, |
540 | {} |
541 | }; |
542 | MODULE_DEVICE_TABLE(of, rockchip_lvds_dt_ids); |
543 | |
544 | static int rockchip_lvds_bind(struct device *dev, struct device *master, |
545 | void *data) |
546 | { |
547 | struct rockchip_lvds *lvds = dev_get_drvdata(dev); |
548 | struct drm_device *drm_dev = data; |
549 | struct drm_encoder *encoder; |
550 | struct drm_connector *connector; |
551 | struct device_node *remote = NULL; |
552 | struct device_node *port, *endpoint; |
553 | int ret = 0, child_count = 0; |
554 | const char *name; |
555 | u32 endpoint_id = 0; |
556 | |
557 | lvds->drm_dev = drm_dev; |
558 | port = of_graph_get_port_by_id(node: dev->of_node, id: 1); |
559 | if (!port) { |
560 | DRM_DEV_ERROR(dev, |
561 | "can't found port point, please init lvds panel port!\n" ); |
562 | return -EINVAL; |
563 | } |
564 | for_each_child_of_node(port, endpoint) { |
565 | child_count++; |
566 | of_property_read_u32(np: endpoint, propname: "reg" , out_value: &endpoint_id); |
567 | ret = drm_of_find_panel_or_bridge(np: dev->of_node, port: 1, endpoint: endpoint_id, |
568 | panel: &lvds->panel, bridge: &lvds->bridge); |
569 | if (!ret) { |
570 | of_node_put(node: endpoint); |
571 | break; |
572 | } |
573 | } |
574 | if (!child_count) { |
575 | DRM_DEV_ERROR(dev, "lvds port does not have any children\n" ); |
576 | ret = -EINVAL; |
577 | goto err_put_port; |
578 | } else if (ret) { |
579 | dev_err_probe(dev, err: ret, fmt: "failed to find panel and bridge node\n" ); |
580 | goto err_put_port; |
581 | } |
582 | if (lvds->panel) |
583 | remote = lvds->panel->dev->of_node; |
584 | else |
585 | remote = lvds->bridge->of_node; |
586 | if (of_property_read_string(np: dev->of_node, propname: "rockchip,output" , out_string: &name)) |
587 | /* default set it as output rgb */ |
588 | lvds->output = DISPLAY_OUTPUT_RGB; |
589 | else |
590 | lvds->output = rockchip_lvds_name_to_output(s: name); |
591 | |
592 | if (lvds->output < 0) { |
593 | DRM_DEV_ERROR(dev, "invalid output type [%s]\n" , name); |
594 | ret = lvds->output; |
595 | goto err_put_remote; |
596 | } |
597 | |
598 | if (of_property_read_string(np: remote, propname: "data-mapping" , out_string: &name)) |
599 | /* default set it as format vesa 18 */ |
600 | lvds->format = LVDS_VESA_18; |
601 | else |
602 | lvds->format = rockchip_lvds_name_to_format(s: name); |
603 | |
604 | if (lvds->format < 0) { |
605 | DRM_DEV_ERROR(dev, "invalid data-mapping format [%s]\n" , name); |
606 | ret = lvds->format; |
607 | goto err_put_remote; |
608 | } |
609 | |
610 | encoder = &lvds->encoder.encoder; |
611 | encoder->possible_crtcs = drm_of_find_possible_crtcs(dev: drm_dev, |
612 | port: dev->of_node); |
613 | |
614 | ret = drm_simple_encoder_init(dev: drm_dev, encoder, DRM_MODE_ENCODER_LVDS); |
615 | if (ret < 0) { |
616 | DRM_DEV_ERROR(drm_dev->dev, |
617 | "failed to initialize encoder: %d\n" , ret); |
618 | goto err_put_remote; |
619 | } |
620 | |
621 | drm_encoder_helper_add(encoder, funcs: lvds->soc_data->helper_funcs); |
622 | connector = &lvds->connector; |
623 | |
624 | if (lvds->panel) { |
625 | connector->dpms = DRM_MODE_DPMS_OFF; |
626 | ret = drm_connector_init(dev: drm_dev, connector, |
627 | funcs: &rockchip_lvds_connector_funcs, |
628 | DRM_MODE_CONNECTOR_LVDS); |
629 | if (ret < 0) { |
630 | DRM_DEV_ERROR(drm_dev->dev, |
631 | "failed to initialize connector: %d\n" , ret); |
632 | goto err_free_encoder; |
633 | } |
634 | |
635 | drm_connector_helper_add(connector, |
636 | funcs: &rockchip_lvds_connector_helper_funcs); |
637 | } else { |
638 | ret = drm_bridge_attach(encoder, bridge: lvds->bridge, NULL, |
639 | flags: DRM_BRIDGE_ATTACH_NO_CONNECTOR); |
640 | if (ret) |
641 | goto err_free_encoder; |
642 | |
643 | connector = drm_bridge_connector_init(drm: lvds->drm_dev, encoder); |
644 | if (IS_ERR(ptr: connector)) { |
645 | DRM_DEV_ERROR(drm_dev->dev, |
646 | "failed to initialize bridge connector: %pe\n" , |
647 | connector); |
648 | ret = PTR_ERR(ptr: connector); |
649 | goto err_free_encoder; |
650 | } |
651 | } |
652 | |
653 | ret = drm_connector_attach_encoder(connector, encoder); |
654 | if (ret < 0) { |
655 | DRM_DEV_ERROR(drm_dev->dev, |
656 | "failed to attach encoder: %d\n" , ret); |
657 | goto err_free_connector; |
658 | } |
659 | |
660 | pm_runtime_enable(dev); |
661 | of_node_put(node: remote); |
662 | of_node_put(node: port); |
663 | |
664 | return 0; |
665 | |
666 | err_free_connector: |
667 | drm_connector_cleanup(connector); |
668 | err_free_encoder: |
669 | drm_encoder_cleanup(encoder); |
670 | err_put_remote: |
671 | of_node_put(node: remote); |
672 | err_put_port: |
673 | of_node_put(node: port); |
674 | |
675 | return ret; |
676 | } |
677 | |
678 | static void rockchip_lvds_unbind(struct device *dev, struct device *master, |
679 | void *data) |
680 | { |
681 | struct rockchip_lvds *lvds = dev_get_drvdata(dev); |
682 | const struct drm_encoder_helper_funcs *encoder_funcs; |
683 | |
684 | encoder_funcs = lvds->soc_data->helper_funcs; |
685 | encoder_funcs->disable(&lvds->encoder.encoder); |
686 | pm_runtime_disable(dev); |
687 | drm_connector_cleanup(connector: &lvds->connector); |
688 | drm_encoder_cleanup(encoder: &lvds->encoder.encoder); |
689 | } |
690 | |
691 | static const struct component_ops rockchip_lvds_component_ops = { |
692 | .bind = rockchip_lvds_bind, |
693 | .unbind = rockchip_lvds_unbind, |
694 | }; |
695 | |
696 | static int rockchip_lvds_probe(struct platform_device *pdev) |
697 | { |
698 | struct device *dev = &pdev->dev; |
699 | struct rockchip_lvds *lvds; |
700 | const struct of_device_id *match; |
701 | int ret; |
702 | |
703 | if (!dev->of_node) |
704 | return -ENODEV; |
705 | |
706 | lvds = devm_kzalloc(dev: &pdev->dev, size: sizeof(*lvds), GFP_KERNEL); |
707 | if (!lvds) |
708 | return -ENOMEM; |
709 | |
710 | lvds->dev = dev; |
711 | match = of_match_node(matches: rockchip_lvds_dt_ids, node: dev->of_node); |
712 | if (!match) |
713 | return -ENODEV; |
714 | lvds->soc_data = match->data; |
715 | |
716 | lvds->grf = syscon_regmap_lookup_by_phandle(np: dev->of_node, |
717 | property: "rockchip,grf" ); |
718 | if (IS_ERR(ptr: lvds->grf)) { |
719 | DRM_DEV_ERROR(dev, "missing rockchip,grf property\n" ); |
720 | return PTR_ERR(ptr: lvds->grf); |
721 | } |
722 | |
723 | ret = lvds->soc_data->probe(pdev, lvds); |
724 | if (ret) { |
725 | DRM_DEV_ERROR(dev, "Platform initialization failed\n" ); |
726 | return ret; |
727 | } |
728 | |
729 | dev_set_drvdata(dev, data: lvds); |
730 | |
731 | ret = component_add(&pdev->dev, &rockchip_lvds_component_ops); |
732 | if (ret < 0) { |
733 | DRM_DEV_ERROR(dev, "failed to add component\n" ); |
734 | clk_unprepare(clk: lvds->pclk); |
735 | } |
736 | |
737 | return ret; |
738 | } |
739 | |
740 | static void rockchip_lvds_remove(struct platform_device *pdev) |
741 | { |
742 | struct rockchip_lvds *lvds = platform_get_drvdata(pdev); |
743 | |
744 | component_del(&pdev->dev, &rockchip_lvds_component_ops); |
745 | clk_unprepare(clk: lvds->pclk); |
746 | } |
747 | |
748 | struct platform_driver rockchip_lvds_driver = { |
749 | .probe = rockchip_lvds_probe, |
750 | .remove_new = rockchip_lvds_remove, |
751 | .driver = { |
752 | .name = "rockchip-lvds" , |
753 | .of_match_table = rockchip_lvds_dt_ids, |
754 | }, |
755 | }; |
756 | |