blob: 4e888ac09b3f4a79de6b263d275684affca45ba0 [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);
Tomi Valkeinen15ffa1d2011-06-16 14:34:06 +03001250 if (!r)
Jani Nikula006db7b2010-04-09 12:25:59 +03001251 r = taal_sleep_in(td);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001252
Jani Nikula006db7b2010-04-09 12:25:59 +03001253 if (r) {
1254 dev_err(&dssdev->dev,
1255 "error disabling panel, issuing HW reset\n");
1256 taal_hw_reset(dssdev);
1257 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001258
Tomi Valkeinen22d6d672010-10-11 11:33:30 +03001259 omapdss_dsi_display_disable(dssdev, true, false);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001260
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001261 td->enabled = 0;
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001262}
1263
Tomi Valkeinenbb5476c2011-03-24 15:00:06 +02001264static int taal_panel_reset(struct omap_dss_device *dssdev)
1265{
1266 dev_err(&dssdev->dev, "performing LCD reset\n");
1267
1268 taal_power_off(dssdev);
1269 taal_hw_reset(dssdev);
1270 return taal_power_on(dssdev);
1271}
1272
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001273static int taal_enable(struct omap_dss_device *dssdev)
1274{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001275 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001276 int r;
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001277
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001278 dev_dbg(&dssdev->dev, "enable\n");
1279
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001280 mutex_lock(&td->lock);
1281
1282 if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) {
1283 r = -EINVAL;
1284 goto err;
1285 }
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001286
Archit Taneja1ffefe72011-05-12 17:26:24 +05301287 dsi_bus_lock(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001288
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001289 r = taal_power_on(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001290
Archit Taneja1ffefe72011-05-12 17:26:24 +05301291 dsi_bus_unlock(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001292
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001293 if (r)
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001294 goto err;
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001295
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +02001296 taal_queue_esd_work(dssdev);
Jani Nikula4571a022010-04-12 10:23:46 +03001297
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001298 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
1299
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001300 mutex_unlock(&td->lock);
1301
1302 return 0;
1303err:
1304 dev_dbg(&dssdev->dev, "enable failed\n");
1305 mutex_unlock(&td->lock);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001306 return r;
1307}
1308
1309static void taal_disable(struct omap_dss_device *dssdev)
1310{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001311 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1312
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001313 dev_dbg(&dssdev->dev, "disable\n");
1314
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001315 mutex_lock(&td->lock);
1316
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001317 taal_cancel_ulps_work(dssdev);
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +02001318 taal_cancel_esd_work(dssdev);
Jani Nikula4571a022010-04-12 10:23:46 +03001319
Archit Taneja1ffefe72011-05-12 17:26:24 +05301320 dsi_bus_lock(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001321
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001322 if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) {
Tomi Valkeinene8945672011-05-31 16:39:01 +03001323 int r;
1324
1325 r = taal_wake_up(dssdev);
1326 if (!r)
1327 taal_power_off(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001328 }
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001329
Archit Taneja1ffefe72011-05-12 17:26:24 +05301330 dsi_bus_unlock(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001331
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001332 dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001333
1334 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001335}
1336
1337static int taal_suspend(struct omap_dss_device *dssdev)
1338{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001339 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1340 int r;
1341
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001342 dev_dbg(&dssdev->dev, "suspend\n");
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001343
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001344 mutex_lock(&td->lock);
1345
1346 if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) {
1347 r = -EINVAL;
1348 goto err;
1349 }
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001350
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001351 taal_cancel_ulps_work(dssdev);
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +02001352 taal_cancel_esd_work(dssdev);
Jani Nikula4571a022010-04-12 10:23:46 +03001353
Archit Taneja1ffefe72011-05-12 17:26:24 +05301354 dsi_bus_lock(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001355
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001356 r = taal_wake_up(dssdev);
1357 if (!r)
1358 taal_power_off(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001359
Archit Taneja1ffefe72011-05-12 17:26:24 +05301360 dsi_bus_unlock(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001361
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001362 dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001363
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001364 mutex_unlock(&td->lock);
1365
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001366 return 0;
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001367err:
1368 mutex_unlock(&td->lock);
1369 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001370}
1371
1372static int taal_resume(struct omap_dss_device *dssdev)
1373{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001374 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001375 int r;
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001376
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001377 dev_dbg(&dssdev->dev, "resume\n");
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001378
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001379 mutex_lock(&td->lock);
1380
1381 if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) {
1382 r = -EINVAL;
1383 goto err;
1384 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001385
Archit Taneja1ffefe72011-05-12 17:26:24 +05301386 dsi_bus_lock(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001387
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001388 r = taal_power_on(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001389
Archit Taneja1ffefe72011-05-12 17:26:24 +05301390 dsi_bus_unlock(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001391
Jani Nikula4571a022010-04-12 10:23:46 +03001392 if (r) {
Jani Nikulafed44b72010-04-12 10:25:21 +03001393 dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
Jani Nikula4571a022010-04-12 10:23:46 +03001394 } else {
Jani Nikulafed44b72010-04-12 10:25:21 +03001395 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +02001396 taal_queue_esd_work(dssdev);
Jani Nikula4571a022010-04-12 10:23:46 +03001397 }
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001398
1399 mutex_unlock(&td->lock);
1400
1401 return r;
1402err:
1403 mutex_unlock(&td->lock);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001404 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001405}
1406
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001407static void taal_framedone_cb(int err, void *data)
1408{
1409 struct omap_dss_device *dssdev = data;
1410 dev_dbg(&dssdev->dev, "framedone, err %d\n", err);
Archit Taneja1ffefe72011-05-12 17:26:24 +05301411 dsi_bus_unlock(dssdev);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001412}
1413
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001414static irqreturn_t taal_te_isr(int irq, void *data)
1415{
1416 struct omap_dss_device *dssdev = data;
1417 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1418 int old;
1419 int r;
1420
1421 old = atomic_cmpxchg(&td->do_update, 1, 0);
1422
1423 if (old) {
1424 cancel_delayed_work(&td->te_timeout_work);
1425
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301426 r = omap_dsi_update(dssdev, td->channel,
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001427 td->update_region.x,
1428 td->update_region.y,
1429 td->update_region.w,
1430 td->update_region.h,
1431 taal_framedone_cb, dssdev);
1432 if (r)
1433 goto err;
1434 }
1435
1436 return IRQ_HANDLED;
1437err:
1438 dev_err(&dssdev->dev, "start update failed\n");
Archit Taneja1ffefe72011-05-12 17:26:24 +05301439 dsi_bus_unlock(dssdev);
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001440 return IRQ_HANDLED;
1441}
1442
1443static void taal_te_timeout_work_callback(struct work_struct *work)
1444{
1445 struct taal_data *td = container_of(work, struct taal_data,
1446 te_timeout_work.work);
1447 struct omap_dss_device *dssdev = td->dssdev;
1448
1449 dev_err(&dssdev->dev, "TE not received for 250ms!\n");
1450
1451 atomic_set(&td->do_update, 0);
Archit Taneja1ffefe72011-05-12 17:26:24 +05301452 dsi_bus_unlock(dssdev);
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001453}
1454
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001455static int taal_update(struct omap_dss_device *dssdev,
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001456 u16 x, u16 y, u16 w, u16 h)
1457{
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001458 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001459 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001460 int r;
1461
1462 dev_dbg(&dssdev->dev, "update %d, %d, %d x %d\n", x, y, w, h);
1463
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001464 mutex_lock(&td->lock);
Archit Taneja1ffefe72011-05-12 17:26:24 +05301465 dsi_bus_lock(dssdev);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001466
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001467 r = taal_wake_up(dssdev);
1468 if (r)
1469 goto err;
1470
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001471 if (!td->enabled) {
1472 r = 0;
1473 goto err;
1474 }
1475
Tomi Valkeinen26a8c252010-06-09 15:31:34 +03001476 r = omap_dsi_prepare_update(dssdev, &x, &y, &w, &h, true);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001477 if (r)
1478 goto err;
1479
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301480 r = taal_set_update_window(td, x, y, w, h);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001481 if (r)
1482 goto err;
1483
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001484 if (td->te_enabled && panel_data->use_ext_te) {
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001485 td->update_region.x = x;
1486 td->update_region.y = y;
1487 td->update_region.w = w;
1488 td->update_region.h = h;
1489 barrier();
1490 schedule_delayed_work(&td->te_timeout_work,
1491 msecs_to_jiffies(250));
1492 atomic_set(&td->do_update, 1);
1493 } else {
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301494 r = omap_dsi_update(dssdev, td->channel, x, y, w, h,
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001495 taal_framedone_cb, dssdev);
1496 if (r)
1497 goto err;
1498 }
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001499
1500 /* note: no bus_unlock here. unlock is in framedone_cb */
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001501 mutex_unlock(&td->lock);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001502 return 0;
1503err:
Archit Taneja1ffefe72011-05-12 17:26:24 +05301504 dsi_bus_unlock(dssdev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001505 mutex_unlock(&td->lock);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001506 return r;
1507}
1508
1509static int taal_sync(struct omap_dss_device *dssdev)
1510{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001511 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1512
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001513 dev_dbg(&dssdev->dev, "sync\n");
1514
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001515 mutex_lock(&td->lock);
Archit Taneja1ffefe72011-05-12 17:26:24 +05301516 dsi_bus_lock(dssdev);
1517 dsi_bus_unlock(dssdev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001518 mutex_unlock(&td->lock);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001519
1520 dev_dbg(&dssdev->dev, "sync done\n");
1521
1522 return 0;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001523}
1524
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001525static int _taal_enable_te(struct omap_dss_device *dssdev, bool enable)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001526{
Jani Nikulae7f6c3f2010-04-15 12:55:38 +03001527 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001528 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001529 int r;
1530
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001531 if (enable)
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301532 r = taal_dcs_write_1(td, DCS_TEAR_ON, 0);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001533 else
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301534 r = taal_dcs_write_0(td, DCS_TEAR_OFF);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001535
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001536 if (!panel_data->use_ext_te)
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001537 omapdss_dsi_enable_te(dssdev, enable);
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001538
Jani Nikulae7f6c3f2010-04-15 12:55:38 +03001539 if (td->panel_config->sleep.enable_te)
1540 msleep(td->panel_config->sleep.enable_te);
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001541
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001542 return r;
1543}
1544
1545static int taal_enable_te(struct omap_dss_device *dssdev, bool enable)
1546{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001547 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001548 int r;
1549
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001550 mutex_lock(&td->lock);
Tomi Valkeinenffb63c92010-05-21 10:09:18 +03001551
1552 if (td->te_enabled == enable)
1553 goto end;
1554
Archit Taneja1ffefe72011-05-12 17:26:24 +05301555 dsi_bus_lock(dssdev);
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001556
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001557 if (td->enabled) {
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001558 r = taal_wake_up(dssdev);
1559 if (r)
1560 goto err;
1561
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001562 r = _taal_enable_te(dssdev, enable);
1563 if (r)
1564 goto err;
1565 }
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001566
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001567 td->te_enabled = enable;
1568
Archit Taneja1ffefe72011-05-12 17:26:24 +05301569 dsi_bus_unlock(dssdev);
Tomi Valkeinenffb63c92010-05-21 10:09:18 +03001570end:
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001571 mutex_unlock(&td->lock);
1572
1573 return 0;
1574err:
Archit Taneja1ffefe72011-05-12 17:26:24 +05301575 dsi_bus_unlock(dssdev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001576 mutex_unlock(&td->lock);
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001577
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001578 return r;
1579}
1580
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001581static int taal_get_te(struct omap_dss_device *dssdev)
1582{
1583 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001584 int r;
1585
1586 mutex_lock(&td->lock);
1587 r = td->te_enabled;
1588 mutex_unlock(&td->lock);
1589
1590 return r;
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001591}
1592
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001593static int taal_rotate(struct omap_dss_device *dssdev, u8 rotate)
1594{
1595 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1596 int r;
1597
1598 dev_dbg(&dssdev->dev, "rotate %d\n", rotate);
1599
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001600 mutex_lock(&td->lock);
Tomi Valkeinenffb63c92010-05-21 10:09:18 +03001601
1602 if (td->rotate == rotate)
1603 goto end;
1604
Archit Taneja1ffefe72011-05-12 17:26:24 +05301605 dsi_bus_lock(dssdev);
Tomi Valkeinen87424e12010-01-08 16:52:48 +02001606
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001607 if (td->enabled) {
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001608 r = taal_wake_up(dssdev);
1609 if (r)
1610 goto err;
1611
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301612 r = taal_set_addr_mode(td, rotate, td->mirror);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001613 if (r)
Tomi Valkeinen87424e12010-01-08 16:52:48 +02001614 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001615 }
1616
1617 td->rotate = rotate;
1618
Archit Taneja1ffefe72011-05-12 17:26:24 +05301619 dsi_bus_unlock(dssdev);
Tomi Valkeinenffb63c92010-05-21 10:09:18 +03001620end:
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001621 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001622 return 0;
Tomi Valkeinen87424e12010-01-08 16:52:48 +02001623err:
Archit Taneja1ffefe72011-05-12 17:26:24 +05301624 dsi_bus_unlock(dssdev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001625 mutex_unlock(&td->lock);
Tomi Valkeinen87424e12010-01-08 16:52:48 +02001626 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001627}
1628
1629static u8 taal_get_rotate(struct omap_dss_device *dssdev)
1630{
1631 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001632 int r;
1633
1634 mutex_lock(&td->lock);
1635 r = td->rotate;
1636 mutex_unlock(&td->lock);
1637
1638 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001639}
1640
1641static int taal_mirror(struct omap_dss_device *dssdev, bool enable)
1642{
1643 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1644 int r;
1645
1646 dev_dbg(&dssdev->dev, "mirror %d\n", enable);
1647
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001648 mutex_lock(&td->lock);
Tomi Valkeinenffb63c92010-05-21 10:09:18 +03001649
1650 if (td->mirror == enable)
1651 goto end;
1652
Archit Taneja1ffefe72011-05-12 17:26:24 +05301653 dsi_bus_lock(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001654 if (td->enabled) {
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001655 r = taal_wake_up(dssdev);
1656 if (r)
1657 goto err;
1658
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301659 r = taal_set_addr_mode(td, td->rotate, enable);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001660 if (r)
Tomi Valkeinen8d8aa612010-01-08 16:30:33 +02001661 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001662 }
1663
1664 td->mirror = enable;
1665
Archit Taneja1ffefe72011-05-12 17:26:24 +05301666 dsi_bus_unlock(dssdev);
Tomi Valkeinenffb63c92010-05-21 10:09:18 +03001667end:
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001668 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001669 return 0;
Tomi Valkeinen8d8aa612010-01-08 16:30:33 +02001670err:
Archit Taneja1ffefe72011-05-12 17:26:24 +05301671 dsi_bus_unlock(dssdev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001672 mutex_unlock(&td->lock);
Tomi Valkeinen8d8aa612010-01-08 16:30:33 +02001673 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001674}
1675
1676static bool taal_get_mirror(struct omap_dss_device *dssdev)
1677{
1678 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001679 int r;
1680
1681 mutex_lock(&td->lock);
1682 r = td->mirror;
1683 mutex_unlock(&td->lock);
1684
1685 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001686}
1687
1688static int taal_run_test(struct omap_dss_device *dssdev, int test_num)
1689{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001690 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001691 u8 id1, id2, id3;
1692 int r;
1693
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001694 mutex_lock(&td->lock);
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001695
1696 if (!td->enabled) {
1697 r = -ENODEV;
1698 goto err1;
1699 }
1700
Archit Taneja1ffefe72011-05-12 17:26:24 +05301701 dsi_bus_lock(dssdev);
Tomi Valkeinen1a75ef42010-01-08 16:21:28 +02001702
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001703 r = taal_wake_up(dssdev);
1704 if (r)
1705 goto err2;
1706
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301707 r = taal_dcs_read_1(td, DCS_GET_ID1, &id1);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001708 if (r)
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001709 goto err2;
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301710 r = taal_dcs_read_1(td, DCS_GET_ID2, &id2);
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_ID3, &id3);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001714 if (r)
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001715 goto err2;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001716
Archit Taneja1ffefe72011-05-12 17:26:24 +05301717 dsi_bus_unlock(dssdev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001718 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001719 return 0;
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001720err2:
Archit Taneja1ffefe72011-05-12 17:26:24 +05301721 dsi_bus_unlock(dssdev);
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001722err1:
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001723 mutex_unlock(&td->lock);
Tomi Valkeinen1a75ef42010-01-08 16:21:28 +02001724 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001725}
1726
1727static int taal_memory_read(struct omap_dss_device *dssdev,
1728 void *buf, size_t size,
1729 u16 x, u16 y, u16 w, u16 h)
1730{
1731 int r;
1732 int first = 1;
1733 int plen;
1734 unsigned buf_used = 0;
Tomi Valkeinenc75d9462010-01-08 16:56:44 +02001735 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1736
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001737 if (size < w * h * 3)
1738 return -ENOMEM;
1739
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001740 mutex_lock(&td->lock);
1741
1742 if (!td->enabled) {
1743 r = -ENODEV;
1744 goto err1;
1745 }
1746
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001747 size = min(w * h * 3,
1748 dssdev->panel.timings.x_res *
1749 dssdev->panel.timings.y_res * 3);
1750
Archit Taneja1ffefe72011-05-12 17:26:24 +05301751 dsi_bus_lock(dssdev);
Tomi Valkeinenc75d9462010-01-08 16:56:44 +02001752
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001753 r = taal_wake_up(dssdev);
1754 if (r)
1755 goto err2;
1756
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001757 /* plen 1 or 2 goes into short packet. until checksum error is fixed,
1758 * use short packets. plen 32 works, but bigger packets seem to cause
1759 * an error. */
1760 if (size % 2)
1761 plen = 1;
1762 else
1763 plen = 2;
1764
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301765 taal_set_update_window(td, x, y, w, h);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001766
Archit Taneja1ffefe72011-05-12 17:26:24 +05301767 r = dsi_vc_set_max_rx_packet_size(dssdev, td->channel, plen);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001768 if (r)
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001769 goto err2;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001770
1771 while (buf_used < size) {
1772 u8 dcs_cmd = first ? 0x2e : 0x3e;
1773 first = 0;
1774
Archit Taneja1ffefe72011-05-12 17:26:24 +05301775 r = dsi_vc_dcs_read(dssdev, td->channel, dcs_cmd,
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001776 buf + buf_used, size - buf_used);
1777
1778 if (r < 0) {
1779 dev_err(&dssdev->dev, "read error\n");
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001780 goto err3;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001781 }
1782
1783 buf_used += r;
1784
1785 if (r < plen) {
1786 dev_err(&dssdev->dev, "short read\n");
1787 break;
1788 }
1789
1790 if (signal_pending(current)) {
1791 dev_err(&dssdev->dev, "signal pending, "
1792 "aborting memory read\n");
1793 r = -ERESTARTSYS;
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001794 goto err3;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001795 }
1796 }
1797
1798 r = buf_used;
1799
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001800err3:
Archit Taneja1ffefe72011-05-12 17:26:24 +05301801 dsi_vc_set_max_rx_packet_size(dssdev, td->channel, 1);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001802err2:
Archit Taneja1ffefe72011-05-12 17:26:24 +05301803 dsi_bus_unlock(dssdev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001804err1:
1805 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001806 return r;
1807}
1808
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001809static void taal_ulps_work(struct work_struct *work)
1810{
1811 struct taal_data *td = container_of(work, struct taal_data,
1812 ulps_work.work);
1813 struct omap_dss_device *dssdev = td->dssdev;
1814
1815 mutex_lock(&td->lock);
1816
1817 if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE || !td->enabled) {
1818 mutex_unlock(&td->lock);
1819 return;
1820 }
1821
Archit Taneja1ffefe72011-05-12 17:26:24 +05301822 dsi_bus_lock(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001823
1824 taal_enter_ulps(dssdev);
1825
Archit Taneja1ffefe72011-05-12 17:26:24 +05301826 dsi_bus_unlock(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001827 mutex_unlock(&td->lock);
1828}
1829
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001830static void taal_esd_work(struct work_struct *work)
1831{
1832 struct taal_data *td = container_of(work, struct taal_data,
1833 esd_work.work);
1834 struct omap_dss_device *dssdev = td->dssdev;
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001835 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001836 u8 state1, state2;
1837 int r;
1838
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001839 mutex_lock(&td->lock);
1840
1841 if (!td->enabled) {
1842 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001843 return;
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001844 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001845
Archit Taneja1ffefe72011-05-12 17:26:24 +05301846 dsi_bus_lock(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001847
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001848 r = taal_wake_up(dssdev);
1849 if (r) {
1850 dev_err(&dssdev->dev, "failed to exit ULPS\n");
1851 goto err;
1852 }
1853
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301854 r = taal_dcs_read_1(td, DCS_RDDSDR, &state1);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001855 if (r) {
1856 dev_err(&dssdev->dev, "failed to read Taal status\n");
1857 goto err;
1858 }
1859
1860 /* Run self diagnostics */
1861 r = taal_sleep_out(td);
1862 if (r) {
1863 dev_err(&dssdev->dev, "failed to run Taal self-diagnostics\n");
1864 goto err;
1865 }
1866
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301867 r = taal_dcs_read_1(td, DCS_RDDSDR, &state2);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001868 if (r) {
1869 dev_err(&dssdev->dev, "failed to read Taal status\n");
1870 goto err;
1871 }
1872
1873 /* Each sleep out command will trigger a self diagnostic and flip
1874 * Bit6 if the test passes.
1875 */
1876 if (!((state1 ^ state2) & (1 << 6))) {
1877 dev_err(&dssdev->dev, "LCD self diagnostics failed\n");
1878 goto err;
1879 }
1880 /* Self-diagnostics result is also shown on TE GPIO line. We need
1881 * to re-enable TE after self diagnostics */
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001882 if (td->te_enabled && panel_data->use_ext_te) {
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301883 r = taal_dcs_write_1(td, DCS_TEAR_ON, 0);
Tomi Valkeinen1189b7f2010-03-01 13:52:10 +02001884 if (r)
1885 goto err;
1886 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001887
Archit Taneja1ffefe72011-05-12 17:26:24 +05301888 dsi_bus_unlock(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001889
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +02001890 taal_queue_esd_work(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001891
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001892 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001893 return;
1894err:
1895 dev_err(&dssdev->dev, "performing LCD reset\n");
1896
Tomi Valkeinenbb5476c2011-03-24 15:00:06 +02001897 taal_panel_reset(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001898
Archit Taneja1ffefe72011-05-12 17:26:24 +05301899 dsi_bus_unlock(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001900
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +02001901 taal_queue_esd_work(dssdev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001902
1903 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001904}
1905
1906static struct omap_dss_driver taal_driver = {
1907 .probe = taal_probe,
Tomi Valkeinen14e4d782011-03-31 12:03:51 +03001908 .remove = __exit_p(taal_remove),
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001909
1910 .enable = taal_enable,
1911 .disable = taal_disable,
1912 .suspend = taal_suspend,
1913 .resume = taal_resume,
1914
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001915 .update = taal_update,
1916 .sync = taal_sync,
1917
Tomi Valkeinen96adcec2010-01-11 13:54:33 +02001918 .get_resolution = taal_get_resolution,
Tomi Valkeinena2699502010-01-11 14:33:40 +02001919 .get_recommended_bpp = omapdss_default_get_recommended_bpp,
1920
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001921 .enable_te = taal_enable_te,
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001922 .get_te = taal_get_te,
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001923
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001924 .set_rotate = taal_rotate,
1925 .get_rotate = taal_get_rotate,
1926 .set_mirror = taal_mirror,
1927 .get_mirror = taal_get_mirror,
1928 .run_test = taal_run_test,
1929 .memory_read = taal_memory_read,
1930
Tomi Valkeinen69b20482010-01-20 12:11:25 +02001931 .get_timings = taal_get_timings,
1932
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001933 .driver = {
1934 .name = "taal",
1935 .owner = THIS_MODULE,
1936 },
1937};
1938
1939static int __init taal_init(void)
1940{
1941 omap_dss_register_driver(&taal_driver);
1942
1943 return 0;
1944}
1945
1946static void __exit taal_exit(void)
1947{
1948 omap_dss_unregister_driver(&taal_driver);
1949}
1950
1951module_init(taal_init);
1952module_exit(taal_exit);
1953
1954MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@nokia.com>");
1955MODULE_DESCRIPTION("Taal Driver");
1956MODULE_LICENSE("GPL");