blob: df720c31288f182aa14fe55b46ac1d738a569b74 [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
65/* #define TAAL_USE_ESD_CHECK */
66#define TAAL_ESD_CHECK_PERIOD msecs_to_jiffies(5000)
67
Jani Nikula7ae2fb12010-04-13 10:57:52 +030068static irqreturn_t taal_te_isr(int irq, void *data);
69static void taal_te_timeout_work_callback(struct work_struct *work);
Tomi Valkeinen21df20f2010-03-02 12:13:55 +020070static int _taal_enable_te(struct omap_dss_device *dssdev, bool enable);
71
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +020072struct taal_data {
Tomi Valkeinena3201a02010-03-03 14:31:45 +020073 struct mutex lock;
74
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +020075 struct backlight_device *bldev;
76
77 unsigned long hw_guard_end; /* next value of jiffies when we can
78 * issue the next sleep in/out command
79 */
80 unsigned long hw_guard_wait; /* max guard time in jiffies */
81
82 struct omap_dss_device *dssdev;
83
84 bool enabled;
85 u8 rotate;
86 bool mirror;
87
88 bool te_enabled;
Jani Nikula7ae2fb12010-04-13 10:57:52 +030089
90 atomic_t do_update;
91 struct {
92 u16 x;
93 u16 y;
94 u16 w;
95 u16 h;
96 } update_region;
97 struct delayed_work te_timeout_work;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +020098
99 bool use_dsi_bl;
100
101 bool cabc_broken;
102 unsigned cabc_mode;
103
104 bool intro_printed;
105
106 struct workqueue_struct *esd_wq;
107 struct delayed_work esd_work;
108};
109
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300110static inline struct nokia_dsi_panel_data
111*get_panel_data(const struct omap_dss_device *dssdev)
112{
113 return (struct nokia_dsi_panel_data *) dssdev->data;
114}
115
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200116static void taal_esd_work(struct work_struct *work);
117
118static void hw_guard_start(struct taal_data *td, int guard_msec)
119{
120 td->hw_guard_wait = msecs_to_jiffies(guard_msec);
121 td->hw_guard_end = jiffies + td->hw_guard_wait;
122}
123
124static void hw_guard_wait(struct taal_data *td)
125{
126 unsigned long wait = td->hw_guard_end - jiffies;
127
128 if ((long)wait > 0 && wait <= td->hw_guard_wait) {
129 set_current_state(TASK_UNINTERRUPTIBLE);
130 schedule_timeout(wait);
131 }
132}
133
134static int taal_dcs_read_1(u8 dcs_cmd, u8 *data)
135{
136 int r;
137 u8 buf[1];
138
139 r = dsi_vc_dcs_read(TCH, dcs_cmd, buf, 1);
140
141 if (r < 0)
142 return r;
143
144 *data = buf[0];
145
146 return 0;
147}
148
149static int taal_dcs_write_0(u8 dcs_cmd)
150{
151 return dsi_vc_dcs_write(TCH, &dcs_cmd, 1);
152}
153
154static int taal_dcs_write_1(u8 dcs_cmd, u8 param)
155{
156 u8 buf[2];
157 buf[0] = dcs_cmd;
158 buf[1] = param;
159 return dsi_vc_dcs_write(TCH, buf, 2);
160}
161
162static int taal_sleep_in(struct taal_data *td)
163
164{
165 u8 cmd;
166 int r;
167
168 hw_guard_wait(td);
169
170 cmd = DCS_SLEEP_IN;
171 r = dsi_vc_dcs_write_nosync(TCH, &cmd, 1);
172 if (r)
173 return r;
174
175 hw_guard_start(td, 120);
176
177 msleep(5);
178
179 return 0;
180}
181
182static int taal_sleep_out(struct taal_data *td)
183{
184 int r;
185
186 hw_guard_wait(td);
187
188 r = taal_dcs_write_0(DCS_SLEEP_OUT);
189 if (r)
190 return r;
191
192 hw_guard_start(td, 120);
193
194 msleep(5);
195
196 return 0;
197}
198
199static int taal_get_id(u8 *id1, u8 *id2, u8 *id3)
200{
201 int r;
202
203 r = taal_dcs_read_1(DCS_GET_ID1, id1);
204 if (r)
205 return r;
206 r = taal_dcs_read_1(DCS_GET_ID2, id2);
207 if (r)
208 return r;
209 r = taal_dcs_read_1(DCS_GET_ID3, id3);
210 if (r)
211 return r;
212
213 return 0;
214}
215
216static int taal_set_addr_mode(u8 rotate, bool mirror)
217{
218 int r;
219 u8 mode;
220 int b5, b6, b7;
221
222 r = taal_dcs_read_1(DCS_READ_MADCTL, &mode);
223 if (r)
224 return r;
225
226 switch (rotate) {
227 default:
228 case 0:
229 b7 = 0;
230 b6 = 0;
231 b5 = 0;
232 break;
233 case 1:
234 b7 = 0;
235 b6 = 1;
236 b5 = 1;
237 break;
238 case 2:
239 b7 = 1;
240 b6 = 1;
241 b5 = 0;
242 break;
243 case 3:
244 b7 = 1;
245 b6 = 0;
246 b5 = 1;
247 break;
248 }
249
250 if (mirror)
251 b6 = !b6;
252
253 mode &= ~((1<<7) | (1<<6) | (1<<5));
254 mode |= (b7 << 7) | (b6 << 6) | (b5 << 5);
255
256 return taal_dcs_write_1(DCS_MEM_ACC_CTRL, mode);
257}
258
259static int taal_set_update_window(u16 x, u16 y, u16 w, u16 h)
260{
261 int r;
262 u16 x1 = x;
263 u16 x2 = x + w - 1;
264 u16 y1 = y;
265 u16 y2 = y + h - 1;
266
267 u8 buf[5];
268 buf[0] = DCS_COLUMN_ADDR;
269 buf[1] = (x1 >> 8) & 0xff;
270 buf[2] = (x1 >> 0) & 0xff;
271 buf[3] = (x2 >> 8) & 0xff;
272 buf[4] = (x2 >> 0) & 0xff;
273
274 r = dsi_vc_dcs_write_nosync(TCH, buf, sizeof(buf));
275 if (r)
276 return r;
277
278 buf[0] = DCS_PAGE_ADDR;
279 buf[1] = (y1 >> 8) & 0xff;
280 buf[2] = (y1 >> 0) & 0xff;
281 buf[3] = (y2 >> 8) & 0xff;
282 buf[4] = (y2 >> 0) & 0xff;
283
284 r = dsi_vc_dcs_write_nosync(TCH, buf, sizeof(buf));
285 if (r)
286 return r;
287
288 dsi_vc_send_bta_sync(TCH);
289
290 return r;
291}
292
293static int taal_bl_update_status(struct backlight_device *dev)
294{
295 struct omap_dss_device *dssdev = dev_get_drvdata(&dev->dev);
296 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300297 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200298 int r;
299 int level;
300
301 if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
302 dev->props.power == FB_BLANK_UNBLANK)
303 level = dev->props.brightness;
304 else
305 level = 0;
306
307 dev_dbg(&dssdev->dev, "update brightness to %d\n", level);
308
Tomi Valkeinen1cbc8702010-04-15 16:41:07 +0300309 mutex_lock(&td->lock);
310
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200311 if (td->use_dsi_bl) {
312 if (td->enabled) {
313 dsi_bus_lock();
314 r = taal_dcs_write_1(DCS_BRIGHTNESS, level);
315 dsi_bus_unlock();
Tomi Valkeinen1cbc8702010-04-15 16:41:07 +0300316 } else {
317 r = 0;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200318 }
319 } else {
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300320 if (!panel_data->set_backlight)
Tomi Valkeinen1cbc8702010-04-15 16:41:07 +0300321 r = -EINVAL;
322 else
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300323 r = panel_data->set_backlight(dssdev, level);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200324 }
325
Tomi Valkeinen1cbc8702010-04-15 16:41:07 +0300326 mutex_unlock(&td->lock);
327
328 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200329}
330
331static int taal_bl_get_intensity(struct backlight_device *dev)
332{
333 if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
334 dev->props.power == FB_BLANK_UNBLANK)
335 return dev->props.brightness;
336
337 return 0;
338}
339
340static struct backlight_ops taal_bl_ops = {
341 .get_brightness = taal_bl_get_intensity,
342 .update_status = taal_bl_update_status,
343};
344
345static void taal_get_timings(struct omap_dss_device *dssdev,
346 struct omap_video_timings *timings)
347{
348 *timings = dssdev->panel.timings;
349}
350
351static void taal_get_resolution(struct omap_dss_device *dssdev,
352 u16 *xres, u16 *yres)
353{
354 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
355
356 if (td->rotate == 0 || td->rotate == 2) {
357 *xres = dssdev->panel.timings.x_res;
358 *yres = dssdev->panel.timings.y_res;
359 } else {
360 *yres = dssdev->panel.timings.x_res;
361 *xres = dssdev->panel.timings.y_res;
362 }
363}
364
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200365static ssize_t taal_num_errors_show(struct device *dev,
366 struct device_attribute *attr, char *buf)
367{
368 struct omap_dss_device *dssdev = to_dss_device(dev);
369 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
370 u8 errors;
371 int r;
372
Jani Nikula6b316712010-04-28 11:15:18 +0300373 mutex_lock(&td->lock);
374
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200375 if (td->enabled) {
376 dsi_bus_lock();
377 r = taal_dcs_read_1(DCS_READ_NUM_ERRORS, &errors);
378 dsi_bus_unlock();
379 } else {
380 r = -ENODEV;
381 }
382
Jani Nikula6b316712010-04-28 11:15:18 +0300383 mutex_unlock(&td->lock);
384
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200385 if (r)
386 return r;
387
388 return snprintf(buf, PAGE_SIZE, "%d\n", errors);
389}
390
391static ssize_t taal_hw_revision_show(struct device *dev,
392 struct device_attribute *attr, char *buf)
393{
394 struct omap_dss_device *dssdev = to_dss_device(dev);
395 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
396 u8 id1, id2, id3;
397 int r;
398
Jani Nikula6b316712010-04-28 11:15:18 +0300399 mutex_lock(&td->lock);
400
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200401 if (td->enabled) {
402 dsi_bus_lock();
403 r = taal_get_id(&id1, &id2, &id3);
404 dsi_bus_unlock();
405 } else {
406 r = -ENODEV;
407 }
408
Jani Nikula6b316712010-04-28 11:15:18 +0300409 mutex_unlock(&td->lock);
410
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200411 if (r)
412 return r;
413
414 return snprintf(buf, PAGE_SIZE, "%02x.%02x.%02x\n", id1, id2, id3);
415}
416
417static const char *cabc_modes[] = {
418 "off", /* used also always when CABC is not supported */
419 "ui",
420 "still-image",
421 "moving-image",
422};
423
424static ssize_t show_cabc_mode(struct device *dev,
425 struct device_attribute *attr,
426 char *buf)
427{
428 struct omap_dss_device *dssdev = to_dss_device(dev);
429 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
430 const char *mode_str;
431 int mode;
432 int len;
433
434 mode = td->cabc_mode;
435
436 mode_str = "unknown";
437 if (mode >= 0 && mode < ARRAY_SIZE(cabc_modes))
438 mode_str = cabc_modes[mode];
439 len = snprintf(buf, PAGE_SIZE, "%s\n", mode_str);
440
441 return len < PAGE_SIZE - 1 ? len : PAGE_SIZE - 1;
442}
443
444static ssize_t store_cabc_mode(struct device *dev,
445 struct device_attribute *attr,
446 const char *buf, size_t count)
447{
448 struct omap_dss_device *dssdev = to_dss_device(dev);
449 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
450 int i;
451
452 for (i = 0; i < ARRAY_SIZE(cabc_modes); i++) {
453 if (sysfs_streq(cabc_modes[i], buf))
454 break;
455 }
456
457 if (i == ARRAY_SIZE(cabc_modes))
458 return -EINVAL;
459
Jani Nikula6b316712010-04-28 11:15:18 +0300460 mutex_lock(&td->lock);
461
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200462 if (td->enabled) {
463 dsi_bus_lock();
464 if (!td->cabc_broken)
465 taal_dcs_write_1(DCS_WRITE_CABC, i);
466 dsi_bus_unlock();
467 }
468
469 td->cabc_mode = i;
470
Jani Nikula6b316712010-04-28 11:15:18 +0300471 mutex_unlock(&td->lock);
472
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200473 return count;
474}
475
476static ssize_t show_cabc_available_modes(struct device *dev,
477 struct device_attribute *attr,
478 char *buf)
479{
480 int len;
481 int i;
482
483 for (i = 0, len = 0;
484 len < PAGE_SIZE && i < ARRAY_SIZE(cabc_modes); i++)
485 len += snprintf(&buf[len], PAGE_SIZE - len, "%s%s%s",
486 i ? " " : "", cabc_modes[i],
487 i == ARRAY_SIZE(cabc_modes) - 1 ? "\n" : "");
488
489 return len < PAGE_SIZE ? len : PAGE_SIZE - 1;
490}
491
492static DEVICE_ATTR(num_dsi_errors, S_IRUGO, taal_num_errors_show, NULL);
493static DEVICE_ATTR(hw_revision, S_IRUGO, taal_hw_revision_show, NULL);
494static DEVICE_ATTR(cabc_mode, S_IRUGO | S_IWUSR,
495 show_cabc_mode, store_cabc_mode);
496static DEVICE_ATTR(cabc_available_modes, S_IRUGO,
497 show_cabc_available_modes, NULL);
498
499static struct attribute *taal_attrs[] = {
500 &dev_attr_num_dsi_errors.attr,
501 &dev_attr_hw_revision.attr,
502 &dev_attr_cabc_mode.attr,
503 &dev_attr_cabc_available_modes.attr,
504 NULL,
505};
506
507static struct attribute_group taal_attr_group = {
508 .attrs = taal_attrs,
509};
510
Jani Nikula006db7b2010-04-09 12:25:59 +0300511static void taal_hw_reset(struct omap_dss_device *dssdev)
512{
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300513 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
514
515 if (panel_data->reset_gpio == -1)
Jani Nikula006db7b2010-04-09 12:25:59 +0300516 return;
517
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300518 gpio_set_value(panel_data->reset_gpio, 1);
Jani Nikula006db7b2010-04-09 12:25:59 +0300519 udelay(10);
520 /* reset the panel */
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300521 gpio_set_value(panel_data->reset_gpio, 0);
Jani Nikula006db7b2010-04-09 12:25:59 +0300522 /* assert reset for at least 10us */
523 udelay(10);
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300524 gpio_set_value(panel_data->reset_gpio, 1);
Jani Nikula006db7b2010-04-09 12:25:59 +0300525 /* wait 5ms after releasing reset */
526 msleep(5);
527}
528
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200529static int taal_probe(struct omap_dss_device *dssdev)
530{
Matthew Garretta19a6ee2010-02-17 16:39:44 -0500531 struct backlight_properties props;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200532 struct taal_data *td;
533 struct backlight_device *bldev;
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300534 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200535 int r;
536
537 const struct omap_video_timings taal_panel_timings = {
538 .x_res = 864,
539 .y_res = 480,
540 };
541
542 dev_dbg(&dssdev->dev, "probe\n");
543
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300544 if (!panel_data || !panel_data->name) {
545 r = -EINVAL;
546 goto err;
547 }
548
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200549 dssdev->panel.config = OMAP_DSS_LCD_TFT;
550 dssdev->panel.timings = taal_panel_timings;
551 dssdev->ctrl.pixel_size = 24;
552
553 td = kzalloc(sizeof(*td), GFP_KERNEL);
554 if (!td) {
555 r = -ENOMEM;
Jani Nikulad2b65782010-04-15 12:24:36 +0300556 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200557 }
558 td->dssdev = dssdev;
559
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200560 mutex_init(&td->lock);
561
Jani Nikula7ae2fb12010-04-13 10:57:52 +0300562 atomic_set(&td->do_update, 0);
563
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200564 td->esd_wq = create_singlethread_workqueue("taal_esd");
565 if (td->esd_wq == NULL) {
566 dev_err(&dssdev->dev, "can't create ESD workqueue\n");
567 r = -ENOMEM;
Jani Nikulad2b65782010-04-15 12:24:36 +0300568 goto err_wq;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200569 }
570 INIT_DELAYED_WORK_DEFERRABLE(&td->esd_work, taal_esd_work);
571
572 dev_set_drvdata(&dssdev->dev, td);
573
Jani Nikula006db7b2010-04-09 12:25:59 +0300574 taal_hw_reset(dssdev);
575
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200576 /* if no platform set_backlight() defined, presume DSI backlight
577 * control */
Matthew Garretta19a6ee2010-02-17 16:39:44 -0500578 memset(&props, 0, sizeof(struct backlight_properties));
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300579 if (!panel_data->set_backlight)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200580 td->use_dsi_bl = true;
581
Matthew Garretta19a6ee2010-02-17 16:39:44 -0500582 if (td->use_dsi_bl)
583 props.max_brightness = 255;
584 else
585 props.max_brightness = 127;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200586 bldev = backlight_device_register("taal", &dssdev->dev, dssdev,
Matthew Garretta19a6ee2010-02-17 16:39:44 -0500587 &taal_bl_ops, &props);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200588 if (IS_ERR(bldev)) {
589 r = PTR_ERR(bldev);
Jani Nikulad2b65782010-04-15 12:24:36 +0300590 goto err_bl;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200591 }
592
593 td->bldev = bldev;
594
595 bldev->props.fb_blank = FB_BLANK_UNBLANK;
596 bldev->props.power = FB_BLANK_UNBLANK;
Matthew Garretta19a6ee2010-02-17 16:39:44 -0500597 if (td->use_dsi_bl)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200598 bldev->props.brightness = 255;
Matthew Garretta19a6ee2010-02-17 16:39:44 -0500599 else
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200600 bldev->props.brightness = 127;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200601
602 taal_bl_update_status(bldev);
603
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300604 if (panel_data->use_ext_te) {
605 int gpio = panel_data->ext_te_gpio;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200606
607 r = gpio_request(gpio, "taal irq");
608 if (r) {
609 dev_err(&dssdev->dev, "GPIO request failed\n");
Jani Nikulad2b65782010-04-15 12:24:36 +0300610 goto err_gpio;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200611 }
612
613 gpio_direction_input(gpio);
614
615 r = request_irq(gpio_to_irq(gpio), taal_te_isr,
616 IRQF_DISABLED | IRQF_TRIGGER_RISING,
617 "taal vsync", dssdev);
618
619 if (r) {
620 dev_err(&dssdev->dev, "IRQ request failed\n");
621 gpio_free(gpio);
Jani Nikulad2b65782010-04-15 12:24:36 +0300622 goto err_irq;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200623 }
624
Jani Nikula7ae2fb12010-04-13 10:57:52 +0300625 INIT_DELAYED_WORK_DEFERRABLE(&td->te_timeout_work,
626 taal_te_timeout_work_callback);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200627
Jani Nikula7ae2fb12010-04-13 10:57:52 +0300628 dev_dbg(&dssdev->dev, "Using GPIO TE\n");
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200629 }
630
631 r = sysfs_create_group(&dssdev->dev.kobj, &taal_attr_group);
632 if (r) {
633 dev_err(&dssdev->dev, "failed to create sysfs files\n");
Jani Nikulad2b65782010-04-15 12:24:36 +0300634 goto err_sysfs;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200635 }
636
637 return 0;
Jani Nikulad2b65782010-04-15 12:24:36 +0300638err_sysfs:
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300639 if (panel_data->use_ext_te)
640 free_irq(gpio_to_irq(panel_data->ext_te_gpio), dssdev);
Jani Nikulad2b65782010-04-15 12:24:36 +0300641err_irq:
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300642 if (panel_data->use_ext_te)
643 gpio_free(panel_data->ext_te_gpio);
Jani Nikulad2b65782010-04-15 12:24:36 +0300644err_gpio:
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200645 backlight_device_unregister(bldev);
Jani Nikulad2b65782010-04-15 12:24:36 +0300646err_bl:
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200647 destroy_workqueue(td->esd_wq);
Jani Nikulad2b65782010-04-15 12:24:36 +0300648err_wq:
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200649 kfree(td);
Jani Nikulad2b65782010-04-15 12:24:36 +0300650err:
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200651 return r;
652}
653
654static void taal_remove(struct omap_dss_device *dssdev)
655{
656 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300657 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200658 struct backlight_device *bldev;
659
660 dev_dbg(&dssdev->dev, "remove\n");
661
662 sysfs_remove_group(&dssdev->dev.kobj, &taal_attr_group);
663
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300664 if (panel_data->use_ext_te) {
665 int gpio = panel_data->ext_te_gpio;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200666 free_irq(gpio_to_irq(gpio), dssdev);
667 gpio_free(gpio);
668 }
669
670 bldev = td->bldev;
671 bldev->props.power = FB_BLANK_POWERDOWN;
672 taal_bl_update_status(bldev);
673 backlight_device_unregister(bldev);
674
Jani Nikula4571a022010-04-12 10:23:46 +0300675 cancel_delayed_work(&td->esd_work);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200676 destroy_workqueue(td->esd_wq);
677
Jani Nikula006db7b2010-04-09 12:25:59 +0300678 /* reset, to be sure that the panel is in a valid state */
679 taal_hw_reset(dssdev);
680
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200681 kfree(td);
682}
683
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200684static int taal_power_on(struct omap_dss_device *dssdev)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200685{
686 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
687 u8 id1, id2, id3;
688 int r;
689
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200690 /* it seems we have to wait a bit until taal is ready */
691 msleep(5);
692
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200693 r = omapdss_dsi_display_enable(dssdev);
694 if (r) {
695 dev_err(&dssdev->dev, "failed to enable DSI\n");
696 goto err0;
697 }
698
Jani Nikula006db7b2010-04-09 12:25:59 +0300699 taal_hw_reset(dssdev);
700
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200701 omapdss_dsi_vc_enable_hs(TCH, false);
702
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200703 r = taal_sleep_out(td);
704 if (r)
705 goto err;
706
707 r = taal_get_id(&id1, &id2, &id3);
708 if (r)
709 goto err;
710
711 /* on early revisions CABC is broken */
712 if (id2 == 0x00 || id2 == 0xff || id2 == 0x81)
713 td->cabc_broken = true;
714
Jani Nikulaf2a8b752010-04-09 14:15:12 +0300715 r = taal_dcs_write_1(DCS_BRIGHTNESS, 0xff);
716 if (r)
717 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200718
Jani Nikulaf2a8b752010-04-09 14:15:12 +0300719 r = taal_dcs_write_1(DCS_CTRL_DISPLAY,
720 (1<<2) | (1<<5)); /* BL | BCTRL */
721 if (r)
722 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200723
Jani Nikulaf2a8b752010-04-09 14:15:12 +0300724 r = taal_dcs_write_1(DCS_PIXEL_FORMAT, 0x7); /* 24bit/pixel */
725 if (r)
726 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200727
Jani Nikulaf2a8b752010-04-09 14:15:12 +0300728 r = taal_set_addr_mode(td->rotate, td->mirror);
729 if (r)
730 goto err;
731
732 if (!td->cabc_broken) {
733 r = taal_dcs_write_1(DCS_WRITE_CABC, td->cabc_mode);
734 if (r)
735 goto err;
736 }
737
738 r = taal_dcs_write_0(DCS_DISPLAY_ON);
739 if (r)
740 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200741
Tomi Valkeinen21df20f2010-03-02 12:13:55 +0200742 r = _taal_enable_te(dssdev, td->te_enabled);
743 if (r)
744 goto err;
745
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200746 td->enabled = 1;
747
748 if (!td->intro_printed) {
749 dev_info(&dssdev->dev, "revision %02x.%02x.%02x\n",
750 id1, id2, id3);
751 if (td->cabc_broken)
752 dev_info(&dssdev->dev,
753 "old Taal version, CABC disabled\n");
754 td->intro_printed = true;
755 }
756
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200757 omapdss_dsi_vc_enable_hs(TCH, true);
758
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200759 return 0;
760err:
Jani Nikula006db7b2010-04-09 12:25:59 +0300761 dev_err(&dssdev->dev, "error while enabling panel, issuing HW reset\n");
762
763 taal_hw_reset(dssdev);
764
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200765 omapdss_dsi_display_disable(dssdev);
766err0:
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200767 return r;
768}
769
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200770static void taal_power_off(struct omap_dss_device *dssdev)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200771{
772 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Jani Nikula006db7b2010-04-09 12:25:59 +0300773 int r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200774
Jani Nikula006db7b2010-04-09 12:25:59 +0300775 r = taal_dcs_write_0(DCS_DISPLAY_OFF);
776 if (!r) {
777 r = taal_sleep_in(td);
778 /* wait a bit so that the message goes through */
779 msleep(10);
780 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200781
Jani Nikula006db7b2010-04-09 12:25:59 +0300782 if (r) {
783 dev_err(&dssdev->dev,
784 "error disabling panel, issuing HW reset\n");
785 taal_hw_reset(dssdev);
786 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200787
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200788 omapdss_dsi_display_disable(dssdev);
789
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200790 td->enabled = 0;
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200791}
792
793static int taal_enable(struct omap_dss_device *dssdev)
794{
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200795 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
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 Nikula4571a022010-04-12 10:23:46 +0300816#ifdef TAAL_USE_ESD_CHECK
817 queue_delayed_work(td->esd_wq, &td->esd_work, TAAL_ESD_CHECK_PERIOD);
818#endif
819
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);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200888 int r;
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200889
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200890 dev_dbg(&dssdev->dev, "resume\n");
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200891
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200892 mutex_lock(&td->lock);
893
894 if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) {
895 r = -EINVAL;
896 goto err;
897 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200898
Jani Nikula2c2fc152010-04-12 10:06:14 +0300899 dsi_bus_lock();
900
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200901 r = taal_power_on(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +0300902
903 dsi_bus_unlock();
904
Jani Nikula4571a022010-04-12 10:23:46 +0300905 if (r) {
Jani Nikulafed44b72010-04-12 10:25:21 +0300906 dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
Jani Nikula4571a022010-04-12 10:23:46 +0300907 } else {
Jani Nikulafed44b72010-04-12 10:25:21 +0300908 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
Jani Nikula4571a022010-04-12 10:23:46 +0300909#ifdef TAAL_USE_ESD_CHECK
910 queue_delayed_work(td->esd_wq, &td->esd_work,
911 TAAL_ESD_CHECK_PERIOD);
912#endif
913 }
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");