OMAPDSS: DPI: always use DSI PLL if available
[linux-omap-dss2:archit-dss2-clone.git] / drivers / video / omap2 / dss / dpi.c
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  */
22
23 #define DSS_SUBSYS_NAME "DPI"
24
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
34 #include <video/omapdss.h>
35
36 #include "dss.h"
37 #include "dss_features.h"
38
39 static struct {
40         struct regulator *vdds_dsi_reg;
41         struct platform_device *dsidev;
42
43         struct mutex lock;
44
45         struct omap_video_timings timings;
46         struct dss_lcd_mgr_config mgr_config;
47         int data_lines;
48
49         struct omap_dss_output output;
50 } dpi;
51
52 static struct platform_device *dpi_get_dsidev(enum omap_channel channel)
53 {
54         switch (channel) {
55         case OMAP_DSS_CHANNEL_LCD:
56                 return dsi_get_dsidev_from_id(0);
57         case OMAP_DSS_CHANNEL_LCD2:
58                 return dsi_get_dsidev_from_id(1);
59         default:
60                 return NULL;
61         }
62 }
63
64 static enum omap_dss_clk_source dpi_get_alt_clk_src(enum omap_channel channel)
65 {
66         switch (channel) {
67         case OMAP_DSS_CHANNEL_LCD:
68                 return OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC;
69         case OMAP_DSS_CHANNEL_LCD2:
70                 return OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC;
71         default:
72                 /* this shouldn't happen */
73                 WARN_ON(1);
74                 return OMAP_DSS_CLK_SRC_FCK;
75         }
76 }
77
78 static int dpi_set_dsi_clk(struct omap_dss_device *dssdev,
79                 unsigned long pck_req, unsigned long *fck, int *lck_div,
80                 int *pck_div)
81 {
82         struct omap_overlay_manager *mgr = dssdev->output->manager;
83         struct dsi_clock_info dsi_cinfo;
84         struct dispc_clock_info dispc_cinfo;
85         int r;
86
87         r = dsi_pll_calc_clock_div_pck(dpi.dsidev, pck_req, &dsi_cinfo,
88                         &dispc_cinfo);
89         if (r)
90                 return r;
91
92         r = dsi_pll_set_clock_div(dpi.dsidev, &dsi_cinfo);
93         if (r)
94                 return r;
95
96         dss_select_lcd_clk_source(mgr->id,
97                         dpi_get_alt_clk_src(mgr->id));
98
99         dpi.mgr_config.clock_info = dispc_cinfo;
100
101         *fck = dsi_cinfo.dsi_pll_hsdiv_dispc_clk;
102         *lck_div = dispc_cinfo.lck_div;
103         *pck_div = dispc_cinfo.pck_div;
104
105         return 0;
106 }
107
108 static int dpi_set_dispc_clk(struct omap_dss_device *dssdev,
109                 unsigned long pck_req, unsigned long *fck, int *lck_div,
110                 int *pck_div)
111 {
112         struct dss_clock_info dss_cinfo;
113         struct dispc_clock_info dispc_cinfo;
114         int r;
115
116         r = dss_calc_clock_div(pck_req, &dss_cinfo, &dispc_cinfo);
117         if (r)
118                 return r;
119
120         r = dss_set_clock_div(&dss_cinfo);
121         if (r)
122                 return r;
123
124         dpi.mgr_config.clock_info = dispc_cinfo;
125
126         *fck = dss_cinfo.fck;
127         *lck_div = dispc_cinfo.lck_div;
128         *pck_div = dispc_cinfo.pck_div;
129
130         return 0;
131 }
132
133 static int dpi_set_mode(struct omap_dss_device *dssdev)
134 {
135         struct omap_video_timings *t = &dpi.timings;
136         struct omap_overlay_manager *mgr = dssdev->output->manager;
137         int lck_div = 0, pck_div = 0;
138         unsigned long fck = 0;
139         unsigned long pck;
140         int r = 0;
141
142         if (dpi.dsidev)
143                 r = dpi_set_dsi_clk(dssdev, t->pixel_clock * 1000, &fck,
144                                 &lck_div, &pck_div);
145         else
146                 r = dpi_set_dispc_clk(dssdev, t->pixel_clock * 1000, &fck,
147                                 &lck_div, &pck_div);
148         if (r)
149                 return r;
150
151         pck = fck / lck_div / pck_div / 1000;
152
153         if (pck != t->pixel_clock) {
154                 DSSWARN("Could not find exact pixel clock. "
155                                 "Requested %d kHz, got %lu kHz\n",
156                                 t->pixel_clock, pck);
157
158                 t->pixel_clock = pck;
159         }
160
161         dss_mgr_set_timings(mgr, t);
162
163         return 0;
164 }
165
166 static void dpi_config_lcd_manager(struct omap_dss_device *dssdev)
167 {
168         struct omap_overlay_manager *mgr = dssdev->output->manager;
169
170         dpi.mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS;
171
172         dpi.mgr_config.stallmode = false;
173         dpi.mgr_config.fifohandcheck = false;
174
175         dpi.mgr_config.video_port_width = dpi.data_lines;
176
177         dpi.mgr_config.lcden_sig_polarity = 0;
178
179         dss_mgr_set_lcd_config(mgr, &dpi.mgr_config);
180 }
181
182 int omapdss_dpi_display_enable(struct omap_dss_device *dssdev)
183 {
184         struct omap_dss_output *out = dssdev->output;
185         int r;
186
187         mutex_lock(&dpi.lock);
188
189         if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI) && !dpi.vdds_dsi_reg) {
190                 DSSERR("no VDSS_DSI regulator\n");
191                 r = -ENODEV;
192                 goto err_no_reg;
193         }
194
195         if (out == NULL || out->manager == NULL) {
196                 DSSERR("failed to enable display: no output/manager\n");
197                 r = -ENODEV;
198                 goto err_no_out_mgr;
199         }
200
201         r = omap_dss_start_device(dssdev);
202         if (r) {
203                 DSSERR("failed to start device\n");
204                 goto err_start_dev;
205         }
206
207         if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI)) {
208                 r = regulator_enable(dpi.vdds_dsi_reg);
209                 if (r)
210                         goto err_reg_enable;
211         }
212
213         r = dispc_runtime_get();
214         if (r)
215                 goto err_get_dispc;
216
217         r = dss_dpi_select_source(dssdev->channel);
218         if (r)
219                 goto err_src_sel;
220
221         if (dpi.dsidev) {
222                 r = dsi_runtime_get(dpi.dsidev);
223                 if (r)
224                         goto err_get_dsi;
225
226                 r = dsi_pll_init(dpi.dsidev, 0, 1);
227                 if (r)
228                         goto err_dsi_pll_init;
229         }
230
231         r = dpi_set_mode(dssdev);
232         if (r)
233                 goto err_set_mode;
234
235         dpi_config_lcd_manager(dssdev);
236
237         mdelay(2);
238
239         r = dss_mgr_enable(out->manager);
240         if (r)
241                 goto err_mgr_enable;
242
243         mutex_unlock(&dpi.lock);
244
245         return 0;
246
247 err_mgr_enable:
248 err_set_mode:
249         if (dpi.dsidev)
250                 dsi_pll_uninit(dpi.dsidev, true);
251 err_dsi_pll_init:
252         if (dpi.dsidev)
253                 dsi_runtime_put(dpi.dsidev);
254 err_get_dsi:
255 err_src_sel:
256         dispc_runtime_put();
257 err_get_dispc:
258         if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI))
259                 regulator_disable(dpi.vdds_dsi_reg);
260 err_reg_enable:
261         omap_dss_stop_device(dssdev);
262 err_start_dev:
263 err_no_out_mgr:
264 err_no_reg:
265         mutex_unlock(&dpi.lock);
266         return r;
267 }
268 EXPORT_SYMBOL(omapdss_dpi_display_enable);
269
270 void omapdss_dpi_display_disable(struct omap_dss_device *dssdev)
271 {
272         struct omap_overlay_manager *mgr = dssdev->output->manager;
273
274         mutex_lock(&dpi.lock);
275
276         dss_mgr_disable(mgr);
277
278         if (dpi.dsidev) {
279                 dss_select_lcd_clk_source(mgr->id, OMAP_DSS_CLK_SRC_FCK);
280                 dsi_pll_uninit(dpi.dsidev, true);
281                 dsi_runtime_put(dpi.dsidev);
282         }
283
284         dispc_runtime_put();
285
286         if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI))
287                 regulator_disable(dpi.vdds_dsi_reg);
288
289         omap_dss_stop_device(dssdev);
290
291         mutex_unlock(&dpi.lock);
292 }
293 EXPORT_SYMBOL(omapdss_dpi_display_disable);
294
295 void omapdss_dpi_set_timings(struct omap_dss_device *dssdev,
296                 struct omap_video_timings *timings)
297 {
298         DSSDBG("dpi_set_timings\n");
299
300         mutex_lock(&dpi.lock);
301
302         dpi.timings = *timings;
303
304         mutex_unlock(&dpi.lock);
305 }
306 EXPORT_SYMBOL(omapdss_dpi_set_timings);
307
308 int dpi_check_timings(struct omap_dss_device *dssdev,
309                         struct omap_video_timings *timings)
310 {
311         int r;
312         struct omap_overlay_manager *mgr = dssdev->output->manager;
313         int lck_div, pck_div;
314         unsigned long fck;
315         unsigned long pck;
316         struct dispc_clock_info dispc_cinfo;
317
318         if (dss_mgr_check_timings(mgr, timings))
319                 return -EINVAL;
320
321         if (timings->pixel_clock == 0)
322                 return -EINVAL;
323
324         if (dpi.dsidev) {
325                 struct dsi_clock_info dsi_cinfo;
326                 r = dsi_pll_calc_clock_div_pck(dpi.dsidev,
327                                 timings->pixel_clock * 1000,
328                                 &dsi_cinfo, &dispc_cinfo);
329
330                 if (r)
331                         return r;
332
333                 fck = dsi_cinfo.dsi_pll_hsdiv_dispc_clk;
334         } else {
335                 struct dss_clock_info dss_cinfo;
336                 r = dss_calc_clock_div(timings->pixel_clock * 1000,
337                                 &dss_cinfo, &dispc_cinfo);
338
339                 if (r)
340                         return r;
341
342                 fck = dss_cinfo.fck;
343         }
344
345         lck_div = dispc_cinfo.lck_div;
346         pck_div = dispc_cinfo.pck_div;
347
348         pck = fck / lck_div / pck_div / 1000;
349
350         timings->pixel_clock = pck;
351
352         return 0;
353 }
354 EXPORT_SYMBOL(dpi_check_timings);
355
356 void omapdss_dpi_set_data_lines(struct omap_dss_device *dssdev, int data_lines)
357 {
358         mutex_lock(&dpi.lock);
359
360         dpi.data_lines = data_lines;
361
362         mutex_unlock(&dpi.lock);
363 }
364 EXPORT_SYMBOL(omapdss_dpi_set_data_lines);
365
366 static int __init dpi_verify_dsi_pll(struct platform_device *dsidev)
367 {
368         int r;
369
370         /* do initial setup with the PLL to see if it is operational */
371
372         r = dsi_runtime_get(dsidev);
373         if (r)
374                 return r;
375
376         r = dsi_pll_init(dsidev, 0, 1);
377         if (r) {
378                 dsi_runtime_put(dsidev);
379                 return r;
380         }
381
382         dsi_pll_uninit(dsidev, true);
383         dsi_runtime_put(dsidev);
384
385         return 0;
386 }
387
388 static int __init dpi_init_display(struct omap_dss_device *dssdev)
389 {
390         struct platform_device *dsidev;
391
392         DSSDBG("init_display\n");
393
394         if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI) &&
395                                         dpi.vdds_dsi_reg == NULL) {
396                 struct regulator *vdds_dsi;
397
398                 vdds_dsi = dss_get_vdds_dsi();
399
400                 if (IS_ERR(vdds_dsi)) {
401                         DSSERR("can't get VDDS_DSI regulator\n");
402                         return PTR_ERR(vdds_dsi);
403                 }
404
405                 dpi.vdds_dsi_reg = vdds_dsi;
406         }
407
408         /*
409          * XXX We shouldn't need dssdev->channel for this. The dsi pll clock
410          * source for DPI is SoC integration detail, not something that should
411          * be configured in the dssdev
412          */
413         dsidev = dpi_get_dsidev(dssdev->channel);
414
415         if (dpi_verify_dsi_pll(dsidev)) {
416                 dsidev = NULL;
417                 DSSWARN("DSI PLL not operational\n");
418         }
419
420         if (dsidev)
421                 DSSDBG("using DSI PLL for DPI clock\n");
422
423         dpi.dsidev = dsidev;
424
425         return 0;
426 }
427
428 static struct omap_dss_device * __init dpi_find_dssdev(struct platform_device *pdev)
429 {
430         struct omap_dss_board_info *pdata = pdev->dev.platform_data;
431         const char *def_disp_name = omapdss_get_default_display_name();
432         struct omap_dss_device *def_dssdev;
433         int i;
434
435         def_dssdev = NULL;
436
437         for (i = 0; i < pdata->num_devices; ++i) {
438                 struct omap_dss_device *dssdev = pdata->devices[i];
439
440                 if (dssdev->type != OMAP_DISPLAY_TYPE_DPI)
441                         continue;
442
443                 if (def_dssdev == NULL)
444                         def_dssdev = dssdev;
445
446                 if (def_disp_name != NULL &&
447                                 strcmp(dssdev->name, def_disp_name) == 0) {
448                         def_dssdev = dssdev;
449                         break;
450                 }
451         }
452
453         return def_dssdev;
454 }
455
456 static void __init dpi_probe_pdata(struct platform_device *dpidev)
457 {
458         struct omap_dss_device *plat_dssdev;
459         struct omap_dss_device *dssdev;
460         int r;
461
462         plat_dssdev = dpi_find_dssdev(dpidev);
463
464         if (!plat_dssdev)
465                 return;
466
467         dssdev = dss_alloc_and_init_device(&dpidev->dev);
468         if (!dssdev)
469                 return;
470
471         dss_copy_device_pdata(dssdev, plat_dssdev);
472
473         r = dpi_init_display(dssdev);
474         if (r) {
475                 DSSERR("device %s init failed: %d\n", dssdev->name, r);
476                 dss_put_device(dssdev);
477                 return;
478         }
479
480         r = dss_add_device(dssdev);
481         if (r) {
482                 DSSERR("device %s register failed: %d\n", dssdev->name, r);
483                 dss_put_device(dssdev);
484                 return;
485         }
486 }
487
488 static void __init dpi_init_output(struct platform_device *pdev)
489 {
490         struct omap_dss_output *out = &dpi.output;
491
492         out->pdev = pdev;
493         out->id = OMAP_DSS_OUTPUT_DPI;
494         out->type = OMAP_DISPLAY_TYPE_DPI;
495
496         dss_register_output(out);
497 }
498
499 static void __exit dpi_uninit_output(struct platform_device *pdev)
500 {
501         struct omap_dss_output *out = &dpi.output;
502
503         dss_unregister_output(out);
504 }
505
506 static int __init omap_dpi_probe(struct platform_device *pdev)
507 {
508         mutex_init(&dpi.lock);
509
510         dpi_init_output(pdev);
511
512         dpi_probe_pdata(pdev);
513
514         return 0;
515 }
516
517 static int __exit omap_dpi_remove(struct platform_device *pdev)
518 {
519         dss_unregister_child_devices(&pdev->dev);
520
521         dpi_uninit_output(pdev);
522
523         return 0;
524 }
525
526 static struct platform_driver omap_dpi_driver = {
527         .remove         = __exit_p(omap_dpi_remove),
528         .driver         = {
529                 .name   = "omapdss_dpi",
530                 .owner  = THIS_MODULE,
531         },
532 };
533
534 int __init dpi_init_platform_driver(void)
535 {
536         return platform_driver_probe(&omap_dpi_driver, omap_dpi_probe);
537 }
538
539 void __exit dpi_uninit_platform_driver(void)
540 {
541         platform_driver_unregister(&omap_dpi_driver);
542 }