OMAPDSS: DPI: use dispc's check_timings
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / drivers / video / omap2 / dss / dpi.c
CommitLineData
553c48cf
TV
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>
553c48cf 26#include <linux/delay.h>
a8a35931 27#include <linux/export.h>
8a2cfea8 28#include <linux/err.h>
553c48cf 29#include <linux/errno.h>
8a2cfea8
TV
30#include <linux/platform_device.h>
31#include <linux/regulator/consumer.h>
13b1ba7d 32#include <linux/string.h>
553c48cf 33
a0b38cc4 34#include <video/omapdss.h>
553c48cf
TV
35
36#include "dss.h"
195e672a 37#include "dss_features.h"
553c48cf
TV
38
39static struct {
8a2cfea8 40 struct regulator *vdds_dsi_reg;
a72b64b9 41 struct platform_device *dsidev;
5cf9a264 42
c8a5e4e8
AT
43 struct mutex lock;
44
c499144c 45 struct omap_video_timings timings;
5cf9a264 46 struct dss_lcd_mgr_config mgr_config;
c6b393d4 47 int data_lines;
81b87f51
AT
48
49 struct omap_dss_output output;
553c48cf
TV
50} dpi;
51
0e8276ef 52static struct platform_device *dpi_get_dsidev(enum omap_channel channel)
a72b64b9 53{
0e8276ef
TV
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 }
a72b64b9
AT
62}
63
0e8276ef 64static enum omap_dss_clk_source dpi_get_alt_clk_src(enum omap_channel channel)
7636b3b4 65{
0e8276ef
TV
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 }
7636b3b4
AT
76}
77
6d523e7b 78static int dpi_set_dsi_clk(struct omap_dss_device *dssdev,
ff1b2cde
SS
79 unsigned long pck_req, unsigned long *fck, int *lck_div,
80 int *pck_div)
553c48cf 81{
a5b8399f 82 struct omap_overlay_manager *mgr = dssdev->output->manager;
553c48cf
TV
83 struct dsi_clock_info dsi_cinfo;
84 struct dispc_clock_info dispc_cinfo;
85 int r;
86
6d523e7b
AT
87 r = dsi_pll_calc_clock_div_pck(dpi.dsidev, pck_req, &dsi_cinfo,
88 &dispc_cinfo);
553c48cf
TV
89 if (r)
90 return r;
91
a72b64b9 92 r = dsi_pll_set_clock_div(dpi.dsidev, &dsi_cinfo);
553c48cf
TV
93 if (r)
94 return r;
95
a5b8399f 96 dss_select_lcd_clk_source(mgr->id,
0e8276ef 97 dpi_get_alt_clk_src(mgr->id));
553c48cf 98
5cf9a264 99 dpi.mgr_config.clock_info = dispc_cinfo;
553c48cf 100
1bb47835 101 *fck = dsi_cinfo.dsi_pll_hsdiv_dispc_clk;
553c48cf
TV
102 *lck_div = dispc_cinfo.lck_div;
103 *pck_div = dispc_cinfo.pck_div;
104
105 return 0;
106}
7636b3b4 107
6d523e7b 108static int dpi_set_dispc_clk(struct omap_dss_device *dssdev,
ff1b2cde
SS
109 unsigned long pck_req, unsigned long *fck, int *lck_div,
110 int *pck_div)
553c48cf
TV
111{
112 struct dss_clock_info dss_cinfo;
113 struct dispc_clock_info dispc_cinfo;
114 int r;
115
6d523e7b 116 r = dss_calc_clock_div(pck_req, &dss_cinfo, &dispc_cinfo);
553c48cf
TV
117 if (r)
118 return r;
119
120 r = dss_set_clock_div(&dss_cinfo);
121 if (r)
122 return r;
123
5cf9a264 124 dpi.mgr_config.clock_info = dispc_cinfo;
553c48cf
TV
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}
553c48cf
TV
132
133static int dpi_set_mode(struct omap_dss_device *dssdev)
134{
c499144c 135 struct omap_video_timings *t = &dpi.timings;
5d512fcd 136 struct omap_overlay_manager *mgr = dssdev->output->manager;
7636b3b4
AT
137 int lck_div = 0, pck_div = 0;
138 unsigned long fck = 0;
553c48cf 139 unsigned long pck;
553c48cf
TV
140 int r = 0;
141
8a3db406 142 if (dpi.dsidev)
6d523e7b
AT
143 r = dpi_set_dsi_clk(dssdev, t->pixel_clock * 1000, &fck,
144 &lck_div, &pck_div);
7636b3b4 145 else
6d523e7b
AT
146 r = dpi_set_dispc_clk(dssdev, t->pixel_clock * 1000, &fck,
147 &lck_div, &pck_div);
553c48cf 148 if (r)
4fbafaf3 149 return r;
553c48cf
TV
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
5d512fcd 161 dss_mgr_set_timings(mgr, t);
553c48cf 162
4fbafaf3 163 return 0;
553c48cf
TV
164}
165
5cf9a264 166static void dpi_config_lcd_manager(struct omap_dss_device *dssdev)
553c48cf 167{
5d512fcd
AT
168 struct omap_overlay_manager *mgr = dssdev->output->manager;
169
5cf9a264 170 dpi.mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS;
569969d6 171
5cf9a264
AT
172 dpi.mgr_config.stallmode = false;
173 dpi.mgr_config.fifohandcheck = false;
174
c6b393d4 175 dpi.mgr_config.video_port_width = dpi.data_lines;
5cf9a264
AT
176
177 dpi.mgr_config.lcden_sig_polarity = 0;
178
5d512fcd 179 dss_mgr_set_lcd_config(mgr, &dpi.mgr_config);
553c48cf
TV
180}
181
37ac60e4 182int omapdss_dpi_display_enable(struct omap_dss_device *dssdev)
553c48cf 183{
5d512fcd 184 struct omap_dss_output *out = dssdev->output;
553c48cf
TV
185 int r;
186
c8a5e4e8
AT
187 mutex_lock(&dpi.lock);
188
195e672a 189 if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI) && !dpi.vdds_dsi_reg) {
40410715 190 DSSERR("no VDSS_DSI regulator\n");
c8a5e4e8
AT
191 r = -ENODEV;
192 goto err_no_reg;
40410715
RK
193 }
194
5d512fcd
AT
195 if (out == NULL || out->manager == NULL) {
196 DSSERR("failed to enable display: no output/manager\n");
c8a5e4e8 197 r = -ENODEV;
5d512fcd 198 goto err_no_out_mgr;
05e1d606
TV
199 }
200
553c48cf
TV
201 r = omap_dss_start_device(dssdev);
202 if (r) {
203 DSSERR("failed to start device\n");
4fbafaf3 204 goto err_start_dev;
553c48cf
TV
205 }
206
195e672a 207 if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI)) {
8a2cfea8
TV
208 r = regulator_enable(dpi.vdds_dsi_reg);
209 if (r)
4fbafaf3 210 goto err_reg_enable;
8a2cfea8
TV
211 }
212
4fbafaf3 213 r = dispc_runtime_get();
553c48cf 214 if (r)
4fbafaf3
TV
215 goto err_get_dispc;
216
de09e455
TV
217 r = dss_dpi_select_source(dssdev->channel);
218 if (r)
219 goto err_src_sel;
220
8a3db406 221 if (dpi.dsidev) {
4fbafaf3
TV
222 r = dsi_runtime_get(dpi.dsidev);
223 if (r)
224 goto err_get_dsi;
225
a72b64b9 226 r = dsi_pll_init(dpi.dsidev, 0, 1);
7636b3b4 227 if (r)
4fbafaf3 228 goto err_dsi_pll_init;
7636b3b4
AT
229 }
230
553c48cf
TV
231 r = dpi_set_mode(dssdev);
232 if (r)
4fbafaf3 233 goto err_set_mode;
553c48cf 234
5cf9a264
AT
235 dpi_config_lcd_manager(dssdev);
236
553c48cf
TV
237 mdelay(2);
238
5d512fcd 239 r = dss_mgr_enable(out->manager);
33ca237f
TV
240 if (r)
241 goto err_mgr_enable;
553c48cf 242
c8a5e4e8
AT
243 mutex_unlock(&dpi.lock);
244
553c48cf
TV
245 return 0;
246
33ca237f 247err_mgr_enable:
4fbafaf3 248err_set_mode:
8a3db406 249 if (dpi.dsidev)
19077a73 250 dsi_pll_uninit(dpi.dsidev, true);
4fbafaf3 251err_dsi_pll_init:
8a3db406 252 if (dpi.dsidev)
4fbafaf3
TV
253 dsi_runtime_put(dpi.dsidev);
254err_get_dsi:
de09e455 255err_src_sel:
4fbafaf3
TV
256 dispc_runtime_put();
257err_get_dispc:
195e672a 258 if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI))
8a2cfea8 259 regulator_disable(dpi.vdds_dsi_reg);
4fbafaf3 260err_reg_enable:
553c48cf 261 omap_dss_stop_device(dssdev);
4fbafaf3 262err_start_dev:
5d512fcd 263err_no_out_mgr:
c8a5e4e8
AT
264err_no_reg:
265 mutex_unlock(&dpi.lock);
553c48cf
TV
266 return r;
267}
37ac60e4 268EXPORT_SYMBOL(omapdss_dpi_display_enable);
553c48cf 269
37ac60e4 270void omapdss_dpi_display_disable(struct omap_dss_device *dssdev)
553c48cf 271{
5d512fcd
AT
272 struct omap_overlay_manager *mgr = dssdev->output->manager;
273
c8a5e4e8
AT
274 mutex_lock(&dpi.lock);
275
5d512fcd 276 dss_mgr_disable(mgr);
553c48cf 277
8a3db406 278 if (dpi.dsidev) {
a5b8399f 279 dss_select_lcd_clk_source(mgr->id, OMAP_DSS_CLK_SRC_FCK);
a72b64b9 280 dsi_pll_uninit(dpi.dsidev, true);
4fbafaf3 281 dsi_runtime_put(dpi.dsidev);
7636b3b4 282 }
553c48cf 283
4fbafaf3 284 dispc_runtime_put();
553c48cf 285
195e672a 286 if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI))
8a2cfea8
TV
287 regulator_disable(dpi.vdds_dsi_reg);
288
553c48cf 289 omap_dss_stop_device(dssdev);
c8a5e4e8
AT
290
291 mutex_unlock(&dpi.lock);
553c48cf 292}
37ac60e4 293EXPORT_SYMBOL(omapdss_dpi_display_disable);
553c48cf 294
c499144c
AT
295void omapdss_dpi_set_timings(struct omap_dss_device *dssdev,
296 struct omap_video_timings *timings)
553c48cf
TV
297{
298 DSSDBG("dpi_set_timings\n");
c8a5e4e8
AT
299
300 mutex_lock(&dpi.lock);
301
c499144c 302 dpi.timings = *timings;
c499144c 303
c8a5e4e8 304 mutex_unlock(&dpi.lock);
553c48cf 305}
c499144c 306EXPORT_SYMBOL(omapdss_dpi_set_timings);
553c48cf 307
69b2048f 308int dpi_check_timings(struct omap_dss_device *dssdev,
553c48cf
TV
309 struct omap_video_timings *timings)
310{
553c48cf 311 int r;
5d512fcd 312 struct omap_overlay_manager *mgr = dssdev->output->manager;
553c48cf
TV
313 int lck_div, pck_div;
314 unsigned long fck;
315 unsigned long pck;
7636b3b4 316 struct dispc_clock_info dispc_cinfo;
553c48cf 317
8b095513 318 if (mgr && !dispc_mgr_timings_ok(mgr->id, timings))
553c48cf
TV
319 return -EINVAL;
320
321 if (timings->pixel_clock == 0)
322 return -EINVAL;
323
8a3db406 324 if (dpi.dsidev) {
553c48cf 325 struct dsi_clock_info dsi_cinfo;
6d523e7b 326 r = dsi_pll_calc_clock_div_pck(dpi.dsidev,
553c48cf
TV
327 timings->pixel_clock * 1000,
328 &dsi_cinfo, &dispc_cinfo);
329
330 if (r)
331 return r;
332
1bb47835 333 fck = dsi_cinfo.dsi_pll_hsdiv_dispc_clk;
7636b3b4 334 } else {
553c48cf 335 struct dss_clock_info dss_cinfo;
6d523e7b 336 r = dss_calc_clock_div(timings->pixel_clock * 1000,
553c48cf
TV
337 &dss_cinfo, &dispc_cinfo);
338
339 if (r)
340 return r;
341
342 fck = dss_cinfo.fck;
553c48cf 343 }
7636b3b4
AT
344
345 lck_div = dispc_cinfo.lck_div;
346 pck_div = dispc_cinfo.pck_div;
553c48cf
TV
347
348 pck = fck / lck_div / pck_div / 1000;
349
350 timings->pixel_clock = pck;
351
352 return 0;
353}
69b2048f 354EXPORT_SYMBOL(dpi_check_timings);
553c48cf 355
c6b393d4
AT
356void 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}
364EXPORT_SYMBOL(omapdss_dpi_set_data_lines);
365
6061675b
TV
366static 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
9d8232a7 388static int __init dpi_init_display(struct omap_dss_device *dssdev)
553c48cf 389{
0e8276ef
TV
390 struct platform_device *dsidev;
391
553c48cf
TV
392 DSSDBG("init_display\n");
393
195e672a
CM
394 if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI) &&
395 dpi.vdds_dsi_reg == NULL) {
5f42f2ce 396 struct regulator *vdds_dsi;
553c48cf 397
5f42f2ce
TV
398 vdds_dsi = dss_get_vdds_dsi();
399
400 if (IS_ERR(vdds_dsi)) {
8a2cfea8 401 DSSERR("can't get VDDS_DSI regulator\n");
5f42f2ce 402 return PTR_ERR(vdds_dsi);
8a2cfea8 403 }
5f42f2ce
TV
404
405 dpi.vdds_dsi_reg = vdds_dsi;
8a2cfea8
TV
406 }
407
0e8276ef
TV
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);
6061675b 414
1de8e129 415 if (dsidev && dpi_verify_dsi_pll(dsidev)) {
0e8276ef
TV
416 dsidev = NULL;
417 DSSWARN("DSI PLL not operational\n");
a72b64b9
AT
418 }
419
0e8276ef
TV
420 if (dsidev)
421 DSSDBG("using DSI PLL for DPI clock\n");
422
423 dpi.dsidev = dsidev;
424
553c48cf
TV
425 return 0;
426}
427
1521653c 428static struct omap_dss_device * __init dpi_find_dssdev(struct platform_device *pdev)
5f42f2ce 429{
35deca3d 430 struct omap_dss_board_info *pdata = pdev->dev.platform_data;
2bbcce5e 431 const char *def_disp_name = omapdss_get_default_display_name();
1521653c
TV
432 struct omap_dss_device *def_dssdev;
433 int i;
434
435 def_dssdev = NULL;
35deca3d
TV
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
1521653c
TV
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;
9d8232a7 450 }
1521653c 451 }
9d8232a7 452
1521653c
TV
453 return def_dssdev;
454}
455
5274484b 456static void __init dpi_probe_pdata(struct platform_device *dpidev)
1521653c 457{
5274484b 458 struct omap_dss_device *plat_dssdev;
1521653c
TV
459 struct omap_dss_device *dssdev;
460 int r;
461
5274484b 462 plat_dssdev = dpi_find_dssdev(dpidev);
1521653c 463
5274484b
TV
464 if (!plat_dssdev)
465 return;
466
467 dssdev = dss_alloc_and_init_device(&dpidev->dev);
1521653c
TV
468 if (!dssdev)
469 return;
470
5274484b
TV
471 dss_copy_device_pdata(dssdev, plat_dssdev);
472
1521653c
TV
473 r = dpi_init_display(dssdev);
474 if (r) {
475 DSSERR("device %s init failed: %d\n", dssdev->name, r);
5274484b 476 dss_put_device(dssdev);
1521653c
TV
477 return;
478 }
479
486c0e17
TV
480 r = omapdss_output_set_device(&dpi.output, dssdev);
481 if (r) {
482 DSSERR("failed to connect output to new device: %s\n",
483 dssdev->name);
484 dss_put_device(dssdev);
485 return;
486 }
487
5274484b 488 r = dss_add_device(dssdev);
1521653c
TV
489 if (r) {
490 DSSERR("device %s register failed: %d\n", dssdev->name, r);
486c0e17 491 omapdss_output_unset_device(&dpi.output);
5274484b 492 dss_put_device(dssdev);
1521653c 493 return;
35deca3d 494 }
38f3daf6
TV
495}
496
81b87f51
AT
497static void __init dpi_init_output(struct platform_device *pdev)
498{
499 struct omap_dss_output *out = &dpi.output;
500
501 out->pdev = pdev;
502 out->id = OMAP_DSS_OUTPUT_DPI;
503 out->type = OMAP_DISPLAY_TYPE_DPI;
504
505 dss_register_output(out);
506}
507
508static void __exit dpi_uninit_output(struct platform_device *pdev)
509{
510 struct omap_dss_output *out = &dpi.output;
511
512 dss_unregister_output(out);
513}
514
38f3daf6
TV
515static int __init omap_dpi_probe(struct platform_device *pdev)
516{
c8a5e4e8
AT
517 mutex_init(&dpi.lock);
518
81b87f51
AT
519 dpi_init_output(pdev);
520
38f3daf6 521 dpi_probe_pdata(pdev);
35deca3d 522
5f42f2ce
TV
523 return 0;
524}
525
6e7e8f06 526static int __exit omap_dpi_remove(struct platform_device *pdev)
553c48cf 527{
5274484b 528 dss_unregister_child_devices(&pdev->dev);
35deca3d 529
81b87f51
AT
530 dpi_uninit_output(pdev);
531
a57dd4fe 532 return 0;
553c48cf
TV
533}
534
a57dd4fe 535static struct platform_driver omap_dpi_driver = {
6e7e8f06 536 .remove = __exit_p(omap_dpi_remove),
a57dd4fe
TV
537 .driver = {
538 .name = "omapdss_dpi",
539 .owner = THIS_MODULE,
540 },
541};
542
6e7e8f06 543int __init dpi_init_platform_driver(void)
a57dd4fe 544{
61055d4b 545 return platform_driver_probe(&omap_dpi_driver, omap_dpi_probe);
a57dd4fe
TV
546}
547
6e7e8f06 548void __exit dpi_uninit_platform_driver(void)
a57dd4fe
TV
549{
550 platform_driver_unregister(&omap_dpi_driver);
551}