OMAPDSS: Add panel dev pointer to dssdev
[linux-2.6-microblaze.git] / drivers / video / omap2 / displays / panel-generic-dpi.c
1 /*
2  * Generic DPI Panels support
3  *
4  * Copyright (C) 2010 Canonical Ltd.
5  * Author: Bryan Wu <bryan.wu@canonical.com>
6  *
7  * LCD panel driver for Sharp LQ043T1DG01
8  *
9  * Copyright (C) 2009 Texas Instruments Inc
10  * Author: Vaibhav Hiremath <hvaibhav@ti.com>
11  *
12  * LCD panel driver for Toppoly TDO35S
13  *
14  * Copyright (C) 2009 CompuLab, Ltd.
15  * Author: Mike Rapoport <mike@compulab.co.il>
16  *
17  * Copyright (C) 2008 Nokia Corporation
18  * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
19  *
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.
23  *
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
27  * more details.
28  *
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/>.
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
41 struct panel_config {
42         struct omap_video_timings timings;
43
44         int power_on_delay;
45         int power_off_delay;
46
47         /*
48          * Used to match device to panel configuration
49          * when use generic panel driver
50          */
51         const char *name;
52 };
53
54 /* Panel configurations */
55 static struct panel_config generic_dpi_panels[] = {
56         /* Sharp LQ043T1DG01 */
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         /* Sharp LS037V7DW01 */
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         /* Toppoly TDO35S */
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         /* Samsung LTE430WQ-F0C */
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         /* Seiko 70WVW1TZ3Z3 */
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         /* Powertip PH480272T */
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         /* Innolux AT070TN83 */
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         /* NEC NL2432DR22-11B */
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         /* Unknown panel used in OMAP H4 */
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         /* FocalTech ETM070003DH6 */
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         /* Microtips Technologies - UMSH-8173MD */
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         /* OrtusTech COM43H4M10XTC */
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         /* Innolux AT080TN52 */
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         /* Mitsubishi AA084SB01 */
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         /* EDT ET0500G0DH6 */
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         /* Prime-View PD050VL1 */
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         /* Prime-View PM070WL4 */
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         /* Prime-View PD104SLF */
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
520 struct panel_drv_data {
521
522         struct omap_dss_device *dssdev;
523
524         struct panel_config *panel_config;
525
526         struct mutex lock;
527 };
528
529 static 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
535 static 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         /* wait couple of vsyncs until enabling the LCD */
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
563 err0:
564         return r;
565 }
566
567 static 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         /* wait couple of vsyncs after disabling the LCD */
583         if (panel_config->power_off_delay)
584                 msleep(panel_config->power_off_delay);
585
586         omapdss_dpi_display_disable(dssdev);
587 }
588
589 static 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
636 static 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
643 static 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;
655 err:
656         mutex_unlock(&drv_data->lock);
657
658         return r;
659 }
660
661 static 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
674 static 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
688 static 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
700 static 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
715 static 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
732 static int __init generic_dpi_panel_drv_init(void)
733 {
734         return omap_dss_register_driver(&dpi_driver);
735 }
736
737 static void __exit generic_dpi_panel_drv_exit(void)
738 {
739         omap_dss_unregister_driver(&dpi_driver);
740 }
741
742 module_init(generic_dpi_panel_drv_init);
743 module_exit(generic_dpi_panel_drv_exit);
744 MODULE_LICENSE("GPL");