blob: e52631efe7e2eb6446055e67bdf277b8af3c3cef [file] [log] [blame]
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001/*
2 * Taal DSI command mode panel
3 *
4 * Copyright (C) 2009 Nokia Corporation
5 * Author: Tomi Valkeinen <tomi.valkeinen@nokia.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 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20/*#define DEBUG*/
21
22#include <linux/module.h>
23#include <linux/delay.h>
24#include <linux/err.h>
25#include <linux/jiffies.h>
26#include <linux/sched.h>
27#include <linux/backlight.h>
28#include <linux/fb.h>
29#include <linux/interrupt.h>
30#include <linux/gpio.h>
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +020031#include <linux/workqueue.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090032#include <linux/slab.h>
Tomi Valkeinena3201a02010-03-03 14:31:45 +020033#include <linux/mutex.h>
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +020034
35#include <plat/display.h>
36
37/* DSI Virtual channel. Hardcoded for now. */
38#define TCH 0
39
40#define DCS_READ_NUM_ERRORS 0x05
41#define DCS_READ_POWER_MODE 0x0a
42#define DCS_READ_MADCTL 0x0b
43#define DCS_READ_PIXEL_FORMAT 0x0c
44#define DCS_RDDSDR 0x0f
45#define DCS_SLEEP_IN 0x10
46#define DCS_SLEEP_OUT 0x11
47#define DCS_DISPLAY_OFF 0x28
48#define DCS_DISPLAY_ON 0x29
49#define DCS_COLUMN_ADDR 0x2a
50#define DCS_PAGE_ADDR 0x2b
51#define DCS_MEMORY_WRITE 0x2c
52#define DCS_TEAR_OFF 0x34
53#define DCS_TEAR_ON 0x35
54#define DCS_MEM_ACC_CTRL 0x36
55#define DCS_PIXEL_FORMAT 0x3a
56#define DCS_BRIGHTNESS 0x51
57#define DCS_CTRL_DISPLAY 0x53
58#define DCS_WRITE_CABC 0x55
59#define DCS_READ_CABC 0x56
60#define DCS_GET_ID1 0xda
61#define DCS_GET_ID2 0xdb
62#define DCS_GET_ID3 0xdc
63
64/* #define TAAL_USE_ESD_CHECK */
65#define TAAL_ESD_CHECK_PERIOD msecs_to_jiffies(5000)
66
Jani Nikula7ae2fb12010-04-13 10:57:52 +030067static irqreturn_t taal_te_isr(int irq, void *data);
68static void taal_te_timeout_work_callback(struct work_struct *work);
Tomi Valkeinen21df20f2010-03-02 12:13:55 +020069static int _taal_enable_te(struct omap_dss_device *dssdev, bool enable);
70
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +020071struct taal_data {
Tomi Valkeinena3201a02010-03-03 14:31:45 +020072 struct mutex lock;
73
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +020074 struct backlight_device *bldev;
75
76 unsigned long hw_guard_end; /* next value of jiffies when we can
77 * issue the next sleep in/out command
78 */
79 unsigned long hw_guard_wait; /* max guard time in jiffies */
80
81 struct omap_dss_device *dssdev;
82
83 bool enabled;
84 u8 rotate;
85 bool mirror;
86
87 bool te_enabled;
88 bool use_ext_te;
Jani Nikula7ae2fb12010-04-13 10:57:52 +030089
90 atomic_t do_update;
91 struct {
92 u16 x;
93 u16 y;
94 u16 w;
95 u16 h;
96 } update_region;
97 struct delayed_work te_timeout_work;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +020098
99 bool use_dsi_bl;
100
101 bool cabc_broken;
102 unsigned cabc_mode;
103
104 bool intro_printed;
105
106 struct workqueue_struct *esd_wq;
107 struct delayed_work esd_work;
108};
109
110static void taal_esd_work(struct work_struct *work);
111
112static void hw_guard_start(struct taal_data *td, int guard_msec)
113{
114 td->hw_guard_wait = msecs_to_jiffies(guard_msec);
115 td->hw_guard_end = jiffies + td->hw_guard_wait;
116}
117
118static void hw_guard_wait(struct taal_data *td)
119{
120 unsigned long wait = td->hw_guard_end - jiffies;
121
122 if ((long)wait > 0 && wait <= td->hw_guard_wait) {
123 set_current_state(TASK_UNINTERRUPTIBLE);
124 schedule_timeout(wait);
125 }
126}
127
128static int taal_dcs_read_1(u8 dcs_cmd, u8 *data)
129{
130 int r;
131 u8 buf[1];
132
133 r = dsi_vc_dcs_read(TCH, dcs_cmd, buf, 1);
134
135 if (r < 0)
136 return r;
137
138 *data = buf[0];
139
140 return 0;
141}
142
143static int taal_dcs_write_0(u8 dcs_cmd)
144{
145 return dsi_vc_dcs_write(TCH, &dcs_cmd, 1);
146}
147
148static int taal_dcs_write_1(u8 dcs_cmd, u8 param)
149{
150 u8 buf[2];
151 buf[0] = dcs_cmd;
152 buf[1] = param;
153 return dsi_vc_dcs_write(TCH, buf, 2);
154}
155
156static int taal_sleep_in(struct taal_data *td)
157
158{
159 u8 cmd;
160 int r;
161
162 hw_guard_wait(td);
163
164 cmd = DCS_SLEEP_IN;
165 r = dsi_vc_dcs_write_nosync(TCH, &cmd, 1);
166 if (r)
167 return r;
168
169 hw_guard_start(td, 120);
170
171 msleep(5);
172
173 return 0;
174}
175
176static int taal_sleep_out(struct taal_data *td)
177{
178 int r;
179
180 hw_guard_wait(td);
181
182 r = taal_dcs_write_0(DCS_SLEEP_OUT);
183 if (r)
184 return r;
185
186 hw_guard_start(td, 120);
187
188 msleep(5);
189
190 return 0;
191}
192
193static int taal_get_id(u8 *id1, u8 *id2, u8 *id3)
194{
195 int r;
196
197 r = taal_dcs_read_1(DCS_GET_ID1, id1);
198 if (r)
199 return r;
200 r = taal_dcs_read_1(DCS_GET_ID2, id2);
201 if (r)
202 return r;
203 r = taal_dcs_read_1(DCS_GET_ID3, id3);
204 if (r)
205 return r;
206
207 return 0;
208}
209
210static int taal_set_addr_mode(u8 rotate, bool mirror)
211{
212 int r;
213 u8 mode;
214 int b5, b6, b7;
215
216 r = taal_dcs_read_1(DCS_READ_MADCTL, &mode);
217 if (r)
218 return r;
219
220 switch (rotate) {
221 default:
222 case 0:
223 b7 = 0;
224 b6 = 0;
225 b5 = 0;
226 break;
227 case 1:
228 b7 = 0;
229 b6 = 1;
230 b5 = 1;
231 break;
232 case 2:
233 b7 = 1;
234 b6 = 1;
235 b5 = 0;
236 break;
237 case 3:
238 b7 = 1;
239 b6 = 0;
240 b5 = 1;
241 break;
242 }
243
244 if (mirror)
245 b6 = !b6;
246
247 mode &= ~((1<<7) | (1<<6) | (1<<5));
248 mode |= (b7 << 7) | (b6 << 6) | (b5 << 5);
249
250 return taal_dcs_write_1(DCS_MEM_ACC_CTRL, mode);
251}
252
253static int taal_set_update_window(u16 x, u16 y, u16 w, u16 h)
254{
255 int r;
256 u16 x1 = x;
257 u16 x2 = x + w - 1;
258 u16 y1 = y;
259 u16 y2 = y + h - 1;
260
261 u8 buf[5];
262 buf[0] = DCS_COLUMN_ADDR;
263 buf[1] = (x1 >> 8) & 0xff;
264 buf[2] = (x1 >> 0) & 0xff;
265 buf[3] = (x2 >> 8) & 0xff;
266 buf[4] = (x2 >> 0) & 0xff;
267
268 r = dsi_vc_dcs_write_nosync(TCH, buf, sizeof(buf));
269 if (r)
270 return r;
271
272 buf[0] = DCS_PAGE_ADDR;
273 buf[1] = (y1 >> 8) & 0xff;
274 buf[2] = (y1 >> 0) & 0xff;
275 buf[3] = (y2 >> 8) & 0xff;
276 buf[4] = (y2 >> 0) & 0xff;
277
278 r = dsi_vc_dcs_write_nosync(TCH, buf, sizeof(buf));
279 if (r)
280 return r;
281
282 dsi_vc_send_bta_sync(TCH);
283
284 return r;
285}
286
287static int taal_bl_update_status(struct backlight_device *dev)
288{
289 struct omap_dss_device *dssdev = dev_get_drvdata(&dev->dev);
290 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
291 int r;
292 int level;
293
294 if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
295 dev->props.power == FB_BLANK_UNBLANK)
296 level = dev->props.brightness;
297 else
298 level = 0;
299
300 dev_dbg(&dssdev->dev, "update brightness to %d\n", level);
301
Tomi Valkeinen1cbc8702010-04-15 16:41:07 +0300302 mutex_lock(&td->lock);
303
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200304 if (td->use_dsi_bl) {
305 if (td->enabled) {
306 dsi_bus_lock();
307 r = taal_dcs_write_1(DCS_BRIGHTNESS, level);
308 dsi_bus_unlock();
Tomi Valkeinen1cbc8702010-04-15 16:41:07 +0300309 } else {
310 r = 0;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200311 }
312 } else {
313 if (!dssdev->set_backlight)
Tomi Valkeinen1cbc8702010-04-15 16:41:07 +0300314 r = -EINVAL;
315 else
316 r = dssdev->set_backlight(dssdev, level);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200317 }
318
Tomi Valkeinen1cbc8702010-04-15 16:41:07 +0300319 mutex_unlock(&td->lock);
320
321 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200322}
323
324static int taal_bl_get_intensity(struct backlight_device *dev)
325{
326 if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
327 dev->props.power == FB_BLANK_UNBLANK)
328 return dev->props.brightness;
329
330 return 0;
331}
332
333static struct backlight_ops taal_bl_ops = {
334 .get_brightness = taal_bl_get_intensity,
335 .update_status = taal_bl_update_status,
336};
337
338static void taal_get_timings(struct omap_dss_device *dssdev,
339 struct omap_video_timings *timings)
340{
341 *timings = dssdev->panel.timings;
342}
343
344static void taal_get_resolution(struct omap_dss_device *dssdev,
345 u16 *xres, u16 *yres)
346{
347 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
348
349 if (td->rotate == 0 || td->rotate == 2) {
350 *xres = dssdev->panel.timings.x_res;
351 *yres = dssdev->panel.timings.y_res;
352 } else {
353 *yres = dssdev->panel.timings.x_res;
354 *xres = dssdev->panel.timings.y_res;
355 }
356}
357
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200358static ssize_t taal_num_errors_show(struct device *dev,
359 struct device_attribute *attr, char *buf)
360{
361 struct omap_dss_device *dssdev = to_dss_device(dev);
362 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
363 u8 errors;
364 int r;
365
Jani Nikula6b316712010-04-28 11:15:18 +0300366 mutex_lock(&td->lock);
367
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200368 if (td->enabled) {
369 dsi_bus_lock();
370 r = taal_dcs_read_1(DCS_READ_NUM_ERRORS, &errors);
371 dsi_bus_unlock();
372 } else {
373 r = -ENODEV;
374 }
375
Jani Nikula6b316712010-04-28 11:15:18 +0300376 mutex_unlock(&td->lock);
377
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200378 if (r)
379 return r;
380
381 return snprintf(buf, PAGE_SIZE, "%d\n", errors);
382}
383
384static ssize_t taal_hw_revision_show(struct device *dev,
385 struct device_attribute *attr, char *buf)
386{
387 struct omap_dss_device *dssdev = to_dss_device(dev);
388 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
389 u8 id1, id2, id3;
390 int r;
391
Jani Nikula6b316712010-04-28 11:15:18 +0300392 mutex_lock(&td->lock);
393
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200394 if (td->enabled) {
395 dsi_bus_lock();
396 r = taal_get_id(&id1, &id2, &id3);
397 dsi_bus_unlock();
398 } else {
399 r = -ENODEV;
400 }
401
Jani Nikula6b316712010-04-28 11:15:18 +0300402 mutex_unlock(&td->lock);
403
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200404 if (r)
405 return r;
406
407 return snprintf(buf, PAGE_SIZE, "%02x.%02x.%02x\n", id1, id2, id3);
408}
409
410static const char *cabc_modes[] = {
411 "off", /* used also always when CABC is not supported */
412 "ui",
413 "still-image",
414 "moving-image",
415};
416
417static ssize_t show_cabc_mode(struct device *dev,
418 struct device_attribute *attr,
419 char *buf)
420{
421 struct omap_dss_device *dssdev = to_dss_device(dev);
422 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
423 const char *mode_str;
424 int mode;
425 int len;
426
427 mode = td->cabc_mode;
428
429 mode_str = "unknown";
430 if (mode >= 0 && mode < ARRAY_SIZE(cabc_modes))
431 mode_str = cabc_modes[mode];
432 len = snprintf(buf, PAGE_SIZE, "%s\n", mode_str);
433
434 return len < PAGE_SIZE - 1 ? len : PAGE_SIZE - 1;
435}
436
437static ssize_t store_cabc_mode(struct device *dev,
438 struct device_attribute *attr,
439 const char *buf, size_t count)
440{
441 struct omap_dss_device *dssdev = to_dss_device(dev);
442 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
443 int i;
444
445 for (i = 0; i < ARRAY_SIZE(cabc_modes); i++) {
446 if (sysfs_streq(cabc_modes[i], buf))
447 break;
448 }
449
450 if (i == ARRAY_SIZE(cabc_modes))
451 return -EINVAL;
452
Jani Nikula6b316712010-04-28 11:15:18 +0300453 mutex_lock(&td->lock);
454
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200455 if (td->enabled) {
456 dsi_bus_lock();
457 if (!td->cabc_broken)
458 taal_dcs_write_1(DCS_WRITE_CABC, i);
459 dsi_bus_unlock();
460 }
461
462 td->cabc_mode = i;
463
Jani Nikula6b316712010-04-28 11:15:18 +0300464 mutex_unlock(&td->lock);
465
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200466 return count;
467}
468
469static ssize_t show_cabc_available_modes(struct device *dev,
470 struct device_attribute *attr,
471 char *buf)
472{
473 int len;
474 int i;
475
476 for (i = 0, len = 0;
477 len < PAGE_SIZE && i < ARRAY_SIZE(cabc_modes); i++)
478 len += snprintf(&buf[len], PAGE_SIZE - len, "%s%s%s",
479 i ? " " : "", cabc_modes[i],
480 i == ARRAY_SIZE(cabc_modes) - 1 ? "\n" : "");
481
482 return len < PAGE_SIZE ? len : PAGE_SIZE - 1;
483}
484
485static DEVICE_ATTR(num_dsi_errors, S_IRUGO, taal_num_errors_show, NULL);
486static DEVICE_ATTR(hw_revision, S_IRUGO, taal_hw_revision_show, NULL);
487static DEVICE_ATTR(cabc_mode, S_IRUGO | S_IWUSR,
488 show_cabc_mode, store_cabc_mode);
489static DEVICE_ATTR(cabc_available_modes, S_IRUGO,
490 show_cabc_available_modes, NULL);
491
492static struct attribute *taal_attrs[] = {
493 &dev_attr_num_dsi_errors.attr,
494 &dev_attr_hw_revision.attr,
495 &dev_attr_cabc_mode.attr,
496 &dev_attr_cabc_available_modes.attr,
497 NULL,
498};
499
500static struct attribute_group taal_attr_group = {
501 .attrs = taal_attrs,
502};
503
Jani Nikula006db7b2010-04-09 12:25:59 +0300504static void taal_hw_reset(struct omap_dss_device *dssdev)
505{
506 if (dssdev->reset_gpio == -1)
507 return;
508
509 gpio_set_value(dssdev->reset_gpio, 1);
510 udelay(10);
511 /* reset the panel */
512 gpio_set_value(dssdev->reset_gpio, 0);
513 /* assert reset for at least 10us */
514 udelay(10);
515 gpio_set_value(dssdev->reset_gpio, 1);
516 /* wait 5ms after releasing reset */
517 msleep(5);
518}
519
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200520static int taal_probe(struct omap_dss_device *dssdev)
521{
Matthew Garretta19a6ee2010-02-17 16:39:44 -0500522 struct backlight_properties props;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200523 struct taal_data *td;
524 struct backlight_device *bldev;
525 int r;
526
527 const struct omap_video_timings taal_panel_timings = {
528 .x_res = 864,
529 .y_res = 480,
530 };
531
532 dev_dbg(&dssdev->dev, "probe\n");
533
534 dssdev->panel.config = OMAP_DSS_LCD_TFT;
535 dssdev->panel.timings = taal_panel_timings;
536 dssdev->ctrl.pixel_size = 24;
537
538 td = kzalloc(sizeof(*td), GFP_KERNEL);
539 if (!td) {
540 r = -ENOMEM;
Jani Nikulad2b65782010-04-15 12:24:36 +0300541 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200542 }
543 td->dssdev = dssdev;
544
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200545 mutex_init(&td->lock);
546
Jani Nikula7ae2fb12010-04-13 10:57:52 +0300547 atomic_set(&td->do_update, 0);
548
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200549 td->esd_wq = create_singlethread_workqueue("taal_esd");
550 if (td->esd_wq == NULL) {
551 dev_err(&dssdev->dev, "can't create ESD workqueue\n");
552 r = -ENOMEM;
Jani Nikulad2b65782010-04-15 12:24:36 +0300553 goto err_wq;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200554 }
555 INIT_DELAYED_WORK_DEFERRABLE(&td->esd_work, taal_esd_work);
556
557 dev_set_drvdata(&dssdev->dev, td);
558
Jani Nikula006db7b2010-04-09 12:25:59 +0300559 taal_hw_reset(dssdev);
560
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200561 /* if no platform set_backlight() defined, presume DSI backlight
562 * control */
Matthew Garretta19a6ee2010-02-17 16:39:44 -0500563 memset(&props, 0, sizeof(struct backlight_properties));
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200564 if (!dssdev->set_backlight)
565 td->use_dsi_bl = true;
566
Matthew Garretta19a6ee2010-02-17 16:39:44 -0500567 if (td->use_dsi_bl)
568 props.max_brightness = 255;
569 else
570 props.max_brightness = 127;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200571 bldev = backlight_device_register("taal", &dssdev->dev, dssdev,
Matthew Garretta19a6ee2010-02-17 16:39:44 -0500572 &taal_bl_ops, &props);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200573 if (IS_ERR(bldev)) {
574 r = PTR_ERR(bldev);
Jani Nikulad2b65782010-04-15 12:24:36 +0300575 goto err_bl;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200576 }
577
578 td->bldev = bldev;
579
580 bldev->props.fb_blank = FB_BLANK_UNBLANK;
581 bldev->props.power = FB_BLANK_UNBLANK;
Matthew Garretta19a6ee2010-02-17 16:39:44 -0500582 if (td->use_dsi_bl)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200583 bldev->props.brightness = 255;
Matthew Garretta19a6ee2010-02-17 16:39:44 -0500584 else
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200585 bldev->props.brightness = 127;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200586
587 taal_bl_update_status(bldev);
588
589 if (dssdev->phy.dsi.ext_te) {
590 int gpio = dssdev->phy.dsi.ext_te_gpio;
591
592 r = gpio_request(gpio, "taal irq");
593 if (r) {
594 dev_err(&dssdev->dev, "GPIO request failed\n");
Jani Nikulad2b65782010-04-15 12:24:36 +0300595 goto err_gpio;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200596 }
597
598 gpio_direction_input(gpio);
599
600 r = request_irq(gpio_to_irq(gpio), taal_te_isr,
601 IRQF_DISABLED | IRQF_TRIGGER_RISING,
602 "taal vsync", dssdev);
603
604 if (r) {
605 dev_err(&dssdev->dev, "IRQ request failed\n");
606 gpio_free(gpio);
Jani Nikulad2b65782010-04-15 12:24:36 +0300607 goto err_irq;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200608 }
609
Jani Nikula7ae2fb12010-04-13 10:57:52 +0300610 INIT_DELAYED_WORK_DEFERRABLE(&td->te_timeout_work,
611 taal_te_timeout_work_callback);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200612
613 td->use_ext_te = true;
Jani Nikula7ae2fb12010-04-13 10:57:52 +0300614
615 dev_dbg(&dssdev->dev, "Using GPIO TE\n");
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200616 }
617
618 r = sysfs_create_group(&dssdev->dev.kobj, &taal_attr_group);
619 if (r) {
620 dev_err(&dssdev->dev, "failed to create sysfs files\n");
Jani Nikulad2b65782010-04-15 12:24:36 +0300621 goto err_sysfs;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200622 }
623
624 return 0;
Jani Nikulad2b65782010-04-15 12:24:36 +0300625err_sysfs:
Jani Nikula2f1e5f62010-04-09 13:52:33 +0300626 if (td->use_ext_te)
627 free_irq(gpio_to_irq(dssdev->phy.dsi.ext_te_gpio), dssdev);
Jani Nikulad2b65782010-04-15 12:24:36 +0300628err_irq:
Jani Nikula2f1e5f62010-04-09 13:52:33 +0300629 if (td->use_ext_te)
630 gpio_free(dssdev->phy.dsi.ext_te_gpio);
Jani Nikulad2b65782010-04-15 12:24:36 +0300631err_gpio:
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200632 backlight_device_unregister(bldev);
Jani Nikulad2b65782010-04-15 12:24:36 +0300633err_bl:
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200634 destroy_workqueue(td->esd_wq);
Jani Nikulad2b65782010-04-15 12:24:36 +0300635err_wq:
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200636 kfree(td);
Jani Nikulad2b65782010-04-15 12:24:36 +0300637err:
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200638 return r;
639}
640
641static void taal_remove(struct omap_dss_device *dssdev)
642{
643 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
644 struct backlight_device *bldev;
645
646 dev_dbg(&dssdev->dev, "remove\n");
647
648 sysfs_remove_group(&dssdev->dev.kobj, &taal_attr_group);
649
650 if (td->use_ext_te) {
651 int gpio = dssdev->phy.dsi.ext_te_gpio;
652 free_irq(gpio_to_irq(gpio), dssdev);
653 gpio_free(gpio);
654 }
655
656 bldev = td->bldev;
657 bldev->props.power = FB_BLANK_POWERDOWN;
658 taal_bl_update_status(bldev);
659 backlight_device_unregister(bldev);
660
Jani Nikula4571a022010-04-12 10:23:46 +0300661 cancel_delayed_work(&td->esd_work);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200662 destroy_workqueue(td->esd_wq);
663
Jani Nikula006db7b2010-04-09 12:25:59 +0300664 /* reset, to be sure that the panel is in a valid state */
665 taal_hw_reset(dssdev);
666
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200667 kfree(td);
668}
669
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200670static int taal_power_on(struct omap_dss_device *dssdev)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200671{
672 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
673 u8 id1, id2, id3;
674 int r;
675
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200676 /* it seems we have to wait a bit until taal is ready */
677 msleep(5);
678
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200679 r = omapdss_dsi_display_enable(dssdev);
680 if (r) {
681 dev_err(&dssdev->dev, "failed to enable DSI\n");
682 goto err0;
683 }
684
Jani Nikula006db7b2010-04-09 12:25:59 +0300685 taal_hw_reset(dssdev);
686
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200687 omapdss_dsi_vc_enable_hs(TCH, false);
688
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200689 r = taal_sleep_out(td);
690 if (r)
691 goto err;
692
693 r = taal_get_id(&id1, &id2, &id3);
694 if (r)
695 goto err;
696
697 /* on early revisions CABC is broken */
698 if (id2 == 0x00 || id2 == 0xff || id2 == 0x81)
699 td->cabc_broken = true;
700
Jani Nikulaf2a8b752010-04-09 14:15:12 +0300701 r = taal_dcs_write_1(DCS_BRIGHTNESS, 0xff);
702 if (r)
703 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200704
Jani Nikulaf2a8b752010-04-09 14:15:12 +0300705 r = taal_dcs_write_1(DCS_CTRL_DISPLAY,
706 (1<<2) | (1<<5)); /* BL | BCTRL */
707 if (r)
708 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200709
Jani Nikulaf2a8b752010-04-09 14:15:12 +0300710 r = taal_dcs_write_1(DCS_PIXEL_FORMAT, 0x7); /* 24bit/pixel */
711 if (r)
712 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200713
Jani Nikulaf2a8b752010-04-09 14:15:12 +0300714 r = taal_set_addr_mode(td->rotate, td->mirror);
715 if (r)
716 goto err;
717
718 if (!td->cabc_broken) {
719 r = taal_dcs_write_1(DCS_WRITE_CABC, td->cabc_mode);
720 if (r)
721 goto err;
722 }
723
724 r = taal_dcs_write_0(DCS_DISPLAY_ON);
725 if (r)
726 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200727
Tomi Valkeinen21df20f2010-03-02 12:13:55 +0200728 r = _taal_enable_te(dssdev, td->te_enabled);
729 if (r)
730 goto err;
731
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200732 td->enabled = 1;
733
734 if (!td->intro_printed) {
735 dev_info(&dssdev->dev, "revision %02x.%02x.%02x\n",
736 id1, id2, id3);
737 if (td->cabc_broken)
738 dev_info(&dssdev->dev,
739 "old Taal version, CABC disabled\n");
740 td->intro_printed = true;
741 }
742
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200743 omapdss_dsi_vc_enable_hs(TCH, true);
744
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200745 return 0;
746err:
Jani Nikula006db7b2010-04-09 12:25:59 +0300747 dev_err(&dssdev->dev, "error while enabling panel, issuing HW reset\n");
748
749 taal_hw_reset(dssdev);
750
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200751 omapdss_dsi_display_disable(dssdev);
752err0:
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200753 return r;
754}
755
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200756static void taal_power_off(struct omap_dss_device *dssdev)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200757{
758 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Jani Nikula006db7b2010-04-09 12:25:59 +0300759 int r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200760
Jani Nikula006db7b2010-04-09 12:25:59 +0300761 r = taal_dcs_write_0(DCS_DISPLAY_OFF);
762 if (!r) {
763 r = taal_sleep_in(td);
764 /* wait a bit so that the message goes through */
765 msleep(10);
766 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200767
Jani Nikula006db7b2010-04-09 12:25:59 +0300768 if (r) {
769 dev_err(&dssdev->dev,
770 "error disabling panel, issuing HW reset\n");
771 taal_hw_reset(dssdev);
772 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200773
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200774 omapdss_dsi_display_disable(dssdev);
775
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200776 td->enabled = 0;
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200777}
778
779static int taal_enable(struct omap_dss_device *dssdev)
780{
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200781 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200782 int r;
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200783
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200784 dev_dbg(&dssdev->dev, "enable\n");
785
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200786 mutex_lock(&td->lock);
787
788 if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) {
789 r = -EINVAL;
790 goto err;
791 }
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200792
Jani Nikula2c2fc152010-04-12 10:06:14 +0300793 dsi_bus_lock();
794
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200795 r = taal_power_on(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +0300796
797 dsi_bus_unlock();
798
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200799 if (r)
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200800 goto err;
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200801
Jani Nikula4571a022010-04-12 10:23:46 +0300802#ifdef TAAL_USE_ESD_CHECK
803 queue_delayed_work(td->esd_wq, &td->esd_work, TAAL_ESD_CHECK_PERIOD);
804#endif
805
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200806 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
807
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200808 mutex_unlock(&td->lock);
809
810 return 0;
811err:
812 dev_dbg(&dssdev->dev, "enable failed\n");
813 mutex_unlock(&td->lock);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200814 return r;
815}
816
817static void taal_disable(struct omap_dss_device *dssdev)
818{
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200819 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
820
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200821 dev_dbg(&dssdev->dev, "disable\n");
822
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200823 mutex_lock(&td->lock);
824
Jani Nikula4571a022010-04-12 10:23:46 +0300825 cancel_delayed_work(&td->esd_work);
826
Jani Nikula2c2fc152010-04-12 10:06:14 +0300827 dsi_bus_lock();
828
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200829 if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
830 taal_power_off(dssdev);
831
Jani Nikula2c2fc152010-04-12 10:06:14 +0300832 dsi_bus_unlock();
833
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200834 dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200835
836 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200837}
838
839static int taal_suspend(struct omap_dss_device *dssdev)
840{
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200841 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
842 int r;
843
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200844 dev_dbg(&dssdev->dev, "suspend\n");
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200845
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200846 mutex_lock(&td->lock);
847
848 if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) {
849 r = -EINVAL;
850 goto err;
851 }
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200852
Jani Nikula4571a022010-04-12 10:23:46 +0300853 cancel_delayed_work(&td->esd_work);
854
Jani Nikula2c2fc152010-04-12 10:06:14 +0300855 dsi_bus_lock();
856
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200857 taal_power_off(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +0300858
859 dsi_bus_unlock();
860
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200861 dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200862
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200863 mutex_unlock(&td->lock);
864
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200865 return 0;
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200866err:
867 mutex_unlock(&td->lock);
868 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200869}
870
871static int taal_resume(struct omap_dss_device *dssdev)
872{
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200873 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200874 int r;
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200875
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200876 dev_dbg(&dssdev->dev, "resume\n");
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200877
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200878 mutex_lock(&td->lock);
879
880 if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) {
881 r = -EINVAL;
882 goto err;
883 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200884
Jani Nikula2c2fc152010-04-12 10:06:14 +0300885 dsi_bus_lock();
886
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200887 r = taal_power_on(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +0300888
889 dsi_bus_unlock();
890
Jani Nikula4571a022010-04-12 10:23:46 +0300891 if (r) {
Jani Nikulafed44b72010-04-12 10:25:21 +0300892 dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
Jani Nikula4571a022010-04-12 10:23:46 +0300893 } else {
Jani Nikulafed44b72010-04-12 10:25:21 +0300894 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
Jani Nikula4571a022010-04-12 10:23:46 +0300895#ifdef TAAL_USE_ESD_CHECK
896 queue_delayed_work(td->esd_wq, &td->esd_work,
897 TAAL_ESD_CHECK_PERIOD);
898#endif
899 }
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200900
901 mutex_unlock(&td->lock);
902
903 return r;
904err:
905 mutex_unlock(&td->lock);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200906 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200907}
908
Tomi Valkeinen18946f62010-01-12 14:16:41 +0200909static void taal_framedone_cb(int err, void *data)
910{
911 struct omap_dss_device *dssdev = data;
912 dev_dbg(&dssdev->dev, "framedone, err %d\n", err);
913 dsi_bus_unlock();
914}
915
Jani Nikula7ae2fb12010-04-13 10:57:52 +0300916static irqreturn_t taal_te_isr(int irq, void *data)
917{
918 struct omap_dss_device *dssdev = data;
919 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
920 int old;
921 int r;
922
923 old = atomic_cmpxchg(&td->do_update, 1, 0);
924
925 if (old) {
926 cancel_delayed_work(&td->te_timeout_work);
927
928 r = omap_dsi_update(dssdev, TCH,
929 td->update_region.x,
930 td->update_region.y,
931 td->update_region.w,
932 td->update_region.h,
933 taal_framedone_cb, dssdev);
934 if (r)
935 goto err;
936 }
937
938 return IRQ_HANDLED;
939err:
940 dev_err(&dssdev->dev, "start update failed\n");
941 dsi_bus_unlock();
942 return IRQ_HANDLED;
943}
944
945static void taal_te_timeout_work_callback(struct work_struct *work)
946{
947 struct taal_data *td = container_of(work, struct taal_data,
948 te_timeout_work.work);
949 struct omap_dss_device *dssdev = td->dssdev;
950
951 dev_err(&dssdev->dev, "TE not received for 250ms!\n");
952
953 atomic_set(&td->do_update, 0);
954 dsi_bus_unlock();
955}
956
Tomi Valkeinen18946f62010-01-12 14:16:41 +0200957static int taal_update(struct omap_dss_device *dssdev,
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200958 u16 x, u16 y, u16 w, u16 h)
959{
Tomi Valkeinen18946f62010-01-12 14:16:41 +0200960 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
961 int r;
962
963 dev_dbg(&dssdev->dev, "update %d, %d, %d x %d\n", x, y, w, h);
964
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200965 mutex_lock(&td->lock);
Tomi Valkeinen18946f62010-01-12 14:16:41 +0200966 dsi_bus_lock();
967
968 if (!td->enabled) {
969 r = 0;
970 goto err;
971 }
972
973 r = omap_dsi_prepare_update(dssdev, &x, &y, &w, &h);
974 if (r)
975 goto err;
976
977 r = taal_set_update_window(x, y, w, h);
978 if (r)
979 goto err;
980
Jani Nikula7ae2fb12010-04-13 10:57:52 +0300981 if (td->te_enabled && td->use_ext_te) {
982 td->update_region.x = x;
983 td->update_region.y = y;
984 td->update_region.w = w;
985 td->update_region.h = h;
986 barrier();
987 schedule_delayed_work(&td->te_timeout_work,
988 msecs_to_jiffies(250));
989 atomic_set(&td->do_update, 1);
990 } else {
991 r = omap_dsi_update(dssdev, TCH, x, y, w, h,
992 taal_framedone_cb, dssdev);
993 if (r)
994 goto err;
995 }
Tomi Valkeinen18946f62010-01-12 14:16:41 +0200996
997 /* note: no bus_unlock here. unlock is in framedone_cb */
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200998 mutex_unlock(&td->lock);
Tomi Valkeinen18946f62010-01-12 14:16:41 +0200999 return 0;
1000err:
1001 dsi_bus_unlock();
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001002 mutex_unlock(&td->lock);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001003 return r;
1004}
1005
1006static int taal_sync(struct omap_dss_device *dssdev)
1007{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001008 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1009
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001010 dev_dbg(&dssdev->dev, "sync\n");
1011
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001012 mutex_lock(&td->lock);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001013 dsi_bus_lock();
1014 dsi_bus_unlock();
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001015 mutex_unlock(&td->lock);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001016
1017 dev_dbg(&dssdev->dev, "sync done\n");
1018
1019 return 0;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001020}
1021
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001022static int _taal_enable_te(struct omap_dss_device *dssdev, bool enable)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001023{
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001024 int r;
1025
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001026 if (enable)
1027 r = taal_dcs_write_1(DCS_TEAR_ON, 0);
1028 else
1029 r = taal_dcs_write_0(DCS_TEAR_OFF);
1030
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001031 if (!td->use_ext_te)
1032 omapdss_dsi_enable_te(dssdev, enable);
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001033
1034 /* XXX for some reason, DSI TE breaks if we don't wait here.
1035 * Panel bug? Needs more studying */
1036 msleep(100);
1037
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001038 return r;
1039}
1040
1041static int taal_enable_te(struct omap_dss_device *dssdev, bool enable)
1042{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001043 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001044 int r;
1045
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001046 mutex_lock(&td->lock);
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001047 dsi_bus_lock();
1048
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001049 if (td->enabled) {
1050 r = _taal_enable_te(dssdev, enable);
1051 if (r)
1052 goto err;
1053 }
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001054
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001055 td->te_enabled = enable;
1056
1057 dsi_bus_unlock();
1058 mutex_unlock(&td->lock);
1059
1060 return 0;
1061err:
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001062 dsi_bus_unlock();
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001063 mutex_unlock(&td->lock);
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001064
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001065 return r;
1066}
1067
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001068static int taal_get_te(struct omap_dss_device *dssdev)
1069{
1070 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001071 int r;
1072
1073 mutex_lock(&td->lock);
1074 r = td->te_enabled;
1075 mutex_unlock(&td->lock);
1076
1077 return r;
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001078}
1079
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001080static int taal_rotate(struct omap_dss_device *dssdev, u8 rotate)
1081{
1082 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1083 int r;
1084
1085 dev_dbg(&dssdev->dev, "rotate %d\n", rotate);
1086
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001087 mutex_lock(&td->lock);
Tomi Valkeinen87424e12010-01-08 16:52:48 +02001088 dsi_bus_lock();
1089
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001090 if (td->enabled) {
1091 r = taal_set_addr_mode(rotate, td->mirror);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001092 if (r)
Tomi Valkeinen87424e12010-01-08 16:52:48 +02001093 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001094 }
1095
1096 td->rotate = rotate;
1097
Tomi Valkeinen87424e12010-01-08 16:52:48 +02001098 dsi_bus_unlock();
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001099 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001100 return 0;
Tomi Valkeinen87424e12010-01-08 16:52:48 +02001101err:
1102 dsi_bus_unlock();
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001103 mutex_unlock(&td->lock);
Tomi Valkeinen87424e12010-01-08 16:52:48 +02001104 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001105}
1106
1107static u8 taal_get_rotate(struct omap_dss_device *dssdev)
1108{
1109 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001110 int r;
1111
1112 mutex_lock(&td->lock);
1113 r = td->rotate;
1114 mutex_unlock(&td->lock);
1115
1116 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001117}
1118
1119static int taal_mirror(struct omap_dss_device *dssdev, bool enable)
1120{
1121 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1122 int r;
1123
1124 dev_dbg(&dssdev->dev, "mirror %d\n", enable);
1125
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001126 mutex_lock(&td->lock);
Tomi Valkeinen8d8aa612010-01-08 16:30:33 +02001127 dsi_bus_lock();
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001128 if (td->enabled) {
1129 r = taal_set_addr_mode(td->rotate, enable);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001130 if (r)
Tomi Valkeinen8d8aa612010-01-08 16:30:33 +02001131 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001132 }
1133
1134 td->mirror = enable;
1135
Tomi Valkeinen8d8aa612010-01-08 16:30:33 +02001136 dsi_bus_unlock();
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001137 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001138 return 0;
Tomi Valkeinen8d8aa612010-01-08 16:30:33 +02001139err:
1140 dsi_bus_unlock();
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001141 mutex_unlock(&td->lock);
Tomi Valkeinen8d8aa612010-01-08 16:30:33 +02001142 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001143}
1144
1145static bool taal_get_mirror(struct omap_dss_device *dssdev)
1146{
1147 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001148 int r;
1149
1150 mutex_lock(&td->lock);
1151 r = td->mirror;
1152 mutex_unlock(&td->lock);
1153
1154 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001155}
1156
1157static int taal_run_test(struct omap_dss_device *dssdev, int test_num)
1158{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001159 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001160 u8 id1, id2, id3;
1161 int r;
1162
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001163 mutex_lock(&td->lock);
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001164
1165 if (!td->enabled) {
1166 r = -ENODEV;
1167 goto err1;
1168 }
1169
Tomi Valkeinen1a75ef42010-01-08 16:21:28 +02001170 dsi_bus_lock();
1171
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001172 r = taal_dcs_read_1(DCS_GET_ID1, &id1);
1173 if (r)
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001174 goto err2;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001175 r = taal_dcs_read_1(DCS_GET_ID2, &id2);
1176 if (r)
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001177 goto err2;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001178 r = taal_dcs_read_1(DCS_GET_ID3, &id3);
1179 if (r)
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001180 goto err2;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001181
Tomi Valkeinen1a75ef42010-01-08 16:21:28 +02001182 dsi_bus_unlock();
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001183 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001184 return 0;
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001185err2:
Tomi Valkeinen1a75ef42010-01-08 16:21:28 +02001186 dsi_bus_unlock();
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001187err1:
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001188 mutex_unlock(&td->lock);
Tomi Valkeinen1a75ef42010-01-08 16:21:28 +02001189 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001190}
1191
1192static int taal_memory_read(struct omap_dss_device *dssdev,
1193 void *buf, size_t size,
1194 u16 x, u16 y, u16 w, u16 h)
1195{
1196 int r;
1197 int first = 1;
1198 int plen;
1199 unsigned buf_used = 0;
Tomi Valkeinenc75d9462010-01-08 16:56:44 +02001200 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1201
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001202 if (size < w * h * 3)
1203 return -ENOMEM;
1204
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001205 mutex_lock(&td->lock);
1206
1207 if (!td->enabled) {
1208 r = -ENODEV;
1209 goto err1;
1210 }
1211
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001212 size = min(w * h * 3,
1213 dssdev->panel.timings.x_res *
1214 dssdev->panel.timings.y_res * 3);
1215
Tomi Valkeinenc75d9462010-01-08 16:56:44 +02001216 dsi_bus_lock();
1217
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001218 /* plen 1 or 2 goes into short packet. until checksum error is fixed,
1219 * use short packets. plen 32 works, but bigger packets seem to cause
1220 * an error. */
1221 if (size % 2)
1222 plen = 1;
1223 else
1224 plen = 2;
1225
Tomi Valkeinenc75d9462010-01-08 16:56:44 +02001226 taal_set_update_window(x, y, w, h);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001227
1228 r = dsi_vc_set_max_rx_packet_size(TCH, plen);
1229 if (r)
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001230 goto err2;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001231
1232 while (buf_used < size) {
1233 u8 dcs_cmd = first ? 0x2e : 0x3e;
1234 first = 0;
1235
1236 r = dsi_vc_dcs_read(TCH, dcs_cmd,
1237 buf + buf_used, size - buf_used);
1238
1239 if (r < 0) {
1240 dev_err(&dssdev->dev, "read error\n");
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001241 goto err3;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001242 }
1243
1244 buf_used += r;
1245
1246 if (r < plen) {
1247 dev_err(&dssdev->dev, "short read\n");
1248 break;
1249 }
1250
1251 if (signal_pending(current)) {
1252 dev_err(&dssdev->dev, "signal pending, "
1253 "aborting memory read\n");
1254 r = -ERESTARTSYS;
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001255 goto err3;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001256 }
1257 }
1258
1259 r = buf_used;
1260
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001261err3:
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001262 dsi_vc_set_max_rx_packet_size(TCH, 1);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001263err2:
Tomi Valkeinenc75d9462010-01-08 16:56:44 +02001264 dsi_bus_unlock();
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001265err1:
1266 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001267 return r;
1268}
1269
1270static void taal_esd_work(struct work_struct *work)
1271{
1272 struct taal_data *td = container_of(work, struct taal_data,
1273 esd_work.work);
1274 struct omap_dss_device *dssdev = td->dssdev;
1275 u8 state1, state2;
1276 int r;
1277
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001278 mutex_lock(&td->lock);
1279
1280 if (!td->enabled) {
1281 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001282 return;
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001283 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001284
1285 dsi_bus_lock();
1286
1287 r = taal_dcs_read_1(DCS_RDDSDR, &state1);
1288 if (r) {
1289 dev_err(&dssdev->dev, "failed to read Taal status\n");
1290 goto err;
1291 }
1292
1293 /* Run self diagnostics */
1294 r = taal_sleep_out(td);
1295 if (r) {
1296 dev_err(&dssdev->dev, "failed to run Taal self-diagnostics\n");
1297 goto err;
1298 }
1299
1300 r = taal_dcs_read_1(DCS_RDDSDR, &state2);
1301 if (r) {
1302 dev_err(&dssdev->dev, "failed to read Taal status\n");
1303 goto err;
1304 }
1305
1306 /* Each sleep out command will trigger a self diagnostic and flip
1307 * Bit6 if the test passes.
1308 */
1309 if (!((state1 ^ state2) & (1 << 6))) {
1310 dev_err(&dssdev->dev, "LCD self diagnostics failed\n");
1311 goto err;
1312 }
1313 /* Self-diagnostics result is also shown on TE GPIO line. We need
1314 * to re-enable TE after self diagnostics */
Tomi Valkeinen1189b7f2010-03-01 13:52:10 +02001315 if (td->use_ext_te && td->te_enabled) {
1316 r = taal_dcs_write_1(DCS_TEAR_ON, 0);
1317 if (r)
1318 goto err;
1319 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001320
1321 dsi_bus_unlock();
1322
1323 queue_delayed_work(td->esd_wq, &td->esd_work, TAAL_ESD_CHECK_PERIOD);
1324
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001325 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001326 return;
1327err:
1328 dev_err(&dssdev->dev, "performing LCD reset\n");
1329
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001330 taal_power_off(dssdev);
Jani Nikula006db7b2010-04-09 12:25:59 +03001331 taal_hw_reset(dssdev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001332 taal_power_on(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001333
1334 dsi_bus_unlock();
1335
1336 queue_delayed_work(td->esd_wq, &td->esd_work, TAAL_ESD_CHECK_PERIOD);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001337
1338 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001339}
1340
Tomi Valkeinen446f7bf2010-01-11 16:12:31 +02001341static int taal_set_update_mode(struct omap_dss_device *dssdev,
1342 enum omap_dss_update_mode mode)
1343{
1344 if (mode != OMAP_DSS_UPDATE_MANUAL)
1345 return -EINVAL;
1346 return 0;
1347}
1348
1349static enum omap_dss_update_mode taal_get_update_mode(
1350 struct omap_dss_device *dssdev)
1351{
1352 return OMAP_DSS_UPDATE_MANUAL;
1353}
1354
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001355static struct omap_dss_driver taal_driver = {
1356 .probe = taal_probe,
1357 .remove = taal_remove,
1358
1359 .enable = taal_enable,
1360 .disable = taal_disable,
1361 .suspend = taal_suspend,
1362 .resume = taal_resume,
1363
Tomi Valkeinen446f7bf2010-01-11 16:12:31 +02001364 .set_update_mode = taal_set_update_mode,
1365 .get_update_mode = taal_get_update_mode,
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001366
1367 .update = taal_update,
1368 .sync = taal_sync,
1369
Tomi Valkeinen96adcec2010-01-11 13:54:33 +02001370 .get_resolution = taal_get_resolution,
Tomi Valkeinena2699502010-01-11 14:33:40 +02001371 .get_recommended_bpp = omapdss_default_get_recommended_bpp,
1372
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001373 .enable_te = taal_enable_te,
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001374 .get_te = taal_get_te,
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001375
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001376 .set_rotate = taal_rotate,
1377 .get_rotate = taal_get_rotate,
1378 .set_mirror = taal_mirror,
1379 .get_mirror = taal_get_mirror,
1380 .run_test = taal_run_test,
1381 .memory_read = taal_memory_read,
1382
Tomi Valkeinen69b20482010-01-20 12:11:25 +02001383 .get_timings = taal_get_timings,
1384
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001385 .driver = {
1386 .name = "taal",
1387 .owner = THIS_MODULE,
1388 },
1389};
1390
1391static int __init taal_init(void)
1392{
1393 omap_dss_register_driver(&taal_driver);
1394
1395 return 0;
1396}
1397
1398static void __exit taal_exit(void)
1399{
1400 omap_dss_unregister_driver(&taal_driver);
1401}
1402
1403module_init(taal_init);
1404module_exit(taal_exit);
1405
1406MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@nokia.com>");
1407MODULE_DESCRIPTION("Taal Driver");
1408MODULE_LICENSE("GPL");