blob: eb95e97fd9b01599b7b80424f1826cd21e30fb97 [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
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;
Jani Nikula7ae2fb12010-04-13 10:57:52 +030088
89 atomic_t do_update;
90 struct {
91 u16 x;
92 u16 y;
93 u16 w;
94 u16 h;
95 } update_region;
96 struct delayed_work te_timeout_work;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +020097
98 bool use_dsi_bl;
99
100 bool cabc_broken;
101 unsigned cabc_mode;
102
103 bool intro_printed;
104
105 struct workqueue_struct *esd_wq;
106 struct delayed_work esd_work;
107};
108
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300109static inline struct nokia_dsi_panel_data
110*get_panel_data(const struct omap_dss_device *dssdev)
111{
112 return (struct nokia_dsi_panel_data *) dssdev->data;
113}
114
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200115static void taal_esd_work(struct work_struct *work);
116
117static void hw_guard_start(struct taal_data *td, int guard_msec)
118{
119 td->hw_guard_wait = msecs_to_jiffies(guard_msec);
120 td->hw_guard_end = jiffies + td->hw_guard_wait;
121}
122
123static void hw_guard_wait(struct taal_data *td)
124{
125 unsigned long wait = td->hw_guard_end - jiffies;
126
127 if ((long)wait > 0 && wait <= td->hw_guard_wait) {
128 set_current_state(TASK_UNINTERRUPTIBLE);
129 schedule_timeout(wait);
130 }
131}
132
133static int taal_dcs_read_1(u8 dcs_cmd, u8 *data)
134{
135 int r;
136 u8 buf[1];
137
138 r = dsi_vc_dcs_read(TCH, dcs_cmd, buf, 1);
139
140 if (r < 0)
141 return r;
142
143 *data = buf[0];
144
145 return 0;
146}
147
148static int taal_dcs_write_0(u8 dcs_cmd)
149{
150 return dsi_vc_dcs_write(TCH, &dcs_cmd, 1);
151}
152
153static int taal_dcs_write_1(u8 dcs_cmd, u8 param)
154{
155 u8 buf[2];
156 buf[0] = dcs_cmd;
157 buf[1] = param;
158 return dsi_vc_dcs_write(TCH, buf, 2);
159}
160
161static int taal_sleep_in(struct taal_data *td)
162
163{
164 u8 cmd;
165 int r;
166
167 hw_guard_wait(td);
168
169 cmd = DCS_SLEEP_IN;
170 r = dsi_vc_dcs_write_nosync(TCH, &cmd, 1);
171 if (r)
172 return r;
173
174 hw_guard_start(td, 120);
175
176 msleep(5);
177
178 return 0;
179}
180
181static int taal_sleep_out(struct taal_data *td)
182{
183 int r;
184
185 hw_guard_wait(td);
186
187 r = taal_dcs_write_0(DCS_SLEEP_OUT);
188 if (r)
189 return r;
190
191 hw_guard_start(td, 120);
192
193 msleep(5);
194
195 return 0;
196}
197
198static int taal_get_id(u8 *id1, u8 *id2, u8 *id3)
199{
200 int r;
201
202 r = taal_dcs_read_1(DCS_GET_ID1, id1);
203 if (r)
204 return r;
205 r = taal_dcs_read_1(DCS_GET_ID2, id2);
206 if (r)
207 return r;
208 r = taal_dcs_read_1(DCS_GET_ID3, id3);
209 if (r)
210 return r;
211
212 return 0;
213}
214
215static int taal_set_addr_mode(u8 rotate, bool mirror)
216{
217 int r;
218 u8 mode;
219 int b5, b6, b7;
220
221 r = taal_dcs_read_1(DCS_READ_MADCTL, &mode);
222 if (r)
223 return r;
224
225 switch (rotate) {
226 default:
227 case 0:
228 b7 = 0;
229 b6 = 0;
230 b5 = 0;
231 break;
232 case 1:
233 b7 = 0;
234 b6 = 1;
235 b5 = 1;
236 break;
237 case 2:
238 b7 = 1;
239 b6 = 1;
240 b5 = 0;
241 break;
242 case 3:
243 b7 = 1;
244 b6 = 0;
245 b5 = 1;
246 break;
247 }
248
249 if (mirror)
250 b6 = !b6;
251
252 mode &= ~((1<<7) | (1<<6) | (1<<5));
253 mode |= (b7 << 7) | (b6 << 6) | (b5 << 5);
254
255 return taal_dcs_write_1(DCS_MEM_ACC_CTRL, mode);
256}
257
258static int taal_set_update_window(u16 x, u16 y, u16 w, u16 h)
259{
260 int r;
261 u16 x1 = x;
262 u16 x2 = x + w - 1;
263 u16 y1 = y;
264 u16 y2 = y + h - 1;
265
266 u8 buf[5];
267 buf[0] = DCS_COLUMN_ADDR;
268 buf[1] = (x1 >> 8) & 0xff;
269 buf[2] = (x1 >> 0) & 0xff;
270 buf[3] = (x2 >> 8) & 0xff;
271 buf[4] = (x2 >> 0) & 0xff;
272
273 r = dsi_vc_dcs_write_nosync(TCH, buf, sizeof(buf));
274 if (r)
275 return r;
276
277 buf[0] = DCS_PAGE_ADDR;
278 buf[1] = (y1 >> 8) & 0xff;
279 buf[2] = (y1 >> 0) & 0xff;
280 buf[3] = (y2 >> 8) & 0xff;
281 buf[4] = (y2 >> 0) & 0xff;
282
283 r = dsi_vc_dcs_write_nosync(TCH, buf, sizeof(buf));
284 if (r)
285 return r;
286
287 dsi_vc_send_bta_sync(TCH);
288
289 return r;
290}
291
292static int taal_bl_update_status(struct backlight_device *dev)
293{
294 struct omap_dss_device *dssdev = dev_get_drvdata(&dev->dev);
295 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300296 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200297 int r;
298 int level;
299
300 if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
301 dev->props.power == FB_BLANK_UNBLANK)
302 level = dev->props.brightness;
303 else
304 level = 0;
305
306 dev_dbg(&dssdev->dev, "update brightness to %d\n", level);
307
Tomi Valkeinen1cbc8702010-04-15 16:41:07 +0300308 mutex_lock(&td->lock);
309
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200310 if (td->use_dsi_bl) {
311 if (td->enabled) {
312 dsi_bus_lock();
313 r = taal_dcs_write_1(DCS_BRIGHTNESS, level);
314 dsi_bus_unlock();
Tomi Valkeinen1cbc8702010-04-15 16:41:07 +0300315 } else {
316 r = 0;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200317 }
318 } else {
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300319 if (!panel_data->set_backlight)
Tomi Valkeinen1cbc8702010-04-15 16:41:07 +0300320 r = -EINVAL;
321 else
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300322 r = panel_data->set_backlight(dssdev, level);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200323 }
324
Tomi Valkeinen1cbc8702010-04-15 16:41:07 +0300325 mutex_unlock(&td->lock);
326
327 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200328}
329
330static int taal_bl_get_intensity(struct backlight_device *dev)
331{
332 if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
333 dev->props.power == FB_BLANK_UNBLANK)
334 return dev->props.brightness;
335
336 return 0;
337}
338
339static struct backlight_ops taal_bl_ops = {
340 .get_brightness = taal_bl_get_intensity,
341 .update_status = taal_bl_update_status,
342};
343
344static void taal_get_timings(struct omap_dss_device *dssdev,
345 struct omap_video_timings *timings)
346{
347 *timings = dssdev->panel.timings;
348}
349
350static void taal_get_resolution(struct omap_dss_device *dssdev,
351 u16 *xres, u16 *yres)
352{
353 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
354
355 if (td->rotate == 0 || td->rotate == 2) {
356 *xres = dssdev->panel.timings.x_res;
357 *yres = dssdev->panel.timings.y_res;
358 } else {
359 *yres = dssdev->panel.timings.x_res;
360 *xres = dssdev->panel.timings.y_res;
361 }
362}
363
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200364static ssize_t taal_num_errors_show(struct device *dev,
365 struct device_attribute *attr, char *buf)
366{
367 struct omap_dss_device *dssdev = to_dss_device(dev);
368 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
369 u8 errors;
370 int r;
371
Jani Nikula6b316712010-04-28 11:15:18 +0300372 mutex_lock(&td->lock);
373
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200374 if (td->enabled) {
375 dsi_bus_lock();
376 r = taal_dcs_read_1(DCS_READ_NUM_ERRORS, &errors);
377 dsi_bus_unlock();
378 } else {
379 r = -ENODEV;
380 }
381
Jani Nikula6b316712010-04-28 11:15:18 +0300382 mutex_unlock(&td->lock);
383
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200384 if (r)
385 return r;
386
387 return snprintf(buf, PAGE_SIZE, "%d\n", errors);
388}
389
390static ssize_t taal_hw_revision_show(struct device *dev,
391 struct device_attribute *attr, char *buf)
392{
393 struct omap_dss_device *dssdev = to_dss_device(dev);
394 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
395 u8 id1, id2, id3;
396 int r;
397
Jani Nikula6b316712010-04-28 11:15:18 +0300398 mutex_lock(&td->lock);
399
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200400 if (td->enabled) {
401 dsi_bus_lock();
402 r = taal_get_id(&id1, &id2, &id3);
403 dsi_bus_unlock();
404 } else {
405 r = -ENODEV;
406 }
407
Jani Nikula6b316712010-04-28 11:15:18 +0300408 mutex_unlock(&td->lock);
409
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200410 if (r)
411 return r;
412
413 return snprintf(buf, PAGE_SIZE, "%02x.%02x.%02x\n", id1, id2, id3);
414}
415
416static const char *cabc_modes[] = {
417 "off", /* used also always when CABC is not supported */
418 "ui",
419 "still-image",
420 "moving-image",
421};
422
423static ssize_t show_cabc_mode(struct device *dev,
424 struct device_attribute *attr,
425 char *buf)
426{
427 struct omap_dss_device *dssdev = to_dss_device(dev);
428 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
429 const char *mode_str;
430 int mode;
431 int len;
432
433 mode = td->cabc_mode;
434
435 mode_str = "unknown";
436 if (mode >= 0 && mode < ARRAY_SIZE(cabc_modes))
437 mode_str = cabc_modes[mode];
438 len = snprintf(buf, PAGE_SIZE, "%s\n", mode_str);
439
440 return len < PAGE_SIZE - 1 ? len : PAGE_SIZE - 1;
441}
442
443static ssize_t store_cabc_mode(struct device *dev,
444 struct device_attribute *attr,
445 const char *buf, size_t count)
446{
447 struct omap_dss_device *dssdev = to_dss_device(dev);
448 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
449 int i;
450
451 for (i = 0; i < ARRAY_SIZE(cabc_modes); i++) {
452 if (sysfs_streq(cabc_modes[i], buf))
453 break;
454 }
455
456 if (i == ARRAY_SIZE(cabc_modes))
457 return -EINVAL;
458
Jani Nikula6b316712010-04-28 11:15:18 +0300459 mutex_lock(&td->lock);
460
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200461 if (td->enabled) {
462 dsi_bus_lock();
463 if (!td->cabc_broken)
464 taal_dcs_write_1(DCS_WRITE_CABC, i);
465 dsi_bus_unlock();
466 }
467
468 td->cabc_mode = i;
469
Jani Nikula6b316712010-04-28 11:15:18 +0300470 mutex_unlock(&td->lock);
471
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200472 return count;
473}
474
475static ssize_t show_cabc_available_modes(struct device *dev,
476 struct device_attribute *attr,
477 char *buf)
478{
479 int len;
480 int i;
481
482 for (i = 0, len = 0;
483 len < PAGE_SIZE && i < ARRAY_SIZE(cabc_modes); i++)
484 len += snprintf(&buf[len], PAGE_SIZE - len, "%s%s%s",
485 i ? " " : "", cabc_modes[i],
486 i == ARRAY_SIZE(cabc_modes) - 1 ? "\n" : "");
487
488 return len < PAGE_SIZE ? len : PAGE_SIZE - 1;
489}
490
491static DEVICE_ATTR(num_dsi_errors, S_IRUGO, taal_num_errors_show, NULL);
492static DEVICE_ATTR(hw_revision, S_IRUGO, taal_hw_revision_show, NULL);
493static DEVICE_ATTR(cabc_mode, S_IRUGO | S_IWUSR,
494 show_cabc_mode, store_cabc_mode);
495static DEVICE_ATTR(cabc_available_modes, S_IRUGO,
496 show_cabc_available_modes, NULL);
497
498static struct attribute *taal_attrs[] = {
499 &dev_attr_num_dsi_errors.attr,
500 &dev_attr_hw_revision.attr,
501 &dev_attr_cabc_mode.attr,
502 &dev_attr_cabc_available_modes.attr,
503 NULL,
504};
505
506static struct attribute_group taal_attr_group = {
507 .attrs = taal_attrs,
508};
509
Jani Nikula006db7b2010-04-09 12:25:59 +0300510static void taal_hw_reset(struct omap_dss_device *dssdev)
511{
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300512 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
513
514 if (panel_data->reset_gpio == -1)
Jani Nikula006db7b2010-04-09 12:25:59 +0300515 return;
516
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300517 gpio_set_value(panel_data->reset_gpio, 1);
Jani Nikula006db7b2010-04-09 12:25:59 +0300518 udelay(10);
519 /* reset the panel */
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300520 gpio_set_value(panel_data->reset_gpio, 0);
Jani Nikula006db7b2010-04-09 12:25:59 +0300521 /* assert reset for at least 10us */
522 udelay(10);
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300523 gpio_set_value(panel_data->reset_gpio, 1);
Jani Nikula006db7b2010-04-09 12:25:59 +0300524 /* wait 5ms after releasing reset */
525 msleep(5);
526}
527
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200528static int taal_probe(struct omap_dss_device *dssdev)
529{
Matthew Garretta19a6ee2010-02-17 16:39:44 -0500530 struct backlight_properties props;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200531 struct taal_data *td;
532 struct backlight_device *bldev;
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300533 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200534 int r;
535
536 const struct omap_video_timings taal_panel_timings = {
537 .x_res = 864,
538 .y_res = 480,
539 };
540
541 dev_dbg(&dssdev->dev, "probe\n");
542
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300543 if (!panel_data || !panel_data->name) {
544 r = -EINVAL;
545 goto err;
546 }
547
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200548 dssdev->panel.config = OMAP_DSS_LCD_TFT;
549 dssdev->panel.timings = taal_panel_timings;
550 dssdev->ctrl.pixel_size = 24;
551
552 td = kzalloc(sizeof(*td), GFP_KERNEL);
553 if (!td) {
554 r = -ENOMEM;
Jani Nikulad2b65782010-04-15 12:24:36 +0300555 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200556 }
557 td->dssdev = dssdev;
558
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200559 mutex_init(&td->lock);
560
Jani Nikula7ae2fb12010-04-13 10:57:52 +0300561 atomic_set(&td->do_update, 0);
562
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200563 td->esd_wq = create_singlethread_workqueue("taal_esd");
564 if (td->esd_wq == NULL) {
565 dev_err(&dssdev->dev, "can't create ESD workqueue\n");
566 r = -ENOMEM;
Jani Nikulad2b65782010-04-15 12:24:36 +0300567 goto err_wq;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200568 }
569 INIT_DELAYED_WORK_DEFERRABLE(&td->esd_work, taal_esd_work);
570
571 dev_set_drvdata(&dssdev->dev, td);
572
Jani Nikula006db7b2010-04-09 12:25:59 +0300573 taal_hw_reset(dssdev);
574
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200575 /* if no platform set_backlight() defined, presume DSI backlight
576 * control */
Matthew Garretta19a6ee2010-02-17 16:39:44 -0500577 memset(&props, 0, sizeof(struct backlight_properties));
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300578 if (!panel_data->set_backlight)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200579 td->use_dsi_bl = true;
580
Matthew Garretta19a6ee2010-02-17 16:39:44 -0500581 if (td->use_dsi_bl)
582 props.max_brightness = 255;
583 else
584 props.max_brightness = 127;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200585 bldev = backlight_device_register("taal", &dssdev->dev, dssdev,
Matthew Garretta19a6ee2010-02-17 16:39:44 -0500586 &taal_bl_ops, &props);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200587 if (IS_ERR(bldev)) {
588 r = PTR_ERR(bldev);
Jani Nikulad2b65782010-04-15 12:24:36 +0300589 goto err_bl;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200590 }
591
592 td->bldev = bldev;
593
594 bldev->props.fb_blank = FB_BLANK_UNBLANK;
595 bldev->props.power = FB_BLANK_UNBLANK;
Matthew Garretta19a6ee2010-02-17 16:39:44 -0500596 if (td->use_dsi_bl)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200597 bldev->props.brightness = 255;
Matthew Garretta19a6ee2010-02-17 16:39:44 -0500598 else
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200599 bldev->props.brightness = 127;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200600
601 taal_bl_update_status(bldev);
602
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300603 if (panel_data->use_ext_te) {
604 int gpio = panel_data->ext_te_gpio;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200605
606 r = gpio_request(gpio, "taal irq");
607 if (r) {
608 dev_err(&dssdev->dev, "GPIO request failed\n");
Jani Nikulad2b65782010-04-15 12:24:36 +0300609 goto err_gpio;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200610 }
611
612 gpio_direction_input(gpio);
613
614 r = request_irq(gpio_to_irq(gpio), taal_te_isr,
615 IRQF_DISABLED | IRQF_TRIGGER_RISING,
616 "taal vsync", dssdev);
617
618 if (r) {
619 dev_err(&dssdev->dev, "IRQ request failed\n");
620 gpio_free(gpio);
Jani Nikulad2b65782010-04-15 12:24:36 +0300621 goto err_irq;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200622 }
623
Jani Nikula7ae2fb12010-04-13 10:57:52 +0300624 INIT_DELAYED_WORK_DEFERRABLE(&td->te_timeout_work,
625 taal_te_timeout_work_callback);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200626
Jani Nikula7ae2fb12010-04-13 10:57:52 +0300627 dev_dbg(&dssdev->dev, "Using GPIO TE\n");
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200628 }
629
630 r = sysfs_create_group(&dssdev->dev.kobj, &taal_attr_group);
631 if (r) {
632 dev_err(&dssdev->dev, "failed to create sysfs files\n");
Jani Nikulad2b65782010-04-15 12:24:36 +0300633 goto err_sysfs;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200634 }
635
636 return 0;
Jani Nikulad2b65782010-04-15 12:24:36 +0300637err_sysfs:
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300638 if (panel_data->use_ext_te)
639 free_irq(gpio_to_irq(panel_data->ext_te_gpio), dssdev);
Jani Nikulad2b65782010-04-15 12:24:36 +0300640err_irq:
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300641 if (panel_data->use_ext_te)
642 gpio_free(panel_data->ext_te_gpio);
Jani Nikulad2b65782010-04-15 12:24:36 +0300643err_gpio:
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200644 backlight_device_unregister(bldev);
Jani Nikulad2b65782010-04-15 12:24:36 +0300645err_bl:
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200646 destroy_workqueue(td->esd_wq);
Jani Nikulad2b65782010-04-15 12:24:36 +0300647err_wq:
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200648 kfree(td);
Jani Nikulad2b65782010-04-15 12:24:36 +0300649err:
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200650 return r;
651}
652
653static void taal_remove(struct omap_dss_device *dssdev)
654{
655 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300656 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200657 struct backlight_device *bldev;
658
659 dev_dbg(&dssdev->dev, "remove\n");
660
661 sysfs_remove_group(&dssdev->dev.kobj, &taal_attr_group);
662
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300663 if (panel_data->use_ext_te) {
664 int gpio = panel_data->ext_te_gpio;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200665 free_irq(gpio_to_irq(gpio), dssdev);
666 gpio_free(gpio);
667 }
668
669 bldev = td->bldev;
670 bldev->props.power = FB_BLANK_POWERDOWN;
671 taal_bl_update_status(bldev);
672 backlight_device_unregister(bldev);
673
Jani Nikula4571a022010-04-12 10:23:46 +0300674 cancel_delayed_work(&td->esd_work);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200675 destroy_workqueue(td->esd_wq);
676
Jani Nikula006db7b2010-04-09 12:25:59 +0300677 /* reset, to be sure that the panel is in a valid state */
678 taal_hw_reset(dssdev);
679
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200680 kfree(td);
681}
682
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200683static int taal_power_on(struct omap_dss_device *dssdev)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200684{
685 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
686 u8 id1, id2, id3;
687 int r;
688
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200689 /* it seems we have to wait a bit until taal is ready */
690 msleep(5);
691
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200692 r = omapdss_dsi_display_enable(dssdev);
693 if (r) {
694 dev_err(&dssdev->dev, "failed to enable DSI\n");
695 goto err0;
696 }
697
Jani Nikula006db7b2010-04-09 12:25:59 +0300698 taal_hw_reset(dssdev);
699
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200700 omapdss_dsi_vc_enable_hs(TCH, false);
701
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200702 r = taal_sleep_out(td);
703 if (r)
704 goto err;
705
706 r = taal_get_id(&id1, &id2, &id3);
707 if (r)
708 goto err;
709
710 /* on early revisions CABC is broken */
711 if (id2 == 0x00 || id2 == 0xff || id2 == 0x81)
712 td->cabc_broken = true;
713
Jani Nikulaf2a8b752010-04-09 14:15:12 +0300714 r = taal_dcs_write_1(DCS_BRIGHTNESS, 0xff);
715 if (r)
716 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200717
Jani Nikulaf2a8b752010-04-09 14:15:12 +0300718 r = taal_dcs_write_1(DCS_CTRL_DISPLAY,
719 (1<<2) | (1<<5)); /* BL | BCTRL */
720 if (r)
721 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200722
Jani Nikulaf2a8b752010-04-09 14:15:12 +0300723 r = taal_dcs_write_1(DCS_PIXEL_FORMAT, 0x7); /* 24bit/pixel */
724 if (r)
725 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200726
Jani Nikulaf2a8b752010-04-09 14:15:12 +0300727 r = taal_set_addr_mode(td->rotate, td->mirror);
728 if (r)
729 goto err;
730
731 if (!td->cabc_broken) {
732 r = taal_dcs_write_1(DCS_WRITE_CABC, td->cabc_mode);
733 if (r)
734 goto err;
735 }
736
737 r = taal_dcs_write_0(DCS_DISPLAY_ON);
738 if (r)
739 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200740
Tomi Valkeinen21df20f2010-03-02 12:13:55 +0200741 r = _taal_enable_te(dssdev, td->te_enabled);
742 if (r)
743 goto err;
744
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200745 td->enabled = 1;
746
747 if (!td->intro_printed) {
748 dev_info(&dssdev->dev, "revision %02x.%02x.%02x\n",
749 id1, id2, id3);
750 if (td->cabc_broken)
751 dev_info(&dssdev->dev,
752 "old Taal version, CABC disabled\n");
753 td->intro_printed = true;
754 }
755
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200756 omapdss_dsi_vc_enable_hs(TCH, true);
757
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200758 return 0;
759err:
Jani Nikula006db7b2010-04-09 12:25:59 +0300760 dev_err(&dssdev->dev, "error while enabling panel, issuing HW reset\n");
761
762 taal_hw_reset(dssdev);
763
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200764 omapdss_dsi_display_disable(dssdev);
765err0:
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200766 return r;
767}
768
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200769static void taal_power_off(struct omap_dss_device *dssdev)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200770{
771 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Jani Nikula006db7b2010-04-09 12:25:59 +0300772 int r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200773
Jani Nikula006db7b2010-04-09 12:25:59 +0300774 r = taal_dcs_write_0(DCS_DISPLAY_OFF);
775 if (!r) {
776 r = taal_sleep_in(td);
777 /* wait a bit so that the message goes through */
778 msleep(10);
779 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200780
Jani Nikula006db7b2010-04-09 12:25:59 +0300781 if (r) {
782 dev_err(&dssdev->dev,
783 "error disabling panel, issuing HW reset\n");
784 taal_hw_reset(dssdev);
785 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200786
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200787 omapdss_dsi_display_disable(dssdev);
788
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200789 td->enabled = 0;
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200790}
791
792static int taal_enable(struct omap_dss_device *dssdev)
793{
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200794 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Jani Nikula448dfe92010-04-15 12:42:16 +0300795 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200796 int r;
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200797
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200798 dev_dbg(&dssdev->dev, "enable\n");
799
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200800 mutex_lock(&td->lock);
801
802 if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) {
803 r = -EINVAL;
804 goto err;
805 }
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200806
Jani Nikula2c2fc152010-04-12 10:06:14 +0300807 dsi_bus_lock();
808
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200809 r = taal_power_on(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +0300810
811 dsi_bus_unlock();
812
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200813 if (r)
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200814 goto err;
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200815
Jani Nikula448dfe92010-04-15 12:42:16 +0300816 if (panel_data->use_esd_check)
817 queue_delayed_work(td->esd_wq, &td->esd_work,
818 TAAL_ESD_CHECK_PERIOD);
Jani Nikula4571a022010-04-12 10:23:46 +0300819
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200820 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
821
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200822 mutex_unlock(&td->lock);
823
824 return 0;
825err:
826 dev_dbg(&dssdev->dev, "enable failed\n");
827 mutex_unlock(&td->lock);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200828 return r;
829}
830
831static void taal_disable(struct omap_dss_device *dssdev)
832{
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200833 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
834
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200835 dev_dbg(&dssdev->dev, "disable\n");
836
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200837 mutex_lock(&td->lock);
838
Jani Nikula4571a022010-04-12 10:23:46 +0300839 cancel_delayed_work(&td->esd_work);
840
Jani Nikula2c2fc152010-04-12 10:06:14 +0300841 dsi_bus_lock();
842
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200843 if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
844 taal_power_off(dssdev);
845
Jani Nikula2c2fc152010-04-12 10:06:14 +0300846 dsi_bus_unlock();
847
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200848 dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200849
850 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200851}
852
853static int taal_suspend(struct omap_dss_device *dssdev)
854{
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200855 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
856 int r;
857
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200858 dev_dbg(&dssdev->dev, "suspend\n");
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200859
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200860 mutex_lock(&td->lock);
861
862 if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) {
863 r = -EINVAL;
864 goto err;
865 }
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200866
Jani Nikula4571a022010-04-12 10:23:46 +0300867 cancel_delayed_work(&td->esd_work);
868
Jani Nikula2c2fc152010-04-12 10:06:14 +0300869 dsi_bus_lock();
870
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200871 taal_power_off(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +0300872
873 dsi_bus_unlock();
874
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200875 dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200876
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200877 mutex_unlock(&td->lock);
878
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200879 return 0;
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200880err:
881 mutex_unlock(&td->lock);
882 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200883}
884
885static int taal_resume(struct omap_dss_device *dssdev)
886{
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200887 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Jani Nikula448dfe92010-04-15 12:42:16 +0300888 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200889 int r;
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200890
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200891 dev_dbg(&dssdev->dev, "resume\n");
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200892
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200893 mutex_lock(&td->lock);
894
895 if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) {
896 r = -EINVAL;
897 goto err;
898 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200899
Jani Nikula2c2fc152010-04-12 10:06:14 +0300900 dsi_bus_lock();
901
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200902 r = taal_power_on(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +0300903
904 dsi_bus_unlock();
905
Jani Nikula4571a022010-04-12 10:23:46 +0300906 if (r) {
Jani Nikulafed44b72010-04-12 10:25:21 +0300907 dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
Jani Nikula4571a022010-04-12 10:23:46 +0300908 } else {
Jani Nikulafed44b72010-04-12 10:25:21 +0300909 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
Jani Nikula448dfe92010-04-15 12:42:16 +0300910 if (panel_data->use_esd_check)
911 queue_delayed_work(td->esd_wq, &td->esd_work,
912 TAAL_ESD_CHECK_PERIOD);
Jani Nikula4571a022010-04-12 10:23:46 +0300913 }
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200914
915 mutex_unlock(&td->lock);
916
917 return r;
918err:
919 mutex_unlock(&td->lock);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200920 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200921}
922
Tomi Valkeinen18946f62010-01-12 14:16:41 +0200923static void taal_framedone_cb(int err, void *data)
924{
925 struct omap_dss_device *dssdev = data;
926 dev_dbg(&dssdev->dev, "framedone, err %d\n", err);
927 dsi_bus_unlock();
928}
929
Jani Nikula7ae2fb12010-04-13 10:57:52 +0300930static irqreturn_t taal_te_isr(int irq, void *data)
931{
932 struct omap_dss_device *dssdev = data;
933 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
934 int old;
935 int r;
936
937 old = atomic_cmpxchg(&td->do_update, 1, 0);
938
939 if (old) {
940 cancel_delayed_work(&td->te_timeout_work);
941
942 r = omap_dsi_update(dssdev, TCH,
943 td->update_region.x,
944 td->update_region.y,
945 td->update_region.w,
946 td->update_region.h,
947 taal_framedone_cb, dssdev);
948 if (r)
949 goto err;
950 }
951
952 return IRQ_HANDLED;
953err:
954 dev_err(&dssdev->dev, "start update failed\n");
955 dsi_bus_unlock();
956 return IRQ_HANDLED;
957}
958
959static void taal_te_timeout_work_callback(struct work_struct *work)
960{
961 struct taal_data *td = container_of(work, struct taal_data,
962 te_timeout_work.work);
963 struct omap_dss_device *dssdev = td->dssdev;
964
965 dev_err(&dssdev->dev, "TE not received for 250ms!\n");
966
967 atomic_set(&td->do_update, 0);
968 dsi_bus_unlock();
969}
970
Tomi Valkeinen18946f62010-01-12 14:16:41 +0200971static int taal_update(struct omap_dss_device *dssdev,
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200972 u16 x, u16 y, u16 w, u16 h)
973{
Tomi Valkeinen18946f62010-01-12 14:16:41 +0200974 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300975 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
Tomi Valkeinen18946f62010-01-12 14:16:41 +0200976 int r;
977
978 dev_dbg(&dssdev->dev, "update %d, %d, %d x %d\n", x, y, w, h);
979
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200980 mutex_lock(&td->lock);
Tomi Valkeinen18946f62010-01-12 14:16:41 +0200981 dsi_bus_lock();
982
983 if (!td->enabled) {
984 r = 0;
985 goto err;
986 }
987
988 r = omap_dsi_prepare_update(dssdev, &x, &y, &w, &h);
989 if (r)
990 goto err;
991
992 r = taal_set_update_window(x, y, w, h);
993 if (r)
994 goto err;
995
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300996 if (td->te_enabled && panel_data->use_ext_te) {
Jani Nikula7ae2fb12010-04-13 10:57:52 +0300997 td->update_region.x = x;
998 td->update_region.y = y;
999 td->update_region.w = w;
1000 td->update_region.h = h;
1001 barrier();
1002 schedule_delayed_work(&td->te_timeout_work,
1003 msecs_to_jiffies(250));
1004 atomic_set(&td->do_update, 1);
1005 } else {
1006 r = omap_dsi_update(dssdev, TCH, x, y, w, h,
1007 taal_framedone_cb, dssdev);
1008 if (r)
1009 goto err;
1010 }
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001011
1012 /* note: no bus_unlock here. unlock is in framedone_cb */
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001013 mutex_unlock(&td->lock);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001014 return 0;
1015err:
1016 dsi_bus_unlock();
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001017 mutex_unlock(&td->lock);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001018 return r;
1019}
1020
1021static int taal_sync(struct omap_dss_device *dssdev)
1022{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001023 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1024
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001025 dev_dbg(&dssdev->dev, "sync\n");
1026
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001027 mutex_lock(&td->lock);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001028 dsi_bus_lock();
1029 dsi_bus_unlock();
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001030 mutex_unlock(&td->lock);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001031
1032 dev_dbg(&dssdev->dev, "sync done\n");
1033
1034 return 0;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001035}
1036
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001037static int _taal_enable_te(struct omap_dss_device *dssdev, bool enable)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001038{
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001039 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001040 int r;
1041
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001042 if (enable)
1043 r = taal_dcs_write_1(DCS_TEAR_ON, 0);
1044 else
1045 r = taal_dcs_write_0(DCS_TEAR_OFF);
1046
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001047 if (!panel_data->use_ext_te)
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001048 omapdss_dsi_enable_te(dssdev, enable);
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001049
1050 /* XXX for some reason, DSI TE breaks if we don't wait here.
1051 * Panel bug? Needs more studying */
1052 msleep(100);
1053
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001054 return r;
1055}
1056
1057static int taal_enable_te(struct omap_dss_device *dssdev, bool enable)
1058{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001059 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001060 int r;
1061
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001062 mutex_lock(&td->lock);
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001063 dsi_bus_lock();
1064
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001065 if (td->enabled) {
1066 r = _taal_enable_te(dssdev, enable);
1067 if (r)
1068 goto err;
1069 }
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001070
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001071 td->te_enabled = enable;
1072
1073 dsi_bus_unlock();
1074 mutex_unlock(&td->lock);
1075
1076 return 0;
1077err:
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001078 dsi_bus_unlock();
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001079 mutex_unlock(&td->lock);
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001080
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001081 return r;
1082}
1083
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001084static int taal_get_te(struct omap_dss_device *dssdev)
1085{
1086 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001087 int r;
1088
1089 mutex_lock(&td->lock);
1090 r = td->te_enabled;
1091 mutex_unlock(&td->lock);
1092
1093 return r;
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001094}
1095
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001096static int taal_rotate(struct omap_dss_device *dssdev, u8 rotate)
1097{
1098 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1099 int r;
1100
1101 dev_dbg(&dssdev->dev, "rotate %d\n", rotate);
1102
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001103 mutex_lock(&td->lock);
Tomi Valkeinen87424e12010-01-08 16:52:48 +02001104 dsi_bus_lock();
1105
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001106 if (td->enabled) {
1107 r = taal_set_addr_mode(rotate, td->mirror);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001108 if (r)
Tomi Valkeinen87424e12010-01-08 16:52:48 +02001109 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001110 }
1111
1112 td->rotate = rotate;
1113
Tomi Valkeinen87424e12010-01-08 16:52:48 +02001114 dsi_bus_unlock();
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001115 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001116 return 0;
Tomi Valkeinen87424e12010-01-08 16:52:48 +02001117err:
1118 dsi_bus_unlock();
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001119 mutex_unlock(&td->lock);
Tomi Valkeinen87424e12010-01-08 16:52:48 +02001120 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001121}
1122
1123static u8 taal_get_rotate(struct omap_dss_device *dssdev)
1124{
1125 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001126 int r;
1127
1128 mutex_lock(&td->lock);
1129 r = td->rotate;
1130 mutex_unlock(&td->lock);
1131
1132 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001133}
1134
1135static int taal_mirror(struct omap_dss_device *dssdev, bool enable)
1136{
1137 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1138 int r;
1139
1140 dev_dbg(&dssdev->dev, "mirror %d\n", enable);
1141
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001142 mutex_lock(&td->lock);
Tomi Valkeinen8d8aa612010-01-08 16:30:33 +02001143 dsi_bus_lock();
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001144 if (td->enabled) {
1145 r = taal_set_addr_mode(td->rotate, enable);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001146 if (r)
Tomi Valkeinen8d8aa612010-01-08 16:30:33 +02001147 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001148 }
1149
1150 td->mirror = enable;
1151
Tomi Valkeinen8d8aa612010-01-08 16:30:33 +02001152 dsi_bus_unlock();
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001153 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001154 return 0;
Tomi Valkeinen8d8aa612010-01-08 16:30:33 +02001155err:
1156 dsi_bus_unlock();
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001157 mutex_unlock(&td->lock);
Tomi Valkeinen8d8aa612010-01-08 16:30:33 +02001158 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001159}
1160
1161static bool taal_get_mirror(struct omap_dss_device *dssdev)
1162{
1163 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001164 int r;
1165
1166 mutex_lock(&td->lock);
1167 r = td->mirror;
1168 mutex_unlock(&td->lock);
1169
1170 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001171}
1172
1173static int taal_run_test(struct omap_dss_device *dssdev, int test_num)
1174{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001175 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001176 u8 id1, id2, id3;
1177 int r;
1178
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001179 mutex_lock(&td->lock);
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001180
1181 if (!td->enabled) {
1182 r = -ENODEV;
1183 goto err1;
1184 }
1185
Tomi Valkeinen1a75ef42010-01-08 16:21:28 +02001186 dsi_bus_lock();
1187
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001188 r = taal_dcs_read_1(DCS_GET_ID1, &id1);
1189 if (r)
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001190 goto err2;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001191 r = taal_dcs_read_1(DCS_GET_ID2, &id2);
1192 if (r)
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001193 goto err2;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001194 r = taal_dcs_read_1(DCS_GET_ID3, &id3);
1195 if (r)
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001196 goto err2;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001197
Tomi Valkeinen1a75ef42010-01-08 16:21:28 +02001198 dsi_bus_unlock();
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001199 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001200 return 0;
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001201err2:
Tomi Valkeinen1a75ef42010-01-08 16:21:28 +02001202 dsi_bus_unlock();
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001203err1:
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001204 mutex_unlock(&td->lock);
Tomi Valkeinen1a75ef42010-01-08 16:21:28 +02001205 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001206}
1207
1208static int taal_memory_read(struct omap_dss_device *dssdev,
1209 void *buf, size_t size,
1210 u16 x, u16 y, u16 w, u16 h)
1211{
1212 int r;
1213 int first = 1;
1214 int plen;
1215 unsigned buf_used = 0;
Tomi Valkeinenc75d9462010-01-08 16:56:44 +02001216 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1217
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001218 if (size < w * h * 3)
1219 return -ENOMEM;
1220
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001221 mutex_lock(&td->lock);
1222
1223 if (!td->enabled) {
1224 r = -ENODEV;
1225 goto err1;
1226 }
1227
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001228 size = min(w * h * 3,
1229 dssdev->panel.timings.x_res *
1230 dssdev->panel.timings.y_res * 3);
1231
Tomi Valkeinenc75d9462010-01-08 16:56:44 +02001232 dsi_bus_lock();
1233
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001234 /* plen 1 or 2 goes into short packet. until checksum error is fixed,
1235 * use short packets. plen 32 works, but bigger packets seem to cause
1236 * an error. */
1237 if (size % 2)
1238 plen = 1;
1239 else
1240 plen = 2;
1241
Tomi Valkeinenc75d9462010-01-08 16:56:44 +02001242 taal_set_update_window(x, y, w, h);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001243
1244 r = dsi_vc_set_max_rx_packet_size(TCH, plen);
1245 if (r)
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001246 goto err2;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001247
1248 while (buf_used < size) {
1249 u8 dcs_cmd = first ? 0x2e : 0x3e;
1250 first = 0;
1251
1252 r = dsi_vc_dcs_read(TCH, dcs_cmd,
1253 buf + buf_used, size - buf_used);
1254
1255 if (r < 0) {
1256 dev_err(&dssdev->dev, "read error\n");
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001257 goto err3;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001258 }
1259
1260 buf_used += r;
1261
1262 if (r < plen) {
1263 dev_err(&dssdev->dev, "short read\n");
1264 break;
1265 }
1266
1267 if (signal_pending(current)) {
1268 dev_err(&dssdev->dev, "signal pending, "
1269 "aborting memory read\n");
1270 r = -ERESTARTSYS;
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001271 goto err3;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001272 }
1273 }
1274
1275 r = buf_used;
1276
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001277err3:
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001278 dsi_vc_set_max_rx_packet_size(TCH, 1);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001279err2:
Tomi Valkeinenc75d9462010-01-08 16:56:44 +02001280 dsi_bus_unlock();
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001281err1:
1282 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001283 return r;
1284}
1285
1286static void taal_esd_work(struct work_struct *work)
1287{
1288 struct taal_data *td = container_of(work, struct taal_data,
1289 esd_work.work);
1290 struct omap_dss_device *dssdev = td->dssdev;
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001291 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001292 u8 state1, state2;
1293 int r;
1294
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001295 mutex_lock(&td->lock);
1296
1297 if (!td->enabled) {
1298 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001299 return;
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001300 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001301
1302 dsi_bus_lock();
1303
1304 r = taal_dcs_read_1(DCS_RDDSDR, &state1);
1305 if (r) {
1306 dev_err(&dssdev->dev, "failed to read Taal status\n");
1307 goto err;
1308 }
1309
1310 /* Run self diagnostics */
1311 r = taal_sleep_out(td);
1312 if (r) {
1313 dev_err(&dssdev->dev, "failed to run Taal self-diagnostics\n");
1314 goto err;
1315 }
1316
1317 r = taal_dcs_read_1(DCS_RDDSDR, &state2);
1318 if (r) {
1319 dev_err(&dssdev->dev, "failed to read Taal status\n");
1320 goto err;
1321 }
1322
1323 /* Each sleep out command will trigger a self diagnostic and flip
1324 * Bit6 if the test passes.
1325 */
1326 if (!((state1 ^ state2) & (1 << 6))) {
1327 dev_err(&dssdev->dev, "LCD self diagnostics failed\n");
1328 goto err;
1329 }
1330 /* Self-diagnostics result is also shown on TE GPIO line. We need
1331 * to re-enable TE after self diagnostics */
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001332 if (td->te_enabled && panel_data->use_ext_te) {
Tomi Valkeinen1189b7f2010-03-01 13:52:10 +02001333 r = taal_dcs_write_1(DCS_TEAR_ON, 0);
1334 if (r)
1335 goto err;
1336 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001337
1338 dsi_bus_unlock();
1339
1340 queue_delayed_work(td->esd_wq, &td->esd_work, TAAL_ESD_CHECK_PERIOD);
1341
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001342 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001343 return;
1344err:
1345 dev_err(&dssdev->dev, "performing LCD reset\n");
1346
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001347 taal_power_off(dssdev);
Jani Nikula006db7b2010-04-09 12:25:59 +03001348 taal_hw_reset(dssdev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001349 taal_power_on(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001350
1351 dsi_bus_unlock();
1352
1353 queue_delayed_work(td->esd_wq, &td->esd_work, TAAL_ESD_CHECK_PERIOD);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001354
1355 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001356}
1357
Tomi Valkeinen446f7bf2010-01-11 16:12:31 +02001358static int taal_set_update_mode(struct omap_dss_device *dssdev,
1359 enum omap_dss_update_mode mode)
1360{
1361 if (mode != OMAP_DSS_UPDATE_MANUAL)
1362 return -EINVAL;
1363 return 0;
1364}
1365
1366static enum omap_dss_update_mode taal_get_update_mode(
1367 struct omap_dss_device *dssdev)
1368{
1369 return OMAP_DSS_UPDATE_MANUAL;
1370}
1371
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001372static struct omap_dss_driver taal_driver = {
1373 .probe = taal_probe,
1374 .remove = taal_remove,
1375
1376 .enable = taal_enable,
1377 .disable = taal_disable,
1378 .suspend = taal_suspend,
1379 .resume = taal_resume,
1380
Tomi Valkeinen446f7bf2010-01-11 16:12:31 +02001381 .set_update_mode = taal_set_update_mode,
1382 .get_update_mode = taal_get_update_mode,
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001383
1384 .update = taal_update,
1385 .sync = taal_sync,
1386
Tomi Valkeinen96adcec2010-01-11 13:54:33 +02001387 .get_resolution = taal_get_resolution,
Tomi Valkeinena2699502010-01-11 14:33:40 +02001388 .get_recommended_bpp = omapdss_default_get_recommended_bpp,
1389
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001390 .enable_te = taal_enable_te,
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001391 .get_te = taal_get_te,
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001392
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001393 .set_rotate = taal_rotate,
1394 .get_rotate = taal_get_rotate,
1395 .set_mirror = taal_mirror,
1396 .get_mirror = taal_get_mirror,
1397 .run_test = taal_run_test,
1398 .memory_read = taal_memory_read,
1399
Tomi Valkeinen69b20482010-01-20 12:11:25 +02001400 .get_timings = taal_get_timings,
1401
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001402 .driver = {
1403 .name = "taal",
1404 .owner = THIS_MODULE,
1405 },
1406};
1407
1408static int __init taal_init(void)
1409{
1410 omap_dss_register_driver(&taal_driver);
1411
1412 return 0;
1413}
1414
1415static void __exit taal_exit(void)
1416{
1417 omap_dss_unregister_driver(&taal_driver);
1418}
1419
1420module_init(taal_init);
1421module_exit(taal_exit);
1422
1423MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@nokia.com>");
1424MODULE_DESCRIPTION("Taal Driver");
1425MODULE_LICENSE("GPL");