diff options
Diffstat (limited to 'drivers/video/fbdev/omap2/displays-new/panel-dsi-cm.c')
-rw-r--r-- | drivers/video/fbdev/omap2/displays-new/panel-dsi-cm.c | 1388 |
1 files changed, 1388 insertions, 0 deletions
diff --git a/drivers/video/fbdev/omap2/displays-new/panel-dsi-cm.c b/drivers/video/fbdev/omap2/displays-new/panel-dsi-cm.c new file mode 100644 index 000000000000..d6f14e8717e8 --- /dev/null +++ b/drivers/video/fbdev/omap2/displays-new/panel-dsi-cm.c | |||
@@ -0,0 +1,1388 @@ | |||
1 | /* | ||
2 | * Generic DSI Command Mode panel driver | ||
3 | * | ||
4 | * Copyright (C) 2013 Texas Instruments | ||
5 | * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify it | ||
8 | * under the terms of the GNU General Public License version 2 as published by | ||
9 | * the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | /* #define DEBUG */ | ||
13 | |||
14 | #include <linux/backlight.h> | ||
15 | #include <linux/delay.h> | ||
16 | #include <linux/fb.h> | ||
17 | #include <linux/gpio.h> | ||
18 | #include <linux/interrupt.h> | ||
19 | #include <linux/jiffies.h> | ||
20 | #include <linux/module.h> | ||
21 | #include <linux/platform_device.h> | ||
22 | #include <linux/sched.h> | ||
23 | #include <linux/slab.h> | ||
24 | #include <linux/workqueue.h> | ||
25 | #include <linux/of_device.h> | ||
26 | #include <linux/of_gpio.h> | ||
27 | |||
28 | #include <video/omapdss.h> | ||
29 | #include <video/omap-panel-data.h> | ||
30 | #include <video/mipi_display.h> | ||
31 | |||
32 | /* DSI Virtual channel. Hardcoded for now. */ | ||
33 | #define TCH 0 | ||
34 | |||
35 | #define DCS_READ_NUM_ERRORS 0x05 | ||
36 | #define DCS_BRIGHTNESS 0x51 | ||
37 | #define DCS_CTRL_DISPLAY 0x53 | ||
38 | #define DCS_GET_ID1 0xda | ||
39 | #define DCS_GET_ID2 0xdb | ||
40 | #define DCS_GET_ID3 0xdc | ||
41 | |||
42 | struct panel_drv_data { | ||
43 | struct omap_dss_device dssdev; | ||
44 | struct omap_dss_device *in; | ||
45 | |||
46 | struct omap_video_timings timings; | ||
47 | |||
48 | struct platform_device *pdev; | ||
49 | |||
50 | struct mutex lock; | ||
51 | |||
52 | struct backlight_device *bldev; | ||
53 | |||
54 | unsigned long hw_guard_end; /* next value of jiffies when we can | ||
55 | * issue the next sleep in/out command | ||
56 | */ | ||
57 | unsigned long hw_guard_wait; /* max guard time in jiffies */ | ||
58 | |||
59 | /* panel HW configuration from DT or platform data */ | ||
60 | int reset_gpio; | ||
61 | int ext_te_gpio; | ||
62 | |||
63 | bool use_dsi_backlight; | ||
64 | |||
65 | struct omap_dsi_pin_config pin_config; | ||
66 | |||
67 | /* runtime variables */ | ||
68 | bool enabled; | ||
69 | |||
70 | bool te_enabled; | ||
71 | |||
72 | atomic_t do_update; | ||
73 | int channel; | ||
74 | |||
75 | struct delayed_work te_timeout_work; | ||
76 | |||
77 | bool intro_printed; | ||
78 | |||
79 | struct workqueue_struct *workqueue; | ||
80 | |||
81 | bool ulps_enabled; | ||
82 | unsigned ulps_timeout; | ||
83 | struct delayed_work ulps_work; | ||
84 | }; | ||
85 | |||
86 | #define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev) | ||
87 | |||
88 | static irqreturn_t dsicm_te_isr(int irq, void *data); | ||
89 | static void dsicm_te_timeout_work_callback(struct work_struct *work); | ||
90 | static int _dsicm_enable_te(struct panel_drv_data *ddata, bool enable); | ||
91 | |||
92 | static int dsicm_panel_reset(struct panel_drv_data *ddata); | ||
93 | |||
94 | static void dsicm_ulps_work(struct work_struct *work); | ||
95 | |||
96 | static void hw_guard_start(struct panel_drv_data *ddata, int guard_msec) | ||
97 | { | ||
98 | ddata->hw_guard_wait = msecs_to_jiffies(guard_msec); | ||
99 | ddata->hw_guard_end = jiffies + ddata->hw_guard_wait; | ||
100 | } | ||
101 | |||
102 | static void hw_guard_wait(struct panel_drv_data *ddata) | ||
103 | { | ||
104 | unsigned long wait = ddata->hw_guard_end - jiffies; | ||
105 | |||
106 | if ((long)wait > 0 && wait <= ddata->hw_guard_wait) { | ||
107 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
108 | schedule_timeout(wait); | ||
109 | } | ||
110 | } | ||
111 | |||
112 | static int dsicm_dcs_read_1(struct panel_drv_data *ddata, u8 dcs_cmd, u8 *data) | ||
113 | { | ||
114 | struct omap_dss_device *in = ddata->in; | ||
115 | int r; | ||
116 | u8 buf[1]; | ||
117 | |||
118 | r = in->ops.dsi->dcs_read(in, ddata->channel, dcs_cmd, buf, 1); | ||
119 | |||
120 | if (r < 0) | ||
121 | return r; | ||
122 | |||
123 | *data = buf[0]; | ||
124 | |||
125 | return 0; | ||
126 | } | ||
127 | |||
128 | static int dsicm_dcs_write_0(struct panel_drv_data *ddata, u8 dcs_cmd) | ||
129 | { | ||
130 | struct omap_dss_device *in = ddata->in; | ||
131 | return in->ops.dsi->dcs_write(in, ddata->channel, &dcs_cmd, 1); | ||
132 | } | ||
133 | |||
134 | static int dsicm_dcs_write_1(struct panel_drv_data *ddata, u8 dcs_cmd, u8 param) | ||
135 | { | ||
136 | struct omap_dss_device *in = ddata->in; | ||
137 | u8 buf[2] = { dcs_cmd, param }; | ||
138 | |||
139 | return in->ops.dsi->dcs_write(in, ddata->channel, buf, 2); | ||
140 | } | ||
141 | |||
142 | static int dsicm_sleep_in(struct panel_drv_data *ddata) | ||
143 | |||
144 | { | ||
145 | struct omap_dss_device *in = ddata->in; | ||
146 | u8 cmd; | ||
147 | int r; | ||
148 | |||
149 | hw_guard_wait(ddata); | ||
150 | |||
151 | cmd = MIPI_DCS_ENTER_SLEEP_MODE; | ||
152 | r = in->ops.dsi->dcs_write_nosync(in, ddata->channel, &cmd, 1); | ||
153 | if (r) | ||
154 | return r; | ||
155 | |||
156 | hw_guard_start(ddata, 120); | ||
157 | |||
158 | usleep_range(5000, 10000); | ||
159 | |||
160 | return 0; | ||
161 | } | ||
162 | |||
163 | static int dsicm_sleep_out(struct panel_drv_data *ddata) | ||
164 | { | ||
165 | int r; | ||
166 | |||
167 | hw_guard_wait(ddata); | ||
168 | |||
169 | r = dsicm_dcs_write_0(ddata, MIPI_DCS_EXIT_SLEEP_MODE); | ||
170 | if (r) | ||
171 | return r; | ||
172 | |||
173 | hw_guard_start(ddata, 120); | ||
174 | |||
175 | usleep_range(5000, 10000); | ||
176 | |||
177 | return 0; | ||
178 | } | ||
179 | |||
180 | static int dsicm_get_id(struct panel_drv_data *ddata, u8 *id1, u8 *id2, u8 *id3) | ||
181 | { | ||
182 | int r; | ||
183 | |||
184 | r = dsicm_dcs_read_1(ddata, DCS_GET_ID1, id1); | ||
185 | if (r) | ||
186 | return r; | ||
187 | r = dsicm_dcs_read_1(ddata, DCS_GET_ID2, id2); | ||
188 | if (r) | ||
189 | return r; | ||
190 | r = dsicm_dcs_read_1(ddata, DCS_GET_ID3, id3); | ||
191 | if (r) | ||
192 | return r; | ||
193 | |||
194 | return 0; | ||
195 | } | ||
196 | |||
197 | static int dsicm_set_update_window(struct panel_drv_data *ddata, | ||
198 | u16 x, u16 y, u16 w, u16 h) | ||
199 | { | ||
200 | struct omap_dss_device *in = ddata->in; | ||
201 | int r; | ||
202 | u16 x1 = x; | ||
203 | u16 x2 = x + w - 1; | ||
204 | u16 y1 = y; | ||
205 | u16 y2 = y + h - 1; | ||
206 | |||
207 | u8 buf[5]; | ||
208 | buf[0] = MIPI_DCS_SET_COLUMN_ADDRESS; | ||
209 | buf[1] = (x1 >> 8) & 0xff; | ||
210 | buf[2] = (x1 >> 0) & 0xff; | ||
211 | buf[3] = (x2 >> 8) & 0xff; | ||
212 | buf[4] = (x2 >> 0) & 0xff; | ||
213 | |||
214 | r = in->ops.dsi->dcs_write_nosync(in, ddata->channel, buf, sizeof(buf)); | ||
215 | if (r) | ||
216 | return r; | ||
217 | |||
218 | buf[0] = MIPI_DCS_SET_PAGE_ADDRESS; | ||
219 | buf[1] = (y1 >> 8) & 0xff; | ||
220 | buf[2] = (y1 >> 0) & 0xff; | ||
221 | buf[3] = (y2 >> 8) & 0xff; | ||
222 | buf[4] = (y2 >> 0) & 0xff; | ||
223 | |||
224 | r = in->ops.dsi->dcs_write_nosync(in, ddata->channel, buf, sizeof(buf)); | ||
225 | if (r) | ||
226 | return r; | ||
227 | |||
228 | in->ops.dsi->bta_sync(in, ddata->channel); | ||
229 | |||
230 | return r; | ||
231 | } | ||
232 | |||
233 | static void dsicm_queue_ulps_work(struct panel_drv_data *ddata) | ||
234 | { | ||
235 | if (ddata->ulps_timeout > 0) | ||
236 | queue_delayed_work(ddata->workqueue, &ddata->ulps_work, | ||
237 | msecs_to_jiffies(ddata->ulps_timeout)); | ||
238 | } | ||
239 | |||
240 | static void dsicm_cancel_ulps_work(struct panel_drv_data *ddata) | ||
241 | { | ||
242 | cancel_delayed_work(&ddata->ulps_work); | ||
243 | } | ||
244 | |||
245 | static int dsicm_enter_ulps(struct panel_drv_data *ddata) | ||
246 | { | ||
247 | struct omap_dss_device *in = ddata->in; | ||
248 | int r; | ||
249 | |||
250 | if (ddata->ulps_enabled) | ||
251 | return 0; | ||
252 | |||
253 | dsicm_cancel_ulps_work(ddata); | ||
254 | |||
255 | r = _dsicm_enable_te(ddata, false); | ||
256 | if (r) | ||
257 | goto err; | ||
258 | |||
259 | if (gpio_is_valid(ddata->ext_te_gpio)) | ||
260 | disable_irq(gpio_to_irq(ddata->ext_te_gpio)); | ||
261 | |||
262 | in->ops.dsi->disable(in, false, true); | ||
263 | |||
264 | ddata->ulps_enabled = true; | ||
265 | |||
266 | return 0; | ||
267 | |||
268 | err: | ||
269 | dev_err(&ddata->pdev->dev, "enter ULPS failed"); | ||
270 | dsicm_panel_reset(ddata); | ||
271 | |||
272 | ddata->ulps_enabled = false; | ||
273 | |||
274 | dsicm_queue_ulps_work(ddata); | ||
275 | |||
276 | return r; | ||
277 | } | ||
278 | |||
279 | static int dsicm_exit_ulps(struct panel_drv_data *ddata) | ||
280 | { | ||
281 | struct omap_dss_device *in = ddata->in; | ||
282 | int r; | ||
283 | |||
284 | if (!ddata->ulps_enabled) | ||
285 | return 0; | ||
286 | |||
287 | r = in->ops.dsi->enable(in); | ||
288 | if (r) { | ||
289 | dev_err(&ddata->pdev->dev, "failed to enable DSI\n"); | ||
290 | goto err1; | ||
291 | } | ||
292 | |||
293 | in->ops.dsi->enable_hs(in, ddata->channel, true); | ||
294 | |||
295 | r = _dsicm_enable_te(ddata, true); | ||
296 | if (r) { | ||
297 | dev_err(&ddata->pdev->dev, "failed to re-enable TE"); | ||
298 | goto err2; | ||
299 | } | ||
300 | |||
301 | if (gpio_is_valid(ddata->ext_te_gpio)) | ||
302 | enable_irq(gpio_to_irq(ddata->ext_te_gpio)); | ||
303 | |||
304 | dsicm_queue_ulps_work(ddata); | ||
305 | |||
306 | ddata->ulps_enabled = false; | ||
307 | |||
308 | return 0; | ||
309 | |||
310 | err2: | ||
311 | dev_err(&ddata->pdev->dev, "failed to exit ULPS"); | ||
312 | |||
313 | r = dsicm_panel_reset(ddata); | ||
314 | if (!r) { | ||
315 | if (gpio_is_valid(ddata->ext_te_gpio)) | ||
316 | enable_irq(gpio_to_irq(ddata->ext_te_gpio)); | ||
317 | ddata->ulps_enabled = false; | ||
318 | } | ||
319 | err1: | ||
320 | dsicm_queue_ulps_work(ddata); | ||
321 | |||
322 | return r; | ||
323 | } | ||
324 | |||
325 | static int dsicm_wake_up(struct panel_drv_data *ddata) | ||
326 | { | ||
327 | if (ddata->ulps_enabled) | ||
328 | return dsicm_exit_ulps(ddata); | ||
329 | |||
330 | dsicm_cancel_ulps_work(ddata); | ||
331 | dsicm_queue_ulps_work(ddata); | ||
332 | return 0; | ||
333 | } | ||
334 | |||
335 | static int dsicm_bl_update_status(struct backlight_device *dev) | ||
336 | { | ||
337 | struct panel_drv_data *ddata = dev_get_drvdata(&dev->dev); | ||
338 | struct omap_dss_device *in = ddata->in; | ||
339 | int r; | ||
340 | int level; | ||
341 | |||
342 | if (dev->props.fb_blank == FB_BLANK_UNBLANK && | ||
343 | dev->props.power == FB_BLANK_UNBLANK) | ||
344 | level = dev->props.brightness; | ||
345 | else | ||
346 | level = 0; | ||
347 | |||
348 | dev_dbg(&ddata->pdev->dev, "update brightness to %d\n", level); | ||
349 | |||
350 | mutex_lock(&ddata->lock); | ||
351 | |||
352 | if (ddata->enabled) { | ||
353 | in->ops.dsi->bus_lock(in); | ||
354 | |||
355 | r = dsicm_wake_up(ddata); | ||
356 | if (!r) | ||
357 | r = dsicm_dcs_write_1(ddata, DCS_BRIGHTNESS, level); | ||
358 | |||
359 | in->ops.dsi->bus_unlock(in); | ||
360 | } else { | ||
361 | r = 0; | ||
362 | } | ||
363 | |||
364 | mutex_unlock(&ddata->lock); | ||
365 | |||
366 | return r; | ||
367 | } | ||
368 | |||
369 | static int dsicm_bl_get_intensity(struct backlight_device *dev) | ||
370 | { | ||
371 | if (dev->props.fb_blank == FB_BLANK_UNBLANK && | ||
372 | dev->props.power == FB_BLANK_UNBLANK) | ||
373 | return dev->props.brightness; | ||
374 | |||
375 | return 0; | ||
376 | } | ||
377 | |||
378 | static const struct backlight_ops dsicm_bl_ops = { | ||
379 | .get_brightness = dsicm_bl_get_intensity, | ||
380 | .update_status = dsicm_bl_update_status, | ||
381 | }; | ||
382 | |||
383 | static void dsicm_get_resolution(struct omap_dss_device *dssdev, | ||
384 | u16 *xres, u16 *yres) | ||
385 | { | ||
386 | *xres = dssdev->panel.timings.x_res; | ||
387 | *yres = dssdev->panel.timings.y_res; | ||
388 | } | ||
389 | |||
390 | static ssize_t dsicm_num_errors_show(struct device *dev, | ||
391 | struct device_attribute *attr, char *buf) | ||
392 | { | ||
393 | struct platform_device *pdev = to_platform_device(dev); | ||
394 | struct panel_drv_data *ddata = platform_get_drvdata(pdev); | ||
395 | struct omap_dss_device *in = ddata->in; | ||
396 | u8 errors = 0; | ||
397 | int r; | ||
398 | |||
399 | mutex_lock(&ddata->lock); | ||
400 | |||
401 | if (ddata->enabled) { | ||
402 | in->ops.dsi->bus_lock(in); | ||
403 | |||
404 | r = dsicm_wake_up(ddata); | ||
405 | if (!r) | ||
406 | r = dsicm_dcs_read_1(ddata, DCS_READ_NUM_ERRORS, | ||
407 | &errors); | ||
408 | |||
409 | in->ops.dsi->bus_unlock(in); | ||
410 | } else { | ||
411 | r = -ENODEV; | ||
412 | } | ||
413 | |||
414 | mutex_unlock(&ddata->lock); | ||
415 | |||
416 | if (r) | ||
417 | return r; | ||
418 | |||
419 | return snprintf(buf, PAGE_SIZE, "%d\n", errors); | ||
420 | } | ||
421 | |||
422 | static ssize_t dsicm_hw_revision_show(struct device *dev, | ||
423 | struct device_attribute *attr, char *buf) | ||
424 | { | ||
425 | struct platform_device *pdev = to_platform_device(dev); | ||
426 | struct panel_drv_data *ddata = platform_get_drvdata(pdev); | ||
427 | struct omap_dss_device *in = ddata->in; | ||
428 | u8 id1, id2, id3; | ||
429 | int r; | ||
430 | |||
431 | mutex_lock(&ddata->lock); | ||
432 | |||
433 | if (ddata->enabled) { | ||
434 | in->ops.dsi->bus_lock(in); | ||
435 | |||
436 | r = dsicm_wake_up(ddata); | ||
437 | if (!r) | ||
438 | r = dsicm_get_id(ddata, &id1, &id2, &id3); | ||
439 | |||
440 | in->ops.dsi->bus_unlock(in); | ||
441 | } else { | ||
442 | r = -ENODEV; | ||
443 | } | ||
444 | |||
445 | mutex_unlock(&ddata->lock); | ||
446 | |||
447 | if (r) | ||
448 | return r; | ||
449 | |||
450 | return snprintf(buf, PAGE_SIZE, "%02x.%02x.%02x\n", id1, id2, id3); | ||
451 | } | ||
452 | |||
453 | static ssize_t dsicm_store_ulps(struct device *dev, | ||
454 | struct device_attribute *attr, | ||
455 | const char *buf, size_t count) | ||
456 | { | ||
457 | struct platform_device *pdev = to_platform_device(dev); | ||
458 | struct panel_drv_data *ddata = platform_get_drvdata(pdev); | ||
459 | struct omap_dss_device *in = ddata->in; | ||
460 | unsigned long t; | ||
461 | int r; | ||
462 | |||
463 | r = kstrtoul(buf, 0, &t); | ||
464 | if (r) | ||
465 | return r; | ||
466 | |||
467 | mutex_lock(&ddata->lock); | ||
468 | |||
469 | if (ddata->enabled) { | ||
470 | in->ops.dsi->bus_lock(in); | ||
471 | |||
472 | if (t) | ||
473 | r = dsicm_enter_ulps(ddata); | ||
474 | else | ||
475 | r = dsicm_wake_up(ddata); | ||
476 | |||
477 | in->ops.dsi->bus_unlock(in); | ||
478 | } | ||
479 | |||
480 | mutex_unlock(&ddata->lock); | ||
481 | |||
482 | if (r) | ||
483 | return r; | ||
484 | |||
485 | return count; | ||
486 | } | ||
487 | |||
488 | static ssize_t dsicm_show_ulps(struct device *dev, | ||
489 | struct device_attribute *attr, | ||
490 | char *buf) | ||
491 | { | ||
492 | struct platform_device *pdev = to_platform_device(dev); | ||
493 | struct panel_drv_data *ddata = platform_get_drvdata(pdev); | ||
494 | unsigned t; | ||
495 | |||
496 | mutex_lock(&ddata->lock); | ||
497 | t = ddata->ulps_enabled; | ||
498 | mutex_unlock(&ddata->lock); | ||
499 | |||
500 | return snprintf(buf, PAGE_SIZE, "%u\n", t); | ||
501 | } | ||
502 | |||
503 | static ssize_t dsicm_store_ulps_timeout(struct device *dev, | ||
504 | struct device_attribute *attr, | ||
505 | const char *buf, size_t count) | ||
506 | { | ||
507 | struct platform_device *pdev = to_platform_device(dev); | ||
508 | struct panel_drv_data *ddata = platform_get_drvdata(pdev); | ||
509 | struct omap_dss_device *in = ddata->in; | ||
510 | unsigned long t; | ||
511 | int r; | ||
512 | |||
513 | r = kstrtoul(buf, 0, &t); | ||
514 | if (r) | ||
515 | return r; | ||
516 | |||
517 | mutex_lock(&ddata->lock); | ||
518 | ddata->ulps_timeout = t; | ||
519 | |||
520 | if (ddata->enabled) { | ||
521 | /* dsicm_wake_up will restart the timer */ | ||
522 | in->ops.dsi->bus_lock(in); | ||
523 | r = dsicm_wake_up(ddata); | ||
524 | in->ops.dsi->bus_unlock(in); | ||
525 | } | ||
526 | |||
527 | mutex_unlock(&ddata->lock); | ||
528 | |||
529 | if (r) | ||
530 | return r; | ||
531 | |||
532 | return count; | ||
533 | } | ||
534 | |||
535 | static ssize_t dsicm_show_ulps_timeout(struct device *dev, | ||
536 | struct device_attribute *attr, | ||
537 | char *buf) | ||
538 | { | ||
539 | struct platform_device *pdev = to_platform_device(dev); | ||
540 | struct panel_drv_data *ddata = platform_get_drvdata(pdev); | ||
541 | unsigned t; | ||
542 | |||
543 | mutex_lock(&ddata->lock); | ||
544 | t = ddata->ulps_timeout; | ||
545 | mutex_unlock(&ddata->lock); | ||
546 | |||
547 | return snprintf(buf, PAGE_SIZE, "%u\n", t); | ||
548 | } | ||
549 | |||
550 | static DEVICE_ATTR(num_dsi_errors, S_IRUGO, dsicm_num_errors_show, NULL); | ||
551 | static DEVICE_ATTR(hw_revision, S_IRUGO, dsicm_hw_revision_show, NULL); | ||
552 | static DEVICE_ATTR(ulps, S_IRUGO | S_IWUSR, | ||
553 | dsicm_show_ulps, dsicm_store_ulps); | ||
554 | static DEVICE_ATTR(ulps_timeout, S_IRUGO | S_IWUSR, | ||
555 | dsicm_show_ulps_timeout, dsicm_store_ulps_timeout); | ||
556 | |||
557 | static struct attribute *dsicm_attrs[] = { | ||
558 | &dev_attr_num_dsi_errors.attr, | ||
559 | &dev_attr_hw_revision.attr, | ||
560 | &dev_attr_ulps.attr, | ||
561 | &dev_attr_ulps_timeout.attr, | ||
562 | NULL, | ||
563 | }; | ||
564 | |||
565 | static struct attribute_group dsicm_attr_group = { | ||
566 | .attrs = dsicm_attrs, | ||
567 | }; | ||
568 | |||
569 | static void dsicm_hw_reset(struct panel_drv_data *ddata) | ||
570 | { | ||
571 | if (!gpio_is_valid(ddata->reset_gpio)) | ||
572 | return; | ||
573 | |||
574 | gpio_set_value(ddata->reset_gpio, 1); | ||
575 | udelay(10); | ||
576 | /* reset the panel */ | ||
577 | gpio_set_value(ddata->reset_gpio, 0); | ||
578 | /* assert reset */ | ||
579 | udelay(10); | ||
580 | gpio_set_value(ddata->reset_gpio, 1); | ||
581 | /* wait after releasing reset */ | ||
582 | usleep_range(5000, 10000); | ||
583 | } | ||
584 | |||
585 | static int dsicm_power_on(struct panel_drv_data *ddata) | ||
586 | { | ||
587 | struct omap_dss_device *in = ddata->in; | ||
588 | u8 id1, id2, id3; | ||
589 | int r; | ||
590 | struct omap_dss_dsi_config dsi_config = { | ||
591 | .mode = OMAP_DSS_DSI_CMD_MODE, | ||
592 | .pixel_format = OMAP_DSS_DSI_FMT_RGB888, | ||
593 | .timings = &ddata->timings, | ||
594 | .hs_clk_min = 150000000, | ||
595 | .hs_clk_max = 300000000, | ||
596 | .lp_clk_min = 7000000, | ||
597 | .lp_clk_max = 10000000, | ||
598 | }; | ||
599 | |||
600 | if (ddata->pin_config.num_pins > 0) { | ||
601 | r = in->ops.dsi->configure_pins(in, &ddata->pin_config); | ||
602 | if (r) { | ||
603 | dev_err(&ddata->pdev->dev, | ||
604 | "failed to configure DSI pins\n"); | ||
605 | goto err0; | ||
606 | } | ||
607 | } | ||
608 | |||
609 | r = in->ops.dsi->set_config(in, &dsi_config); | ||
610 | if (r) { | ||
611 | dev_err(&ddata->pdev->dev, "failed to configure DSI\n"); | ||
612 | goto err0; | ||
613 | } | ||
614 | |||
615 | r = in->ops.dsi->enable(in); | ||
616 | if (r) { | ||
617 | dev_err(&ddata->pdev->dev, "failed to enable DSI\n"); | ||
618 | goto err0; | ||
619 | } | ||
620 | |||
621 | dsicm_hw_reset(ddata); | ||
622 | |||
623 | in->ops.dsi->enable_hs(in, ddata->channel, false); | ||
624 | |||
625 | r = dsicm_sleep_out(ddata); | ||
626 | if (r) | ||
627 | goto err; | ||
628 | |||
629 | r = dsicm_get_id(ddata, &id1, &id2, &id3); | ||
630 | if (r) | ||
631 | goto err; | ||
632 | |||
633 | r = dsicm_dcs_write_1(ddata, DCS_BRIGHTNESS, 0xff); | ||
634 | if (r) | ||
635 | goto err; | ||
636 | |||
637 | r = dsicm_dcs_write_1(ddata, DCS_CTRL_DISPLAY, | ||
638 | (1<<2) | (1<<5)); /* BL | BCTRL */ | ||
639 | if (r) | ||
640 | goto err; | ||
641 | |||
642 | r = dsicm_dcs_write_1(ddata, MIPI_DCS_SET_PIXEL_FORMAT, | ||
643 | MIPI_DCS_PIXEL_FMT_24BIT); | ||
644 | if (r) | ||
645 | goto err; | ||
646 | |||
647 | r = dsicm_dcs_write_0(ddata, MIPI_DCS_SET_DISPLAY_ON); | ||
648 | if (r) | ||
649 | goto err; | ||
650 | |||
651 | r = _dsicm_enable_te(ddata, ddata->te_enabled); | ||
652 | if (r) | ||
653 | goto err; | ||
654 | |||
655 | r = in->ops.dsi->enable_video_output(in, ddata->channel); | ||
656 | if (r) | ||
657 | goto err; | ||
658 | |||
659 | ddata->enabled = 1; | ||
660 | |||
661 | if (!ddata->intro_printed) { | ||
662 | dev_info(&ddata->pdev->dev, "panel revision %02x.%02x.%02x\n", | ||
663 | id1, id2, id3); | ||
664 | ddata->intro_printed = true; | ||
665 | } | ||
666 | |||
667 | in->ops.dsi->enable_hs(in, ddata->channel, true); | ||
668 | |||
669 | return 0; | ||
670 | err: | ||
671 | dev_err(&ddata->pdev->dev, "error while enabling panel, issuing HW reset\n"); | ||
672 | |||
673 | dsicm_hw_reset(ddata); | ||
674 | |||
675 | in->ops.dsi->disable(in, true, false); | ||
676 | err0: | ||
677 | return r; | ||
678 | } | ||
679 | |||
680 | static void dsicm_power_off(struct panel_drv_data *ddata) | ||
681 | { | ||
682 | struct omap_dss_device *in = ddata->in; | ||
683 | int r; | ||
684 | |||
685 | in->ops.dsi->disable_video_output(in, ddata->channel); | ||
686 | |||
687 | r = dsicm_dcs_write_0(ddata, MIPI_DCS_SET_DISPLAY_OFF); | ||
688 | if (!r) | ||
689 | r = dsicm_sleep_in(ddata); | ||
690 | |||
691 | if (r) { | ||
692 | dev_err(&ddata->pdev->dev, | ||
693 | "error disabling panel, issuing HW reset\n"); | ||
694 | dsicm_hw_reset(ddata); | ||
695 | } | ||
696 | |||
697 | in->ops.dsi->disable(in, true, false); | ||
698 | |||
699 | ddata->enabled = 0; | ||
700 | } | ||
701 | |||
702 | static int dsicm_panel_reset(struct panel_drv_data *ddata) | ||
703 | { | ||
704 | dev_err(&ddata->pdev->dev, "performing LCD reset\n"); | ||
705 | |||
706 | dsicm_power_off(ddata); | ||
707 | dsicm_hw_reset(ddata); | ||
708 | return dsicm_power_on(ddata); | ||
709 | } | ||
710 | |||
711 | static int dsicm_connect(struct omap_dss_device *dssdev) | ||
712 | { | ||
713 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
714 | struct omap_dss_device *in = ddata->in; | ||
715 | struct device *dev = &ddata->pdev->dev; | ||
716 | int r; | ||
717 | |||
718 | if (omapdss_device_is_connected(dssdev)) | ||
719 | return 0; | ||
720 | |||
721 | r = in->ops.dsi->connect(in, dssdev); | ||
722 | if (r) { | ||
723 | dev_err(dev, "Failed to connect to video source\n"); | ||
724 | return r; | ||
725 | } | ||
726 | |||
727 | r = in->ops.dsi->request_vc(ddata->in, &ddata->channel); | ||
728 | if (r) { | ||
729 | dev_err(dev, "failed to get virtual channel\n"); | ||
730 | goto err_req_vc; | ||
731 | } | ||
732 | |||
733 | r = in->ops.dsi->set_vc_id(ddata->in, ddata->channel, TCH); | ||
734 | if (r) { | ||
735 | dev_err(dev, "failed to set VC_ID\n"); | ||
736 | goto err_vc_id; | ||
737 | } | ||
738 | |||
739 | return 0; | ||
740 | |||
741 | err_vc_id: | ||
742 | in->ops.dsi->release_vc(ddata->in, ddata->channel); | ||
743 | err_req_vc: | ||
744 | in->ops.dsi->disconnect(in, dssdev); | ||
745 | return r; | ||
746 | } | ||
747 | |||
748 | static void dsicm_disconnect(struct omap_dss_device *dssdev) | ||
749 | { | ||
750 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
751 | struct omap_dss_device *in = ddata->in; | ||
752 | |||
753 | if (!omapdss_device_is_connected(dssdev)) | ||
754 | return; | ||
755 | |||
756 | in->ops.dsi->release_vc(in, ddata->channel); | ||
757 | in->ops.dsi->disconnect(in, dssdev); | ||
758 | } | ||
759 | |||
760 | static int dsicm_enable(struct omap_dss_device *dssdev) | ||
761 | { | ||
762 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
763 | struct omap_dss_device *in = ddata->in; | ||
764 | int r; | ||
765 | |||
766 | dev_dbg(&ddata->pdev->dev, "enable\n"); | ||
767 | |||
768 | mutex_lock(&ddata->lock); | ||
769 | |||
770 | if (!omapdss_device_is_connected(dssdev)) { | ||
771 | r = -ENODEV; | ||
772 | goto err; | ||
773 | } | ||
774 | |||
775 | if (omapdss_device_is_enabled(dssdev)) { | ||
776 | r = 0; | ||
777 | goto err; | ||
778 | } | ||
779 | |||
780 | in->ops.dsi->bus_lock(in); | ||
781 | |||
782 | r = dsicm_power_on(ddata); | ||
783 | |||
784 | in->ops.dsi->bus_unlock(in); | ||
785 | |||
786 | if (r) | ||
787 | goto err; | ||
788 | |||
789 | dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; | ||
790 | |||
791 | mutex_unlock(&ddata->lock); | ||
792 | |||
793 | return 0; | ||
794 | err: | ||
795 | dev_dbg(&ddata->pdev->dev, "enable failed\n"); | ||
796 | mutex_unlock(&ddata->lock); | ||
797 | return r; | ||
798 | } | ||
799 | |||
800 | static void dsicm_disable(struct omap_dss_device *dssdev) | ||
801 | { | ||
802 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
803 | struct omap_dss_device *in = ddata->in; | ||
804 | int r; | ||
805 | |||
806 | dev_dbg(&ddata->pdev->dev, "disable\n"); | ||
807 | |||
808 | mutex_lock(&ddata->lock); | ||
809 | |||
810 | dsicm_cancel_ulps_work(ddata); | ||
811 | |||
812 | in->ops.dsi->bus_lock(in); | ||
813 | |||
814 | if (omapdss_device_is_enabled(dssdev)) { | ||
815 | r = dsicm_wake_up(ddata); | ||
816 | if (!r) | ||
817 | dsicm_power_off(ddata); | ||
818 | } | ||
819 | |||
820 | in->ops.dsi->bus_unlock(in); | ||
821 | |||
822 | dssdev->state = OMAP_DSS_DISPLAY_DISABLED; | ||
823 | |||
824 | mutex_unlock(&ddata->lock); | ||
825 | } | ||
826 | |||
827 | static void dsicm_framedone_cb(int err, void *data) | ||
828 | { | ||
829 | struct panel_drv_data *ddata = data; | ||
830 | struct omap_dss_device *in = ddata->in; | ||
831 | |||
832 | dev_dbg(&ddata->pdev->dev, "framedone, err %d\n", err); | ||
833 | in->ops.dsi->bus_unlock(ddata->in); | ||
834 | } | ||
835 | |||
836 | static irqreturn_t dsicm_te_isr(int irq, void *data) | ||
837 | { | ||
838 | struct panel_drv_data *ddata = data; | ||
839 | struct omap_dss_device *in = ddata->in; | ||
840 | int old; | ||
841 | int r; | ||
842 | |||
843 | old = atomic_cmpxchg(&ddata->do_update, 1, 0); | ||
844 | |||
845 | if (old) { | ||
846 | cancel_delayed_work(&ddata->te_timeout_work); | ||
847 | |||
848 | r = in->ops.dsi->update(in, ddata->channel, dsicm_framedone_cb, | ||
849 | ddata); | ||
850 | if (r) | ||
851 | goto err; | ||
852 | } | ||
853 | |||
854 | return IRQ_HANDLED; | ||
855 | err: | ||
856 | dev_err(&ddata->pdev->dev, "start update failed\n"); | ||
857 | in->ops.dsi->bus_unlock(in); | ||
858 | return IRQ_HANDLED; | ||
859 | } | ||
860 | |||
861 | static void dsicm_te_timeout_work_callback(struct work_struct *work) | ||
862 | { | ||
863 | struct panel_drv_data *ddata = container_of(work, struct panel_drv_data, | ||
864 | te_timeout_work.work); | ||
865 | struct omap_dss_device *in = ddata->in; | ||
866 | |||
867 | dev_err(&ddata->pdev->dev, "TE not received for 250ms!\n"); | ||
868 | |||
869 | atomic_set(&ddata->do_update, 0); | ||
870 | in->ops.dsi->bus_unlock(in); | ||
871 | } | ||
872 | |||
873 | static int dsicm_update(struct omap_dss_device *dssdev, | ||
874 | u16 x, u16 y, u16 w, u16 h) | ||
875 | { | ||
876 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
877 | struct omap_dss_device *in = ddata->in; | ||
878 | int r; | ||
879 | |||
880 | dev_dbg(&ddata->pdev->dev, "update %d, %d, %d x %d\n", x, y, w, h); | ||
881 | |||
882 | mutex_lock(&ddata->lock); | ||
883 | in->ops.dsi->bus_lock(in); | ||
884 | |||
885 | r = dsicm_wake_up(ddata); | ||
886 | if (r) | ||
887 | goto err; | ||
888 | |||
889 | if (!ddata->enabled) { | ||
890 | r = 0; | ||
891 | goto err; | ||
892 | } | ||
893 | |||
894 | /* XXX no need to send this every frame, but dsi break if not done */ | ||
895 | r = dsicm_set_update_window(ddata, 0, 0, | ||
896 | dssdev->panel.timings.x_res, | ||
897 | dssdev->panel.timings.y_res); | ||
898 | if (r) | ||
899 | goto err; | ||
900 | |||
901 | if (ddata->te_enabled && gpio_is_valid(ddata->ext_te_gpio)) { | ||
902 | schedule_delayed_work(&ddata->te_timeout_work, | ||
903 | msecs_to_jiffies(250)); | ||
904 | atomic_set(&ddata->do_update, 1); | ||
905 | } else { | ||
906 | r = in->ops.dsi->update(in, ddata->channel, dsicm_framedone_cb, | ||
907 | ddata); | ||
908 | if (r) | ||
909 | goto err; | ||
910 | } | ||
911 | |||
912 | /* note: no bus_unlock here. unlock is in framedone_cb */ | ||
913 | mutex_unlock(&ddata->lock); | ||
914 | return 0; | ||
915 | err: | ||
916 | in->ops.dsi->bus_unlock(in); | ||
917 | mutex_unlock(&ddata->lock); | ||
918 | return r; | ||
919 | } | ||
920 | |||
921 | static int dsicm_sync(struct omap_dss_device *dssdev) | ||
922 | { | ||
923 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
924 | struct omap_dss_device *in = ddata->in; | ||
925 | |||
926 | dev_dbg(&ddata->pdev->dev, "sync\n"); | ||
927 | |||
928 | mutex_lock(&ddata->lock); | ||
929 | in->ops.dsi->bus_lock(in); | ||
930 | in->ops.dsi->bus_unlock(in); | ||
931 | mutex_unlock(&ddata->lock); | ||
932 | |||
933 | dev_dbg(&ddata->pdev->dev, "sync done\n"); | ||
934 | |||
935 | return 0; | ||
936 | } | ||
937 | |||
938 | static int _dsicm_enable_te(struct panel_drv_data *ddata, bool enable) | ||
939 | { | ||
940 | struct omap_dss_device *in = ddata->in; | ||
941 | int r; | ||
942 | |||
943 | if (enable) | ||
944 | r = dsicm_dcs_write_1(ddata, MIPI_DCS_SET_TEAR_ON, 0); | ||
945 | else | ||
946 | r = dsicm_dcs_write_0(ddata, MIPI_DCS_SET_TEAR_OFF); | ||
947 | |||
948 | if (!gpio_is_valid(ddata->ext_te_gpio)) | ||
949 | in->ops.dsi->enable_te(in, enable); | ||
950 | |||
951 | /* possible panel bug */ | ||
952 | msleep(100); | ||
953 | |||
954 | return r; | ||
955 | } | ||
956 | |||
957 | static int dsicm_enable_te(struct omap_dss_device *dssdev, bool enable) | ||
958 | { | ||
959 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
960 | struct omap_dss_device *in = ddata->in; | ||
961 | int r; | ||
962 | |||
963 | mutex_lock(&ddata->lock); | ||
964 | |||
965 | if (ddata->te_enabled == enable) | ||
966 | goto end; | ||
967 | |||
968 | in->ops.dsi->bus_lock(in); | ||
969 | |||
970 | if (ddata->enabled) { | ||
971 | r = dsicm_wake_up(ddata); | ||
972 | if (r) | ||
973 | goto err; | ||
974 | |||
975 | r = _dsicm_enable_te(ddata, enable); | ||
976 | if (r) | ||
977 | goto err; | ||
978 | } | ||
979 | |||
980 | ddata->te_enabled = enable; | ||
981 | |||
982 | in->ops.dsi->bus_unlock(in); | ||
983 | end: | ||
984 | mutex_unlock(&ddata->lock); | ||
985 | |||
986 | return 0; | ||
987 | err: | ||
988 | in->ops.dsi->bus_unlock(in); | ||
989 | mutex_unlock(&ddata->lock); | ||
990 | |||
991 | return r; | ||
992 | } | ||
993 | |||
994 | static int dsicm_get_te(struct omap_dss_device *dssdev) | ||
995 | { | ||
996 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
997 | int r; | ||
998 | |||
999 | mutex_lock(&ddata->lock); | ||
1000 | r = ddata->te_enabled; | ||
1001 | mutex_unlock(&ddata->lock); | ||
1002 | |||
1003 | return r; | ||
1004 | } | ||
1005 | |||
1006 | static int dsicm_memory_read(struct omap_dss_device *dssdev, | ||
1007 | void *buf, size_t size, | ||
1008 | u16 x, u16 y, u16 w, u16 h) | ||
1009 | { | ||
1010 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
1011 | struct omap_dss_device *in = ddata->in; | ||
1012 | int r; | ||
1013 | int first = 1; | ||
1014 | int plen; | ||
1015 | unsigned buf_used = 0; | ||
1016 | |||
1017 | if (size < w * h * 3) | ||
1018 | return -ENOMEM; | ||
1019 | |||
1020 | mutex_lock(&ddata->lock); | ||
1021 | |||
1022 | if (!ddata->enabled) { | ||
1023 | r = -ENODEV; | ||
1024 | goto err1; | ||
1025 | } | ||
1026 | |||
1027 | size = min(w * h * 3, | ||
1028 | dssdev->panel.timings.x_res * | ||
1029 | dssdev->panel.timings.y_res * 3); | ||
1030 | |||
1031 | in->ops.dsi->bus_lock(in); | ||
1032 | |||
1033 | r = dsicm_wake_up(ddata); | ||
1034 | if (r) | ||
1035 | goto err2; | ||
1036 | |||
1037 | /* plen 1 or 2 goes into short packet. until checksum error is fixed, | ||
1038 | * use short packets. plen 32 works, but bigger packets seem to cause | ||
1039 | * an error. */ | ||
1040 | if (size % 2) | ||
1041 | plen = 1; | ||
1042 | else | ||
1043 | plen = 2; | ||
1044 | |||
1045 | dsicm_set_update_window(ddata, x, y, w, h); | ||
1046 | |||
1047 | r = in->ops.dsi->set_max_rx_packet_size(in, ddata->channel, plen); | ||
1048 | if (r) | ||
1049 | goto err2; | ||
1050 | |||
1051 | while (buf_used < size) { | ||
1052 | u8 dcs_cmd = first ? 0x2e : 0x3e; | ||
1053 | first = 0; | ||
1054 | |||
1055 | r = in->ops.dsi->dcs_read(in, ddata->channel, dcs_cmd, | ||
1056 | buf + buf_used, size - buf_used); | ||
1057 | |||
1058 | if (r < 0) { | ||
1059 | dev_err(dssdev->dev, "read error\n"); | ||
1060 | goto err3; | ||
1061 | } | ||
1062 | |||
1063 | buf_used += r; | ||
1064 | |||
1065 | if (r < plen) { | ||
1066 | dev_err(&ddata->pdev->dev, "short read\n"); | ||
1067 | break; | ||
1068 | } | ||
1069 | |||
1070 | if (signal_pending(current)) { | ||
1071 | dev_err(&ddata->pdev->dev, "signal pending, " | ||
1072 | "aborting memory read\n"); | ||
1073 | r = -ERESTARTSYS; | ||
1074 | goto err3; | ||
1075 | } | ||
1076 | } | ||
1077 | |||
1078 | r = buf_used; | ||
1079 | |||
1080 | err3: | ||
1081 | in->ops.dsi->set_max_rx_packet_size(in, ddata->channel, 1); | ||
1082 | err2: | ||
1083 | in->ops.dsi->bus_unlock(in); | ||
1084 | err1: | ||
1085 | mutex_unlock(&ddata->lock); | ||
1086 | return r; | ||
1087 | } | ||
1088 | |||
1089 | static void dsicm_ulps_work(struct work_struct *work) | ||
1090 | { | ||
1091 | struct panel_drv_data *ddata = container_of(work, struct panel_drv_data, | ||
1092 | ulps_work.work); | ||
1093 | struct omap_dss_device *dssdev = &ddata->dssdev; | ||
1094 | struct omap_dss_device *in = ddata->in; | ||
1095 | |||
1096 | mutex_lock(&ddata->lock); | ||
1097 | |||
1098 | if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE || !ddata->enabled) { | ||
1099 | mutex_unlock(&ddata->lock); | ||
1100 | return; | ||
1101 | } | ||
1102 | |||
1103 | in->ops.dsi->bus_lock(in); | ||
1104 | |||
1105 | dsicm_enter_ulps(ddata); | ||
1106 | |||
1107 | in->ops.dsi->bus_unlock(in); | ||
1108 | mutex_unlock(&ddata->lock); | ||
1109 | } | ||
1110 | |||
1111 | static struct omap_dss_driver dsicm_ops = { | ||
1112 | .connect = dsicm_connect, | ||
1113 | .disconnect = dsicm_disconnect, | ||
1114 | |||
1115 | .enable = dsicm_enable, | ||
1116 | .disable = dsicm_disable, | ||
1117 | |||
1118 | .update = dsicm_update, | ||
1119 | .sync = dsicm_sync, | ||
1120 | |||
1121 | .get_resolution = dsicm_get_resolution, | ||
1122 | .get_recommended_bpp = omapdss_default_get_recommended_bpp, | ||
1123 | |||
1124 | .enable_te = dsicm_enable_te, | ||
1125 | .get_te = dsicm_get_te, | ||
1126 | |||
1127 | .memory_read = dsicm_memory_read, | ||
1128 | }; | ||
1129 | |||
1130 | static int dsicm_probe_pdata(struct platform_device *pdev) | ||
1131 | { | ||
1132 | const struct panel_dsicm_platform_data *pdata; | ||
1133 | struct panel_drv_data *ddata = platform_get_drvdata(pdev); | ||
1134 | struct omap_dss_device *dssdev, *in; | ||
1135 | |||
1136 | pdata = dev_get_platdata(&pdev->dev); | ||
1137 | |||
1138 | in = omap_dss_find_output(pdata->source); | ||
1139 | if (in == NULL) { | ||
1140 | dev_err(&pdev->dev, "failed to find video source\n"); | ||
1141 | return -EPROBE_DEFER; | ||
1142 | } | ||
1143 | ddata->in = in; | ||
1144 | |||
1145 | ddata->reset_gpio = pdata->reset_gpio; | ||
1146 | |||
1147 | if (pdata->use_ext_te) | ||
1148 | ddata->ext_te_gpio = pdata->ext_te_gpio; | ||
1149 | else | ||
1150 | ddata->ext_te_gpio = -1; | ||
1151 | |||
1152 | ddata->ulps_timeout = pdata->ulps_timeout; | ||
1153 | |||
1154 | ddata->use_dsi_backlight = pdata->use_dsi_backlight; | ||
1155 | |||
1156 | ddata->pin_config = pdata->pin_config; | ||
1157 | |||
1158 | dssdev = &ddata->dssdev; | ||
1159 | dssdev->name = pdata->name; | ||
1160 | |||
1161 | return 0; | ||
1162 | } | ||
1163 | |||
1164 | static int dsicm_probe_of(struct platform_device *pdev) | ||
1165 | { | ||
1166 | struct device_node *node = pdev->dev.of_node; | ||
1167 | struct panel_drv_data *ddata = platform_get_drvdata(pdev); | ||
1168 | struct omap_dss_device *in; | ||
1169 | int gpio; | ||
1170 | |||
1171 | gpio = of_get_named_gpio(node, "reset-gpios", 0); | ||
1172 | if (!gpio_is_valid(gpio)) { | ||
1173 | dev_err(&pdev->dev, "failed to parse reset gpio\n"); | ||
1174 | return gpio; | ||
1175 | } | ||
1176 | ddata->reset_gpio = gpio; | ||
1177 | |||
1178 | gpio = of_get_named_gpio(node, "te-gpios", 0); | ||
1179 | if (gpio_is_valid(gpio) || gpio == -ENOENT) { | ||
1180 | ddata->ext_te_gpio = gpio; | ||
1181 | } else { | ||
1182 | dev_err(&pdev->dev, "failed to parse TE gpio\n"); | ||
1183 | return gpio; | ||
1184 | } | ||
1185 | |||
1186 | in = omapdss_of_find_source_for_first_ep(node); | ||
1187 | if (IS_ERR(in)) { | ||
1188 | dev_err(&pdev->dev, "failed to find video source\n"); | ||
1189 | return PTR_ERR(in); | ||
1190 | } | ||
1191 | |||
1192 | ddata->in = in; | ||
1193 | |||
1194 | /* TODO: ulps, backlight */ | ||
1195 | |||
1196 | return 0; | ||
1197 | } | ||
1198 | |||
1199 | static int dsicm_probe(struct platform_device *pdev) | ||
1200 | { | ||
1201 | struct backlight_properties props; | ||
1202 | struct panel_drv_data *ddata; | ||
1203 | struct backlight_device *bldev = NULL; | ||
1204 | struct device *dev = &pdev->dev; | ||
1205 | struct omap_dss_device *dssdev; | ||
1206 | int r; | ||
1207 | |||
1208 | dev_dbg(dev, "probe\n"); | ||
1209 | |||
1210 | ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL); | ||
1211 | if (!ddata) | ||
1212 | return -ENOMEM; | ||
1213 | |||
1214 | platform_set_drvdata(pdev, ddata); | ||
1215 | ddata->pdev = pdev; | ||
1216 | |||
1217 | if (dev_get_platdata(dev)) { | ||
1218 | r = dsicm_probe_pdata(pdev); | ||
1219 | if (r) | ||
1220 | return r; | ||
1221 | } else if (pdev->dev.of_node) { | ||
1222 | r = dsicm_probe_of(pdev); | ||
1223 | if (r) | ||
1224 | return r; | ||
1225 | } else { | ||
1226 | return -ENODEV; | ||
1227 | } | ||
1228 | |||
1229 | ddata->timings.x_res = 864; | ||
1230 | ddata->timings.y_res = 480; | ||
1231 | ddata->timings.pixelclock = 864 * 480 * 60; | ||
1232 | |||
1233 | dssdev = &ddata->dssdev; | ||
1234 | dssdev->dev = dev; | ||
1235 | dssdev->driver = &dsicm_ops; | ||
1236 | dssdev->panel.timings = ddata->timings; | ||
1237 | dssdev->type = OMAP_DISPLAY_TYPE_DSI; | ||
1238 | dssdev->owner = THIS_MODULE; | ||
1239 | |||
1240 | dssdev->panel.dsi_pix_fmt = OMAP_DSS_DSI_FMT_RGB888; | ||
1241 | dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE | | ||
1242 | OMAP_DSS_DISPLAY_CAP_TEAR_ELIM; | ||
1243 | |||
1244 | r = omapdss_register_display(dssdev); | ||
1245 | if (r) { | ||
1246 | dev_err(dev, "Failed to register panel\n"); | ||
1247 | goto err_reg; | ||
1248 | } | ||
1249 | |||
1250 | mutex_init(&ddata->lock); | ||
1251 | |||
1252 | atomic_set(&ddata->do_update, 0); | ||
1253 | |||
1254 | if (gpio_is_valid(ddata->reset_gpio)) { | ||
1255 | r = devm_gpio_request_one(dev, ddata->reset_gpio, | ||
1256 | GPIOF_OUT_INIT_LOW, "taal rst"); | ||
1257 | if (r) { | ||
1258 | dev_err(dev, "failed to request reset gpio\n"); | ||
1259 | return r; | ||
1260 | } | ||
1261 | } | ||
1262 | |||
1263 | if (gpio_is_valid(ddata->ext_te_gpio)) { | ||
1264 | r = devm_gpio_request_one(dev, ddata->ext_te_gpio, | ||
1265 | GPIOF_IN, "taal irq"); | ||
1266 | if (r) { | ||
1267 | dev_err(dev, "GPIO request failed\n"); | ||
1268 | return r; | ||
1269 | } | ||
1270 | |||
1271 | r = devm_request_irq(dev, gpio_to_irq(ddata->ext_te_gpio), | ||
1272 | dsicm_te_isr, | ||
1273 | IRQF_TRIGGER_RISING, | ||
1274 | "taal vsync", ddata); | ||
1275 | |||
1276 | if (r) { | ||
1277 | dev_err(dev, "IRQ request failed\n"); | ||
1278 | return r; | ||
1279 | } | ||
1280 | |||
1281 | INIT_DEFERRABLE_WORK(&ddata->te_timeout_work, | ||
1282 | dsicm_te_timeout_work_callback); | ||
1283 | |||
1284 | dev_dbg(dev, "Using GPIO TE\n"); | ||
1285 | } | ||
1286 | |||
1287 | ddata->workqueue = create_singlethread_workqueue("dsicm_wq"); | ||
1288 | if (ddata->workqueue == NULL) { | ||
1289 | dev_err(dev, "can't create workqueue\n"); | ||
1290 | return -ENOMEM; | ||
1291 | } | ||
1292 | INIT_DELAYED_WORK(&ddata->ulps_work, dsicm_ulps_work); | ||
1293 | |||
1294 | dsicm_hw_reset(ddata); | ||
1295 | |||
1296 | if (ddata->use_dsi_backlight) { | ||
1297 | memset(&props, 0, sizeof(struct backlight_properties)); | ||
1298 | props.max_brightness = 255; | ||
1299 | |||
1300 | props.type = BACKLIGHT_RAW; | ||
1301 | bldev = backlight_device_register(dev_name(dev), | ||
1302 | dev, ddata, &dsicm_bl_ops, &props); | ||
1303 | if (IS_ERR(bldev)) { | ||
1304 | r = PTR_ERR(bldev); | ||
1305 | goto err_bl; | ||
1306 | } | ||
1307 | |||
1308 | ddata->bldev = bldev; | ||
1309 | |||
1310 | bldev->props.fb_blank = FB_BLANK_UNBLANK; | ||
1311 | bldev->props.power = FB_BLANK_UNBLANK; | ||
1312 | bldev->props.brightness = 255; | ||
1313 | |||
1314 | dsicm_bl_update_status(bldev); | ||
1315 | } | ||
1316 | |||
1317 | r = sysfs_create_group(&dev->kobj, &dsicm_attr_group); | ||
1318 | if (r) { | ||
1319 | dev_err(dev, "failed to create sysfs files\n"); | ||
1320 | goto err_sysfs_create; | ||
1321 | } | ||
1322 | |||
1323 | return 0; | ||
1324 | |||
1325 | err_sysfs_create: | ||
1326 | if (bldev != NULL) | ||
1327 | backlight_device_unregister(bldev); | ||
1328 | err_bl: | ||
1329 | destroy_workqueue(ddata->workqueue); | ||
1330 | err_reg: | ||
1331 | return r; | ||
1332 | } | ||
1333 | |||
1334 | static int __exit dsicm_remove(struct platform_device *pdev) | ||
1335 | { | ||
1336 | struct panel_drv_data *ddata = platform_get_drvdata(pdev); | ||
1337 | struct omap_dss_device *dssdev = &ddata->dssdev; | ||
1338 | struct backlight_device *bldev; | ||
1339 | |||
1340 | dev_dbg(&pdev->dev, "remove\n"); | ||
1341 | |||
1342 | omapdss_unregister_display(dssdev); | ||
1343 | |||
1344 | dsicm_disable(dssdev); | ||
1345 | dsicm_disconnect(dssdev); | ||
1346 | |||
1347 | sysfs_remove_group(&pdev->dev.kobj, &dsicm_attr_group); | ||
1348 | |||
1349 | bldev = ddata->bldev; | ||
1350 | if (bldev != NULL) { | ||
1351 | bldev->props.power = FB_BLANK_POWERDOWN; | ||
1352 | dsicm_bl_update_status(bldev); | ||
1353 | backlight_device_unregister(bldev); | ||
1354 | } | ||
1355 | |||
1356 | omap_dss_put_device(ddata->in); | ||
1357 | |||
1358 | dsicm_cancel_ulps_work(ddata); | ||
1359 | destroy_workqueue(ddata->workqueue); | ||
1360 | |||
1361 | /* reset, to be sure that the panel is in a valid state */ | ||
1362 | dsicm_hw_reset(ddata); | ||
1363 | |||
1364 | return 0; | ||
1365 | } | ||
1366 | |||
1367 | static const struct of_device_id dsicm_of_match[] = { | ||
1368 | { .compatible = "omapdss,panel-dsi-cm", }, | ||
1369 | {}, | ||
1370 | }; | ||
1371 | |||
1372 | MODULE_DEVICE_TABLE(of, dsicm_of_match); | ||
1373 | |||
1374 | static struct platform_driver dsicm_driver = { | ||
1375 | .probe = dsicm_probe, | ||
1376 | .remove = __exit_p(dsicm_remove), | ||
1377 | .driver = { | ||
1378 | .name = "panel-dsi-cm", | ||
1379 | .owner = THIS_MODULE, | ||
1380 | .of_match_table = dsicm_of_match, | ||
1381 | }, | ||
1382 | }; | ||
1383 | |||
1384 | module_platform_driver(dsicm_driver); | ||
1385 | |||
1386 | MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); | ||
1387 | MODULE_DESCRIPTION("Generic DSI Command Mode Panel Driver"); | ||
1388 | MODULE_LICENSE("GPL"); | ||