blob: 4cb0449d50980919b4bb68566b5e28e5e720f314 [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 Valkeinenc8cd4542010-06-09 15:24:46 +030033#include <linux/regulator/consumer.h>
Tomi Valkeinena3201a02010-03-03 14:31:45 +020034#include <linux/mutex.h>
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +020035
Tomi Valkeinena0b38cc2011-05-11 14:05:07 +030036#include <video/omapdss.h>
Tomi Valkeinen4e9f99d2011-05-11 14:10:07 +030037#include <video/omap-panel-nokia-dsi.h>
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +020038
39/* DSI Virtual channel. Hardcoded for now. */
40#define TCH 0
41
42#define DCS_READ_NUM_ERRORS 0x05
43#define DCS_READ_POWER_MODE 0x0a
44#define DCS_READ_MADCTL 0x0b
45#define DCS_READ_PIXEL_FORMAT 0x0c
46#define DCS_RDDSDR 0x0f
47#define DCS_SLEEP_IN 0x10
48#define DCS_SLEEP_OUT 0x11
49#define DCS_DISPLAY_OFF 0x28
50#define DCS_DISPLAY_ON 0x29
51#define DCS_COLUMN_ADDR 0x2a
52#define DCS_PAGE_ADDR 0x2b
53#define DCS_MEMORY_WRITE 0x2c
54#define DCS_TEAR_OFF 0x34
55#define DCS_TEAR_ON 0x35
56#define DCS_MEM_ACC_CTRL 0x36
57#define DCS_PIXEL_FORMAT 0x3a
58#define DCS_BRIGHTNESS 0x51
59#define DCS_CTRL_DISPLAY 0x53
60#define DCS_WRITE_CABC 0x55
61#define DCS_READ_CABC 0x56
62#define DCS_GET_ID1 0xda
63#define DCS_GET_ID2 0xdb
64#define DCS_GET_ID3 0xdc
65
Jani Nikula7ae2fb12010-04-13 10:57:52 +030066static irqreturn_t taal_te_isr(int irq, void *data);
67static void taal_te_timeout_work_callback(struct work_struct *work);
Tomi Valkeinen21df20f2010-03-02 12:13:55 +020068static int _taal_enable_te(struct omap_dss_device *dssdev, bool enable);
69
Tomi Valkeinen1abf7812011-03-24 14:53:27 +020070static int taal_panel_reset(struct omap_dss_device *dssdev);
71
Tomi Valkeinenc8cd4542010-06-09 15:24:46 +030072struct panel_regulator {
73 struct regulator *regulator;
74 const char *name;
75 int min_uV;
76 int max_uV;
77};
78
79static void free_regulators(struct panel_regulator *regulators, int n)
80{
81 int i;
82
83 for (i = 0; i < n; i++) {
84 /* disable/put in reverse order */
85 regulator_disable(regulators[n - i - 1].regulator);
86 regulator_put(regulators[n - i - 1].regulator);
87 }
88}
89
90static int init_regulators(struct omap_dss_device *dssdev,
91 struct panel_regulator *regulators, int n)
92{
93 int r, i, v;
94
95 for (i = 0; i < n; i++) {
96 struct regulator *reg;
97
98 reg = regulator_get(&dssdev->dev, regulators[i].name);
99 if (IS_ERR(reg)) {
100 dev_err(&dssdev->dev, "failed to get regulator %s\n",
101 regulators[i].name);
102 r = PTR_ERR(reg);
103 goto err;
104 }
105
106 /* FIXME: better handling of fixed vs. variable regulators */
107 v = regulator_get_voltage(reg);
108 if (v < regulators[i].min_uV || v > regulators[i].max_uV) {
109 r = regulator_set_voltage(reg, regulators[i].min_uV,
110 regulators[i].max_uV);
111 if (r) {
112 dev_err(&dssdev->dev,
113 "failed to set regulator %s voltage\n",
114 regulators[i].name);
115 regulator_put(reg);
116 goto err;
117 }
118 }
119
120 r = regulator_enable(reg);
121 if (r) {
122 dev_err(&dssdev->dev, "failed to enable regulator %s\n",
123 regulators[i].name);
124 regulator_put(reg);
125 goto err;
126 }
127
128 regulators[i].regulator = reg;
129 }
130
131 return 0;
132
133err:
134 free_regulators(regulators, i);
135
136 return r;
137}
138
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300139/**
140 * struct panel_config - panel configuration
141 * @name: panel name
142 * @type: panel type
143 * @timings: panel resolution
144 * @sleep: various panel specific delays, passed to msleep() if non-zero
145 * @reset_sequence: reset sequence timings, passed to udelay() if non-zero
Tomi Valkeinenc8cd4542010-06-09 15:24:46 +0300146 * @regulators: array of panel regulators
147 * @num_regulators: number of regulators in the array
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300148 */
149struct panel_config {
150 const char *name;
151 int type;
152
153 struct omap_video_timings timings;
154
155 struct {
156 unsigned int sleep_in;
157 unsigned int sleep_out;
158 unsigned int hw_reset;
159 unsigned int enable_te;
160 } sleep;
161
162 struct {
163 unsigned int high;
164 unsigned int low;
165 } reset_sequence;
Tomi Valkeinenc8cd4542010-06-09 15:24:46 +0300166
167 struct panel_regulator *regulators;
168 int num_regulators;
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300169};
170
171enum {
172 PANEL_TAAL,
173};
174
175static struct panel_config panel_configs[] = {
176 {
177 .name = "taal",
178 .type = PANEL_TAAL,
179 .timings = {
180 .x_res = 864,
181 .y_res = 480,
182 },
183 .sleep = {
184 .sleep_in = 5,
185 .sleep_out = 5,
186 .hw_reset = 5,
187 .enable_te = 100, /* possible panel bug */
188 },
189 .reset_sequence = {
190 .high = 10,
191 .low = 10,
192 },
193 },
194};
195
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200196struct taal_data {
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200197 struct mutex lock;
198
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200199 struct backlight_device *bldev;
200
201 unsigned long hw_guard_end; /* next value of jiffies when we can
202 * issue the next sleep in/out command
203 */
204 unsigned long hw_guard_wait; /* max guard time in jiffies */
205
206 struct omap_dss_device *dssdev;
207
208 bool enabled;
209 u8 rotate;
210 bool mirror;
211
212 bool te_enabled;
Jani Nikula7ae2fb12010-04-13 10:57:52 +0300213
214 atomic_t do_update;
215 struct {
216 u16 x;
217 u16 y;
218 u16 w;
219 u16 h;
220 } update_region;
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530221 int channel;
222
Jani Nikula7ae2fb12010-04-13 10:57:52 +0300223 struct delayed_work te_timeout_work;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200224
225 bool use_dsi_bl;
226
227 bool cabc_broken;
228 unsigned cabc_mode;
229
230 bool intro_printed;
231
Tomi Valkeinen883b9ac2011-03-24 15:01:30 +0200232 struct workqueue_struct *workqueue;
233
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200234 struct delayed_work esd_work;
Tomi Valkeinen33a410b2011-03-24 13:58:01 +0200235 unsigned esd_interval;
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300236
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200237 bool ulps_enabled;
238 unsigned ulps_timeout;
239 struct delayed_work ulps_work;
240
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300241 struct panel_config *panel_config;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200242};
243
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300244static inline struct nokia_dsi_panel_data
245*get_panel_data(const struct omap_dss_device *dssdev)
246{
247 return (struct nokia_dsi_panel_data *) dssdev->data;
248}
249
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200250static void taal_esd_work(struct work_struct *work);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200251static void taal_ulps_work(struct work_struct *work);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200252
253static void hw_guard_start(struct taal_data *td, int guard_msec)
254{
255 td->hw_guard_wait = msecs_to_jiffies(guard_msec);
256 td->hw_guard_end = jiffies + td->hw_guard_wait;
257}
258
259static void hw_guard_wait(struct taal_data *td)
260{
261 unsigned long wait = td->hw_guard_end - jiffies;
262
263 if ((long)wait > 0 && wait <= td->hw_guard_wait) {
264 set_current_state(TASK_UNINTERRUPTIBLE);
265 schedule_timeout(wait);
266 }
267}
268
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530269static int taal_dcs_read_1(struct taal_data *td, u8 dcs_cmd, u8 *data)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200270{
271 int r;
272 u8 buf[1];
273
Archit Taneja1ffefe72011-05-12 17:26:24 +0530274 r = dsi_vc_dcs_read(td->dssdev, td->channel, dcs_cmd, buf, 1);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200275
276 if (r < 0)
277 return r;
278
279 *data = buf[0];
280
281 return 0;
282}
283
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530284static int taal_dcs_write_0(struct taal_data *td, u8 dcs_cmd)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200285{
Archit Taneja1ffefe72011-05-12 17:26:24 +0530286 return dsi_vc_dcs_write(td->dssdev, td->channel, &dcs_cmd, 1);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200287}
288
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530289static int taal_dcs_write_1(struct taal_data *td, u8 dcs_cmd, u8 param)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200290{
291 u8 buf[2];
292 buf[0] = dcs_cmd;
293 buf[1] = param;
Archit Taneja1ffefe72011-05-12 17:26:24 +0530294 return dsi_vc_dcs_write(td->dssdev, td->channel, buf, 2);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200295}
296
297static int taal_sleep_in(struct taal_data *td)
298
299{
300 u8 cmd;
301 int r;
302
303 hw_guard_wait(td);
304
305 cmd = DCS_SLEEP_IN;
Archit Taneja1ffefe72011-05-12 17:26:24 +0530306 r = dsi_vc_dcs_write_nosync(td->dssdev, td->channel, &cmd, 1);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200307 if (r)
308 return r;
309
310 hw_guard_start(td, 120);
311
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300312 if (td->panel_config->sleep.sleep_in)
313 msleep(td->panel_config->sleep.sleep_in);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200314
315 return 0;
316}
317
318static int taal_sleep_out(struct taal_data *td)
319{
320 int r;
321
322 hw_guard_wait(td);
323
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530324 r = taal_dcs_write_0(td, DCS_SLEEP_OUT);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200325 if (r)
326 return r;
327
328 hw_guard_start(td, 120);
329
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300330 if (td->panel_config->sleep.sleep_out)
331 msleep(td->panel_config->sleep.sleep_out);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200332
333 return 0;
334}
335
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530336static int taal_get_id(struct taal_data *td, u8 *id1, u8 *id2, u8 *id3)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200337{
338 int r;
339
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530340 r = taal_dcs_read_1(td, DCS_GET_ID1, id1);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200341 if (r)
342 return r;
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530343 r = taal_dcs_read_1(td, DCS_GET_ID2, id2);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200344 if (r)
345 return r;
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530346 r = taal_dcs_read_1(td, DCS_GET_ID3, id3);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200347 if (r)
348 return r;
349
350 return 0;
351}
352
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530353static int taal_set_addr_mode(struct taal_data *td, u8 rotate, bool mirror)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200354{
355 int r;
356 u8 mode;
357 int b5, b6, b7;
358
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530359 r = taal_dcs_read_1(td, DCS_READ_MADCTL, &mode);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200360 if (r)
361 return r;
362
363 switch (rotate) {
364 default:
365 case 0:
366 b7 = 0;
367 b6 = 0;
368 b5 = 0;
369 break;
370 case 1:
371 b7 = 0;
372 b6 = 1;
373 b5 = 1;
374 break;
375 case 2:
376 b7 = 1;
377 b6 = 1;
378 b5 = 0;
379 break;
380 case 3:
381 b7 = 1;
382 b6 = 0;
383 b5 = 1;
384 break;
385 }
386
387 if (mirror)
388 b6 = !b6;
389
390 mode &= ~((1<<7) | (1<<6) | (1<<5));
391 mode |= (b7 << 7) | (b6 << 6) | (b5 << 5);
392
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530393 return taal_dcs_write_1(td, DCS_MEM_ACC_CTRL, mode);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200394}
395
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530396static int taal_set_update_window(struct taal_data *td,
397 u16 x, u16 y, u16 w, u16 h)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200398{
399 int r;
400 u16 x1 = x;
401 u16 x2 = x + w - 1;
402 u16 y1 = y;
403 u16 y2 = y + h - 1;
404
405 u8 buf[5];
406 buf[0] = DCS_COLUMN_ADDR;
407 buf[1] = (x1 >> 8) & 0xff;
408 buf[2] = (x1 >> 0) & 0xff;
409 buf[3] = (x2 >> 8) & 0xff;
410 buf[4] = (x2 >> 0) & 0xff;
411
Archit Taneja1ffefe72011-05-12 17:26:24 +0530412 r = dsi_vc_dcs_write_nosync(td->dssdev, td->channel, buf, sizeof(buf));
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200413 if (r)
414 return r;
415
416 buf[0] = DCS_PAGE_ADDR;
417 buf[1] = (y1 >> 8) & 0xff;
418 buf[2] = (y1 >> 0) & 0xff;
419 buf[3] = (y2 >> 8) & 0xff;
420 buf[4] = (y2 >> 0) & 0xff;
421
Archit Taneja1ffefe72011-05-12 17:26:24 +0530422 r = dsi_vc_dcs_write_nosync(td->dssdev, td->channel, buf, sizeof(buf));
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200423 if (r)
424 return r;
425
Archit Taneja1ffefe72011-05-12 17:26:24 +0530426 dsi_vc_send_bta_sync(td->dssdev, td->channel);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200427
428 return r;
429}
430
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +0200431static void taal_queue_esd_work(struct omap_dss_device *dssdev)
432{
433 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
434
435 if (td->esd_interval > 0)
Tomi Valkeinen883b9ac2011-03-24 15:01:30 +0200436 queue_delayed_work(td->workqueue, &td->esd_work,
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +0200437 msecs_to_jiffies(td->esd_interval));
438}
439
440static void taal_cancel_esd_work(struct omap_dss_device *dssdev)
441{
442 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
443
444 cancel_delayed_work(&td->esd_work);
445}
446
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200447static void taal_queue_ulps_work(struct omap_dss_device *dssdev)
448{
449 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
450
451 if (td->ulps_timeout > 0)
452 queue_delayed_work(td->workqueue, &td->ulps_work,
453 msecs_to_jiffies(td->ulps_timeout));
454}
455
456static void taal_cancel_ulps_work(struct omap_dss_device *dssdev)
457{
458 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
459
460 cancel_delayed_work(&td->ulps_work);
461}
462
463static int taal_enter_ulps(struct omap_dss_device *dssdev)
464{
465 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
466 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
467 int r;
468
469 if (td->ulps_enabled)
470 return 0;
471
472 taal_cancel_ulps_work(dssdev);
473
474 r = _taal_enable_te(dssdev, false);
475 if (r)
476 goto err;
477
478 disable_irq(gpio_to_irq(panel_data->ext_te_gpio));
479
480 omapdss_dsi_display_disable(dssdev, false, true);
481
482 td->ulps_enabled = true;
483
484 return 0;
485
486err:
487 dev_err(&dssdev->dev, "enter ULPS failed");
488 taal_panel_reset(dssdev);
489
490 td->ulps_enabled = false;
491
492 taal_queue_ulps_work(dssdev);
493
494 return r;
495}
496
497static int taal_exit_ulps(struct omap_dss_device *dssdev)
498{
499 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
500 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
501 int r;
502
503 if (!td->ulps_enabled)
504 return 0;
505
506 r = omapdss_dsi_display_enable(dssdev);
Tomi Valkeinene8945672011-05-31 16:39:01 +0300507 if (r) {
508 dev_err(&dssdev->dev, "failed to enable DSI\n");
509 goto err1;
510 }
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200511
Archit Taneja1ffefe72011-05-12 17:26:24 +0530512 omapdss_dsi_vc_enable_hs(dssdev, td->channel, true);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200513
514 r = _taal_enable_te(dssdev, true);
Tomi Valkeinene8945672011-05-31 16:39:01 +0300515 if (r) {
516 dev_err(&dssdev->dev, "failed to re-enable TE");
517 goto err2;
518 }
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200519
520 enable_irq(gpio_to_irq(panel_data->ext_te_gpio));
521
522 taal_queue_ulps_work(dssdev);
523
524 td->ulps_enabled = false;
525
526 return 0;
527
Tomi Valkeinene8945672011-05-31 16:39:01 +0300528err2:
529 dev_err(&dssdev->dev, "failed to exit ULPS");
530
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200531 r = taal_panel_reset(dssdev);
Tomi Valkeinene8945672011-05-31 16:39:01 +0300532 if (!r) {
533 enable_irq(gpio_to_irq(panel_data->ext_te_gpio));
534 td->ulps_enabled = false;
535 }
536err1:
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200537 taal_queue_ulps_work(dssdev);
538
539 return r;
540}
541
542static int taal_wake_up(struct omap_dss_device *dssdev)
543{
544 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
545
546 if (td->ulps_enabled)
547 return taal_exit_ulps(dssdev);
548
549 taal_cancel_ulps_work(dssdev);
550 taal_queue_ulps_work(dssdev);
551 return 0;
552}
553
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200554static int taal_bl_update_status(struct backlight_device *dev)
555{
556 struct omap_dss_device *dssdev = dev_get_drvdata(&dev->dev);
557 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300558 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200559 int r;
560 int level;
561
562 if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
563 dev->props.power == FB_BLANK_UNBLANK)
564 level = dev->props.brightness;
565 else
566 level = 0;
567
568 dev_dbg(&dssdev->dev, "update brightness to %d\n", level);
569
Tomi Valkeinen1cbc8702010-04-15 16:41:07 +0300570 mutex_lock(&td->lock);
571
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200572 if (td->use_dsi_bl) {
573 if (td->enabled) {
Archit Taneja1ffefe72011-05-12 17:26:24 +0530574 dsi_bus_lock(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200575
576 r = taal_wake_up(dssdev);
577 if (!r)
578 r = taal_dcs_write_1(td, DCS_BRIGHTNESS, level);
579
Archit Taneja1ffefe72011-05-12 17:26:24 +0530580 dsi_bus_unlock(dssdev);
Tomi Valkeinen1cbc8702010-04-15 16:41:07 +0300581 } else {
582 r = 0;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200583 }
584 } else {
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300585 if (!panel_data->set_backlight)
Tomi Valkeinen1cbc8702010-04-15 16:41:07 +0300586 r = -EINVAL;
587 else
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300588 r = panel_data->set_backlight(dssdev, level);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200589 }
590
Tomi Valkeinen1cbc8702010-04-15 16:41:07 +0300591 mutex_unlock(&td->lock);
592
593 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200594}
595
596static int taal_bl_get_intensity(struct backlight_device *dev)
597{
598 if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
599 dev->props.power == FB_BLANK_UNBLANK)
600 return dev->props.brightness;
601
602 return 0;
603}
604
Lionel Debrouxacc24722010-11-16 14:14:02 +0100605static const struct backlight_ops taal_bl_ops = {
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200606 .get_brightness = taal_bl_get_intensity,
607 .update_status = taal_bl_update_status,
608};
609
610static void taal_get_timings(struct omap_dss_device *dssdev,
611 struct omap_video_timings *timings)
612{
613 *timings = dssdev->panel.timings;
614}
615
616static void taal_get_resolution(struct omap_dss_device *dssdev,
617 u16 *xres, u16 *yres)
618{
619 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
620
621 if (td->rotate == 0 || td->rotate == 2) {
622 *xres = dssdev->panel.timings.x_res;
623 *yres = dssdev->panel.timings.y_res;
624 } else {
625 *yres = dssdev->panel.timings.x_res;
626 *xres = dssdev->panel.timings.y_res;
627 }
628}
629
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200630static ssize_t taal_num_errors_show(struct device *dev,
631 struct device_attribute *attr, char *buf)
632{
633 struct omap_dss_device *dssdev = to_dss_device(dev);
634 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
635 u8 errors;
636 int r;
637
Jani Nikula6b316712010-04-28 11:15:18 +0300638 mutex_lock(&td->lock);
639
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200640 if (td->enabled) {
Archit Taneja1ffefe72011-05-12 17:26:24 +0530641 dsi_bus_lock(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200642
643 r = taal_wake_up(dssdev);
644 if (!r)
645 r = taal_dcs_read_1(td, DCS_READ_NUM_ERRORS, &errors);
646
Archit Taneja1ffefe72011-05-12 17:26:24 +0530647 dsi_bus_unlock(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200648 } else {
649 r = -ENODEV;
650 }
651
Jani Nikula6b316712010-04-28 11:15:18 +0300652 mutex_unlock(&td->lock);
653
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200654 if (r)
655 return r;
656
657 return snprintf(buf, PAGE_SIZE, "%d\n", errors);
658}
659
660static ssize_t taal_hw_revision_show(struct device *dev,
661 struct device_attribute *attr, char *buf)
662{
663 struct omap_dss_device *dssdev = to_dss_device(dev);
664 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
665 u8 id1, id2, id3;
666 int r;
667
Jani Nikula6b316712010-04-28 11:15:18 +0300668 mutex_lock(&td->lock);
669
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200670 if (td->enabled) {
Archit Taneja1ffefe72011-05-12 17:26:24 +0530671 dsi_bus_lock(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200672
673 r = taal_wake_up(dssdev);
674 if (!r)
675 r = taal_get_id(td, &id1, &id2, &id3);
676
Archit Taneja1ffefe72011-05-12 17:26:24 +0530677 dsi_bus_unlock(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200678 } else {
679 r = -ENODEV;
680 }
681
Jani Nikula6b316712010-04-28 11:15:18 +0300682 mutex_unlock(&td->lock);
683
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200684 if (r)
685 return r;
686
687 return snprintf(buf, PAGE_SIZE, "%02x.%02x.%02x\n", id1, id2, id3);
688}
689
690static const char *cabc_modes[] = {
691 "off", /* used also always when CABC is not supported */
692 "ui",
693 "still-image",
694 "moving-image",
695};
696
697static ssize_t show_cabc_mode(struct device *dev,
698 struct device_attribute *attr,
699 char *buf)
700{
701 struct omap_dss_device *dssdev = to_dss_device(dev);
702 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
703 const char *mode_str;
704 int mode;
705 int len;
706
707 mode = td->cabc_mode;
708
709 mode_str = "unknown";
710 if (mode >= 0 && mode < ARRAY_SIZE(cabc_modes))
711 mode_str = cabc_modes[mode];
712 len = snprintf(buf, PAGE_SIZE, "%s\n", mode_str);
713
714 return len < PAGE_SIZE - 1 ? len : PAGE_SIZE - 1;
715}
716
717static ssize_t store_cabc_mode(struct device *dev,
718 struct device_attribute *attr,
719 const char *buf, size_t count)
720{
721 struct omap_dss_device *dssdev = to_dss_device(dev);
722 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
723 int i;
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200724 int r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200725
726 for (i = 0; i < ARRAY_SIZE(cabc_modes); i++) {
727 if (sysfs_streq(cabc_modes[i], buf))
728 break;
729 }
730
731 if (i == ARRAY_SIZE(cabc_modes))
732 return -EINVAL;
733
Jani Nikula6b316712010-04-28 11:15:18 +0300734 mutex_lock(&td->lock);
735
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200736 if (td->enabled) {
Archit Taneja1ffefe72011-05-12 17:26:24 +0530737 dsi_bus_lock(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200738
739 if (!td->cabc_broken) {
740 r = taal_wake_up(dssdev);
741 if (r)
742 goto err;
743
744 r = taal_dcs_write_1(td, DCS_WRITE_CABC, i);
745 if (r)
746 goto err;
747 }
748
Archit Taneja1ffefe72011-05-12 17:26:24 +0530749 dsi_bus_unlock(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200750 }
751
752 td->cabc_mode = i;
753
Jani Nikula6b316712010-04-28 11:15:18 +0300754 mutex_unlock(&td->lock);
755
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200756 return count;
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200757err:
Archit Taneja1ffefe72011-05-12 17:26:24 +0530758 dsi_bus_unlock(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200759 mutex_unlock(&td->lock);
760 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200761}
762
763static ssize_t show_cabc_available_modes(struct device *dev,
764 struct device_attribute *attr,
765 char *buf)
766{
767 int len;
768 int i;
769
770 for (i = 0, len = 0;
771 len < PAGE_SIZE && i < ARRAY_SIZE(cabc_modes); i++)
772 len += snprintf(&buf[len], PAGE_SIZE - len, "%s%s%s",
773 i ? " " : "", cabc_modes[i],
774 i == ARRAY_SIZE(cabc_modes) - 1 ? "\n" : "");
775
776 return len < PAGE_SIZE ? len : PAGE_SIZE - 1;
777}
778
Tomi Valkeinen1f8fa452011-03-24 14:06:51 +0200779static ssize_t taal_store_esd_interval(struct device *dev,
780 struct device_attribute *attr,
781 const char *buf, size_t count)
782{
783 struct omap_dss_device *dssdev = to_dss_device(dev);
784 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
785
786 unsigned long t;
787 int r;
788
789 r = strict_strtoul(buf, 10, &t);
790 if (r)
791 return r;
792
793 mutex_lock(&td->lock);
794 taal_cancel_esd_work(dssdev);
795 td->esd_interval = t;
796 if (td->enabled)
797 taal_queue_esd_work(dssdev);
798 mutex_unlock(&td->lock);
799
800 return count;
801}
802
803static ssize_t taal_show_esd_interval(struct device *dev,
804 struct device_attribute *attr,
805 char *buf)
806{
807 struct omap_dss_device *dssdev = to_dss_device(dev);
808 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
809 unsigned t;
810
811 mutex_lock(&td->lock);
812 t = td->esd_interval;
813 mutex_unlock(&td->lock);
814
815 return snprintf(buf, PAGE_SIZE, "%u\n", t);
816}
817
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200818static ssize_t taal_store_ulps(struct device *dev,
819 struct device_attribute *attr,
820 const char *buf, size_t count)
821{
822 struct omap_dss_device *dssdev = to_dss_device(dev);
823 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
824 unsigned long t;
825 int r;
826
827 r = strict_strtoul(buf, 10, &t);
828 if (r)
829 return r;
830
831 mutex_lock(&td->lock);
832
833 if (td->enabled) {
Archit Taneja1ffefe72011-05-12 17:26:24 +0530834 dsi_bus_lock(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200835
836 if (t)
837 r = taal_enter_ulps(dssdev);
838 else
839 r = taal_wake_up(dssdev);
840
Archit Taneja1ffefe72011-05-12 17:26:24 +0530841 dsi_bus_unlock(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200842 }
843
844 mutex_unlock(&td->lock);
845
846 if (r)
847 return r;
848
849 return count;
850}
851
852static ssize_t taal_show_ulps(struct device *dev,
853 struct device_attribute *attr,
854 char *buf)
855{
856 struct omap_dss_device *dssdev = to_dss_device(dev);
857 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
858 unsigned t;
859
860 mutex_lock(&td->lock);
861 t = td->ulps_enabled;
862 mutex_unlock(&td->lock);
863
864 return snprintf(buf, PAGE_SIZE, "%u\n", t);
865}
866
867static ssize_t taal_store_ulps_timeout(struct device *dev,
868 struct device_attribute *attr,
869 const char *buf, size_t count)
870{
871 struct omap_dss_device *dssdev = to_dss_device(dev);
872 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
873 unsigned long t;
874 int r;
875
876 r = strict_strtoul(buf, 10, &t);
877 if (r)
878 return r;
879
880 mutex_lock(&td->lock);
881 td->ulps_timeout = t;
882
883 if (td->enabled) {
884 /* taal_wake_up will restart the timer */
Archit Taneja1ffefe72011-05-12 17:26:24 +0530885 dsi_bus_lock(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200886 r = taal_wake_up(dssdev);
Archit Taneja1ffefe72011-05-12 17:26:24 +0530887 dsi_bus_unlock(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200888 }
889
890 mutex_unlock(&td->lock);
891
892 if (r)
893 return r;
894
895 return count;
896}
897
898static ssize_t taal_show_ulps_timeout(struct device *dev,
899 struct device_attribute *attr,
900 char *buf)
901{
902 struct omap_dss_device *dssdev = to_dss_device(dev);
903 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
904 unsigned t;
905
906 mutex_lock(&td->lock);
907 t = td->ulps_timeout;
908 mutex_unlock(&td->lock);
909
910 return snprintf(buf, PAGE_SIZE, "%u\n", t);
911}
912
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200913static DEVICE_ATTR(num_dsi_errors, S_IRUGO, taal_num_errors_show, NULL);
914static DEVICE_ATTR(hw_revision, S_IRUGO, taal_hw_revision_show, NULL);
915static DEVICE_ATTR(cabc_mode, S_IRUGO | S_IWUSR,
916 show_cabc_mode, store_cabc_mode);
917static DEVICE_ATTR(cabc_available_modes, S_IRUGO,
918 show_cabc_available_modes, NULL);
Tomi Valkeinen1f8fa452011-03-24 14:06:51 +0200919static DEVICE_ATTR(esd_interval, S_IRUGO | S_IWUSR,
920 taal_show_esd_interval, taal_store_esd_interval);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200921static DEVICE_ATTR(ulps, S_IRUGO | S_IWUSR,
922 taal_show_ulps, taal_store_ulps);
923static DEVICE_ATTR(ulps_timeout, S_IRUGO | S_IWUSR,
924 taal_show_ulps_timeout, taal_store_ulps_timeout);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200925
926static struct attribute *taal_attrs[] = {
927 &dev_attr_num_dsi_errors.attr,
928 &dev_attr_hw_revision.attr,
929 &dev_attr_cabc_mode.attr,
930 &dev_attr_cabc_available_modes.attr,
Tomi Valkeinen1f8fa452011-03-24 14:06:51 +0200931 &dev_attr_esd_interval.attr,
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200932 &dev_attr_ulps.attr,
933 &dev_attr_ulps_timeout.attr,
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200934 NULL,
935};
936
937static struct attribute_group taal_attr_group = {
938 .attrs = taal_attrs,
939};
940
Jani Nikula006db7b2010-04-09 12:25:59 +0300941static void taal_hw_reset(struct omap_dss_device *dssdev)
942{
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300943 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300944 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
945
946 if (panel_data->reset_gpio == -1)
Jani Nikula006db7b2010-04-09 12:25:59 +0300947 return;
948
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300949 gpio_set_value(panel_data->reset_gpio, 1);
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300950 if (td->panel_config->reset_sequence.high)
951 udelay(td->panel_config->reset_sequence.high);
Jani Nikula006db7b2010-04-09 12:25:59 +0300952 /* reset the panel */
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300953 gpio_set_value(panel_data->reset_gpio, 0);
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300954 /* assert reset */
955 if (td->panel_config->reset_sequence.low)
956 udelay(td->panel_config->reset_sequence.low);
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300957 gpio_set_value(panel_data->reset_gpio, 1);
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300958 /* wait after releasing reset */
959 if (td->panel_config->sleep.hw_reset)
960 msleep(td->panel_config->sleep.hw_reset);
Jani Nikula006db7b2010-04-09 12:25:59 +0300961}
962
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200963static int taal_probe(struct omap_dss_device *dssdev)
964{
Matthew Garretta19a6ee2010-02-17 16:39:44 -0500965 struct backlight_properties props;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200966 struct taal_data *td;
967 struct backlight_device *bldev;
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300968 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300969 struct panel_config *panel_config = NULL;
970 int r, i;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200971
972 dev_dbg(&dssdev->dev, "probe\n");
973
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300974 if (!panel_data || !panel_data->name) {
975 r = -EINVAL;
976 goto err;
977 }
978
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300979 for (i = 0; i < ARRAY_SIZE(panel_configs); i++) {
980 if (strcmp(panel_data->name, panel_configs[i].name) == 0) {
981 panel_config = &panel_configs[i];
982 break;
983 }
984 }
985
986 if (!panel_config) {
987 r = -EINVAL;
988 goto err;
989 }
990
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200991 dssdev->panel.config = OMAP_DSS_LCD_TFT;
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300992 dssdev->panel.timings = panel_config->timings;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200993 dssdev->ctrl.pixel_size = 24;
994
995 td = kzalloc(sizeof(*td), GFP_KERNEL);
996 if (!td) {
997 r = -ENOMEM;
Jani Nikulad2b65782010-04-15 12:24:36 +0300998 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200999 }
1000 td->dssdev = dssdev;
Jani Nikulae7f6c3f2010-04-15 12:55:38 +03001001 td->panel_config = panel_config;
Tomi Valkeinen33a410b2011-03-24 13:58:01 +02001002 td->esd_interval = panel_data->esd_interval;
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001003 td->ulps_enabled = false;
1004 td->ulps_timeout = panel_data->ulps_timeout;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001005
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001006 mutex_init(&td->lock);
1007
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001008 atomic_set(&td->do_update, 0);
1009
Tomi Valkeinenc8cd4542010-06-09 15:24:46 +03001010 r = init_regulators(dssdev, panel_config->regulators,
1011 panel_config->num_regulators);
1012 if (r)
1013 goto err_reg;
1014
Tomi Valkeinen883b9ac2011-03-24 15:01:30 +02001015 td->workqueue = create_singlethread_workqueue("taal_esd");
1016 if (td->workqueue == NULL) {
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001017 dev_err(&dssdev->dev, "can't create ESD workqueue\n");
1018 r = -ENOMEM;
Jani Nikulad2b65782010-04-15 12:24:36 +03001019 goto err_wq;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001020 }
1021 INIT_DELAYED_WORK_DEFERRABLE(&td->esd_work, taal_esd_work);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001022 INIT_DELAYED_WORK(&td->ulps_work, taal_ulps_work);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001023
1024 dev_set_drvdata(&dssdev->dev, td);
1025
Jani Nikula006db7b2010-04-09 12:25:59 +03001026 taal_hw_reset(dssdev);
1027
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001028 /* if no platform set_backlight() defined, presume DSI backlight
1029 * control */
Matthew Garretta19a6ee2010-02-17 16:39:44 -05001030 memset(&props, 0, sizeof(struct backlight_properties));
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001031 if (!panel_data->set_backlight)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001032 td->use_dsi_bl = true;
1033
Matthew Garretta19a6ee2010-02-17 16:39:44 -05001034 if (td->use_dsi_bl)
1035 props.max_brightness = 255;
1036 else
1037 props.max_brightness = 127;
Matthew Garrettbb7ca742011-03-22 16:30:21 -07001038
1039 props.type = BACKLIGHT_RAW;
Archit Taneja634610a2011-05-12 17:26:30 +05301040 bldev = backlight_device_register(dev_name(&dssdev->dev), &dssdev->dev,
1041 dssdev, &taal_bl_ops, &props);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001042 if (IS_ERR(bldev)) {
1043 r = PTR_ERR(bldev);
Jani Nikulad2b65782010-04-15 12:24:36 +03001044 goto err_bl;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001045 }
1046
1047 td->bldev = bldev;
1048
1049 bldev->props.fb_blank = FB_BLANK_UNBLANK;
1050 bldev->props.power = FB_BLANK_UNBLANK;
Matthew Garretta19a6ee2010-02-17 16:39:44 -05001051 if (td->use_dsi_bl)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001052 bldev->props.brightness = 255;
Matthew Garretta19a6ee2010-02-17 16:39:44 -05001053 else
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001054 bldev->props.brightness = 127;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001055
1056 taal_bl_update_status(bldev);
1057
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001058 if (panel_data->use_ext_te) {
1059 int gpio = panel_data->ext_te_gpio;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001060
1061 r = gpio_request(gpio, "taal irq");
1062 if (r) {
1063 dev_err(&dssdev->dev, "GPIO request failed\n");
Jani Nikulad2b65782010-04-15 12:24:36 +03001064 goto err_gpio;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001065 }
1066
1067 gpio_direction_input(gpio);
1068
1069 r = request_irq(gpio_to_irq(gpio), taal_te_isr,
1070 IRQF_DISABLED | IRQF_TRIGGER_RISING,
1071 "taal vsync", dssdev);
1072
1073 if (r) {
1074 dev_err(&dssdev->dev, "IRQ request failed\n");
1075 gpio_free(gpio);
Jani Nikulad2b65782010-04-15 12:24:36 +03001076 goto err_irq;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001077 }
1078
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001079 INIT_DELAYED_WORK_DEFERRABLE(&td->te_timeout_work,
1080 taal_te_timeout_work_callback);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001081
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001082 dev_dbg(&dssdev->dev, "Using GPIO TE\n");
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001083 }
1084
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301085 r = omap_dsi_request_vc(dssdev, &td->channel);
1086 if (r) {
1087 dev_err(&dssdev->dev, "failed to get virtual channel\n");
1088 goto err_req_vc;
1089 }
1090
1091 r = omap_dsi_set_vc_id(dssdev, td->channel, TCH);
1092 if (r) {
1093 dev_err(&dssdev->dev, "failed to set VC_ID\n");
1094 goto err_vc_id;
1095 }
1096
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001097 r = sysfs_create_group(&dssdev->dev.kobj, &taal_attr_group);
1098 if (r) {
1099 dev_err(&dssdev->dev, "failed to create sysfs files\n");
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301100 goto err_vc_id;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001101 }
1102
1103 return 0;
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301104
1105err_vc_id:
1106 omap_dsi_release_vc(dssdev, td->channel);
1107err_req_vc:
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001108 if (panel_data->use_ext_te)
1109 free_irq(gpio_to_irq(panel_data->ext_te_gpio), dssdev);
Jani Nikulad2b65782010-04-15 12:24:36 +03001110err_irq:
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001111 if (panel_data->use_ext_te)
1112 gpio_free(panel_data->ext_te_gpio);
Jani Nikulad2b65782010-04-15 12:24:36 +03001113err_gpio:
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001114 backlight_device_unregister(bldev);
Jani Nikulad2b65782010-04-15 12:24:36 +03001115err_bl:
Tomi Valkeinen883b9ac2011-03-24 15:01:30 +02001116 destroy_workqueue(td->workqueue);
Jani Nikulad2b65782010-04-15 12:24:36 +03001117err_wq:
Tomi Valkeinenc8cd4542010-06-09 15:24:46 +03001118 free_regulators(panel_config->regulators, panel_config->num_regulators);
1119err_reg:
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001120 kfree(td);
Jani Nikulad2b65782010-04-15 12:24:36 +03001121err:
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001122 return r;
1123}
1124
Tomi Valkeinen14e4d782011-03-31 12:03:51 +03001125static void __exit taal_remove(struct omap_dss_device *dssdev)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001126{
1127 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001128 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001129 struct backlight_device *bldev;
1130
1131 dev_dbg(&dssdev->dev, "remove\n");
1132
1133 sysfs_remove_group(&dssdev->dev.kobj, &taal_attr_group);
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301134 omap_dsi_release_vc(dssdev, td->channel);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001135
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001136 if (panel_data->use_ext_te) {
1137 int gpio = panel_data->ext_te_gpio;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001138 free_irq(gpio_to_irq(gpio), dssdev);
1139 gpio_free(gpio);
1140 }
1141
1142 bldev = td->bldev;
1143 bldev->props.power = FB_BLANK_POWERDOWN;
1144 taal_bl_update_status(bldev);
1145 backlight_device_unregister(bldev);
1146
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001147 taal_cancel_ulps_work(dssdev);
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +02001148 taal_cancel_esd_work(dssdev);
Tomi Valkeinen883b9ac2011-03-24 15:01:30 +02001149 destroy_workqueue(td->workqueue);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001150
Jani Nikula006db7b2010-04-09 12:25:59 +03001151 /* reset, to be sure that the panel is in a valid state */
1152 taal_hw_reset(dssdev);
1153
Tomi Valkeinenc8cd4542010-06-09 15:24:46 +03001154 free_regulators(td->panel_config->regulators,
1155 td->panel_config->num_regulators);
1156
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001157 kfree(td);
1158}
1159
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001160static int taal_power_on(struct omap_dss_device *dssdev)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001161{
1162 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1163 u8 id1, id2, id3;
1164 int r;
1165
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001166 r = omapdss_dsi_display_enable(dssdev);
1167 if (r) {
1168 dev_err(&dssdev->dev, "failed to enable DSI\n");
1169 goto err0;
1170 }
1171
Jani Nikula006db7b2010-04-09 12:25:59 +03001172 taal_hw_reset(dssdev);
1173
Archit Taneja1ffefe72011-05-12 17:26:24 +05301174 omapdss_dsi_vc_enable_hs(dssdev, td->channel, false);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001175
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001176 r = taal_sleep_out(td);
1177 if (r)
1178 goto err;
1179
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301180 r = taal_get_id(td, &id1, &id2, &id3);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001181 if (r)
1182 goto err;
1183
Jani Nikula1e8943d2010-04-15 17:07:39 +03001184 /* on early Taal revisions CABC is broken */
1185 if (td->panel_config->type == PANEL_TAAL &&
1186 (id2 == 0x00 || id2 == 0xff || id2 == 0x81))
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001187 td->cabc_broken = true;
1188
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301189 r = taal_dcs_write_1(td, DCS_BRIGHTNESS, 0xff);
Jani Nikulaf2a8b752010-04-09 14:15:12 +03001190 if (r)
1191 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001192
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301193 r = taal_dcs_write_1(td, DCS_CTRL_DISPLAY,
Jani Nikulaf2a8b752010-04-09 14:15:12 +03001194 (1<<2) | (1<<5)); /* BL | BCTRL */
1195 if (r)
1196 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001197
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301198 r = taal_dcs_write_1(td, DCS_PIXEL_FORMAT, 0x7); /* 24bit/pixel */
Jani Nikulaf2a8b752010-04-09 14:15:12 +03001199 if (r)
1200 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001201
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301202 r = taal_set_addr_mode(td, td->rotate, td->mirror);
Jani Nikulaf2a8b752010-04-09 14:15:12 +03001203 if (r)
1204 goto err;
1205
1206 if (!td->cabc_broken) {
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301207 r = taal_dcs_write_1(td, DCS_WRITE_CABC, td->cabc_mode);
Jani Nikulaf2a8b752010-04-09 14:15:12 +03001208 if (r)
1209 goto err;
1210 }
1211
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301212 r = taal_dcs_write_0(td, DCS_DISPLAY_ON);
Jani Nikulaf2a8b752010-04-09 14:15:12 +03001213 if (r)
1214 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001215
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001216 r = _taal_enable_te(dssdev, td->te_enabled);
1217 if (r)
1218 goto err;
1219
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001220 td->enabled = 1;
1221
1222 if (!td->intro_printed) {
Jani Nikula0f45bdd2010-04-16 14:21:14 +03001223 dev_info(&dssdev->dev, "%s panel revision %02x.%02x.%02x\n",
1224 td->panel_config->name, id1, id2, id3);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001225 if (td->cabc_broken)
1226 dev_info(&dssdev->dev,
1227 "old Taal version, CABC disabled\n");
1228 td->intro_printed = true;
1229 }
1230
Archit Taneja1ffefe72011-05-12 17:26:24 +05301231 omapdss_dsi_vc_enable_hs(dssdev, td->channel, true);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001232
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001233 return 0;
1234err:
Jani Nikula006db7b2010-04-09 12:25:59 +03001235 dev_err(&dssdev->dev, "error while enabling panel, issuing HW reset\n");
1236
1237 taal_hw_reset(dssdev);
1238
Tomi Valkeinen22d6d672010-10-11 11:33:30 +03001239 omapdss_dsi_display_disable(dssdev, true, false);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001240err0:
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001241 return r;
1242}
1243
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001244static void taal_power_off(struct omap_dss_device *dssdev)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001245{
1246 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Jani Nikula006db7b2010-04-09 12:25:59 +03001247 int r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001248
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301249 r = taal_dcs_write_0(td, DCS_DISPLAY_OFF);
Jani Nikula006db7b2010-04-09 12:25:59 +03001250 if (!r) {
1251 r = taal_sleep_in(td);
Jani Nikulae7f6c3f2010-04-15 12:55:38 +03001252 /* HACK: wait a bit so that the message goes through */
Jani Nikula006db7b2010-04-09 12:25:59 +03001253 msleep(10);
1254 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001255
Jani Nikula006db7b2010-04-09 12:25:59 +03001256 if (r) {
1257 dev_err(&dssdev->dev,
1258 "error disabling panel, issuing HW reset\n");
1259 taal_hw_reset(dssdev);
1260 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001261
Tomi Valkeinen22d6d672010-10-11 11:33:30 +03001262 omapdss_dsi_display_disable(dssdev, true, false);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001263
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001264 td->enabled = 0;
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001265}
1266
Tomi Valkeinenbb5476c2011-03-24 15:00:06 +02001267static int taal_panel_reset(struct omap_dss_device *dssdev)
1268{
1269 dev_err(&dssdev->dev, "performing LCD reset\n");
1270
1271 taal_power_off(dssdev);
1272 taal_hw_reset(dssdev);
1273 return taal_power_on(dssdev);
1274}
1275
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001276static int taal_enable(struct omap_dss_device *dssdev)
1277{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001278 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001279 int r;
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001280
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001281 dev_dbg(&dssdev->dev, "enable\n");
1282
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001283 mutex_lock(&td->lock);
1284
1285 if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) {
1286 r = -EINVAL;
1287 goto err;
1288 }
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001289
Archit Taneja1ffefe72011-05-12 17:26:24 +05301290 dsi_bus_lock(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001291
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001292 r = taal_power_on(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001293
Archit Taneja1ffefe72011-05-12 17:26:24 +05301294 dsi_bus_unlock(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001295
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001296 if (r)
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001297 goto err;
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001298
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +02001299 taal_queue_esd_work(dssdev);
Jani Nikula4571a022010-04-12 10:23:46 +03001300
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001301 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
1302
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001303 mutex_unlock(&td->lock);
1304
1305 return 0;
1306err:
1307 dev_dbg(&dssdev->dev, "enable failed\n");
1308 mutex_unlock(&td->lock);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001309 return r;
1310}
1311
1312static void taal_disable(struct omap_dss_device *dssdev)
1313{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001314 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1315
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001316 dev_dbg(&dssdev->dev, "disable\n");
1317
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001318 mutex_lock(&td->lock);
1319
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001320 taal_cancel_ulps_work(dssdev);
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +02001321 taal_cancel_esd_work(dssdev);
Jani Nikula4571a022010-04-12 10:23:46 +03001322
Archit Taneja1ffefe72011-05-12 17:26:24 +05301323 dsi_bus_lock(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001324
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001325 if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) {
Tomi Valkeinene8945672011-05-31 16:39:01 +03001326 int r;
1327
1328 r = taal_wake_up(dssdev);
1329 if (!r)
1330 taal_power_off(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001331 }
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001332
Archit Taneja1ffefe72011-05-12 17:26:24 +05301333 dsi_bus_unlock(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001334
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001335 dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001336
1337 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001338}
1339
1340static int taal_suspend(struct omap_dss_device *dssdev)
1341{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001342 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1343 int r;
1344
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001345 dev_dbg(&dssdev->dev, "suspend\n");
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001346
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001347 mutex_lock(&td->lock);
1348
1349 if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) {
1350 r = -EINVAL;
1351 goto err;
1352 }
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001353
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001354 taal_cancel_ulps_work(dssdev);
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +02001355 taal_cancel_esd_work(dssdev);
Jani Nikula4571a022010-04-12 10:23:46 +03001356
Archit Taneja1ffefe72011-05-12 17:26:24 +05301357 dsi_bus_lock(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001358
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001359 r = taal_wake_up(dssdev);
1360 if (!r)
1361 taal_power_off(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001362
Archit Taneja1ffefe72011-05-12 17:26:24 +05301363 dsi_bus_unlock(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001364
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001365 dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001366
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001367 mutex_unlock(&td->lock);
1368
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001369 return 0;
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001370err:
1371 mutex_unlock(&td->lock);
1372 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001373}
1374
1375static int taal_resume(struct omap_dss_device *dssdev)
1376{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001377 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001378 int r;
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001379
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001380 dev_dbg(&dssdev->dev, "resume\n");
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001381
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001382 mutex_lock(&td->lock);
1383
1384 if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) {
1385 r = -EINVAL;
1386 goto err;
1387 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001388
Archit Taneja1ffefe72011-05-12 17:26:24 +05301389 dsi_bus_lock(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001390
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001391 r = taal_power_on(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001392
Archit Taneja1ffefe72011-05-12 17:26:24 +05301393 dsi_bus_unlock(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001394
Jani Nikula4571a022010-04-12 10:23:46 +03001395 if (r) {
Jani Nikulafed44b72010-04-12 10:25:21 +03001396 dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
Jani Nikula4571a022010-04-12 10:23:46 +03001397 } else {
Jani Nikulafed44b72010-04-12 10:25:21 +03001398 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +02001399 taal_queue_esd_work(dssdev);
Jani Nikula4571a022010-04-12 10:23:46 +03001400 }
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001401
1402 mutex_unlock(&td->lock);
1403
1404 return r;
1405err:
1406 mutex_unlock(&td->lock);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001407 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001408}
1409
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001410static void taal_framedone_cb(int err, void *data)
1411{
1412 struct omap_dss_device *dssdev = data;
1413 dev_dbg(&dssdev->dev, "framedone, err %d\n", err);
Archit Taneja1ffefe72011-05-12 17:26:24 +05301414 dsi_bus_unlock(dssdev);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001415}
1416
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001417static irqreturn_t taal_te_isr(int irq, void *data)
1418{
1419 struct omap_dss_device *dssdev = data;
1420 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1421 int old;
1422 int r;
1423
1424 old = atomic_cmpxchg(&td->do_update, 1, 0);
1425
1426 if (old) {
1427 cancel_delayed_work(&td->te_timeout_work);
1428
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301429 r = omap_dsi_update(dssdev, td->channel,
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001430 td->update_region.x,
1431 td->update_region.y,
1432 td->update_region.w,
1433 td->update_region.h,
1434 taal_framedone_cb, dssdev);
1435 if (r)
1436 goto err;
1437 }
1438
1439 return IRQ_HANDLED;
1440err:
1441 dev_err(&dssdev->dev, "start update failed\n");
Archit Taneja1ffefe72011-05-12 17:26:24 +05301442 dsi_bus_unlock(dssdev);
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001443 return IRQ_HANDLED;
1444}
1445
1446static void taal_te_timeout_work_callback(struct work_struct *work)
1447{
1448 struct taal_data *td = container_of(work, struct taal_data,
1449 te_timeout_work.work);
1450 struct omap_dss_device *dssdev = td->dssdev;
1451
1452 dev_err(&dssdev->dev, "TE not received for 250ms!\n");
1453
1454 atomic_set(&td->do_update, 0);
Archit Taneja1ffefe72011-05-12 17:26:24 +05301455 dsi_bus_unlock(dssdev);
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001456}
1457
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001458static int taal_update(struct omap_dss_device *dssdev,
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001459 u16 x, u16 y, u16 w, u16 h)
1460{
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001461 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001462 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001463 int r;
1464
1465 dev_dbg(&dssdev->dev, "update %d, %d, %d x %d\n", x, y, w, h);
1466
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001467 mutex_lock(&td->lock);
Archit Taneja1ffefe72011-05-12 17:26:24 +05301468 dsi_bus_lock(dssdev);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001469
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001470 r = taal_wake_up(dssdev);
1471 if (r)
1472 goto err;
1473
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001474 if (!td->enabled) {
1475 r = 0;
1476 goto err;
1477 }
1478
Tomi Valkeinen26a8c252010-06-09 15:31:34 +03001479 r = omap_dsi_prepare_update(dssdev, &x, &y, &w, &h, true);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001480 if (r)
1481 goto err;
1482
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301483 r = taal_set_update_window(td, x, y, w, h);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001484 if (r)
1485 goto err;
1486
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001487 if (td->te_enabled && panel_data->use_ext_te) {
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001488 td->update_region.x = x;
1489 td->update_region.y = y;
1490 td->update_region.w = w;
1491 td->update_region.h = h;
1492 barrier();
1493 schedule_delayed_work(&td->te_timeout_work,
1494 msecs_to_jiffies(250));
1495 atomic_set(&td->do_update, 1);
1496 } else {
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301497 r = omap_dsi_update(dssdev, td->channel, x, y, w, h,
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001498 taal_framedone_cb, dssdev);
1499 if (r)
1500 goto err;
1501 }
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001502
1503 /* note: no bus_unlock here. unlock is in framedone_cb */
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001504 mutex_unlock(&td->lock);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001505 return 0;
1506err:
Archit Taneja1ffefe72011-05-12 17:26:24 +05301507 dsi_bus_unlock(dssdev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001508 mutex_unlock(&td->lock);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001509 return r;
1510}
1511
1512static int taal_sync(struct omap_dss_device *dssdev)
1513{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001514 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1515
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001516 dev_dbg(&dssdev->dev, "sync\n");
1517
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001518 mutex_lock(&td->lock);
Archit Taneja1ffefe72011-05-12 17:26:24 +05301519 dsi_bus_lock(dssdev);
1520 dsi_bus_unlock(dssdev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001521 mutex_unlock(&td->lock);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001522
1523 dev_dbg(&dssdev->dev, "sync done\n");
1524
1525 return 0;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001526}
1527
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001528static int _taal_enable_te(struct omap_dss_device *dssdev, bool enable)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001529{
Jani Nikulae7f6c3f2010-04-15 12:55:38 +03001530 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001531 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001532 int r;
1533
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001534 if (enable)
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301535 r = taal_dcs_write_1(td, DCS_TEAR_ON, 0);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001536 else
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301537 r = taal_dcs_write_0(td, DCS_TEAR_OFF);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001538
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001539 if (!panel_data->use_ext_te)
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001540 omapdss_dsi_enable_te(dssdev, enable);
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001541
Jani Nikulae7f6c3f2010-04-15 12:55:38 +03001542 if (td->panel_config->sleep.enable_te)
1543 msleep(td->panel_config->sleep.enable_te);
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001544
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001545 return r;
1546}
1547
1548static int taal_enable_te(struct omap_dss_device *dssdev, bool enable)
1549{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001550 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001551 int r;
1552
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001553 mutex_lock(&td->lock);
Tomi Valkeinenffb63c92010-05-21 10:09:18 +03001554
1555 if (td->te_enabled == enable)
1556 goto end;
1557
Archit Taneja1ffefe72011-05-12 17:26:24 +05301558 dsi_bus_lock(dssdev);
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001559
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001560 if (td->enabled) {
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001561 r = taal_wake_up(dssdev);
1562 if (r)
1563 goto err;
1564
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001565 r = _taal_enable_te(dssdev, enable);
1566 if (r)
1567 goto err;
1568 }
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001569
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001570 td->te_enabled = enable;
1571
Archit Taneja1ffefe72011-05-12 17:26:24 +05301572 dsi_bus_unlock(dssdev);
Tomi Valkeinenffb63c92010-05-21 10:09:18 +03001573end:
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001574 mutex_unlock(&td->lock);
1575
1576 return 0;
1577err:
Archit Taneja1ffefe72011-05-12 17:26:24 +05301578 dsi_bus_unlock(dssdev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001579 mutex_unlock(&td->lock);
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001580
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001581 return r;
1582}
1583
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001584static int taal_get_te(struct omap_dss_device *dssdev)
1585{
1586 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001587 int r;
1588
1589 mutex_lock(&td->lock);
1590 r = td->te_enabled;
1591 mutex_unlock(&td->lock);
1592
1593 return r;
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001594}
1595
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001596static int taal_rotate(struct omap_dss_device *dssdev, u8 rotate)
1597{
1598 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1599 int r;
1600
1601 dev_dbg(&dssdev->dev, "rotate %d\n", rotate);
1602
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001603 mutex_lock(&td->lock);
Tomi Valkeinenffb63c92010-05-21 10:09:18 +03001604
1605 if (td->rotate == rotate)
1606 goto end;
1607
Archit Taneja1ffefe72011-05-12 17:26:24 +05301608 dsi_bus_lock(dssdev);
Tomi Valkeinen87424e12010-01-08 16:52:48 +02001609
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001610 if (td->enabled) {
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001611 r = taal_wake_up(dssdev);
1612 if (r)
1613 goto err;
1614
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301615 r = taal_set_addr_mode(td, rotate, td->mirror);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001616 if (r)
Tomi Valkeinen87424e12010-01-08 16:52:48 +02001617 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001618 }
1619
1620 td->rotate = rotate;
1621
Archit Taneja1ffefe72011-05-12 17:26:24 +05301622 dsi_bus_unlock(dssdev);
Tomi Valkeinenffb63c92010-05-21 10:09:18 +03001623end:
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001624 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001625 return 0;
Tomi Valkeinen87424e12010-01-08 16:52:48 +02001626err:
Archit Taneja1ffefe72011-05-12 17:26:24 +05301627 dsi_bus_unlock(dssdev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001628 mutex_unlock(&td->lock);
Tomi Valkeinen87424e12010-01-08 16:52:48 +02001629 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001630}
1631
1632static u8 taal_get_rotate(struct omap_dss_device *dssdev)
1633{
1634 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001635 int r;
1636
1637 mutex_lock(&td->lock);
1638 r = td->rotate;
1639 mutex_unlock(&td->lock);
1640
1641 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001642}
1643
1644static int taal_mirror(struct omap_dss_device *dssdev, bool enable)
1645{
1646 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1647 int r;
1648
1649 dev_dbg(&dssdev->dev, "mirror %d\n", enable);
1650
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001651 mutex_lock(&td->lock);
Tomi Valkeinenffb63c92010-05-21 10:09:18 +03001652
1653 if (td->mirror == enable)
1654 goto end;
1655
Archit Taneja1ffefe72011-05-12 17:26:24 +05301656 dsi_bus_lock(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001657 if (td->enabled) {
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001658 r = taal_wake_up(dssdev);
1659 if (r)
1660 goto err;
1661
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301662 r = taal_set_addr_mode(td, td->rotate, enable);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001663 if (r)
Tomi Valkeinen8d8aa612010-01-08 16:30:33 +02001664 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001665 }
1666
1667 td->mirror = enable;
1668
Archit Taneja1ffefe72011-05-12 17:26:24 +05301669 dsi_bus_unlock(dssdev);
Tomi Valkeinenffb63c92010-05-21 10:09:18 +03001670end:
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001671 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001672 return 0;
Tomi Valkeinen8d8aa612010-01-08 16:30:33 +02001673err:
Archit Taneja1ffefe72011-05-12 17:26:24 +05301674 dsi_bus_unlock(dssdev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001675 mutex_unlock(&td->lock);
Tomi Valkeinen8d8aa612010-01-08 16:30:33 +02001676 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001677}
1678
1679static bool taal_get_mirror(struct omap_dss_device *dssdev)
1680{
1681 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001682 int r;
1683
1684 mutex_lock(&td->lock);
1685 r = td->mirror;
1686 mutex_unlock(&td->lock);
1687
1688 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001689}
1690
1691static int taal_run_test(struct omap_dss_device *dssdev, int test_num)
1692{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001693 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001694 u8 id1, id2, id3;
1695 int r;
1696
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001697 mutex_lock(&td->lock);
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001698
1699 if (!td->enabled) {
1700 r = -ENODEV;
1701 goto err1;
1702 }
1703
Archit Taneja1ffefe72011-05-12 17:26:24 +05301704 dsi_bus_lock(dssdev);
Tomi Valkeinen1a75ef42010-01-08 16:21:28 +02001705
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001706 r = taal_wake_up(dssdev);
1707 if (r)
1708 goto err2;
1709
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301710 r = taal_dcs_read_1(td, DCS_GET_ID1, &id1);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001711 if (r)
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001712 goto err2;
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301713 r = taal_dcs_read_1(td, DCS_GET_ID2, &id2);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001714 if (r)
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001715 goto err2;
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301716 r = taal_dcs_read_1(td, DCS_GET_ID3, &id3);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001717 if (r)
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001718 goto err2;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001719
Archit Taneja1ffefe72011-05-12 17:26:24 +05301720 dsi_bus_unlock(dssdev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001721 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001722 return 0;
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001723err2:
Archit Taneja1ffefe72011-05-12 17:26:24 +05301724 dsi_bus_unlock(dssdev);
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001725err1:
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001726 mutex_unlock(&td->lock);
Tomi Valkeinen1a75ef42010-01-08 16:21:28 +02001727 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001728}
1729
1730static int taal_memory_read(struct omap_dss_device *dssdev,
1731 void *buf, size_t size,
1732 u16 x, u16 y, u16 w, u16 h)
1733{
1734 int r;
1735 int first = 1;
1736 int plen;
1737 unsigned buf_used = 0;
Tomi Valkeinenc75d9462010-01-08 16:56:44 +02001738 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1739
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001740 if (size < w * h * 3)
1741 return -ENOMEM;
1742
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001743 mutex_lock(&td->lock);
1744
1745 if (!td->enabled) {
1746 r = -ENODEV;
1747 goto err1;
1748 }
1749
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001750 size = min(w * h * 3,
1751 dssdev->panel.timings.x_res *
1752 dssdev->panel.timings.y_res * 3);
1753
Archit Taneja1ffefe72011-05-12 17:26:24 +05301754 dsi_bus_lock(dssdev);
Tomi Valkeinenc75d9462010-01-08 16:56:44 +02001755
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001756 r = taal_wake_up(dssdev);
1757 if (r)
1758 goto err2;
1759
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001760 /* plen 1 or 2 goes into short packet. until checksum error is fixed,
1761 * use short packets. plen 32 works, but bigger packets seem to cause
1762 * an error. */
1763 if (size % 2)
1764 plen = 1;
1765 else
1766 plen = 2;
1767
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301768 taal_set_update_window(td, x, y, w, h);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001769
Archit Taneja1ffefe72011-05-12 17:26:24 +05301770 r = dsi_vc_set_max_rx_packet_size(dssdev, td->channel, plen);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001771 if (r)
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001772 goto err2;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001773
1774 while (buf_used < size) {
1775 u8 dcs_cmd = first ? 0x2e : 0x3e;
1776 first = 0;
1777
Archit Taneja1ffefe72011-05-12 17:26:24 +05301778 r = dsi_vc_dcs_read(dssdev, td->channel, dcs_cmd,
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001779 buf + buf_used, size - buf_used);
1780
1781 if (r < 0) {
1782 dev_err(&dssdev->dev, "read error\n");
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001783 goto err3;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001784 }
1785
1786 buf_used += r;
1787
1788 if (r < plen) {
1789 dev_err(&dssdev->dev, "short read\n");
1790 break;
1791 }
1792
1793 if (signal_pending(current)) {
1794 dev_err(&dssdev->dev, "signal pending, "
1795 "aborting memory read\n");
1796 r = -ERESTARTSYS;
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001797 goto err3;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001798 }
1799 }
1800
1801 r = buf_used;
1802
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001803err3:
Archit Taneja1ffefe72011-05-12 17:26:24 +05301804 dsi_vc_set_max_rx_packet_size(dssdev, td->channel, 1);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001805err2:
Archit Taneja1ffefe72011-05-12 17:26:24 +05301806 dsi_bus_unlock(dssdev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001807err1:
1808 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001809 return r;
1810}
1811
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001812static void taal_ulps_work(struct work_struct *work)
1813{
1814 struct taal_data *td = container_of(work, struct taal_data,
1815 ulps_work.work);
1816 struct omap_dss_device *dssdev = td->dssdev;
1817
1818 mutex_lock(&td->lock);
1819
1820 if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE || !td->enabled) {
1821 mutex_unlock(&td->lock);
1822 return;
1823 }
1824
Archit Taneja1ffefe72011-05-12 17:26:24 +05301825 dsi_bus_lock(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001826
1827 taal_enter_ulps(dssdev);
1828
Archit Taneja1ffefe72011-05-12 17:26:24 +05301829 dsi_bus_unlock(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001830 mutex_unlock(&td->lock);
1831}
1832
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001833static void taal_esd_work(struct work_struct *work)
1834{
1835 struct taal_data *td = container_of(work, struct taal_data,
1836 esd_work.work);
1837 struct omap_dss_device *dssdev = td->dssdev;
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001838 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001839 u8 state1, state2;
1840 int r;
1841
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001842 mutex_lock(&td->lock);
1843
1844 if (!td->enabled) {
1845 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001846 return;
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001847 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001848
Archit Taneja1ffefe72011-05-12 17:26:24 +05301849 dsi_bus_lock(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001850
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001851 r = taal_wake_up(dssdev);
1852 if (r) {
1853 dev_err(&dssdev->dev, "failed to exit ULPS\n");
1854 goto err;
1855 }
1856
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301857 r = taal_dcs_read_1(td, DCS_RDDSDR, &state1);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001858 if (r) {
1859 dev_err(&dssdev->dev, "failed to read Taal status\n");
1860 goto err;
1861 }
1862
1863 /* Run self diagnostics */
1864 r = taal_sleep_out(td);
1865 if (r) {
1866 dev_err(&dssdev->dev, "failed to run Taal self-diagnostics\n");
1867 goto err;
1868 }
1869
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301870 r = taal_dcs_read_1(td, DCS_RDDSDR, &state2);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001871 if (r) {
1872 dev_err(&dssdev->dev, "failed to read Taal status\n");
1873 goto err;
1874 }
1875
1876 /* Each sleep out command will trigger a self diagnostic and flip
1877 * Bit6 if the test passes.
1878 */
1879 if (!((state1 ^ state2) & (1 << 6))) {
1880 dev_err(&dssdev->dev, "LCD self diagnostics failed\n");
1881 goto err;
1882 }
1883 /* Self-diagnostics result is also shown on TE GPIO line. We need
1884 * to re-enable TE after self diagnostics */
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001885 if (td->te_enabled && panel_data->use_ext_te) {
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301886 r = taal_dcs_write_1(td, DCS_TEAR_ON, 0);
Tomi Valkeinen1189b7f2010-03-01 13:52:10 +02001887 if (r)
1888 goto err;
1889 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001890
Archit Taneja1ffefe72011-05-12 17:26:24 +05301891 dsi_bus_unlock(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001892
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +02001893 taal_queue_esd_work(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001894
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001895 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001896 return;
1897err:
1898 dev_err(&dssdev->dev, "performing LCD reset\n");
1899
Tomi Valkeinenbb5476c2011-03-24 15:00:06 +02001900 taal_panel_reset(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001901
Archit Taneja1ffefe72011-05-12 17:26:24 +05301902 dsi_bus_unlock(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001903
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +02001904 taal_queue_esd_work(dssdev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001905
1906 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001907}
1908
1909static struct omap_dss_driver taal_driver = {
1910 .probe = taal_probe,
Tomi Valkeinen14e4d782011-03-31 12:03:51 +03001911 .remove = __exit_p(taal_remove),
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001912
1913 .enable = taal_enable,
1914 .disable = taal_disable,
1915 .suspend = taal_suspend,
1916 .resume = taal_resume,
1917
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001918 .update = taal_update,
1919 .sync = taal_sync,
1920
Tomi Valkeinen96adcec2010-01-11 13:54:33 +02001921 .get_resolution = taal_get_resolution,
Tomi Valkeinena2699502010-01-11 14:33:40 +02001922 .get_recommended_bpp = omapdss_default_get_recommended_bpp,
1923
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001924 .enable_te = taal_enable_te,
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001925 .get_te = taal_get_te,
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001926
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001927 .set_rotate = taal_rotate,
1928 .get_rotate = taal_get_rotate,
1929 .set_mirror = taal_mirror,
1930 .get_mirror = taal_get_mirror,
1931 .run_test = taal_run_test,
1932 .memory_read = taal_memory_read,
1933
Tomi Valkeinen69b20482010-01-20 12:11:25 +02001934 .get_timings = taal_get_timings,
1935
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001936 .driver = {
1937 .name = "taal",
1938 .owner = THIS_MODULE,
1939 },
1940};
1941
1942static int __init taal_init(void)
1943{
1944 omap_dss_register_driver(&taal_driver);
1945
1946 return 0;
1947}
1948
1949static void __exit taal_exit(void)
1950{
1951 omap_dss_unregister_driver(&taal_driver);
1952}
1953
1954module_init(taal_init);
1955module_exit(taal_exit);
1956
1957MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@nokia.com>");
1958MODULE_DESCRIPTION("Taal Driver");
1959MODULE_LICENSE("GPL");