1 /*
2 * linux/drivers/video/omap2/dss/dpi.c
3 *
4 * Copyright (C) 2009 Nokia Corporation
5 * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
6 *
7 * Some code and ideas taken from drivers/video/omap/ driver
8 * by Imre Deak.
9 *
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License version 2 as published by
12 * the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17 * more details.
18 *
19 * You should have received a copy of the GNU General Public License along with
20 * this program. If not, see <http://www.gnu.org/licenses/>.
21 */
23 #define DSS_SUBSYS_NAME "DPI"
25 #include <linux/kernel.h>
26 #include <linux/delay.h>
27 #include <linux/export.h>
28 #include <linux/err.h>
29 #include <linux/errno.h>
30 #include <linux/platform_device.h>
31 #include <linux/regulator/consumer.h>
32 #include <linux/string.h>
33 #include <linux/slab.h>
34 #include <linux/of.h>
36 #include <video/omapdss.h>
38 #include "dss.h"
39 #include "dss_features.h"
41 static struct {
42 struct regulator *vdds_dsi_reg;
43 struct platform_device *dsidev;
45 struct mutex lock;
47 struct omap_video_timings timings;
48 struct dss_lcd_mgr_config mgr_config;
49 int data_lines;
51 struct omap_dss_output output;
52 } dpi;
54 static struct platform_device *dpi_get_dsidev(enum omap_channel channel)
55 {
56 /*
57 * XXX we can't currently use DSI PLL for DPI with OMAP3, as the DSI PLL
58 * would also be used for DISPC fclk. Meaning, when the DPI output is
59 * disabled, DISPC clock will be disabled, and TV out will stop.
60 */
61 switch (omapdss_get_version()) {
62 case OMAPDSS_VER_OMAP24xx:
63 case OMAPDSS_VER_OMAP34xx_ES1:
64 case OMAPDSS_VER_OMAP34xx_ES3:
65 case OMAPDSS_VER_OMAP3630:
66 case OMAPDSS_VER_AM35xx:
67 return NULL;
68 default:
69 break;
70 }
72 switch (channel) {
73 case OMAP_DSS_CHANNEL_LCD:
74 return dsi_get_dsidev_from_id(0);
75 case OMAP_DSS_CHANNEL_LCD2:
76 return dsi_get_dsidev_from_id(1);
77 default:
78 return NULL;
79 }
80 }
82 static enum omap_dss_clk_source dpi_get_alt_clk_src(enum omap_channel channel)
83 {
84 switch (channel) {
85 case OMAP_DSS_CHANNEL_LCD:
86 return OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC;
87 case OMAP_DSS_CHANNEL_LCD2:
88 return OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC;
89 default:
90 /* this shouldn't happen */
91 WARN_ON(1);
92 return OMAP_DSS_CLK_SRC_FCK;
93 }
94 }
96 static int dpi_set_dsi_clk(struct omap_dss_device *dssdev,
97 unsigned long pck_req, unsigned long *fck, int *lck_div,
98 int *pck_div)
99 {
100 struct omap_overlay_manager *mgr = dssdev->output->manager;
101 struct dsi_clock_info dsi_cinfo;
102 struct dispc_clock_info dispc_cinfo;
103 int r;
105 r = dsi_pll_calc_clock_div_pck(dpi.dsidev, pck_req, &dsi_cinfo,
106 &dispc_cinfo);
107 if (r)
108 return r;
110 r = dsi_pll_set_clock_div(dpi.dsidev, &dsi_cinfo);
111 if (r)
112 return r;
114 dss_select_lcd_clk_source(mgr->id,
115 dpi_get_alt_clk_src(mgr->id));
117 dpi.mgr_config.clock_info = dispc_cinfo;
119 *fck = dsi_cinfo.dsi_pll_hsdiv_dispc_clk;
120 *lck_div = dispc_cinfo.lck_div;
121 *pck_div = dispc_cinfo.pck_div;
123 return 0;
124 }
126 static int dpi_set_dispc_clk(struct omap_dss_device *dssdev,
127 unsigned long pck_req, unsigned long *fck, int *lck_div,
128 int *pck_div)
129 {
130 struct dss_clock_info dss_cinfo;
131 struct dispc_clock_info dispc_cinfo;
132 int r;
134 r = dss_calc_clock_div(pck_req, &dss_cinfo, &dispc_cinfo);
135 if (r)
136 return r;
138 r = dss_set_clock_div(&dss_cinfo);
139 if (r)
140 return r;
142 dpi.mgr_config.clock_info = dispc_cinfo;
144 *fck = dss_cinfo.fck;
145 *lck_div = dispc_cinfo.lck_div;
146 *pck_div = dispc_cinfo.pck_div;
148 return 0;
149 }
151 static int dpi_set_mode(struct omap_dss_device *dssdev)
152 {
153 struct omap_video_timings *t = &dpi.timings;
154 struct omap_overlay_manager *mgr = dssdev->output->manager;
155 int lck_div = 0, pck_div = 0;
156 unsigned long fck = 0;
157 unsigned long pck;
158 int r = 0;
160 if (dpi.dsidev)
161 r = dpi_set_dsi_clk(dssdev, t->pixel_clock * 1000, &fck,
162 &lck_div, &pck_div);
163 else
164 r = dpi_set_dispc_clk(dssdev, t->pixel_clock * 1000, &fck,
165 &lck_div, &pck_div);
166 if (r)
167 return r;
169 pck = fck / lck_div / pck_div / 1000;
171 if (pck != t->pixel_clock) {
172 DSSWARN("Could not find exact pixel clock. "
173 "Requested %d kHz, got %lu kHz\n",
174 t->pixel_clock, pck);
176 t->pixel_clock = pck;
177 }
179 dss_mgr_set_timings(mgr, t);
181 return 0;
182 }
184 static void dpi_config_lcd_manager(struct omap_dss_device *dssdev)
185 {
186 struct omap_overlay_manager *mgr = dssdev->output->manager;
188 dpi.mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS;
190 dpi.mgr_config.stallmode = false;
191 dpi.mgr_config.fifohandcheck = false;
193 dpi.mgr_config.video_port_width = dpi.data_lines;
195 dpi.mgr_config.lcden_sig_polarity = 0;
197 dss_mgr_set_lcd_config(mgr, &dpi.mgr_config);
198 }
200 static int omap_dpi_display_enable(struct omap_dss_device *dssdev)
201 {
202 struct omap_dss_output *out = dssdev->output;
203 int r;
205 mutex_lock(&dpi.lock);
207 if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI) && !dpi.vdds_dsi_reg) {
208 DSSERR("no VDSS_DSI regulator\n");
209 r = -ENODEV;
210 goto err_no_reg;
211 }
213 if (out == NULL || out->manager == NULL) {
214 DSSERR("failed to enable display: no output/manager\n");
215 r = -ENODEV;
216 goto err_no_out_mgr;
217 }
219 r = omap_dss_start_device(dssdev);
220 if (r) {
221 DSSERR("failed to start device\n");
222 goto err_start_dev;
223 }
225 if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI)) {
226 r = regulator_enable(dpi.vdds_dsi_reg);
227 if (r)
228 goto err_reg_enable;
229 }
231 r = dispc_runtime_get();
232 if (r)
233 goto err_get_dispc;
235 r = dss_dpi_select_source(0, dssdev->channel);
236 if (r)
237 goto err_src_sel;
239 if (dpi.dsidev) {
240 r = dsi_runtime_get(dpi.dsidev);
241 if (r)
242 goto err_get_dsi;
244 r = dsi_pll_init(dpi.dsidev, 0, 1);
245 if (r)
246 goto err_dsi_pll_init;
247 }
249 r = dpi_set_mode(dssdev);
250 if (r)
251 goto err_set_mode;
253 dpi_config_lcd_manager(dssdev);
255 mdelay(2);
257 r = dss_mgr_enable(out->manager);
258 if (r)
259 goto err_mgr_enable;
261 mutex_unlock(&dpi.lock);
263 return 0;
265 err_mgr_enable:
266 err_set_mode:
267 if (dpi.dsidev)
268 dsi_pll_uninit(dpi.dsidev, true);
269 err_dsi_pll_init:
270 if (dpi.dsidev)
271 dsi_runtime_put(dpi.dsidev);
272 err_get_dsi:
273 err_src_sel:
274 dispc_runtime_put();
275 err_get_dispc:
276 if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI))
277 regulator_disable(dpi.vdds_dsi_reg);
278 err_reg_enable:
279 omap_dss_stop_device(dssdev);
280 err_start_dev:
281 err_no_out_mgr:
282 err_no_reg:
283 mutex_unlock(&dpi.lock);
284 return r;
285 }
287 static void omap_dpi_display_disable(struct omap_dss_device *dssdev)
288 {
289 struct omap_overlay_manager *mgr = dssdev->output->manager;
291 mutex_lock(&dpi.lock);
293 dss_mgr_disable(mgr);
295 if (dpi.dsidev) {
296 dss_select_lcd_clk_source(mgr->id, OMAP_DSS_CLK_SRC_FCK);
297 dsi_pll_uninit(dpi.dsidev, true);
298 dsi_runtime_put(dpi.dsidev);
299 }
301 dispc_runtime_put();
303 if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI))
304 regulator_disable(dpi.vdds_dsi_reg);
306 omap_dss_stop_device(dssdev);
308 mutex_unlock(&dpi.lock);
309 }
311 static void omap_dpi_set_timings(struct omap_dss_device *dssdev,
312 struct omap_video_timings *timings)
313 {
314 DSSDBG("dpi_set_timings\n");
316 mutex_lock(&dpi.lock);
318 dpi.timings = *timings;
320 mutex_unlock(&dpi.lock);
321 }
323 static int omap_dpi_check_timings(struct omap_dss_device *dssdev,
324 struct omap_video_timings *timings)
325 {
326 int r;
327 struct omap_overlay_manager *mgr = dssdev->output->manager;
328 int lck_div, pck_div;
329 unsigned long fck;
330 unsigned long pck;
331 struct dispc_clock_info dispc_cinfo;
333 if (mgr && !dispc_mgr_timings_ok(mgr->id, timings))
334 return -EINVAL;
336 if (timings->pixel_clock == 0)
337 return -EINVAL;
339 if (dpi.dsidev) {
340 struct dsi_clock_info dsi_cinfo;
341 r = dsi_pll_calc_clock_div_pck(dpi.dsidev,
342 timings->pixel_clock * 1000,
343 &dsi_cinfo, &dispc_cinfo);
345 if (r)
346 return r;
348 fck = dsi_cinfo.dsi_pll_hsdiv_dispc_clk;
349 } else {
350 struct dss_clock_info dss_cinfo;
351 r = dss_calc_clock_div(timings->pixel_clock * 1000,
352 &dss_cinfo, &dispc_cinfo);
354 if (r)
355 return r;
357 fck = dss_cinfo.fck;
358 }
360 lck_div = dispc_cinfo.lck_div;
361 pck_div = dispc_cinfo.pck_div;
363 pck = fck / lck_div / pck_div / 1000;
365 timings->pixel_clock = pck;
367 return 0;
368 }
370 static void omap_dpi_set_data_lines(struct omap_dss_device *dssdev, int data_lines)
371 {
372 mutex_lock(&dpi.lock);
374 dpi.data_lines = data_lines;
376 mutex_unlock(&dpi.lock);
377 }
379 static int __init dpi_verify_dsi_pll(struct platform_device *dsidev)
380 {
381 int r;
383 /* do initial setup with the PLL to see if it is operational */
385 r = dsi_runtime_get(dsidev);
386 if (r)
387 return r;
389 r = dsi_pll_init(dsidev, 0, 1);
390 if (r) {
391 dsi_runtime_put(dsidev);
392 return r;
393 }
395 dsi_pll_uninit(dsidev, true);
396 dsi_runtime_put(dsidev);
398 return 0;
399 }
401 static int __init dpi_init_display(struct omap_dss_device *dssdev)
402 {
403 struct platform_device *dsidev;
405 DSSDBG("init_display\n");
407 if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI) &&
408 dpi.vdds_dsi_reg == NULL) {
409 struct regulator *vdds_dsi;
411 vdds_dsi = dss_get_vdds_dsi();
413 if (IS_ERR(vdds_dsi)) {
414 DSSERR("can't get VDDS_DSI regulator\n");
415 return PTR_ERR(vdds_dsi);
416 }
418 dpi.vdds_dsi_reg = vdds_dsi;
419 }
421 /*
422 * XXX We shouldn't need dssdev->channel for this. The dsi pll clock
423 * source for DPI is SoC integration detail, not something that should
424 * be configured in the dssdev
425 */
426 dsidev = dpi_get_dsidev(dssdev->channel);
428 if (dsidev && dpi_verify_dsi_pll(dsidev)) {
429 dsidev = NULL;
430 DSSWARN("DSI PLL not operational\n");
431 }
433 if (dsidev)
434 DSSDBG("using DSI PLL for DPI clock\n");
436 dpi.dsidev = dsidev;
438 return 0;
439 }
441 static struct omap_dss_device * __init dpi_find_dssdev(struct platform_device *pdev)
442 {
443 struct omap_dss_board_info *pdata = pdev->dev.platform_data;
444 const char *def_disp_name = omapdss_get_default_display_name();
445 struct omap_dss_device *def_dssdev;
446 int i;
448 def_dssdev = NULL;
450 for (i = 0; i < pdata->num_devices; ++i) {
451 struct omap_dss_device *dssdev = pdata->devices[i];
453 if (dssdev->type != OMAP_DISPLAY_TYPE_DPI)
454 continue;
456 if (def_dssdev == NULL)
457 def_dssdev = dssdev;
459 if (def_disp_name != NULL &&
460 strcmp(dssdev->name, def_disp_name) == 0) {
461 def_dssdev = dssdev;
462 break;
463 }
464 }
466 return def_dssdev;
467 }
469 static void __init dpi_probe_pdata(struct platform_device *dpidev)
470 {
471 struct omap_dss_device *plat_dssdev;
472 struct omap_dss_device *dssdev;
473 int r;
475 plat_dssdev = dpi_find_dssdev(dpidev);
477 if (!plat_dssdev)
478 return;
480 dssdev = dss_alloc_and_init_device(&dpidev->dev);
481 if (!dssdev)
482 return;
484 dss_copy_device_pdata(dssdev, plat_dssdev);
486 r = dpi_init_display(dssdev);
487 if (r) {
488 DSSERR("device %s init failed: %d\n", dssdev->name, r);
489 dss_put_device(dssdev);
490 return;
491 }
493 r = omapdss_output_set_device(&dpi.output, dssdev);
494 if (r) {
495 DSSERR("failed to connect output to new device: %s\n",
496 dssdev->name);
497 dss_put_device(dssdev);
498 return;
499 }
501 r = dss_add_device(dssdev);
502 if (r) {
503 DSSERR("device %s register failed: %d\n", dssdev->name, r);
504 omapdss_output_unset_device(&dpi.output);
505 dss_put_device(dssdev);
506 return;
507 }
508 }
510 static void __init dpi_probe_of(struct platform_device *pdev)
511 {
512 struct device_node *node = pdev->dev.of_node;
513 struct device_node *child;
514 struct omap_dss_device *dssdev;
515 enum omap_channel channel;
516 u32 v;
517 int r;
519 child = of_get_next_available_child(node, NULL);
521 if (!child)
522 return;
524 r = of_property_read_u32(node, "video-source", &v);
525 if (r) {
526 DSSERR("parsing channel failed\n");
527 return;
528 }
530 channel = v;
532 dssdev = dss_alloc_and_init_device(&pdev->dev);
533 if (!dssdev)
534 return;
536 dssdev->dev.of_node = child;
537 dssdev->type = OMAP_DISPLAY_TYPE_DPI;
538 dssdev->name = child->name;
539 dssdev->channel = channel;
541 r = dpi_init_display(dssdev);
542 if (r) {
543 DSSERR("device %s init failed: %d\n", dssdev->name, r);
544 dss_put_device(dssdev);
545 return;
546 }
548 r = omapdss_output_set_device(&dpi.output, dssdev);
549 if (r) {
550 DSSERR("failed to connect output to new device: %s\n",
551 dssdev->name);
552 dss_put_device(dssdev);
553 return;
554 }
556 r = dss_add_device(dssdev);
557 if (r) {
558 DSSERR("dss_add_device failed %d\n", r);
559 dss_put_device(dssdev);
560 return;
561 }
562 }
564 static void __init dpi_init_output(struct platform_device *pdev)
565 {
566 struct omap_dss_output *out = &dpi.output;
568 out->pdev = pdev;
569 out->id = OMAP_DSS_OUTPUT_DPI;
570 out->type = OMAP_DISPLAY_TYPE_DPI;
572 dss_register_output(out);
573 }
575 static void __exit dpi_uninit_output(struct platform_device *pdev)
576 {
577 struct omap_dss_output *out = &dpi.output;
579 dss_unregister_output(out);
580 }
582 static struct dpi_common_ops ops = {
583 .enable = omap_dpi_display_enable,
584 .disable = omap_dpi_display_disable,
585 .set_timings = omap_dpi_set_timings,
586 .check_timings = omap_dpi_check_timings,
587 .set_data_lines = omap_dpi_set_data_lines,
588 };
590 static int __init omap_dpi_probe(struct platform_device *pdev)
591 {
592 mutex_init(&dpi.lock);
594 dpi_common_set_ops(&ops);
596 dpi_init_output(pdev);
598 if (pdev->dev.of_node)
599 dpi_probe_of(pdev);
600 else if (pdev->dev.platform_data)
601 dpi_probe_pdata(pdev);
603 return 0;
604 }
606 static int __exit omap_dpi_remove(struct platform_device *pdev)
607 {
608 dss_unregister_child_devices(&pdev->dev);
610 dpi_uninit_output(pdev);
612 return 0;
613 }
615 #if defined(CONFIG_OF)
616 static const struct of_device_id dpi_of_match[] = {
617 {
618 .compatible = "ti,omap4-dpi",
619 },
620 {},
621 };
622 #else
623 #define dpi_of_match NULL
624 #endif
626 static struct platform_driver omap_dpi_driver = {
627 .remove = __exit_p(omap_dpi_remove),
628 .driver = {
629 .name = "omapdss_dpi",
630 .owner = THIS_MODULE,
631 .of_match_table = dpi_of_match,
632 },
633 };
635 int __init omap_dpi_init_platform_driver(void)
636 {
637 return platform_driver_probe(&omap_dpi_driver, omap_dpi_probe);
638 }
640 void __exit omap_dpi_uninit_platform_driver(void)
641 {
642 platform_driver_unregister(&omap_dpi_driver);
643 }