blob: 311bd5fb14a6686ceec5ed4ef84e06c86742cff6 [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>
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +030036#include <plat/nokia-dsi-panel.h>
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +020037
38/* DSI Virtual channel. Hardcoded for now. */
39#define TCH 0
40
41#define DCS_READ_NUM_ERRORS 0x05
42#define DCS_READ_POWER_MODE 0x0a
43#define DCS_READ_MADCTL 0x0b
44#define DCS_READ_PIXEL_FORMAT 0x0c
45#define DCS_RDDSDR 0x0f
46#define DCS_SLEEP_IN 0x10
47#define DCS_SLEEP_OUT 0x11
48#define DCS_DISPLAY_OFF 0x28
49#define DCS_DISPLAY_ON 0x29
50#define DCS_COLUMN_ADDR 0x2a
51#define DCS_PAGE_ADDR 0x2b
52#define DCS_MEMORY_WRITE 0x2c
53#define DCS_TEAR_OFF 0x34
54#define DCS_TEAR_ON 0x35
55#define DCS_MEM_ACC_CTRL 0x36
56#define DCS_PIXEL_FORMAT 0x3a
57#define DCS_BRIGHTNESS 0x51
58#define DCS_CTRL_DISPLAY 0x53
59#define DCS_WRITE_CABC 0x55
60#define DCS_READ_CABC 0x56
61#define DCS_GET_ID1 0xda
62#define DCS_GET_ID2 0xdb
63#define DCS_GET_ID3 0xdc
64
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +020065#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
Jani Nikulae7f6c3f2010-04-15 12:55:38 +030071/**
72 * struct panel_config - panel configuration
73 * @name: panel name
74 * @type: panel type
75 * @timings: panel resolution
76 * @sleep: various panel specific delays, passed to msleep() if non-zero
77 * @reset_sequence: reset sequence timings, passed to udelay() if non-zero
78 */
79struct panel_config {
80 const char *name;
81 int type;
82
83 struct omap_video_timings timings;
84
85 struct {
86 unsigned int sleep_in;
87 unsigned int sleep_out;
88 unsigned int hw_reset;
89 unsigned int enable_te;
90 } sleep;
91
92 struct {
93 unsigned int high;
94 unsigned int low;
95 } reset_sequence;
96};
97
98enum {
99 PANEL_TAAL,
100};
101
102static struct panel_config panel_configs[] = {
103 {
104 .name = "taal",
105 .type = PANEL_TAAL,
106 .timings = {
107 .x_res = 864,
108 .y_res = 480,
109 },
110 .sleep = {
111 .sleep_in = 5,
112 .sleep_out = 5,
113 .hw_reset = 5,
114 .enable_te = 100, /* possible panel bug */
115 },
116 .reset_sequence = {
117 .high = 10,
118 .low = 10,
119 },
120 },
121};
122
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200123struct taal_data {
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200124 struct mutex lock;
125
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200126 struct backlight_device *bldev;
127
128 unsigned long hw_guard_end; /* next value of jiffies when we can
129 * issue the next sleep in/out command
130 */
131 unsigned long hw_guard_wait; /* max guard time in jiffies */
132
133 struct omap_dss_device *dssdev;
134
135 bool enabled;
136 u8 rotate;
137 bool mirror;
138
139 bool te_enabled;
Jani Nikula7ae2fb12010-04-13 10:57:52 +0300140
141 atomic_t do_update;
142 struct {
143 u16 x;
144 u16 y;
145 u16 w;
146 u16 h;
147 } update_region;
148 struct delayed_work te_timeout_work;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200149
150 bool use_dsi_bl;
151
152 bool cabc_broken;
153 unsigned cabc_mode;
154
155 bool intro_printed;
156
157 struct workqueue_struct *esd_wq;
158 struct delayed_work esd_work;
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300159
160 struct panel_config *panel_config;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200161};
162
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300163static inline struct nokia_dsi_panel_data
164*get_panel_data(const struct omap_dss_device *dssdev)
165{
166 return (struct nokia_dsi_panel_data *) dssdev->data;
167}
168
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200169static void taal_esd_work(struct work_struct *work);
170
171static void hw_guard_start(struct taal_data *td, int guard_msec)
172{
173 td->hw_guard_wait = msecs_to_jiffies(guard_msec);
174 td->hw_guard_end = jiffies + td->hw_guard_wait;
175}
176
177static void hw_guard_wait(struct taal_data *td)
178{
179 unsigned long wait = td->hw_guard_end - jiffies;
180
181 if ((long)wait > 0 && wait <= td->hw_guard_wait) {
182 set_current_state(TASK_UNINTERRUPTIBLE);
183 schedule_timeout(wait);
184 }
185}
186
187static int taal_dcs_read_1(u8 dcs_cmd, u8 *data)
188{
189 int r;
190 u8 buf[1];
191
192 r = dsi_vc_dcs_read(TCH, dcs_cmd, buf, 1);
193
194 if (r < 0)
195 return r;
196
197 *data = buf[0];
198
199 return 0;
200}
201
202static int taal_dcs_write_0(u8 dcs_cmd)
203{
204 return dsi_vc_dcs_write(TCH, &dcs_cmd, 1);
205}
206
207static int taal_dcs_write_1(u8 dcs_cmd, u8 param)
208{
209 u8 buf[2];
210 buf[0] = dcs_cmd;
211 buf[1] = param;
212 return dsi_vc_dcs_write(TCH, buf, 2);
213}
214
215static int taal_sleep_in(struct taal_data *td)
216
217{
218 u8 cmd;
219 int r;
220
221 hw_guard_wait(td);
222
223 cmd = DCS_SLEEP_IN;
224 r = dsi_vc_dcs_write_nosync(TCH, &cmd, 1);
225 if (r)
226 return r;
227
228 hw_guard_start(td, 120);
229
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300230 if (td->panel_config->sleep.sleep_in)
231 msleep(td->panel_config->sleep.sleep_in);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200232
233 return 0;
234}
235
236static int taal_sleep_out(struct taal_data *td)
237{
238 int r;
239
240 hw_guard_wait(td);
241
242 r = taal_dcs_write_0(DCS_SLEEP_OUT);
243 if (r)
244 return r;
245
246 hw_guard_start(td, 120);
247
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300248 if (td->panel_config->sleep.sleep_out)
249 msleep(td->panel_config->sleep.sleep_out);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200250
251 return 0;
252}
253
254static int taal_get_id(u8 *id1, u8 *id2, u8 *id3)
255{
256 int r;
257
258 r = taal_dcs_read_1(DCS_GET_ID1, id1);
259 if (r)
260 return r;
261 r = taal_dcs_read_1(DCS_GET_ID2, id2);
262 if (r)
263 return r;
264 r = taal_dcs_read_1(DCS_GET_ID3, id3);
265 if (r)
266 return r;
267
268 return 0;
269}
270
271static int taal_set_addr_mode(u8 rotate, bool mirror)
272{
273 int r;
274 u8 mode;
275 int b5, b6, b7;
276
277 r = taal_dcs_read_1(DCS_READ_MADCTL, &mode);
278 if (r)
279 return r;
280
281 switch (rotate) {
282 default:
283 case 0:
284 b7 = 0;
285 b6 = 0;
286 b5 = 0;
287 break;
288 case 1:
289 b7 = 0;
290 b6 = 1;
291 b5 = 1;
292 break;
293 case 2:
294 b7 = 1;
295 b6 = 1;
296 b5 = 0;
297 break;
298 case 3:
299 b7 = 1;
300 b6 = 0;
301 b5 = 1;
302 break;
303 }
304
305 if (mirror)
306 b6 = !b6;
307
308 mode &= ~((1<<7) | (1<<6) | (1<<5));
309 mode |= (b7 << 7) | (b6 << 6) | (b5 << 5);
310
311 return taal_dcs_write_1(DCS_MEM_ACC_CTRL, mode);
312}
313
314static int taal_set_update_window(u16 x, u16 y, u16 w, u16 h)
315{
316 int r;
317 u16 x1 = x;
318 u16 x2 = x + w - 1;
319 u16 y1 = y;
320 u16 y2 = y + h - 1;
321
322 u8 buf[5];
323 buf[0] = DCS_COLUMN_ADDR;
324 buf[1] = (x1 >> 8) & 0xff;
325 buf[2] = (x1 >> 0) & 0xff;
326 buf[3] = (x2 >> 8) & 0xff;
327 buf[4] = (x2 >> 0) & 0xff;
328
329 r = dsi_vc_dcs_write_nosync(TCH, buf, sizeof(buf));
330 if (r)
331 return r;
332
333 buf[0] = DCS_PAGE_ADDR;
334 buf[1] = (y1 >> 8) & 0xff;
335 buf[2] = (y1 >> 0) & 0xff;
336 buf[3] = (y2 >> 8) & 0xff;
337 buf[4] = (y2 >> 0) & 0xff;
338
339 r = dsi_vc_dcs_write_nosync(TCH, buf, sizeof(buf));
340 if (r)
341 return r;
342
343 dsi_vc_send_bta_sync(TCH);
344
345 return r;
346}
347
348static int taal_bl_update_status(struct backlight_device *dev)
349{
350 struct omap_dss_device *dssdev = dev_get_drvdata(&dev->dev);
351 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300352 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200353 int r;
354 int level;
355
356 if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
357 dev->props.power == FB_BLANK_UNBLANK)
358 level = dev->props.brightness;
359 else
360 level = 0;
361
362 dev_dbg(&dssdev->dev, "update brightness to %d\n", level);
363
Tomi Valkeinen1cbc8702010-04-15 16:41:07 +0300364 mutex_lock(&td->lock);
365
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200366 if (td->use_dsi_bl) {
367 if (td->enabled) {
368 dsi_bus_lock();
369 r = taal_dcs_write_1(DCS_BRIGHTNESS, level);
370 dsi_bus_unlock();
Tomi Valkeinen1cbc8702010-04-15 16:41:07 +0300371 } else {
372 r = 0;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200373 }
374 } else {
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300375 if (!panel_data->set_backlight)
Tomi Valkeinen1cbc8702010-04-15 16:41:07 +0300376 r = -EINVAL;
377 else
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300378 r = panel_data->set_backlight(dssdev, level);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200379 }
380
Tomi Valkeinen1cbc8702010-04-15 16:41:07 +0300381 mutex_unlock(&td->lock);
382
383 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200384}
385
386static int taal_bl_get_intensity(struct backlight_device *dev)
387{
388 if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
389 dev->props.power == FB_BLANK_UNBLANK)
390 return dev->props.brightness;
391
392 return 0;
393}
394
395static struct backlight_ops taal_bl_ops = {
396 .get_brightness = taal_bl_get_intensity,
397 .update_status = taal_bl_update_status,
398};
399
400static void taal_get_timings(struct omap_dss_device *dssdev,
401 struct omap_video_timings *timings)
402{
403 *timings = dssdev->panel.timings;
404}
405
406static void taal_get_resolution(struct omap_dss_device *dssdev,
407 u16 *xres, u16 *yres)
408{
409 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
410
411 if (td->rotate == 0 || td->rotate == 2) {
412 *xres = dssdev->panel.timings.x_res;
413 *yres = dssdev->panel.timings.y_res;
414 } else {
415 *yres = dssdev->panel.timings.x_res;
416 *xres = dssdev->panel.timings.y_res;
417 }
418}
419
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200420static ssize_t taal_num_errors_show(struct device *dev,
421 struct device_attribute *attr, char *buf)
422{
423 struct omap_dss_device *dssdev = to_dss_device(dev);
424 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
425 u8 errors;
426 int r;
427
Jani Nikula6b316712010-04-28 11:15:18 +0300428 mutex_lock(&td->lock);
429
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200430 if (td->enabled) {
431 dsi_bus_lock();
432 r = taal_dcs_read_1(DCS_READ_NUM_ERRORS, &errors);
433 dsi_bus_unlock();
434 } else {
435 r = -ENODEV;
436 }
437
Jani Nikula6b316712010-04-28 11:15:18 +0300438 mutex_unlock(&td->lock);
439
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200440 if (r)
441 return r;
442
443 return snprintf(buf, PAGE_SIZE, "%d\n", errors);
444}
445
446static ssize_t taal_hw_revision_show(struct device *dev,
447 struct device_attribute *attr, char *buf)
448{
449 struct omap_dss_device *dssdev = to_dss_device(dev);
450 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
451 u8 id1, id2, id3;
452 int r;
453
Jani Nikula6b316712010-04-28 11:15:18 +0300454 mutex_lock(&td->lock);
455
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200456 if (td->enabled) {
457 dsi_bus_lock();
458 r = taal_get_id(&id1, &id2, &id3);
459 dsi_bus_unlock();
460 } else {
461 r = -ENODEV;
462 }
463
Jani Nikula6b316712010-04-28 11:15:18 +0300464 mutex_unlock(&td->lock);
465
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200466 if (r)
467 return r;
468
469 return snprintf(buf, PAGE_SIZE, "%02x.%02x.%02x\n", id1, id2, id3);
470}
471
472static const char *cabc_modes[] = {
473 "off", /* used also always when CABC is not supported */
474 "ui",
475 "still-image",
476 "moving-image",
477};
478
479static ssize_t show_cabc_mode(struct device *dev,
480 struct device_attribute *attr,
481 char *buf)
482{
483 struct omap_dss_device *dssdev = to_dss_device(dev);
484 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
485 const char *mode_str;
486 int mode;
487 int len;
488
489 mode = td->cabc_mode;
490
491 mode_str = "unknown";
492 if (mode >= 0 && mode < ARRAY_SIZE(cabc_modes))
493 mode_str = cabc_modes[mode];
494 len = snprintf(buf, PAGE_SIZE, "%s\n", mode_str);
495
496 return len < PAGE_SIZE - 1 ? len : PAGE_SIZE - 1;
497}
498
499static ssize_t store_cabc_mode(struct device *dev,
500 struct device_attribute *attr,
501 const char *buf, size_t count)
502{
503 struct omap_dss_device *dssdev = to_dss_device(dev);
504 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
505 int i;
506
507 for (i = 0; i < ARRAY_SIZE(cabc_modes); i++) {
508 if (sysfs_streq(cabc_modes[i], buf))
509 break;
510 }
511
512 if (i == ARRAY_SIZE(cabc_modes))
513 return -EINVAL;
514
Jani Nikula6b316712010-04-28 11:15:18 +0300515 mutex_lock(&td->lock);
516
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200517 if (td->enabled) {
518 dsi_bus_lock();
519 if (!td->cabc_broken)
520 taal_dcs_write_1(DCS_WRITE_CABC, i);
521 dsi_bus_unlock();
522 }
523
524 td->cabc_mode = i;
525
Jani Nikula6b316712010-04-28 11:15:18 +0300526 mutex_unlock(&td->lock);
527
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200528 return count;
529}
530
531static ssize_t show_cabc_available_modes(struct device *dev,
532 struct device_attribute *attr,
533 char *buf)
534{
535 int len;
536 int i;
537
538 for (i = 0, len = 0;
539 len < PAGE_SIZE && i < ARRAY_SIZE(cabc_modes); i++)
540 len += snprintf(&buf[len], PAGE_SIZE - len, "%s%s%s",
541 i ? " " : "", cabc_modes[i],
542 i == ARRAY_SIZE(cabc_modes) - 1 ? "\n" : "");
543
544 return len < PAGE_SIZE ? len : PAGE_SIZE - 1;
545}
546
547static DEVICE_ATTR(num_dsi_errors, S_IRUGO, taal_num_errors_show, NULL);
548static DEVICE_ATTR(hw_revision, S_IRUGO, taal_hw_revision_show, NULL);
549static DEVICE_ATTR(cabc_mode, S_IRUGO | S_IWUSR,
550 show_cabc_mode, store_cabc_mode);
551static DEVICE_ATTR(cabc_available_modes, S_IRUGO,
552 show_cabc_available_modes, NULL);
553
554static struct attribute *taal_attrs[] = {
555 &dev_attr_num_dsi_errors.attr,
556 &dev_attr_hw_revision.attr,
557 &dev_attr_cabc_mode.attr,
558 &dev_attr_cabc_available_modes.attr,
559 NULL,
560};
561
562static struct attribute_group taal_attr_group = {
563 .attrs = taal_attrs,
564};
565
Jani Nikula006db7b2010-04-09 12:25:59 +0300566static void taal_hw_reset(struct omap_dss_device *dssdev)
567{
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300568 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300569 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
570
571 if (panel_data->reset_gpio == -1)
Jani Nikula006db7b2010-04-09 12:25:59 +0300572 return;
573
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300574 gpio_set_value(panel_data->reset_gpio, 1);
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300575 if (td->panel_config->reset_sequence.high)
576 udelay(td->panel_config->reset_sequence.high);
Jani Nikula006db7b2010-04-09 12:25:59 +0300577 /* reset the panel */
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300578 gpio_set_value(panel_data->reset_gpio, 0);
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300579 /* assert reset */
580 if (td->panel_config->reset_sequence.low)
581 udelay(td->panel_config->reset_sequence.low);
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300582 gpio_set_value(panel_data->reset_gpio, 1);
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300583 /* wait after releasing reset */
584 if (td->panel_config->sleep.hw_reset)
585 msleep(td->panel_config->sleep.hw_reset);
Jani Nikula006db7b2010-04-09 12:25:59 +0300586}
587
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200588static int taal_probe(struct omap_dss_device *dssdev)
589{
Matthew Garretta19a6ee2010-02-17 16:39:44 -0500590 struct backlight_properties props;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200591 struct taal_data *td;
592 struct backlight_device *bldev;
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300593 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300594 struct panel_config *panel_config = NULL;
595 int r, i;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200596
597 dev_dbg(&dssdev->dev, "probe\n");
598
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300599 if (!panel_data || !panel_data->name) {
600 r = -EINVAL;
601 goto err;
602 }
603
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300604 for (i = 0; i < ARRAY_SIZE(panel_configs); i++) {
605 if (strcmp(panel_data->name, panel_configs[i].name) == 0) {
606 panel_config = &panel_configs[i];
607 break;
608 }
609 }
610
611 if (!panel_config) {
612 r = -EINVAL;
613 goto err;
614 }
615
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200616 dssdev->panel.config = OMAP_DSS_LCD_TFT;
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300617 dssdev->panel.timings = panel_config->timings;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200618 dssdev->ctrl.pixel_size = 24;
619
620 td = kzalloc(sizeof(*td), GFP_KERNEL);
621 if (!td) {
622 r = -ENOMEM;
Jani Nikulad2b65782010-04-15 12:24:36 +0300623 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200624 }
625 td->dssdev = dssdev;
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300626 td->panel_config = panel_config;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200627
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200628 mutex_init(&td->lock);
629
Jani Nikula7ae2fb12010-04-13 10:57:52 +0300630 atomic_set(&td->do_update, 0);
631
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200632 td->esd_wq = create_singlethread_workqueue("taal_esd");
633 if (td->esd_wq == NULL) {
634 dev_err(&dssdev->dev, "can't create ESD workqueue\n");
635 r = -ENOMEM;
Jani Nikulad2b65782010-04-15 12:24:36 +0300636 goto err_wq;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200637 }
638 INIT_DELAYED_WORK_DEFERRABLE(&td->esd_work, taal_esd_work);
639
640 dev_set_drvdata(&dssdev->dev, td);
641
Jani Nikula006db7b2010-04-09 12:25:59 +0300642 taal_hw_reset(dssdev);
643
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200644 /* if no platform set_backlight() defined, presume DSI backlight
645 * control */
Matthew Garretta19a6ee2010-02-17 16:39:44 -0500646 memset(&props, 0, sizeof(struct backlight_properties));
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300647 if (!panel_data->set_backlight)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200648 td->use_dsi_bl = true;
649
Matthew Garretta19a6ee2010-02-17 16:39:44 -0500650 if (td->use_dsi_bl)
651 props.max_brightness = 255;
652 else
653 props.max_brightness = 127;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200654 bldev = backlight_device_register("taal", &dssdev->dev, dssdev,
Matthew Garretta19a6ee2010-02-17 16:39:44 -0500655 &taal_bl_ops, &props);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200656 if (IS_ERR(bldev)) {
657 r = PTR_ERR(bldev);
Jani Nikulad2b65782010-04-15 12:24:36 +0300658 goto err_bl;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200659 }
660
661 td->bldev = bldev;
662
663 bldev->props.fb_blank = FB_BLANK_UNBLANK;
664 bldev->props.power = FB_BLANK_UNBLANK;
Matthew Garretta19a6ee2010-02-17 16:39:44 -0500665 if (td->use_dsi_bl)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200666 bldev->props.brightness = 255;
Matthew Garretta19a6ee2010-02-17 16:39:44 -0500667 else
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200668 bldev->props.brightness = 127;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200669
670 taal_bl_update_status(bldev);
671
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300672 if (panel_data->use_ext_te) {
673 int gpio = panel_data->ext_te_gpio;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200674
675 r = gpio_request(gpio, "taal irq");
676 if (r) {
677 dev_err(&dssdev->dev, "GPIO request failed\n");
Jani Nikulad2b65782010-04-15 12:24:36 +0300678 goto err_gpio;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200679 }
680
681 gpio_direction_input(gpio);
682
683 r = request_irq(gpio_to_irq(gpio), taal_te_isr,
684 IRQF_DISABLED | IRQF_TRIGGER_RISING,
685 "taal vsync", dssdev);
686
687 if (r) {
688 dev_err(&dssdev->dev, "IRQ request failed\n");
689 gpio_free(gpio);
Jani Nikulad2b65782010-04-15 12:24:36 +0300690 goto err_irq;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200691 }
692
Jani Nikula7ae2fb12010-04-13 10:57:52 +0300693 INIT_DELAYED_WORK_DEFERRABLE(&td->te_timeout_work,
694 taal_te_timeout_work_callback);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200695
Jani Nikula7ae2fb12010-04-13 10:57:52 +0300696 dev_dbg(&dssdev->dev, "Using GPIO TE\n");
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200697 }
698
699 r = sysfs_create_group(&dssdev->dev.kobj, &taal_attr_group);
700 if (r) {
701 dev_err(&dssdev->dev, "failed to create sysfs files\n");
Jani Nikulad2b65782010-04-15 12:24:36 +0300702 goto err_sysfs;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200703 }
704
705 return 0;
Jani Nikulad2b65782010-04-15 12:24:36 +0300706err_sysfs:
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300707 if (panel_data->use_ext_te)
708 free_irq(gpio_to_irq(panel_data->ext_te_gpio), dssdev);
Jani Nikulad2b65782010-04-15 12:24:36 +0300709err_irq:
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300710 if (panel_data->use_ext_te)
711 gpio_free(panel_data->ext_te_gpio);
Jani Nikulad2b65782010-04-15 12:24:36 +0300712err_gpio:
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200713 backlight_device_unregister(bldev);
Jani Nikulad2b65782010-04-15 12:24:36 +0300714err_bl:
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200715 destroy_workqueue(td->esd_wq);
Jani Nikulad2b65782010-04-15 12:24:36 +0300716err_wq:
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200717 kfree(td);
Jani Nikulad2b65782010-04-15 12:24:36 +0300718err:
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200719 return r;
720}
721
722static void taal_remove(struct omap_dss_device *dssdev)
723{
724 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300725 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200726 struct backlight_device *bldev;
727
728 dev_dbg(&dssdev->dev, "remove\n");
729
730 sysfs_remove_group(&dssdev->dev.kobj, &taal_attr_group);
731
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300732 if (panel_data->use_ext_te) {
733 int gpio = panel_data->ext_te_gpio;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200734 free_irq(gpio_to_irq(gpio), dssdev);
735 gpio_free(gpio);
736 }
737
738 bldev = td->bldev;
739 bldev->props.power = FB_BLANK_POWERDOWN;
740 taal_bl_update_status(bldev);
741 backlight_device_unregister(bldev);
742
Jani Nikula4571a022010-04-12 10:23:46 +0300743 cancel_delayed_work(&td->esd_work);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200744 destroy_workqueue(td->esd_wq);
745
Jani Nikula006db7b2010-04-09 12:25:59 +0300746 /* reset, to be sure that the panel is in a valid state */
747 taal_hw_reset(dssdev);
748
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200749 kfree(td);
750}
751
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200752static int taal_power_on(struct omap_dss_device *dssdev)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200753{
754 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
755 u8 id1, id2, id3;
756 int r;
757
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200758 r = omapdss_dsi_display_enable(dssdev);
759 if (r) {
760 dev_err(&dssdev->dev, "failed to enable DSI\n");
761 goto err0;
762 }
763
Jani Nikula006db7b2010-04-09 12:25:59 +0300764 taal_hw_reset(dssdev);
765
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200766 omapdss_dsi_vc_enable_hs(TCH, false);
767
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200768 r = taal_sleep_out(td);
769 if (r)
770 goto err;
771
772 r = taal_get_id(&id1, &id2, &id3);
773 if (r)
774 goto err;
775
776 /* on early revisions CABC is broken */
777 if (id2 == 0x00 || id2 == 0xff || id2 == 0x81)
778 td->cabc_broken = true;
779
Jani Nikulaf2a8b752010-04-09 14:15:12 +0300780 r = taal_dcs_write_1(DCS_BRIGHTNESS, 0xff);
781 if (r)
782 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200783
Jani Nikulaf2a8b752010-04-09 14:15:12 +0300784 r = taal_dcs_write_1(DCS_CTRL_DISPLAY,
785 (1<<2) | (1<<5)); /* BL | BCTRL */
786 if (r)
787 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200788
Jani Nikulaf2a8b752010-04-09 14:15:12 +0300789 r = taal_dcs_write_1(DCS_PIXEL_FORMAT, 0x7); /* 24bit/pixel */
790 if (r)
791 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200792
Jani Nikulaf2a8b752010-04-09 14:15:12 +0300793 r = taal_set_addr_mode(td->rotate, td->mirror);
794 if (r)
795 goto err;
796
797 if (!td->cabc_broken) {
798 r = taal_dcs_write_1(DCS_WRITE_CABC, td->cabc_mode);
799 if (r)
800 goto err;
801 }
802
803 r = taal_dcs_write_0(DCS_DISPLAY_ON);
804 if (r)
805 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200806
Tomi Valkeinen21df20f2010-03-02 12:13:55 +0200807 r = _taal_enable_te(dssdev, td->te_enabled);
808 if (r)
809 goto err;
810
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200811 td->enabled = 1;
812
813 if (!td->intro_printed) {
814 dev_info(&dssdev->dev, "revision %02x.%02x.%02x\n",
815 id1, id2, id3);
816 if (td->cabc_broken)
817 dev_info(&dssdev->dev,
818 "old Taal version, CABC disabled\n");
819 td->intro_printed = true;
820 }
821
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200822 omapdss_dsi_vc_enable_hs(TCH, true);
823
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200824 return 0;
825err:
Jani Nikula006db7b2010-04-09 12:25:59 +0300826 dev_err(&dssdev->dev, "error while enabling panel, issuing HW reset\n");
827
828 taal_hw_reset(dssdev);
829
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200830 omapdss_dsi_display_disable(dssdev);
831err0:
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200832 return r;
833}
834
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200835static void taal_power_off(struct omap_dss_device *dssdev)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200836{
837 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Jani Nikula006db7b2010-04-09 12:25:59 +0300838 int r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200839
Jani Nikula006db7b2010-04-09 12:25:59 +0300840 r = taal_dcs_write_0(DCS_DISPLAY_OFF);
841 if (!r) {
842 r = taal_sleep_in(td);
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300843 /* HACK: wait a bit so that the message goes through */
Jani Nikula006db7b2010-04-09 12:25:59 +0300844 msleep(10);
845 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200846
Jani Nikula006db7b2010-04-09 12:25:59 +0300847 if (r) {
848 dev_err(&dssdev->dev,
849 "error disabling panel, issuing HW reset\n");
850 taal_hw_reset(dssdev);
851 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200852
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200853 omapdss_dsi_display_disable(dssdev);
854
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200855 td->enabled = 0;
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200856}
857
858static int taal_enable(struct omap_dss_device *dssdev)
859{
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200860 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Jani Nikula448dfe92010-04-15 12:42:16 +0300861 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200862 int r;
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200863
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200864 dev_dbg(&dssdev->dev, "enable\n");
865
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200866 mutex_lock(&td->lock);
867
868 if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) {
869 r = -EINVAL;
870 goto err;
871 }
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200872
Jani Nikula2c2fc152010-04-12 10:06:14 +0300873 dsi_bus_lock();
874
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200875 r = taal_power_on(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +0300876
877 dsi_bus_unlock();
878
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200879 if (r)
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200880 goto err;
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200881
Jani Nikula448dfe92010-04-15 12:42:16 +0300882 if (panel_data->use_esd_check)
883 queue_delayed_work(td->esd_wq, &td->esd_work,
884 TAAL_ESD_CHECK_PERIOD);
Jani Nikula4571a022010-04-12 10:23:46 +0300885
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200886 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
887
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200888 mutex_unlock(&td->lock);
889
890 return 0;
891err:
892 dev_dbg(&dssdev->dev, "enable failed\n");
893 mutex_unlock(&td->lock);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200894 return r;
895}
896
897static void taal_disable(struct omap_dss_device *dssdev)
898{
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200899 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
900
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200901 dev_dbg(&dssdev->dev, "disable\n");
902
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200903 mutex_lock(&td->lock);
904
Jani Nikula4571a022010-04-12 10:23:46 +0300905 cancel_delayed_work(&td->esd_work);
906
Jani Nikula2c2fc152010-04-12 10:06:14 +0300907 dsi_bus_lock();
908
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200909 if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
910 taal_power_off(dssdev);
911
Jani Nikula2c2fc152010-04-12 10:06:14 +0300912 dsi_bus_unlock();
913
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200914 dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200915
916 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200917}
918
919static int taal_suspend(struct omap_dss_device *dssdev)
920{
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200921 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
922 int r;
923
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200924 dev_dbg(&dssdev->dev, "suspend\n");
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200925
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200926 mutex_lock(&td->lock);
927
928 if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) {
929 r = -EINVAL;
930 goto err;
931 }
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200932
Jani Nikula4571a022010-04-12 10:23:46 +0300933 cancel_delayed_work(&td->esd_work);
934
Jani Nikula2c2fc152010-04-12 10:06:14 +0300935 dsi_bus_lock();
936
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200937 taal_power_off(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +0300938
939 dsi_bus_unlock();
940
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200941 dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200942
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200943 mutex_unlock(&td->lock);
944
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200945 return 0;
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200946err:
947 mutex_unlock(&td->lock);
948 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200949}
950
951static int taal_resume(struct omap_dss_device *dssdev)
952{
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200953 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Jani Nikula448dfe92010-04-15 12:42:16 +0300954 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200955 int r;
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200956
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200957 dev_dbg(&dssdev->dev, "resume\n");
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200958
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200959 mutex_lock(&td->lock);
960
961 if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) {
962 r = -EINVAL;
963 goto err;
964 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200965
Jani Nikula2c2fc152010-04-12 10:06:14 +0300966 dsi_bus_lock();
967
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200968 r = taal_power_on(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +0300969
970 dsi_bus_unlock();
971
Jani Nikula4571a022010-04-12 10:23:46 +0300972 if (r) {
Jani Nikulafed44b72010-04-12 10:25:21 +0300973 dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
Jani Nikula4571a022010-04-12 10:23:46 +0300974 } else {
Jani Nikulafed44b72010-04-12 10:25:21 +0300975 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
Jani Nikula448dfe92010-04-15 12:42:16 +0300976 if (panel_data->use_esd_check)
977 queue_delayed_work(td->esd_wq, &td->esd_work,
978 TAAL_ESD_CHECK_PERIOD);
Jani Nikula4571a022010-04-12 10:23:46 +0300979 }
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200980
981 mutex_unlock(&td->lock);
982
983 return r;
984err:
985 mutex_unlock(&td->lock);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200986 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200987}
988
Tomi Valkeinen18946f62010-01-12 14:16:41 +0200989static void taal_framedone_cb(int err, void *data)
990{
991 struct omap_dss_device *dssdev = data;
992 dev_dbg(&dssdev->dev, "framedone, err %d\n", err);
993 dsi_bus_unlock();
994}
995
Jani Nikula7ae2fb12010-04-13 10:57:52 +0300996static irqreturn_t taal_te_isr(int irq, void *data)
997{
998 struct omap_dss_device *dssdev = data;
999 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1000 int old;
1001 int r;
1002
1003 old = atomic_cmpxchg(&td->do_update, 1, 0);
1004
1005 if (old) {
1006 cancel_delayed_work(&td->te_timeout_work);
1007
1008 r = omap_dsi_update(dssdev, TCH,
1009 td->update_region.x,
1010 td->update_region.y,
1011 td->update_region.w,
1012 td->update_region.h,
1013 taal_framedone_cb, dssdev);
1014 if (r)
1015 goto err;
1016 }
1017
1018 return IRQ_HANDLED;
1019err:
1020 dev_err(&dssdev->dev, "start update failed\n");
1021 dsi_bus_unlock();
1022 return IRQ_HANDLED;
1023}
1024
1025static void taal_te_timeout_work_callback(struct work_struct *work)
1026{
1027 struct taal_data *td = container_of(work, struct taal_data,
1028 te_timeout_work.work);
1029 struct omap_dss_device *dssdev = td->dssdev;
1030
1031 dev_err(&dssdev->dev, "TE not received for 250ms!\n");
1032
1033 atomic_set(&td->do_update, 0);
1034 dsi_bus_unlock();
1035}
1036
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001037static int taal_update(struct omap_dss_device *dssdev,
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001038 u16 x, u16 y, u16 w, u16 h)
1039{
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001040 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001041 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001042 int r;
1043
1044 dev_dbg(&dssdev->dev, "update %d, %d, %d x %d\n", x, y, w, h);
1045
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001046 mutex_lock(&td->lock);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001047 dsi_bus_lock();
1048
1049 if (!td->enabled) {
1050 r = 0;
1051 goto err;
1052 }
1053
1054 r = omap_dsi_prepare_update(dssdev, &x, &y, &w, &h);
1055 if (r)
1056 goto err;
1057
1058 r = taal_set_update_window(x, y, w, h);
1059 if (r)
1060 goto err;
1061
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001062 if (td->te_enabled && panel_data->use_ext_te) {
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001063 td->update_region.x = x;
1064 td->update_region.y = y;
1065 td->update_region.w = w;
1066 td->update_region.h = h;
1067 barrier();
1068 schedule_delayed_work(&td->te_timeout_work,
1069 msecs_to_jiffies(250));
1070 atomic_set(&td->do_update, 1);
1071 } else {
1072 r = omap_dsi_update(dssdev, TCH, x, y, w, h,
1073 taal_framedone_cb, dssdev);
1074 if (r)
1075 goto err;
1076 }
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001077
1078 /* note: no bus_unlock here. unlock is in framedone_cb */
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001079 mutex_unlock(&td->lock);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001080 return 0;
1081err:
1082 dsi_bus_unlock();
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001083 mutex_unlock(&td->lock);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001084 return r;
1085}
1086
1087static int taal_sync(struct omap_dss_device *dssdev)
1088{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001089 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1090
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001091 dev_dbg(&dssdev->dev, "sync\n");
1092
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001093 mutex_lock(&td->lock);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001094 dsi_bus_lock();
1095 dsi_bus_unlock();
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001096 mutex_unlock(&td->lock);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001097
1098 dev_dbg(&dssdev->dev, "sync done\n");
1099
1100 return 0;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001101}
1102
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001103static int _taal_enable_te(struct omap_dss_device *dssdev, bool enable)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001104{
Jani Nikulae7f6c3f2010-04-15 12:55:38 +03001105 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001106 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001107 int r;
1108
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001109 if (enable)
1110 r = taal_dcs_write_1(DCS_TEAR_ON, 0);
1111 else
1112 r = taal_dcs_write_0(DCS_TEAR_OFF);
1113
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001114 if (!panel_data->use_ext_te)
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001115 omapdss_dsi_enable_te(dssdev, enable);
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001116
Jani Nikulae7f6c3f2010-04-15 12:55:38 +03001117 if (td->panel_config->sleep.enable_te)
1118 msleep(td->panel_config->sleep.enable_te);
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001119
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001120 return r;
1121}
1122
1123static int taal_enable_te(struct omap_dss_device *dssdev, bool enable)
1124{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001125 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001126 int r;
1127
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001128 mutex_lock(&td->lock);
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001129 dsi_bus_lock();
1130
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001131 if (td->enabled) {
1132 r = _taal_enable_te(dssdev, enable);
1133 if (r)
1134 goto err;
1135 }
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001136
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001137 td->te_enabled = enable;
1138
1139 dsi_bus_unlock();
1140 mutex_unlock(&td->lock);
1141
1142 return 0;
1143err:
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001144 dsi_bus_unlock();
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001145 mutex_unlock(&td->lock);
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001146
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001147 return r;
1148}
1149
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001150static int taal_get_te(struct omap_dss_device *dssdev)
1151{
1152 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001153 int r;
1154
1155 mutex_lock(&td->lock);
1156 r = td->te_enabled;
1157 mutex_unlock(&td->lock);
1158
1159 return r;
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001160}
1161
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001162static int taal_rotate(struct omap_dss_device *dssdev, u8 rotate)
1163{
1164 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1165 int r;
1166
1167 dev_dbg(&dssdev->dev, "rotate %d\n", rotate);
1168
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001169 mutex_lock(&td->lock);
Tomi Valkeinen87424e12010-01-08 16:52:48 +02001170 dsi_bus_lock();
1171
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001172 if (td->enabled) {
1173 r = taal_set_addr_mode(rotate, td->mirror);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001174 if (r)
Tomi Valkeinen87424e12010-01-08 16:52:48 +02001175 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001176 }
1177
1178 td->rotate = rotate;
1179
Tomi Valkeinen87424e12010-01-08 16:52:48 +02001180 dsi_bus_unlock();
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001181 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001182 return 0;
Tomi Valkeinen87424e12010-01-08 16:52:48 +02001183err:
1184 dsi_bus_unlock();
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001185 mutex_unlock(&td->lock);
Tomi Valkeinen87424e12010-01-08 16:52:48 +02001186 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001187}
1188
1189static u8 taal_get_rotate(struct omap_dss_device *dssdev)
1190{
1191 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001192 int r;
1193
1194 mutex_lock(&td->lock);
1195 r = td->rotate;
1196 mutex_unlock(&td->lock);
1197
1198 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001199}
1200
1201static int taal_mirror(struct omap_dss_device *dssdev, bool enable)
1202{
1203 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1204 int r;
1205
1206 dev_dbg(&dssdev->dev, "mirror %d\n", enable);
1207
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001208 mutex_lock(&td->lock);
Tomi Valkeinen8d8aa612010-01-08 16:30:33 +02001209 dsi_bus_lock();
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001210 if (td->enabled) {
1211 r = taal_set_addr_mode(td->rotate, enable);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001212 if (r)
Tomi Valkeinen8d8aa612010-01-08 16:30:33 +02001213 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001214 }
1215
1216 td->mirror = enable;
1217
Tomi Valkeinen8d8aa612010-01-08 16:30:33 +02001218 dsi_bus_unlock();
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001219 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001220 return 0;
Tomi Valkeinen8d8aa612010-01-08 16:30:33 +02001221err:
1222 dsi_bus_unlock();
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001223 mutex_unlock(&td->lock);
Tomi Valkeinen8d8aa612010-01-08 16:30:33 +02001224 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001225}
1226
1227static bool taal_get_mirror(struct omap_dss_device *dssdev)
1228{
1229 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001230 int r;
1231
1232 mutex_lock(&td->lock);
1233 r = td->mirror;
1234 mutex_unlock(&td->lock);
1235
1236 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001237}
1238
1239static int taal_run_test(struct omap_dss_device *dssdev, int test_num)
1240{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001241 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001242 u8 id1, id2, id3;
1243 int r;
1244
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001245 mutex_lock(&td->lock);
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001246
1247 if (!td->enabled) {
1248 r = -ENODEV;
1249 goto err1;
1250 }
1251
Tomi Valkeinen1a75ef42010-01-08 16:21:28 +02001252 dsi_bus_lock();
1253
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001254 r = taal_dcs_read_1(DCS_GET_ID1, &id1);
1255 if (r)
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001256 goto err2;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001257 r = taal_dcs_read_1(DCS_GET_ID2, &id2);
1258 if (r)
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001259 goto err2;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001260 r = taal_dcs_read_1(DCS_GET_ID3, &id3);
1261 if (r)
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001262 goto err2;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001263
Tomi Valkeinen1a75ef42010-01-08 16:21:28 +02001264 dsi_bus_unlock();
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001265 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001266 return 0;
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001267err2:
Tomi Valkeinen1a75ef42010-01-08 16:21:28 +02001268 dsi_bus_unlock();
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001269err1:
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001270 mutex_unlock(&td->lock);
Tomi Valkeinen1a75ef42010-01-08 16:21:28 +02001271 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001272}
1273
1274static int taal_memory_read(struct omap_dss_device *dssdev,
1275 void *buf, size_t size,
1276 u16 x, u16 y, u16 w, u16 h)
1277{
1278 int r;
1279 int first = 1;
1280 int plen;
1281 unsigned buf_used = 0;
Tomi Valkeinenc75d9462010-01-08 16:56:44 +02001282 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1283
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001284 if (size < w * h * 3)
1285 return -ENOMEM;
1286
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001287 mutex_lock(&td->lock);
1288
1289 if (!td->enabled) {
1290 r = -ENODEV;
1291 goto err1;
1292 }
1293
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001294 size = min(w * h * 3,
1295 dssdev->panel.timings.x_res *
1296 dssdev->panel.timings.y_res * 3);
1297
Tomi Valkeinenc75d9462010-01-08 16:56:44 +02001298 dsi_bus_lock();
1299
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001300 /* plen 1 or 2 goes into short packet. until checksum error is fixed,
1301 * use short packets. plen 32 works, but bigger packets seem to cause
1302 * an error. */
1303 if (size % 2)
1304 plen = 1;
1305 else
1306 plen = 2;
1307
Tomi Valkeinenc75d9462010-01-08 16:56:44 +02001308 taal_set_update_window(x, y, w, h);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001309
1310 r = dsi_vc_set_max_rx_packet_size(TCH, plen);
1311 if (r)
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001312 goto err2;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001313
1314 while (buf_used < size) {
1315 u8 dcs_cmd = first ? 0x2e : 0x3e;
1316 first = 0;
1317
1318 r = dsi_vc_dcs_read(TCH, dcs_cmd,
1319 buf + buf_used, size - buf_used);
1320
1321 if (r < 0) {
1322 dev_err(&dssdev->dev, "read error\n");
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001323 goto err3;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001324 }
1325
1326 buf_used += r;
1327
1328 if (r < plen) {
1329 dev_err(&dssdev->dev, "short read\n");
1330 break;
1331 }
1332
1333 if (signal_pending(current)) {
1334 dev_err(&dssdev->dev, "signal pending, "
1335 "aborting memory read\n");
1336 r = -ERESTARTSYS;
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001337 goto err3;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001338 }
1339 }
1340
1341 r = buf_used;
1342
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001343err3:
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001344 dsi_vc_set_max_rx_packet_size(TCH, 1);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001345err2:
Tomi Valkeinenc75d9462010-01-08 16:56:44 +02001346 dsi_bus_unlock();
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001347err1:
1348 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001349 return r;
1350}
1351
1352static void taal_esd_work(struct work_struct *work)
1353{
1354 struct taal_data *td = container_of(work, struct taal_data,
1355 esd_work.work);
1356 struct omap_dss_device *dssdev = td->dssdev;
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001357 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001358 u8 state1, state2;
1359 int r;
1360
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001361 mutex_lock(&td->lock);
1362
1363 if (!td->enabled) {
1364 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001365 return;
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001366 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001367
1368 dsi_bus_lock();
1369
1370 r = taal_dcs_read_1(DCS_RDDSDR, &state1);
1371 if (r) {
1372 dev_err(&dssdev->dev, "failed to read Taal status\n");
1373 goto err;
1374 }
1375
1376 /* Run self diagnostics */
1377 r = taal_sleep_out(td);
1378 if (r) {
1379 dev_err(&dssdev->dev, "failed to run Taal self-diagnostics\n");
1380 goto err;
1381 }
1382
1383 r = taal_dcs_read_1(DCS_RDDSDR, &state2);
1384 if (r) {
1385 dev_err(&dssdev->dev, "failed to read Taal status\n");
1386 goto err;
1387 }
1388
1389 /* Each sleep out command will trigger a self diagnostic and flip
1390 * Bit6 if the test passes.
1391 */
1392 if (!((state1 ^ state2) & (1 << 6))) {
1393 dev_err(&dssdev->dev, "LCD self diagnostics failed\n");
1394 goto err;
1395 }
1396 /* Self-diagnostics result is also shown on TE GPIO line. We need
1397 * to re-enable TE after self diagnostics */
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001398 if (td->te_enabled && panel_data->use_ext_te) {
Tomi Valkeinen1189b7f2010-03-01 13:52:10 +02001399 r = taal_dcs_write_1(DCS_TEAR_ON, 0);
1400 if (r)
1401 goto err;
1402 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001403
1404 dsi_bus_unlock();
1405
1406 queue_delayed_work(td->esd_wq, &td->esd_work, TAAL_ESD_CHECK_PERIOD);
1407
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001408 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001409 return;
1410err:
1411 dev_err(&dssdev->dev, "performing LCD reset\n");
1412
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001413 taal_power_off(dssdev);
Jani Nikula006db7b2010-04-09 12:25:59 +03001414 taal_hw_reset(dssdev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001415 taal_power_on(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001416
1417 dsi_bus_unlock();
1418
1419 queue_delayed_work(td->esd_wq, &td->esd_work, TAAL_ESD_CHECK_PERIOD);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001420
1421 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001422}
1423
Tomi Valkeinen446f7bf2010-01-11 16:12:31 +02001424static int taal_set_update_mode(struct omap_dss_device *dssdev,
1425 enum omap_dss_update_mode mode)
1426{
1427 if (mode != OMAP_DSS_UPDATE_MANUAL)
1428 return -EINVAL;
1429 return 0;
1430}
1431
1432static enum omap_dss_update_mode taal_get_update_mode(
1433 struct omap_dss_device *dssdev)
1434{
1435 return OMAP_DSS_UPDATE_MANUAL;
1436}
1437
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001438static struct omap_dss_driver taal_driver = {
1439 .probe = taal_probe,
1440 .remove = taal_remove,
1441
1442 .enable = taal_enable,
1443 .disable = taal_disable,
1444 .suspend = taal_suspend,
1445 .resume = taal_resume,
1446
Tomi Valkeinen446f7bf2010-01-11 16:12:31 +02001447 .set_update_mode = taal_set_update_mode,
1448 .get_update_mode = taal_get_update_mode,
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001449
1450 .update = taal_update,
1451 .sync = taal_sync,
1452
Tomi Valkeinen96adcec2010-01-11 13:54:33 +02001453 .get_resolution = taal_get_resolution,
Tomi Valkeinena2699502010-01-11 14:33:40 +02001454 .get_recommended_bpp = omapdss_default_get_recommended_bpp,
1455
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001456 .enable_te = taal_enable_te,
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001457 .get_te = taal_get_te,
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001458
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001459 .set_rotate = taal_rotate,
1460 .get_rotate = taal_get_rotate,
1461 .set_mirror = taal_mirror,
1462 .get_mirror = taal_get_mirror,
1463 .run_test = taal_run_test,
1464 .memory_read = taal_memory_read,
1465
Tomi Valkeinen69b20482010-01-20 12:11:25 +02001466 .get_timings = taal_get_timings,
1467
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001468 .driver = {
1469 .name = "taal",
1470 .owner = THIS_MODULE,
1471 },
1472};
1473
1474static int __init taal_init(void)
1475{
1476 omap_dss_register_driver(&taal_driver);
1477
1478 return 0;
1479}
1480
1481static void __exit taal_exit(void)
1482{
1483 omap_dss_unregister_driver(&taal_driver);
1484}
1485
1486module_init(taal_init);
1487module_exit(taal_exit);
1488
1489MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@nokia.com>");
1490MODULE_DESCRIPTION("Taal Driver");
1491MODULE_LICENSE("GPL");