2 * Generic DPI Panels support
4 * Copyright (C) 2010 Canonical Ltd.
5 * Author: Bryan Wu <bryan.wu@canonical.com>
7 * LCD panel driver for Sharp LQ043T1DG01
9 * Copyright (C) 2009 Texas Instruments Inc
10 * Author: Vaibhav Hiremath <hvaibhav@ti.com>
12 * LCD panel driver for Toppoly TDO35S
14 * Copyright (C) 2009 CompuLab, Ltd.
15 * Author: Mike Rapoport <mike@compulab.co.il>
17 * Copyright (C) 2008 Nokia Corporation
18 * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
20 * This program is free software; you can redistribute it and/or modify it
21 * under the terms of the GNU General Public License version 2 as published by
22 * the Free Software Foundation.
24 * This program is distributed in the hope that it will be useful, but WITHOUT
25 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
26 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
29 * You should have received a copy of the GNU General Public License along with
30 * this program. If not, see <http://www.gnu.org/licenses/>.
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>
39 #include <video/omap-panel-data.h>
42 struct omap_video_timings timings;
48 * Used to match device to panel configuration
49 * when use generic panel driver
54 /* Panel configurations */
55 static struct panel_config generic_dpi_panels[] = {
56 /* Sharp LQ043T1DG01 */
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,
79 .power_off_delay = 100,
83 /* Sharp LS037V7DW01 */
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,
105 .power_on_delay = 50,
106 .power_off_delay = 100,
116 .pixel_clock = 26000,
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,
133 .power_off_delay = 0,
134 .name = "toppoly_tdo35s",
137 /* Samsung LTE430WQ-F0C */
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,
160 .power_off_delay = 0,
161 .name = "samsung_lte430wq_f0c",
164 /* Seiko 70WVW1TZ3Z3 */
170 .pixel_clock = 33000,
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,
187 .power_off_delay = 0,
188 .name = "seiko_70wvw1tz3",
191 /* Powertip PH480272T */
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,
214 .power_off_delay = 0,
215 .name = "powertip_ph480272t",
218 /* Innolux AT070TN83 */
224 .pixel_clock = 40000,
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,
241 .power_off_delay = 0,
242 .name = "innolux_at070tn83",
245 /* NEC NL2432DR22-11B */
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,
267 .name = "nec_nl2432dr22-11b",
270 /* Unknown panel used in OMAP H4 */
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,
295 /* FocalTech ETM070003DH6 */
301 .pixel_clock = 28000,
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,
317 .name = "focaltech_etm070003dh6",
320 /* Microtips Technologies - UMSH-8173MD */
326 .pixel_clock = 34560,
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,
343 .power_off_delay = 0,
344 .name = "microtips_umsh_8173md",
347 /* OrtusTech COM43H4M10XTC */
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,
369 .name = "ortustech_com43h4m10xtc",
372 /* Innolux AT080TN52 */
378 .pixel_clock = 41142,
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,
394 .name = "innolux_at080tn52",
397 /* Mitsubishi AA084SB01 */
402 .pixel_clock = 40000,
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,
418 .name = "mitsubishi_aa084sb01",
420 /* EDT ET0500G0DH6 */
425 .pixel_clock = 33260,
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,
441 .name = "edt_et0500g0dh6",
444 /* Prime-View PD050VL1 */
450 .pixel_clock = 25000,
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,
466 .name = "primeview_pd050vl1",
469 /* Prime-View PM070WL4 */
475 .pixel_clock = 32000,
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,
491 .name = "primeview_pm070wl4",
494 /* Prime-View PD104SLF */
500 .pixel_clock = 40000,
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,
516 .name = "primeview_pd104slf",
520 struct panel_drv_data {
522 struct omap_dss_device *dssdev;
524 struct panel_config *panel_config;
529 static inline struct panel_generic_dpi_data
530 *get_panel_data(const struct omap_dss_device *dssdev)
532 return (struct panel_generic_dpi_data *) dssdev->data;
535 static int generic_dpi_panel_power_on(struct omap_dss_device *dssdev)
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;
542 if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
545 omapdss_dpi_set_timings(dssdev, &dssdev->panel.timings);
546 omapdss_dpi_set_data_lines(dssdev, dssdev->phy.dpi.data_lines);
548 r = omapdss_dpi_display_enable(dssdev);
552 /* wait couple of vsyncs until enabling the LCD */
553 if (panel_config->power_on_delay)
554 msleep(panel_config->power_on_delay);
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);
567 static void generic_dpi_panel_power_off(struct omap_dss_device *dssdev)
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;
574 if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
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);
582 /* wait couple of vsyncs after disabling the LCD */
583 if (panel_config->power_off_delay)
584 msleep(panel_config->power_off_delay);
586 omapdss_dpi_display_disable(dssdev);
589 static int generic_dpi_panel_probe(struct omap_dss_device *dssdev)
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;
596 dev_dbg(&dssdev->dev, "probe\n");
598 if (!panel_data || !panel_data->name)
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];
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,
620 dssdev->panel.timings = panel_config->timings;
622 drv_data = devm_kzalloc(&dssdev->dev, sizeof(*drv_data), GFP_KERNEL);
626 drv_data->dssdev = dssdev;
627 drv_data->panel_config = panel_config;
629 mutex_init(&drv_data->lock);
631 dev_set_drvdata(&dssdev->dev, drv_data);
636 static void __exit generic_dpi_panel_remove(struct omap_dss_device *dssdev)
638 dev_dbg(&dssdev->dev, "remove\n");
640 dev_set_drvdata(&dssdev->dev, NULL);
643 static int generic_dpi_panel_enable(struct omap_dss_device *dssdev)
645 struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev);
648 mutex_lock(&drv_data->lock);
650 r = generic_dpi_panel_power_on(dssdev);
654 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
656 mutex_unlock(&drv_data->lock);
661 static void generic_dpi_panel_disable(struct omap_dss_device *dssdev)
663 struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev);
665 mutex_lock(&drv_data->lock);
667 generic_dpi_panel_power_off(dssdev);
669 dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
671 mutex_unlock(&drv_data->lock);
674 static void generic_dpi_panel_set_timings(struct omap_dss_device *dssdev,
675 struct omap_video_timings *timings)
677 struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev);
679 mutex_lock(&drv_data->lock);
681 omapdss_dpi_set_timings(dssdev, timings);
683 dssdev->panel.timings = *timings;
685 mutex_unlock(&drv_data->lock);
688 static void generic_dpi_panel_get_timings(struct omap_dss_device *dssdev,
689 struct omap_video_timings *timings)
691 struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev);
693 mutex_lock(&drv_data->lock);
695 *timings = dssdev->panel.timings;
697 mutex_unlock(&drv_data->lock);
700 static int generic_dpi_panel_check_timings(struct omap_dss_device *dssdev,
701 struct omap_video_timings *timings)
703 struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev);
706 mutex_lock(&drv_data->lock);
708 r = dpi_check_timings(dssdev, timings);
710 mutex_unlock(&drv_data->lock);
715 static struct omap_dss_driver dpi_driver = {
716 .probe = generic_dpi_panel_probe,
717 .remove = __exit_p(generic_dpi_panel_remove),
719 .enable = generic_dpi_panel_enable,
720 .disable = generic_dpi_panel_disable,
722 .set_timings = generic_dpi_panel_set_timings,
723 .get_timings = generic_dpi_panel_get_timings,
724 .check_timings = generic_dpi_panel_check_timings,
727 .name = "generic_dpi_panel",
728 .owner = THIS_MODULE,
732 static int __init generic_dpi_panel_drv_init(void)
734 return omap_dss_register_driver(&dpi_driver);
737 static void __exit generic_dpi_panel_drv_exit(void)
739 omap_dss_unregister_driver(&dpi_driver);
742 module_init(generic_dpi_panel_drv_init);
743 module_exit(generic_dpi_panel_drv_exit);
744 MODULE_LICENSE("GPL");