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 <video/omapdss.h>
37 
38 #include <video/omap-panel-generic-dpi.h>
39 
40 struct panel_config {
41 	struct omap_video_timings timings;
42 
43 	int acbi;	/* ac-bias pin transitions per interrupt */
44 	/* Unit: line clocks */
45 	int acb;	/* ac-bias pin frequency */
46 
47 	enum omap_panel_config config;
48 
49 	int power_on_delay;
50 	int power_off_delay;
51 
52 	/*
53 	 * Used to match device to panel configuration
54 	 * when use generic panel driver
55 	 */
56 	const char *name;
57 };
58 
59 /* Panel configurations */
60 static struct panel_config generic_dpi_panels[] = {
61 	/* Sharp LQ043T1DG01 */
62 	{
63 		{
64 			.x_res		= 480,
65 			.y_res		= 272,
66 
67 			.pixel_clock	= 9000,
68 
69 			.hsw		= 42,
70 			.hfp		= 3,
71 			.hbp		= 2,
72 
73 			.vsw		= 11,
74 			.vfp		= 3,
75 			.vbp		= 2,
76 		},
77 		.acbi			= 0x0,
78 		.acb			= 0x0,
79 		.config			= OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
80 					OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IEO,
81 		.power_on_delay		= 50,
82 		.power_off_delay	= 100,
83 		.name			= "sharp_lq",
84 	},
85 
86 	/* Sharp LS037V7DW01 */
87 	{
88 		{
89 			.x_res		= 480,
90 			.y_res		= 640,
91 
92 			.pixel_clock	= 19200,
93 
94 			.hsw		= 2,
95 			.hfp		= 1,
96 			.hbp		= 28,
97 
98 			.vsw		= 1,
99 			.vfp		= 1,
100 			.vbp		= 1,
101 		},
102 		.acbi			= 0x0,
103 		.acb			= 0x28,
104 		.config			= OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
105 						OMAP_DSS_LCD_IHS,
106 		.power_on_delay		= 50,
107 		.power_off_delay	= 100,
108 		.name			= "sharp_ls",
109 	},
110 
111 	/* Toppoly TDO35S */
112 	{
113 		{
114 			.x_res		= 480,
115 			.y_res		= 640,
116 
117 			.pixel_clock	= 26000,
118 
119 			.hfp		= 104,
120 			.hsw		= 8,
121 			.hbp		= 8,
122 
123 			.vfp		= 4,
124 			.vsw		= 2,
125 			.vbp		= 2,
126 		},
127 		.acbi			= 0x0,
128 		.acb			= 0x0,
129 		.config			= OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
130 					OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC |
131 					OMAP_DSS_LCD_ONOFF,
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 		.acbi			= 0x0,
154 		.acb			= 0x0,
155 		.config			= OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
156 						OMAP_DSS_LCD_IHS,
157 		.power_on_delay		= 0,
158 		.power_off_delay	= 0,
159 		.name			= "samsung_lte430wq_f0c",
160 	},
161 
162 	/* Seiko 70WVW1TZ3Z3 */
163 	{
164 		{
165 			.x_res		= 800,
166 			.y_res		= 480,
167 
168 			.pixel_clock	= 33000,
169 
170 			.hsw		= 128,
171 			.hfp		= 10,
172 			.hbp		= 10,
173 
174 			.vsw		= 2,
175 			.vfp		= 4,
176 			.vbp		= 11,
177 		},
178 		.acbi			= 0x0,
179 		.acb			= 0x0,
180 		.config			= OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
181 						OMAP_DSS_LCD_IHS,
182 		.power_on_delay		= 0,
183 		.power_off_delay	= 0,
184 		.name			= "seiko_70wvw1tz3",
185 	},
186 
187 	/* Powertip PH480272T */
188 	{
189 		{
190 			.x_res		= 480,
191 			.y_res		= 272,
192 
193 			.pixel_clock	= 9000,
194 
195 			.hsw		= 40,
196 			.hfp		= 2,
197 			.hbp		= 2,
198 
199 			.vsw		= 10,
200 			.vfp		= 2,
201 			.vbp		= 2,
202 		},
203 		.acbi			= 0x0,
204 		.acb			= 0x0,
205 		.config			= OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
206 					  OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IEO,
207 		.power_on_delay		= 0,
208 		.power_off_delay	= 0,
209 		.name			= "powertip_ph480272t",
210 	},
211 
212 	/* Innolux AT070TN83 */
213 	{
214 		{
215 			.x_res		= 800,
216 			.y_res		= 480,
217 
218 			.pixel_clock	= 40000,
219 
220 			.hsw		= 48,
221 			.hfp		= 1,
222 			.hbp		= 1,
223 
224 			.vsw		= 3,
225 			.vfp		= 12,
226 			.vbp		= 25,
227 		},
228 		.acbi			= 0x0,
229 		.acb			= 0x28,
230 		.config			= OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
231 					  OMAP_DSS_LCD_IHS,
232 		.power_on_delay		= 0,
233 		.power_off_delay	= 0,
234 		.name			= "innolux_at070tn83",
235 	},
236 
237 	/* NEC NL2432DR22-11B */
238 	{
239 		{
240 			.x_res		= 240,
241 			.y_res		= 320,
242 
243 			.pixel_clock	= 5400,
244 
245 			.hsw		= 3,
246 			.hfp		= 3,
247 			.hbp		= 39,
248 
249 			.vsw		= 1,
250 			.vfp		= 2,
251 			.vbp		= 7,
252 		},
253 		.config			= OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
254 						OMAP_DSS_LCD_IHS,
255 		.name			= "nec_nl2432dr22-11b",
256 	},
257 
258 	/* Unknown panel used in OMAP H4 */
259 	{
260 		{
261 			.x_res		= 240,
262 			.y_res		= 320,
263 
264 			.pixel_clock	= 6250,
265 
266 			.hsw		= 15,
267 			.hfp		= 15,
268 			.hbp		= 60,
269 
270 			.vsw		= 1,
271 			.vfp		= 1,
272 			.vbp		= 1,
273 		},
274 		.config			= OMAP_DSS_LCD_TFT,
275 
276 		.name			= "h4",
277 	},
278 
279 	/* Unknown panel used in Samsung OMAP2 Apollon */
280 	{
281 		{
282 			.x_res		= 480,
283 			.y_res		= 272,
284 
285 			.pixel_clock	= 6250,
286 
287 			.hsw		= 41,
288 			.hfp		= 2,
289 			.hbp		= 2,
290 
291 			.vsw		= 10,
292 			.vfp		= 2,
293 			.vbp		= 2,
294 		},
295 		.config			= OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
296 						OMAP_DSS_LCD_IHS,
297 
298 		.name			= "apollon",
299 	},
300 	/* FocalTech ETM070003DH6 */
301 	{
302 		{
303 			.x_res		= 800,
304 			.y_res		= 480,
305 
306 			.pixel_clock	= 28000,
307 
308 			.hsw		= 48,
309 			.hfp		= 40,
310 			.hbp		= 40,
311 
312 			.vsw		= 3,
313 			.vfp		= 13,
314 			.vbp		= 29,
315 		},
316 		.config			= OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
317 					  OMAP_DSS_LCD_IHS,
318 		.name			= "focaltech_etm070003dh6",
319 	},
320 
321 	/* Microtips Technologies - UMSH-8173MD */
322 	{
323 		{
324 			.x_res		= 800,
325 			.y_res		= 480,
326 
327 			.pixel_clock	= 34560,
328 
329 			.hsw		= 13,
330 			.hfp		= 101,
331 			.hbp		= 101,
332 
333 			.vsw		= 23,
334 			.vfp		= 1,
335 			.vbp		= 1,
336 		},
337 		.acbi			= 0x0,
338 		.acb			= 0x0,
339 		.config			= OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
340 					  OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC,
341 		.power_on_delay		= 0,
342 		.power_off_delay	= 0,
343 		.name			= "microtips_umsh_8173md",
344 	},
345 
346 	/* OrtusTech COM43H4M10XTC */
347 	{
348 		{
349 			.x_res		= 480,
350 			.y_res		= 272,
351 
352 			.pixel_clock	= 8000,
353 
354 			.hsw		= 41,
355 			.hfp		= 8,
356 			.hbp		= 4,
357 
358 			.vsw		= 10,
359 			.vfp		= 4,
360 			.vbp		= 2,
361 		},
362 		.config			= OMAP_DSS_LCD_TFT,
363 
364 		.name			= "ortustech_com43h4m10xtc",
365 	},
366 };
367 
368 struct panel_drv_data {
369 
370 	struct omap_dss_device *dssdev;
371 
372 	struct panel_config *panel_config;
373 };
374 
375 static inline struct panel_generic_dpi_data
get_panel_data(const struct omap_dss_device * dssdev)376 *get_panel_data(const struct omap_dss_device *dssdev)
377 {
378 	return (struct panel_generic_dpi_data *) dssdev->data;
379 }
380 
generic_dpi_panel_power_on(struct omap_dss_device * dssdev)381 static int generic_dpi_panel_power_on(struct omap_dss_device *dssdev)
382 {
383 	int r;
384 	struct panel_generic_dpi_data *panel_data = get_panel_data(dssdev);
385 	struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev);
386 	struct panel_config *panel_config = drv_data->panel_config;
387 
388 	if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
389 		return 0;
390 
391 	r = omapdss_dpi_display_enable(dssdev);
392 	if (r)
393 		goto err0;
394 
395 	/* wait couple of vsyncs until enabling the LCD */
396 	if (panel_config->power_on_delay)
397 		msleep(panel_config->power_on_delay);
398 
399 	if (panel_data->platform_enable) {
400 		r = panel_data->platform_enable(dssdev);
401 		if (r)
402 			goto err1;
403 	}
404 
405 	return 0;
406 err1:
407 	omapdss_dpi_display_disable(dssdev);
408 err0:
409 	return r;
410 }
411 
generic_dpi_panel_power_off(struct omap_dss_device * dssdev)412 static void generic_dpi_panel_power_off(struct omap_dss_device *dssdev)
413 {
414 	struct panel_generic_dpi_data *panel_data = get_panel_data(dssdev);
415 	struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev);
416 	struct panel_config *panel_config = drv_data->panel_config;
417 
418 	if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
419 		return;
420 
421 	if (panel_data->platform_disable)
422 		panel_data->platform_disable(dssdev);
423 
424 	/* wait couple of vsyncs after disabling the LCD */
425 	if (panel_config->power_off_delay)
426 		msleep(panel_config->power_off_delay);
427 
428 	omapdss_dpi_display_disable(dssdev);
429 }
430 
generic_dpi_panel_probe(struct omap_dss_device * dssdev)431 static int generic_dpi_panel_probe(struct omap_dss_device *dssdev)
432 {
433 	struct panel_generic_dpi_data *panel_data = get_panel_data(dssdev);
434 	struct panel_config *panel_config = NULL;
435 	struct panel_drv_data *drv_data = NULL;
436 	int i;
437 
438 	dev_dbg(&dssdev->dev, "probe\n");
439 
440 	if (!panel_data || !panel_data->name)
441 		return -EINVAL;
442 
443 	for (i = 0; i < ARRAY_SIZE(generic_dpi_panels); i++) {
444 		if (strcmp(panel_data->name, generic_dpi_panels[i].name) == 0) {
445 			panel_config = &generic_dpi_panels[i];
446 			break;
447 		}
448 	}
449 
450 	if (!panel_config)
451 		return -EINVAL;
452 
453 	dssdev->panel.config = panel_config->config;
454 	dssdev->panel.timings = panel_config->timings;
455 	dssdev->panel.acb = panel_config->acb;
456 	dssdev->panel.acbi = panel_config->acbi;
457 
458 	drv_data = kzalloc(sizeof(*drv_data), GFP_KERNEL);
459 	if (!drv_data)
460 		return -ENOMEM;
461 
462 	drv_data->dssdev = dssdev;
463 	drv_data->panel_config = panel_config;
464 
465 	dev_set_drvdata(&dssdev->dev, drv_data);
466 
467 	return 0;
468 }
469 
generic_dpi_panel_remove(struct omap_dss_device * dssdev)470 static void __exit generic_dpi_panel_remove(struct omap_dss_device *dssdev)
471 {
472 	struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev);
473 
474 	dev_dbg(&dssdev->dev, "remove\n");
475 
476 	kfree(drv_data);
477 
478 	dev_set_drvdata(&dssdev->dev, NULL);
479 }
480 
generic_dpi_panel_enable(struct omap_dss_device * dssdev)481 static int generic_dpi_panel_enable(struct omap_dss_device *dssdev)
482 {
483 	int r = 0;
484 
485 	r = generic_dpi_panel_power_on(dssdev);
486 	if (r)
487 		return r;
488 
489 	dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
490 
491 	return 0;
492 }
493 
generic_dpi_panel_disable(struct omap_dss_device * dssdev)494 static void generic_dpi_panel_disable(struct omap_dss_device *dssdev)
495 {
496 	generic_dpi_panel_power_off(dssdev);
497 
498 	dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
499 }
500 
generic_dpi_panel_suspend(struct omap_dss_device * dssdev)501 static int generic_dpi_panel_suspend(struct omap_dss_device *dssdev)
502 {
503 	generic_dpi_panel_power_off(dssdev);
504 
505 	dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
506 
507 	return 0;
508 }
509 
generic_dpi_panel_resume(struct omap_dss_device * dssdev)510 static int generic_dpi_panel_resume(struct omap_dss_device *dssdev)
511 {
512 	int r = 0;
513 
514 	r = generic_dpi_panel_power_on(dssdev);
515 	if (r)
516 		return r;
517 
518 	dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
519 
520 	return 0;
521 }
522 
generic_dpi_panel_set_timings(struct omap_dss_device * dssdev,struct omap_video_timings * timings)523 static void generic_dpi_panel_set_timings(struct omap_dss_device *dssdev,
524 		struct omap_video_timings *timings)
525 {
526 	dpi_set_timings(dssdev, timings);
527 }
528 
generic_dpi_panel_get_timings(struct omap_dss_device * dssdev,struct omap_video_timings * timings)529 static void generic_dpi_panel_get_timings(struct omap_dss_device *dssdev,
530 		struct omap_video_timings *timings)
531 {
532 	*timings = dssdev->panel.timings;
533 }
534 
generic_dpi_panel_check_timings(struct omap_dss_device * dssdev,struct omap_video_timings * timings)535 static int generic_dpi_panel_check_timings(struct omap_dss_device *dssdev,
536 		struct omap_video_timings *timings)
537 {
538 	return dpi_check_timings(dssdev, timings);
539 }
540 
541 static struct omap_dss_driver dpi_driver = {
542 	.probe		= generic_dpi_panel_probe,
543 	.remove		= __exit_p(generic_dpi_panel_remove),
544 
545 	.enable		= generic_dpi_panel_enable,
546 	.disable	= generic_dpi_panel_disable,
547 	.suspend	= generic_dpi_panel_suspend,
548 	.resume		= generic_dpi_panel_resume,
549 
550 	.set_timings	= generic_dpi_panel_set_timings,
551 	.get_timings	= generic_dpi_panel_get_timings,
552 	.check_timings	= generic_dpi_panel_check_timings,
553 
554 	.driver         = {
555 		.name   = "generic_dpi_panel",
556 		.owner  = THIS_MODULE,
557 	},
558 };
559 
generic_dpi_panel_drv_init(void)560 static int __init generic_dpi_panel_drv_init(void)
561 {
562 	return omap_dss_register_driver(&dpi_driver);
563 }
564 
generic_dpi_panel_drv_exit(void)565 static void __exit generic_dpi_panel_drv_exit(void)
566 {
567 	omap_dss_unregister_driver(&dpi_driver);
568 }
569 
570 module_init(generic_dpi_panel_drv_init);
571 module_exit(generic_dpi_panel_drv_exit);
572 MODULE_LICENSE("GPL");
573