1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2009 Nokia Corporation |
4 | * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> |
5 | * |
6 | * Some code and ideas taken from drivers/video/omap/ driver |
7 | * by Imre Deak. |
8 | */ |
9 | |
10 | #define DSS_SUBSYS_NAME "DPI" |
11 | |
12 | #include <linux/clk.h> |
13 | #include <linux/delay.h> |
14 | #include <linux/err.h> |
15 | #include <linux/errno.h> |
16 | #include <linux/export.h> |
17 | #include <linux/kernel.h> |
18 | #include <linux/of.h> |
19 | #include <linux/platform_device.h> |
20 | #include <linux/regulator/consumer.h> |
21 | #include <linux/string.h> |
22 | #include <linux/sys_soc.h> |
23 | |
24 | #include <drm/drm_bridge.h> |
25 | |
26 | #include "dss.h" |
27 | #include "omapdss.h" |
28 | |
29 | struct dpi_data { |
30 | struct platform_device *pdev; |
31 | enum dss_model dss_model; |
32 | struct dss_device *dss; |
33 | unsigned int id; |
34 | |
35 | struct regulator *vdds_dsi_reg; |
36 | enum dss_clk_source clk_src; |
37 | struct dss_pll *pll; |
38 | |
39 | struct dss_lcd_mgr_config mgr_config; |
40 | unsigned long pixelclock; |
41 | int data_lines; |
42 | |
43 | struct omap_dss_device output; |
44 | struct drm_bridge bridge; |
45 | }; |
46 | |
47 | #define drm_bridge_to_dpi(bridge) container_of(bridge, struct dpi_data, bridge) |
48 | |
49 | /* ----------------------------------------------------------------------------- |
50 | * Clock Handling and PLL |
51 | */ |
52 | |
53 | static enum dss_clk_source dpi_get_clk_src_dra7xx(struct dpi_data *dpi, |
54 | enum omap_channel channel) |
55 | { |
56 | /* |
57 | * Possible clock sources: |
58 | * LCD1: FCK/PLL1_1/HDMI_PLL |
59 | * LCD2: FCK/PLL1_3/HDMI_PLL (DRA74x: PLL2_3) |
60 | * LCD3: FCK/PLL1_3/HDMI_PLL (DRA74x: PLL2_1) |
61 | */ |
62 | |
63 | switch (channel) { |
64 | case OMAP_DSS_CHANNEL_LCD: |
65 | { |
66 | if (dss_pll_find_by_src(dss: dpi->dss, src: DSS_CLK_SRC_PLL1_1)) |
67 | return DSS_CLK_SRC_PLL1_1; |
68 | break; |
69 | } |
70 | case OMAP_DSS_CHANNEL_LCD2: |
71 | { |
72 | if (dss_pll_find_by_src(dss: dpi->dss, src: DSS_CLK_SRC_PLL1_3)) |
73 | return DSS_CLK_SRC_PLL1_3; |
74 | if (dss_pll_find_by_src(dss: dpi->dss, src: DSS_CLK_SRC_PLL2_3)) |
75 | return DSS_CLK_SRC_PLL2_3; |
76 | break; |
77 | } |
78 | case OMAP_DSS_CHANNEL_LCD3: |
79 | { |
80 | if (dss_pll_find_by_src(dss: dpi->dss, src: DSS_CLK_SRC_PLL2_1)) |
81 | return DSS_CLK_SRC_PLL2_1; |
82 | if (dss_pll_find_by_src(dss: dpi->dss, src: DSS_CLK_SRC_PLL1_3)) |
83 | return DSS_CLK_SRC_PLL1_3; |
84 | break; |
85 | } |
86 | default: |
87 | break; |
88 | } |
89 | |
90 | return DSS_CLK_SRC_FCK; |
91 | } |
92 | |
93 | static enum dss_clk_source dpi_get_clk_src(struct dpi_data *dpi) |
94 | { |
95 | enum omap_channel channel = dpi->output.dispc_channel; |
96 | |
97 | /* |
98 | * XXX we can't currently use DSI PLL for DPI with OMAP3, as the DSI PLL |
99 | * would also be used for DISPC fclk. Meaning, when the DPI output is |
100 | * disabled, DISPC clock will be disabled, and TV out will stop. |
101 | */ |
102 | switch (dpi->dss_model) { |
103 | case DSS_MODEL_OMAP2: |
104 | case DSS_MODEL_OMAP3: |
105 | return DSS_CLK_SRC_FCK; |
106 | |
107 | case DSS_MODEL_OMAP4: |
108 | switch (channel) { |
109 | case OMAP_DSS_CHANNEL_LCD: |
110 | return DSS_CLK_SRC_PLL1_1; |
111 | case OMAP_DSS_CHANNEL_LCD2: |
112 | return DSS_CLK_SRC_PLL2_1; |
113 | default: |
114 | return DSS_CLK_SRC_FCK; |
115 | } |
116 | |
117 | case DSS_MODEL_OMAP5: |
118 | switch (channel) { |
119 | case OMAP_DSS_CHANNEL_LCD: |
120 | return DSS_CLK_SRC_PLL1_1; |
121 | case OMAP_DSS_CHANNEL_LCD3: |
122 | return DSS_CLK_SRC_PLL2_1; |
123 | case OMAP_DSS_CHANNEL_LCD2: |
124 | default: |
125 | return DSS_CLK_SRC_FCK; |
126 | } |
127 | |
128 | case DSS_MODEL_DRA7: |
129 | return dpi_get_clk_src_dra7xx(dpi, channel); |
130 | |
131 | default: |
132 | return DSS_CLK_SRC_FCK; |
133 | } |
134 | } |
135 | |
136 | struct dpi_clk_calc_ctx { |
137 | struct dpi_data *dpi; |
138 | unsigned int clkout_idx; |
139 | |
140 | /* inputs */ |
141 | |
142 | unsigned long pck_min, pck_max; |
143 | |
144 | /* outputs */ |
145 | |
146 | struct dss_pll_clock_info pll_cinfo; |
147 | unsigned long fck; |
148 | struct dispc_clock_info dispc_cinfo; |
149 | }; |
150 | |
151 | static bool dpi_calc_dispc_cb(int lckd, int pckd, unsigned long lck, |
152 | unsigned long pck, void *data) |
153 | { |
154 | struct dpi_clk_calc_ctx *ctx = data; |
155 | |
156 | /* |
157 | * Odd dividers give us uneven duty cycle, causing problem when level |
158 | * shifted. So skip all odd dividers when the pixel clock is on the |
159 | * higher side. |
160 | */ |
161 | if (ctx->pck_min >= 100000000) { |
162 | if (lckd > 1 && lckd % 2 != 0) |
163 | return false; |
164 | |
165 | if (pckd > 1 && pckd % 2 != 0) |
166 | return false; |
167 | } |
168 | |
169 | ctx->dispc_cinfo.lck_div = lckd; |
170 | ctx->dispc_cinfo.pck_div = pckd; |
171 | ctx->dispc_cinfo.lck = lck; |
172 | ctx->dispc_cinfo.pck = pck; |
173 | |
174 | return true; |
175 | } |
176 | |
177 | |
178 | static bool dpi_calc_hsdiv_cb(int m_dispc, unsigned long dispc, |
179 | void *data) |
180 | { |
181 | struct dpi_clk_calc_ctx *ctx = data; |
182 | |
183 | ctx->pll_cinfo.mX[ctx->clkout_idx] = m_dispc; |
184 | ctx->pll_cinfo.clkout[ctx->clkout_idx] = dispc; |
185 | |
186 | return dispc_div_calc(dispc: ctx->dpi->dss->dispc, dispc_freq: dispc, |
187 | pck_min: ctx->pck_min, pck_max: ctx->pck_max, |
188 | func: dpi_calc_dispc_cb, data: ctx); |
189 | } |
190 | |
191 | |
192 | static bool dpi_calc_pll_cb(int n, int m, unsigned long fint, |
193 | unsigned long clkdco, |
194 | void *data) |
195 | { |
196 | struct dpi_clk_calc_ctx *ctx = data; |
197 | |
198 | ctx->pll_cinfo.n = n; |
199 | ctx->pll_cinfo.m = m; |
200 | ctx->pll_cinfo.fint = fint; |
201 | ctx->pll_cinfo.clkdco = clkdco; |
202 | |
203 | return dss_pll_hsdiv_calc_a(pll: ctx->dpi->pll, clkdco, |
204 | out_min: ctx->pck_min, out_max: dss_get_max_fck_rate(dss: ctx->dpi->dss), |
205 | func: dpi_calc_hsdiv_cb, data: ctx); |
206 | } |
207 | |
208 | static bool dpi_calc_dss_cb(unsigned long fck, void *data) |
209 | { |
210 | struct dpi_clk_calc_ctx *ctx = data; |
211 | |
212 | ctx->fck = fck; |
213 | |
214 | return dispc_div_calc(dispc: ctx->dpi->dss->dispc, dispc_freq: fck, |
215 | pck_min: ctx->pck_min, pck_max: ctx->pck_max, |
216 | func: dpi_calc_dispc_cb, data: ctx); |
217 | } |
218 | |
219 | static bool dpi_pll_clk_calc(struct dpi_data *dpi, unsigned long pck, |
220 | struct dpi_clk_calc_ctx *ctx) |
221 | { |
222 | unsigned long clkin; |
223 | |
224 | memset(ctx, 0, sizeof(*ctx)); |
225 | ctx->dpi = dpi; |
226 | ctx->clkout_idx = dss_pll_get_clkout_idx_for_src(src: dpi->clk_src); |
227 | |
228 | clkin = clk_get_rate(clk: dpi->pll->clkin); |
229 | |
230 | if (dpi->pll->hw->type == DSS_PLL_TYPE_A) { |
231 | unsigned long pll_min, pll_max; |
232 | |
233 | ctx->pck_min = pck - 1000; |
234 | ctx->pck_max = pck + 1000; |
235 | |
236 | pll_min = 0; |
237 | pll_max = 0; |
238 | |
239 | return dss_pll_calc_a(pll: ctx->dpi->pll, clkin, |
240 | pll_min, pll_max, |
241 | func: dpi_calc_pll_cb, data: ctx); |
242 | } else { /* DSS_PLL_TYPE_B */ |
243 | dss_pll_calc_b(pll: dpi->pll, clkin, target_clkout: pck, cinfo: &ctx->pll_cinfo); |
244 | |
245 | ctx->dispc_cinfo.lck_div = 1; |
246 | ctx->dispc_cinfo.pck_div = 1; |
247 | ctx->dispc_cinfo.lck = ctx->pll_cinfo.clkout[0]; |
248 | ctx->dispc_cinfo.pck = ctx->dispc_cinfo.lck; |
249 | |
250 | return true; |
251 | } |
252 | } |
253 | |
254 | static bool dpi_dss_clk_calc(struct dpi_data *dpi, unsigned long pck, |
255 | struct dpi_clk_calc_ctx *ctx) |
256 | { |
257 | int i; |
258 | |
259 | /* |
260 | * DSS fck gives us very few possibilities, so finding a good pixel |
261 | * clock may not be possible. We try multiple times to find the clock, |
262 | * each time widening the pixel clock range we look for, up to |
263 | * +/- ~15MHz. |
264 | */ |
265 | |
266 | for (i = 0; i < 25; ++i) { |
267 | bool ok; |
268 | |
269 | memset(ctx, 0, sizeof(*ctx)); |
270 | ctx->dpi = dpi; |
271 | if (pck > 1000 * i * i * i) |
272 | ctx->pck_min = max(pck - 1000 * i * i * i, 0lu); |
273 | else |
274 | ctx->pck_min = 0; |
275 | ctx->pck_max = pck + 1000 * i * i * i; |
276 | |
277 | ok = dss_div_calc(dss: dpi->dss, pck, fck_min: ctx->pck_min, |
278 | func: dpi_calc_dss_cb, data: ctx); |
279 | if (ok) |
280 | return ok; |
281 | } |
282 | |
283 | return false; |
284 | } |
285 | |
286 | |
287 | |
288 | static int dpi_set_pll_clk(struct dpi_data *dpi, unsigned long pck_req) |
289 | { |
290 | struct dpi_clk_calc_ctx ctx; |
291 | int r; |
292 | bool ok; |
293 | |
294 | ok = dpi_pll_clk_calc(dpi, pck: pck_req, ctx: &ctx); |
295 | if (!ok) |
296 | return -EINVAL; |
297 | |
298 | r = dss_pll_set_config(pll: dpi->pll, cinfo: &ctx.pll_cinfo); |
299 | if (r) |
300 | return r; |
301 | |
302 | dss_select_lcd_clk_source(dss: dpi->dss, channel: dpi->output.dispc_channel, |
303 | clk_src: dpi->clk_src); |
304 | |
305 | dpi->mgr_config.clock_info = ctx.dispc_cinfo; |
306 | |
307 | return 0; |
308 | } |
309 | |
310 | static int dpi_set_dispc_clk(struct dpi_data *dpi, unsigned long pck_req) |
311 | { |
312 | struct dpi_clk_calc_ctx ctx; |
313 | int r; |
314 | bool ok; |
315 | |
316 | ok = dpi_dss_clk_calc(dpi, pck: pck_req, ctx: &ctx); |
317 | if (!ok) |
318 | return -EINVAL; |
319 | |
320 | r = dss_set_fck_rate(dss: dpi->dss, rate: ctx.fck); |
321 | if (r) |
322 | return r; |
323 | |
324 | dpi->mgr_config.clock_info = ctx.dispc_cinfo; |
325 | |
326 | return 0; |
327 | } |
328 | |
329 | static int dpi_set_mode(struct dpi_data *dpi) |
330 | { |
331 | int r; |
332 | |
333 | if (dpi->pll) |
334 | r = dpi_set_pll_clk(dpi, pck_req: dpi->pixelclock); |
335 | else |
336 | r = dpi_set_dispc_clk(dpi, pck_req: dpi->pixelclock); |
337 | |
338 | return r; |
339 | } |
340 | |
341 | static void dpi_config_lcd_manager(struct dpi_data *dpi) |
342 | { |
343 | dpi->mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS; |
344 | |
345 | dpi->mgr_config.stallmode = false; |
346 | dpi->mgr_config.fifohandcheck = false; |
347 | |
348 | dpi->mgr_config.video_port_width = dpi->data_lines; |
349 | |
350 | dpi->mgr_config.lcden_sig_polarity = 0; |
351 | |
352 | dss_mgr_set_lcd_config(dssdev: &dpi->output, config: &dpi->mgr_config); |
353 | } |
354 | |
355 | static int dpi_clock_update(struct dpi_data *dpi, unsigned long *clock) |
356 | { |
357 | int lck_div, pck_div; |
358 | unsigned long fck; |
359 | struct dpi_clk_calc_ctx ctx; |
360 | |
361 | if (dpi->pll) { |
362 | if (!dpi_pll_clk_calc(dpi, pck: *clock, ctx: &ctx)) |
363 | return -EINVAL; |
364 | |
365 | fck = ctx.pll_cinfo.clkout[ctx.clkout_idx]; |
366 | } else { |
367 | if (!dpi_dss_clk_calc(dpi, pck: *clock, ctx: &ctx)) |
368 | return -EINVAL; |
369 | |
370 | fck = ctx.fck; |
371 | } |
372 | |
373 | lck_div = ctx.dispc_cinfo.lck_div; |
374 | pck_div = ctx.dispc_cinfo.pck_div; |
375 | |
376 | *clock = fck / lck_div / pck_div; |
377 | |
378 | return 0; |
379 | } |
380 | |
381 | static int dpi_verify_pll(struct dss_pll *pll) |
382 | { |
383 | int r; |
384 | |
385 | /* do initial setup with the PLL to see if it is operational */ |
386 | |
387 | r = dss_pll_enable(pll); |
388 | if (r) |
389 | return r; |
390 | |
391 | dss_pll_disable(pll); |
392 | |
393 | return 0; |
394 | } |
395 | |
396 | static void dpi_init_pll(struct dpi_data *dpi) |
397 | { |
398 | struct dss_pll *pll; |
399 | |
400 | if (dpi->pll) |
401 | return; |
402 | |
403 | dpi->clk_src = dpi_get_clk_src(dpi); |
404 | |
405 | pll = dss_pll_find_by_src(dss: dpi->dss, src: dpi->clk_src); |
406 | if (!pll) |
407 | return; |
408 | |
409 | if (dpi_verify_pll(pll)) { |
410 | DSSWARN("PLL not operational\n" ); |
411 | return; |
412 | } |
413 | |
414 | dpi->pll = pll; |
415 | } |
416 | |
417 | /* ----------------------------------------------------------------------------- |
418 | * DRM Bridge Operations |
419 | */ |
420 | |
421 | static int dpi_bridge_attach(struct drm_bridge *bridge, |
422 | enum drm_bridge_attach_flags flags) |
423 | { |
424 | struct dpi_data *dpi = drm_bridge_to_dpi(bridge); |
425 | |
426 | if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) |
427 | return -EINVAL; |
428 | |
429 | dpi_init_pll(dpi); |
430 | |
431 | return drm_bridge_attach(encoder: bridge->encoder, bridge: dpi->output.next_bridge, |
432 | previous: bridge, flags); |
433 | } |
434 | |
435 | static enum drm_mode_status |
436 | dpi_bridge_mode_valid(struct drm_bridge *bridge, |
437 | const struct drm_display_info *info, |
438 | const struct drm_display_mode *mode) |
439 | { |
440 | struct dpi_data *dpi = drm_bridge_to_dpi(bridge); |
441 | unsigned long clock = mode->clock * 1000; |
442 | int ret; |
443 | |
444 | if (mode->hdisplay % 8 != 0) |
445 | return MODE_BAD_WIDTH; |
446 | |
447 | if (mode->clock == 0) |
448 | return MODE_NOCLOCK; |
449 | |
450 | ret = dpi_clock_update(dpi, clock: &clock); |
451 | if (ret < 0) |
452 | return MODE_CLOCK_RANGE; |
453 | |
454 | return MODE_OK; |
455 | } |
456 | |
457 | static bool dpi_bridge_mode_fixup(struct drm_bridge *bridge, |
458 | const struct drm_display_mode *mode, |
459 | struct drm_display_mode *adjusted_mode) |
460 | { |
461 | struct dpi_data *dpi = drm_bridge_to_dpi(bridge); |
462 | unsigned long clock = mode->clock * 1000; |
463 | int ret; |
464 | |
465 | ret = dpi_clock_update(dpi, clock: &clock); |
466 | if (ret < 0) |
467 | return false; |
468 | |
469 | adjusted_mode->clock = clock / 1000; |
470 | |
471 | return true; |
472 | } |
473 | |
474 | static void dpi_bridge_mode_set(struct drm_bridge *bridge, |
475 | const struct drm_display_mode *mode, |
476 | const struct drm_display_mode *adjusted_mode) |
477 | { |
478 | struct dpi_data *dpi = drm_bridge_to_dpi(bridge); |
479 | |
480 | dpi->pixelclock = adjusted_mode->clock * 1000; |
481 | } |
482 | |
483 | static void dpi_bridge_enable(struct drm_bridge *bridge) |
484 | { |
485 | struct dpi_data *dpi = drm_bridge_to_dpi(bridge); |
486 | int r; |
487 | |
488 | if (dpi->vdds_dsi_reg) { |
489 | r = regulator_enable(regulator: dpi->vdds_dsi_reg); |
490 | if (r) |
491 | return; |
492 | } |
493 | |
494 | r = dispc_runtime_get(dispc: dpi->dss->dispc); |
495 | if (r) |
496 | goto err_get_dispc; |
497 | |
498 | r = dss_dpi_select_source(dss: dpi->dss, port: dpi->id, channel: dpi->output.dispc_channel); |
499 | if (r) |
500 | goto err_src_sel; |
501 | |
502 | if (dpi->pll) { |
503 | r = dss_pll_enable(pll: dpi->pll); |
504 | if (r) |
505 | goto err_pll_init; |
506 | } |
507 | |
508 | r = dpi_set_mode(dpi); |
509 | if (r) |
510 | goto err_set_mode; |
511 | |
512 | dpi_config_lcd_manager(dpi); |
513 | |
514 | mdelay(2); |
515 | |
516 | r = dss_mgr_enable(dssdev: &dpi->output); |
517 | if (r) |
518 | goto err_mgr_enable; |
519 | |
520 | return; |
521 | |
522 | err_mgr_enable: |
523 | err_set_mode: |
524 | if (dpi->pll) |
525 | dss_pll_disable(pll: dpi->pll); |
526 | err_pll_init: |
527 | err_src_sel: |
528 | dispc_runtime_put(dispc: dpi->dss->dispc); |
529 | err_get_dispc: |
530 | if (dpi->vdds_dsi_reg) |
531 | regulator_disable(regulator: dpi->vdds_dsi_reg); |
532 | } |
533 | |
534 | static void dpi_bridge_disable(struct drm_bridge *bridge) |
535 | { |
536 | struct dpi_data *dpi = drm_bridge_to_dpi(bridge); |
537 | |
538 | dss_mgr_disable(dssdev: &dpi->output); |
539 | |
540 | if (dpi->pll) { |
541 | dss_select_lcd_clk_source(dss: dpi->dss, channel: dpi->output.dispc_channel, |
542 | clk_src: DSS_CLK_SRC_FCK); |
543 | dss_pll_disable(pll: dpi->pll); |
544 | } |
545 | |
546 | dispc_runtime_put(dispc: dpi->dss->dispc); |
547 | |
548 | if (dpi->vdds_dsi_reg) |
549 | regulator_disable(regulator: dpi->vdds_dsi_reg); |
550 | } |
551 | |
552 | static const struct drm_bridge_funcs dpi_bridge_funcs = { |
553 | .attach = dpi_bridge_attach, |
554 | .mode_valid = dpi_bridge_mode_valid, |
555 | .mode_fixup = dpi_bridge_mode_fixup, |
556 | .mode_set = dpi_bridge_mode_set, |
557 | .enable = dpi_bridge_enable, |
558 | .disable = dpi_bridge_disable, |
559 | }; |
560 | |
561 | static void dpi_bridge_init(struct dpi_data *dpi) |
562 | { |
563 | dpi->bridge.funcs = &dpi_bridge_funcs; |
564 | dpi->bridge.of_node = dpi->pdev->dev.of_node; |
565 | dpi->bridge.type = DRM_MODE_CONNECTOR_DPI; |
566 | |
567 | drm_bridge_add(bridge: &dpi->bridge); |
568 | } |
569 | |
570 | static void dpi_bridge_cleanup(struct dpi_data *dpi) |
571 | { |
572 | drm_bridge_remove(bridge: &dpi->bridge); |
573 | } |
574 | |
575 | /* ----------------------------------------------------------------------------- |
576 | * Initialisation and Cleanup |
577 | */ |
578 | |
579 | /* |
580 | * Return a hardcoded channel for the DPI output. This should work for |
581 | * current use cases, but this can be later expanded to either resolve |
582 | * the channel in some more dynamic manner, or get the channel as a user |
583 | * parameter. |
584 | */ |
585 | static enum omap_channel dpi_get_channel(struct dpi_data *dpi) |
586 | { |
587 | switch (dpi->dss_model) { |
588 | case DSS_MODEL_OMAP2: |
589 | case DSS_MODEL_OMAP3: |
590 | return OMAP_DSS_CHANNEL_LCD; |
591 | |
592 | case DSS_MODEL_DRA7: |
593 | switch (dpi->id) { |
594 | case 2: |
595 | return OMAP_DSS_CHANNEL_LCD3; |
596 | case 1: |
597 | return OMAP_DSS_CHANNEL_LCD2; |
598 | case 0: |
599 | default: |
600 | return OMAP_DSS_CHANNEL_LCD; |
601 | } |
602 | |
603 | case DSS_MODEL_OMAP4: |
604 | return OMAP_DSS_CHANNEL_LCD2; |
605 | |
606 | case DSS_MODEL_OMAP5: |
607 | return OMAP_DSS_CHANNEL_LCD3; |
608 | |
609 | default: |
610 | DSSWARN("unsupported DSS version\n" ); |
611 | return OMAP_DSS_CHANNEL_LCD; |
612 | } |
613 | } |
614 | |
615 | static int dpi_init_output_port(struct dpi_data *dpi, struct device_node *port) |
616 | { |
617 | struct omap_dss_device *out = &dpi->output; |
618 | u32 port_num = 0; |
619 | int r; |
620 | |
621 | dpi_bridge_init(dpi); |
622 | |
623 | of_property_read_u32(np: port, propname: "reg" , out_value: &port_num); |
624 | dpi->id = port_num <= 2 ? port_num : 0; |
625 | |
626 | switch (port_num) { |
627 | case 2: |
628 | out->name = "dpi.2" ; |
629 | break; |
630 | case 1: |
631 | out->name = "dpi.1" ; |
632 | break; |
633 | case 0: |
634 | default: |
635 | out->name = "dpi.0" ; |
636 | break; |
637 | } |
638 | |
639 | out->dev = &dpi->pdev->dev; |
640 | out->id = OMAP_DSS_OUTPUT_DPI; |
641 | out->type = OMAP_DISPLAY_TYPE_DPI; |
642 | out->dispc_channel = dpi_get_channel(dpi); |
643 | out->of_port = port_num; |
644 | |
645 | r = omapdss_device_init_output(out, local_bridge: &dpi->bridge); |
646 | if (r < 0) { |
647 | dpi_bridge_cleanup(dpi); |
648 | return r; |
649 | } |
650 | |
651 | omapdss_device_register(dssdev: out); |
652 | |
653 | return 0; |
654 | } |
655 | |
656 | static void dpi_uninit_output_port(struct device_node *port) |
657 | { |
658 | struct dpi_data *dpi = port->data; |
659 | struct omap_dss_device *out = &dpi->output; |
660 | |
661 | omapdss_device_unregister(dssdev: out); |
662 | omapdss_device_cleanup_output(out); |
663 | |
664 | dpi_bridge_cleanup(dpi); |
665 | } |
666 | |
667 | /* ----------------------------------------------------------------------------- |
668 | * Initialisation and Cleanup |
669 | */ |
670 | |
671 | static const struct soc_device_attribute dpi_soc_devices[] = { |
672 | { .machine = "OMAP3[456]*" }, |
673 | { .machine = "[AD]M37*" }, |
674 | { /* sentinel */ } |
675 | }; |
676 | |
677 | static int dpi_init_regulator(struct dpi_data *dpi) |
678 | { |
679 | struct regulator *vdds_dsi; |
680 | |
681 | /* |
682 | * The DPI uses the DSI VDDS on OMAP34xx, OMAP35xx, OMAP36xx, AM37xx and |
683 | * DM37xx only. |
684 | */ |
685 | if (!soc_device_match(matches: dpi_soc_devices)) |
686 | return 0; |
687 | |
688 | vdds_dsi = devm_regulator_get(dev: &dpi->pdev->dev, id: "vdds_dsi" ); |
689 | if (IS_ERR(ptr: vdds_dsi)) { |
690 | if (PTR_ERR(ptr: vdds_dsi) != -EPROBE_DEFER) |
691 | DSSERR("can't get VDDS_DSI regulator\n" ); |
692 | return PTR_ERR(ptr: vdds_dsi); |
693 | } |
694 | |
695 | dpi->vdds_dsi_reg = vdds_dsi; |
696 | |
697 | return 0; |
698 | } |
699 | |
700 | int dpi_init_port(struct dss_device *dss, struct platform_device *pdev, |
701 | struct device_node *port, enum dss_model dss_model) |
702 | { |
703 | struct dpi_data *dpi; |
704 | struct device_node *ep; |
705 | u32 datalines; |
706 | int r; |
707 | |
708 | dpi = devm_kzalloc(dev: &pdev->dev, size: sizeof(*dpi), GFP_KERNEL); |
709 | if (!dpi) |
710 | return -ENOMEM; |
711 | |
712 | ep = of_get_next_child(node: port, NULL); |
713 | if (!ep) |
714 | return 0; |
715 | |
716 | r = of_property_read_u32(np: ep, propname: "data-lines" , out_value: &datalines); |
717 | of_node_put(node: ep); |
718 | if (r) { |
719 | DSSERR("failed to parse datalines\n" ); |
720 | return r; |
721 | } |
722 | |
723 | dpi->data_lines = datalines; |
724 | |
725 | dpi->pdev = pdev; |
726 | dpi->dss_model = dss_model; |
727 | dpi->dss = dss; |
728 | port->data = dpi; |
729 | |
730 | r = dpi_init_regulator(dpi); |
731 | if (r) |
732 | return r; |
733 | |
734 | return dpi_init_output_port(dpi, port); |
735 | } |
736 | |
737 | void dpi_uninit_port(struct device_node *port) |
738 | { |
739 | struct dpi_data *dpi = port->data; |
740 | |
741 | if (!dpi) |
742 | return; |
743 | |
744 | dpi_uninit_output_port(port); |
745 | } |
746 | |