1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33#include <linux/module.h>
34#include <linux/delay.h>
35#include <linux/slab.h>
36#include <linux/gpio.h>
37#include <video/omapdss.h>
38
39#include <video/omap-panel-data.h>
40
41struct panel_config {
42 struct omap_video_timings timings;
43
44 int power_on_delay;
45 int power_off_delay;
46
47
48
49
50
51 const char *name;
52};
53
54
55static struct panel_config generic_dpi_panels[] = {
56
57 {
58 {
59 .x_res = 480,
60 .y_res = 272,
61
62 .pixel_clock = 9000,
63
64 .hsw = 42,
65 .hfp = 3,
66 .hbp = 2,
67
68 .vsw = 11,
69 .vfp = 3,
70 .vbp = 2,
71
72 .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
73 .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
74 .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
75 .de_level = OMAPDSS_SIG_ACTIVE_LOW,
76 .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
77 },
78 .power_on_delay = 50,
79 .power_off_delay = 100,
80 .name = "sharp_lq",
81 },
82
83
84 {
85 {
86 .x_res = 480,
87 .y_res = 640,
88
89 .pixel_clock = 19200,
90
91 .hsw = 2,
92 .hfp = 1,
93 .hbp = 28,
94
95 .vsw = 1,
96 .vfp = 1,
97 .vbp = 1,
98
99 .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
100 .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
101 .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
102 .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
103 .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
104 },
105 .power_on_delay = 50,
106 .power_off_delay = 100,
107 .name = "sharp_ls",
108 },
109
110
111 {
112 {
113 .x_res = 480,
114 .y_res = 640,
115
116 .pixel_clock = 26000,
117
118 .hfp = 104,
119 .hsw = 8,
120 .hbp = 8,
121
122 .vfp = 4,
123 .vsw = 2,
124 .vbp = 2,
125
126 .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
127 .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
128 .data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
129 .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
130 .sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
131 },
132 .power_on_delay = 0,
133 .power_off_delay = 0,
134 .name = "toppoly_tdo35s",
135 },
136
137
138 {
139 {
140 .x_res = 480,
141 .y_res = 272,
142
143 .pixel_clock = 9200,
144
145 .hfp = 8,
146 .hsw = 41,
147 .hbp = 45 - 41,
148
149 .vfp = 4,
150 .vsw = 10,
151 .vbp = 12 - 10,
152
153 .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
154 .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
155 .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
156 .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
157 .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
158 },
159 .power_on_delay = 0,
160 .power_off_delay = 0,
161 .name = "samsung_lte430wq_f0c",
162 },
163
164
165 {
166 {
167 .x_res = 800,
168 .y_res = 480,
169
170 .pixel_clock = 33000,
171
172 .hsw = 128,
173 .hfp = 10,
174 .hbp = 10,
175
176 .vsw = 2,
177 .vfp = 4,
178 .vbp = 11,
179
180 .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
181 .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
182 .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
183 .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
184 .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
185 },
186 .power_on_delay = 0,
187 .power_off_delay = 0,
188 .name = "seiko_70wvw1tz3",
189 },
190
191
192 {
193 {
194 .x_res = 480,
195 .y_res = 272,
196
197 .pixel_clock = 9000,
198
199 .hsw = 40,
200 .hfp = 2,
201 .hbp = 2,
202
203 .vsw = 10,
204 .vfp = 2,
205 .vbp = 2,
206
207 .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
208 .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
209 .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
210 .de_level = OMAPDSS_SIG_ACTIVE_LOW,
211 .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
212 },
213 .power_on_delay = 0,
214 .power_off_delay = 0,
215 .name = "powertip_ph480272t",
216 },
217
218
219 {
220 {
221 .x_res = 800,
222 .y_res = 480,
223
224 .pixel_clock = 40000,
225
226 .hsw = 48,
227 .hfp = 1,
228 .hbp = 1,
229
230 .vsw = 3,
231 .vfp = 12,
232 .vbp = 25,
233
234 .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
235 .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
236 .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
237 .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
238 .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
239 },
240 .power_on_delay = 0,
241 .power_off_delay = 0,
242 .name = "innolux_at070tn83",
243 },
244
245
246 {
247 {
248 .x_res = 240,
249 .y_res = 320,
250
251 .pixel_clock = 5400,
252
253 .hsw = 3,
254 .hfp = 3,
255 .hbp = 39,
256
257 .vsw = 1,
258 .vfp = 2,
259 .vbp = 7,
260
261 .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
262 .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
263 .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
264 .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
265 .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
266 },
267 .name = "nec_nl2432dr22-11b",
268 },
269
270
271 {
272 {
273 .x_res = 240,
274 .y_res = 320,
275
276 .pixel_clock = 6250,
277
278 .hsw = 15,
279 .hfp = 15,
280 .hbp = 60,
281
282 .vsw = 1,
283 .vfp = 1,
284 .vbp = 1,
285
286 .vsync_level = OMAPDSS_SIG_ACTIVE_HIGH,
287 .hsync_level = OMAPDSS_SIG_ACTIVE_HIGH,
288 .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
289 .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
290 .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
291 },
292 .name = "h4",
293 },
294
295
296 {
297 {
298 .x_res = 800,
299 .y_res = 480,
300
301 .pixel_clock = 28000,
302
303 .hsw = 48,
304 .hfp = 40,
305 .hbp = 40,
306
307 .vsw = 3,
308 .vfp = 13,
309 .vbp = 29,
310
311 .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
312 .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
313 .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
314 .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
315 .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
316 },
317 .name = "focaltech_etm070003dh6",
318 },
319
320
321 {
322 {
323 .x_res = 800,
324 .y_res = 480,
325
326 .pixel_clock = 34560,
327
328 .hsw = 13,
329 .hfp = 101,
330 .hbp = 101,
331
332 .vsw = 23,
333 .vfp = 1,
334 .vbp = 1,
335
336 .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
337 .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
338 .data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
339 .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
340 .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
341 },
342 .power_on_delay = 0,
343 .power_off_delay = 0,
344 .name = "microtips_umsh_8173md",
345 },
346
347
348 {
349 {
350 .x_res = 480,
351 .y_res = 272,
352
353 .pixel_clock = 8000,
354
355 .hsw = 41,
356 .hfp = 8,
357 .hbp = 4,
358
359 .vsw = 10,
360 .vfp = 4,
361 .vbp = 2,
362
363 .vsync_level = OMAPDSS_SIG_ACTIVE_HIGH,
364 .hsync_level = OMAPDSS_SIG_ACTIVE_HIGH,
365 .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
366 .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
367 .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
368 },
369 .name = "ortustech_com43h4m10xtc",
370 },
371
372
373 {
374 {
375 .x_res = 800,
376 .y_res = 600,
377
378 .pixel_clock = 41142,
379
380 .hsw = 20,
381 .hfp = 210,
382 .hbp = 46,
383
384 .vsw = 10,
385 .vfp = 12,
386 .vbp = 23,
387
388 .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
389 .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
390 .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
391 .de_level = OMAPDSS_SIG_ACTIVE_LOW,
392 .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
393 },
394 .name = "innolux_at080tn52",
395 },
396
397
398 {
399 {
400 .x_res = 800,
401 .y_res = 600,
402 .pixel_clock = 40000,
403
404 .hsw = 1,
405 .hfp = 254,
406 .hbp = 1,
407
408 .vsw = 1,
409 .vfp = 26,
410 .vbp = 1,
411
412 .vsync_level = OMAPDSS_SIG_ACTIVE_HIGH,
413 .hsync_level = OMAPDSS_SIG_ACTIVE_HIGH,
414 .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
415 .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
416 .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
417 },
418 .name = "mitsubishi_aa084sb01",
419 },
420
421 {
422 {
423 .x_res = 800,
424 .y_res = 480,
425 .pixel_clock = 33260,
426
427 .hsw = 128,
428 .hfp = 216,
429 .hbp = 40,
430
431 .vsw = 2,
432 .vfp = 35,
433 .vbp = 10,
434
435 .vsync_level = OMAPDSS_SIG_ACTIVE_HIGH,
436 .hsync_level = OMAPDSS_SIG_ACTIVE_HIGH,
437 .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
438 .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
439 .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
440 },
441 .name = "edt_et0500g0dh6",
442 },
443
444
445 {
446 {
447 .x_res = 640,
448 .y_res = 480,
449
450 .pixel_clock = 25000,
451
452 .hsw = 96,
453 .hfp = 18,
454 .hbp = 46,
455
456 .vsw = 2,
457 .vfp = 10,
458 .vbp = 33,
459
460 .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
461 .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
462 .data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
463 .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
464 .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
465 },
466 .name = "primeview_pd050vl1",
467 },
468
469
470 {
471 {
472 .x_res = 800,
473 .y_res = 480,
474
475 .pixel_clock = 32000,
476
477 .hsw = 128,
478 .hfp = 42,
479 .hbp = 86,
480
481 .vsw = 2,
482 .vfp = 10,
483 .vbp = 33,
484
485 .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
486 .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
487 .data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
488 .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
489 .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
490 },
491 .name = "primeview_pm070wl4",
492 },
493
494
495 {
496 {
497 .x_res = 800,
498 .y_res = 600,
499
500 .pixel_clock = 40000,
501
502 .hsw = 128,
503 .hfp = 42,
504 .hbp = 86,
505
506 .vsw = 4,
507 .vfp = 1,
508 .vbp = 23,
509
510 .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
511 .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
512 .data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
513 .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
514 .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
515 },
516 .name = "primeview_pd104slf",
517 },
518};
519
520struct panel_drv_data {
521
522 struct omap_dss_device *dssdev;
523
524 struct panel_config *panel_config;
525
526 struct mutex lock;
527};
528
529static inline struct panel_generic_dpi_data
530*get_panel_data(const struct omap_dss_device *dssdev)
531{
532 return (struct panel_generic_dpi_data *) dssdev->data;
533}
534
535static int generic_dpi_panel_power_on(struct omap_dss_device *dssdev)
536{
537 int r, i;
538 struct panel_generic_dpi_data *panel_data = get_panel_data(dssdev);
539 struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev);
540 struct panel_config *panel_config = drv_data->panel_config;
541
542 if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
543 return 0;
544
545 omapdss_dpi_set_timings(dssdev, &dssdev->panel.timings);
546 omapdss_dpi_set_data_lines(dssdev, dssdev->phy.dpi.data_lines);
547
548 r = omapdss_dpi_display_enable(dssdev);
549 if (r)
550 goto err0;
551
552
553 if (panel_config->power_on_delay)
554 msleep(panel_config->power_on_delay);
555
556 for (i = 0; i < panel_data->num_gpios; ++i) {
557 gpio_set_value_cansleep(panel_data->gpios[i],
558 panel_data->gpio_invert[i] ? 0 : 1);
559 }
560
561 return 0;
562
563err0:
564 return r;
565}
566
567static void generic_dpi_panel_power_off(struct omap_dss_device *dssdev)
568{
569 struct panel_generic_dpi_data *panel_data = get_panel_data(dssdev);
570 struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev);
571 struct panel_config *panel_config = drv_data->panel_config;
572 int i;
573
574 if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
575 return;
576
577 for (i = panel_data->num_gpios - 1; i >= 0; --i) {
578 gpio_set_value_cansleep(panel_data->gpios[i],
579 panel_data->gpio_invert[i] ? 1 : 0);
580 }
581
582
583 if (panel_config->power_off_delay)
584 msleep(panel_config->power_off_delay);
585
586 omapdss_dpi_display_disable(dssdev);
587}
588
589static int generic_dpi_panel_probe(struct omap_dss_device *dssdev)
590{
591 struct panel_generic_dpi_data *panel_data = get_panel_data(dssdev);
592 struct panel_config *panel_config = NULL;
593 struct panel_drv_data *drv_data = NULL;
594 int i, r;
595
596 dev_dbg(&dssdev->dev, "probe\n");
597
598 if (!panel_data || !panel_data->name)
599 return -EINVAL;
600
601 for (i = 0; i < ARRAY_SIZE(generic_dpi_panels); i++) {
602 if (strcmp(panel_data->name, generic_dpi_panels[i].name) == 0) {
603 panel_config = &generic_dpi_panels[i];
604 break;
605 }
606 }
607
608 if (!panel_config)
609 return -EINVAL;
610
611 for (i = 0; i < panel_data->num_gpios; ++i) {
612 r = devm_gpio_request_one(&dssdev->dev, panel_data->gpios[i],
613 panel_data->gpio_invert[i] ?
614 GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW,
615 "panel gpio");
616 if (r)
617 return r;
618 }
619
620 dssdev->panel.timings = panel_config->timings;
621
622 drv_data = devm_kzalloc(&dssdev->dev, sizeof(*drv_data), GFP_KERNEL);
623 if (!drv_data)
624 return -ENOMEM;
625
626 drv_data->dssdev = dssdev;
627 drv_data->panel_config = panel_config;
628
629 mutex_init(&drv_data->lock);
630
631 dev_set_drvdata(&dssdev->dev, drv_data);
632
633 return 0;
634}
635
636static void __exit generic_dpi_panel_remove(struct omap_dss_device *dssdev)
637{
638 dev_dbg(&dssdev->dev, "remove\n");
639
640 dev_set_drvdata(&dssdev->dev, NULL);
641}
642
643static int generic_dpi_panel_enable(struct omap_dss_device *dssdev)
644{
645 struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev);
646 int r;
647
648 mutex_lock(&drv_data->lock);
649
650 r = generic_dpi_panel_power_on(dssdev);
651 if (r)
652 goto err;
653
654 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
655err:
656 mutex_unlock(&drv_data->lock);
657
658 return r;
659}
660
661static void generic_dpi_panel_disable(struct omap_dss_device *dssdev)
662{
663 struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev);
664
665 mutex_lock(&drv_data->lock);
666
667 generic_dpi_panel_power_off(dssdev);
668
669 dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
670
671 mutex_unlock(&drv_data->lock);
672}
673
674static void generic_dpi_panel_set_timings(struct omap_dss_device *dssdev,
675 struct omap_video_timings *timings)
676{
677 struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev);
678
679 mutex_lock(&drv_data->lock);
680
681 omapdss_dpi_set_timings(dssdev, timings);
682
683 dssdev->panel.timings = *timings;
684
685 mutex_unlock(&drv_data->lock);
686}
687
688static void generic_dpi_panel_get_timings(struct omap_dss_device *dssdev,
689 struct omap_video_timings *timings)
690{
691 struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev);
692
693 mutex_lock(&drv_data->lock);
694
695 *timings = dssdev->panel.timings;
696
697 mutex_unlock(&drv_data->lock);
698}
699
700static int generic_dpi_panel_check_timings(struct omap_dss_device *dssdev,
701 struct omap_video_timings *timings)
702{
703 struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev);
704 int r;
705
706 mutex_lock(&drv_data->lock);
707
708 r = dpi_check_timings(dssdev, timings);
709
710 mutex_unlock(&drv_data->lock);
711
712 return r;
713}
714
715static struct omap_dss_driver dpi_driver = {
716 .probe = generic_dpi_panel_probe,
717 .remove = __exit_p(generic_dpi_panel_remove),
718
719 .enable = generic_dpi_panel_enable,
720 .disable = generic_dpi_panel_disable,
721
722 .set_timings = generic_dpi_panel_set_timings,
723 .get_timings = generic_dpi_panel_get_timings,
724 .check_timings = generic_dpi_panel_check_timings,
725
726 .driver = {
727 .name = "generic_dpi_panel",
728 .owner = THIS_MODULE,
729 },
730};
731
732static int __init generic_dpi_panel_drv_init(void)
733{
734 return omap_dss_register_driver(&dpi_driver);
735}
736
737static void __exit generic_dpi_panel_drv_exit(void)
738{
739 omap_dss_unregister_driver(&dpi_driver);
740}
741
742module_init(generic_dpi_panel_drv_init);
743module_exit(generic_dpi_panel_drv_exit);
744MODULE_LICENSE("GPL");
745