blob: ddc696d52abe506a9deebde43c07f5fe91c20c1e [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>
Archit Taneja7a7c48f2011-08-25 18:25:03 +053038#include <video/mipi_display.h>
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +020039
40/* DSI Virtual channel. Hardcoded for now. */
41#define TCH 0
42
43#define DCS_READ_NUM_ERRORS 0x05
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +020044#define DCS_BRIGHTNESS 0x51
45#define DCS_CTRL_DISPLAY 0x53
46#define DCS_WRITE_CABC 0x55
47#define DCS_READ_CABC 0x56
48#define DCS_GET_ID1 0xda
49#define DCS_GET_ID2 0xdb
50#define DCS_GET_ID3 0xdc
51
Jani Nikula7ae2fb12010-04-13 10:57:52 +030052static irqreturn_t taal_te_isr(int irq, void *data);
53static void taal_te_timeout_work_callback(struct work_struct *work);
Tomi Valkeinen21df20f2010-03-02 12:13:55 +020054static int _taal_enable_te(struct omap_dss_device *dssdev, bool enable);
55
Tomi Valkeinen1abf7812011-03-24 14:53:27 +020056static int taal_panel_reset(struct omap_dss_device *dssdev);
57
Tomi Valkeinenc8cd4542010-06-09 15:24:46 +030058struct panel_regulator {
59 struct regulator *regulator;
60 const char *name;
61 int min_uV;
62 int max_uV;
63};
64
65static void free_regulators(struct panel_regulator *regulators, int n)
66{
67 int i;
68
69 for (i = 0; i < n; i++) {
70 /* disable/put in reverse order */
71 regulator_disable(regulators[n - i - 1].regulator);
72 regulator_put(regulators[n - i - 1].regulator);
73 }
74}
75
76static int init_regulators(struct omap_dss_device *dssdev,
77 struct panel_regulator *regulators, int n)
78{
79 int r, i, v;
80
81 for (i = 0; i < n; i++) {
82 struct regulator *reg;
83
84 reg = regulator_get(&dssdev->dev, regulators[i].name);
85 if (IS_ERR(reg)) {
86 dev_err(&dssdev->dev, "failed to get regulator %s\n",
87 regulators[i].name);
88 r = PTR_ERR(reg);
89 goto err;
90 }
91
92 /* FIXME: better handling of fixed vs. variable regulators */
93 v = regulator_get_voltage(reg);
94 if (v < regulators[i].min_uV || v > regulators[i].max_uV) {
95 r = regulator_set_voltage(reg, regulators[i].min_uV,
96 regulators[i].max_uV);
97 if (r) {
98 dev_err(&dssdev->dev,
99 "failed to set regulator %s voltage\n",
100 regulators[i].name);
101 regulator_put(reg);
102 goto err;
103 }
104 }
105
106 r = regulator_enable(reg);
107 if (r) {
108 dev_err(&dssdev->dev, "failed to enable regulator %s\n",
109 regulators[i].name);
110 regulator_put(reg);
111 goto err;
112 }
113
114 regulators[i].regulator = reg;
115 }
116
117 return 0;
118
119err:
120 free_regulators(regulators, i);
121
122 return r;
123}
124
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300125/**
126 * struct panel_config - panel configuration
127 * @name: panel name
128 * @type: panel type
129 * @timings: panel resolution
130 * @sleep: various panel specific delays, passed to msleep() if non-zero
131 * @reset_sequence: reset sequence timings, passed to udelay() if non-zero
Tomi Valkeinenc8cd4542010-06-09 15:24:46 +0300132 * @regulators: array of panel regulators
133 * @num_regulators: number of regulators in the array
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300134 */
135struct panel_config {
136 const char *name;
137 int type;
138
139 struct omap_video_timings timings;
140
141 struct {
142 unsigned int sleep_in;
143 unsigned int sleep_out;
144 unsigned int hw_reset;
145 unsigned int enable_te;
146 } sleep;
147
148 struct {
149 unsigned int high;
150 unsigned int low;
151 } reset_sequence;
Tomi Valkeinenc8cd4542010-06-09 15:24:46 +0300152
153 struct panel_regulator *regulators;
154 int num_regulators;
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300155};
156
157enum {
158 PANEL_TAAL,
159};
160
161static struct panel_config panel_configs[] = {
162 {
163 .name = "taal",
164 .type = PANEL_TAAL,
165 .timings = {
166 .x_res = 864,
167 .y_res = 480,
168 },
169 .sleep = {
170 .sleep_in = 5,
171 .sleep_out = 5,
172 .hw_reset = 5,
173 .enable_te = 100, /* possible panel bug */
174 },
175 .reset_sequence = {
176 .high = 10,
177 .low = 10,
178 },
179 },
180};
181
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200182struct taal_data {
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200183 struct mutex lock;
184
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200185 struct backlight_device *bldev;
186
187 unsigned long hw_guard_end; /* next value of jiffies when we can
188 * issue the next sleep in/out command
189 */
190 unsigned long hw_guard_wait; /* max guard time in jiffies */
191
192 struct omap_dss_device *dssdev;
193
194 bool enabled;
195 u8 rotate;
196 bool mirror;
197
198 bool te_enabled;
Jani Nikula7ae2fb12010-04-13 10:57:52 +0300199
200 atomic_t do_update;
201 struct {
202 u16 x;
203 u16 y;
204 u16 w;
205 u16 h;
206 } update_region;
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530207 int channel;
208
Jani Nikula7ae2fb12010-04-13 10:57:52 +0300209 struct delayed_work te_timeout_work;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200210
211 bool use_dsi_bl;
212
213 bool cabc_broken;
214 unsigned cabc_mode;
215
216 bool intro_printed;
217
Tomi Valkeinen883b9ac2011-03-24 15:01:30 +0200218 struct workqueue_struct *workqueue;
219
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200220 struct delayed_work esd_work;
Tomi Valkeinen33a410b2011-03-24 13:58:01 +0200221 unsigned esd_interval;
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300222
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200223 bool ulps_enabled;
224 unsigned ulps_timeout;
225 struct delayed_work ulps_work;
226
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300227 struct panel_config *panel_config;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200228};
229
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300230static inline struct nokia_dsi_panel_data
231*get_panel_data(const struct omap_dss_device *dssdev)
232{
233 return (struct nokia_dsi_panel_data *) dssdev->data;
234}
235
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200236static void taal_esd_work(struct work_struct *work);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200237static void taal_ulps_work(struct work_struct *work);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200238
239static void hw_guard_start(struct taal_data *td, int guard_msec)
240{
241 td->hw_guard_wait = msecs_to_jiffies(guard_msec);
242 td->hw_guard_end = jiffies + td->hw_guard_wait;
243}
244
245static void hw_guard_wait(struct taal_data *td)
246{
247 unsigned long wait = td->hw_guard_end - jiffies;
248
249 if ((long)wait > 0 && wait <= td->hw_guard_wait) {
250 set_current_state(TASK_UNINTERRUPTIBLE);
251 schedule_timeout(wait);
252 }
253}
254
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530255static int taal_dcs_read_1(struct taal_data *td, u8 dcs_cmd, u8 *data)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200256{
257 int r;
258 u8 buf[1];
259
Archit Taneja1ffefe72011-05-12 17:26:24 +0530260 r = dsi_vc_dcs_read(td->dssdev, td->channel, dcs_cmd, buf, 1);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200261
262 if (r < 0)
263 return r;
264
265 *data = buf[0];
266
267 return 0;
268}
269
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530270static int taal_dcs_write_0(struct taal_data *td, u8 dcs_cmd)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200271{
Archit Taneja1ffefe72011-05-12 17:26:24 +0530272 return dsi_vc_dcs_write(td->dssdev, td->channel, &dcs_cmd, 1);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200273}
274
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530275static int taal_dcs_write_1(struct taal_data *td, u8 dcs_cmd, u8 param)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200276{
277 u8 buf[2];
278 buf[0] = dcs_cmd;
279 buf[1] = param;
Archit Taneja1ffefe72011-05-12 17:26:24 +0530280 return dsi_vc_dcs_write(td->dssdev, td->channel, buf, 2);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200281}
282
283static int taal_sleep_in(struct taal_data *td)
284
285{
286 u8 cmd;
287 int r;
288
289 hw_guard_wait(td);
290
Archit Taneja7a7c48f2011-08-25 18:25:03 +0530291 cmd = MIPI_DCS_ENTER_SLEEP_MODE;
Archit Taneja1ffefe72011-05-12 17:26:24 +0530292 r = dsi_vc_dcs_write_nosync(td->dssdev, td->channel, &cmd, 1);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200293 if (r)
294 return r;
295
296 hw_guard_start(td, 120);
297
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300298 if (td->panel_config->sleep.sleep_in)
299 msleep(td->panel_config->sleep.sleep_in);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200300
301 return 0;
302}
303
304static int taal_sleep_out(struct taal_data *td)
305{
306 int r;
307
308 hw_guard_wait(td);
309
Archit Taneja7a7c48f2011-08-25 18:25:03 +0530310 r = taal_dcs_write_0(td, MIPI_DCS_EXIT_SLEEP_MODE);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200311 if (r)
312 return r;
313
314 hw_guard_start(td, 120);
315
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300316 if (td->panel_config->sleep.sleep_out)
317 msleep(td->panel_config->sleep.sleep_out);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200318
319 return 0;
320}
321
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530322static int taal_get_id(struct taal_data *td, u8 *id1, u8 *id2, u8 *id3)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200323{
324 int r;
325
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530326 r = taal_dcs_read_1(td, DCS_GET_ID1, id1);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200327 if (r)
328 return r;
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530329 r = taal_dcs_read_1(td, DCS_GET_ID2, id2);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200330 if (r)
331 return r;
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530332 r = taal_dcs_read_1(td, DCS_GET_ID3, id3);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200333 if (r)
334 return r;
335
336 return 0;
337}
338
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530339static int taal_set_addr_mode(struct taal_data *td, u8 rotate, bool mirror)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200340{
341 int r;
342 u8 mode;
343 int b5, b6, b7;
344
Archit Taneja7a7c48f2011-08-25 18:25:03 +0530345 r = taal_dcs_read_1(td, MIPI_DCS_GET_ADDRESS_MODE, &mode);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200346 if (r)
347 return r;
348
349 switch (rotate) {
350 default:
351 case 0:
352 b7 = 0;
353 b6 = 0;
354 b5 = 0;
355 break;
356 case 1:
357 b7 = 0;
358 b6 = 1;
359 b5 = 1;
360 break;
361 case 2:
362 b7 = 1;
363 b6 = 1;
364 b5 = 0;
365 break;
366 case 3:
367 b7 = 1;
368 b6 = 0;
369 b5 = 1;
370 break;
371 }
372
373 if (mirror)
374 b6 = !b6;
375
376 mode &= ~((1<<7) | (1<<6) | (1<<5));
377 mode |= (b7 << 7) | (b6 << 6) | (b5 << 5);
378
Archit Taneja7a7c48f2011-08-25 18:25:03 +0530379 return taal_dcs_write_1(td, MIPI_DCS_SET_ADDRESS_MODE, mode);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200380}
381
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530382static int taal_set_update_window(struct taal_data *td,
383 u16 x, u16 y, u16 w, u16 h)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200384{
385 int r;
386 u16 x1 = x;
387 u16 x2 = x + w - 1;
388 u16 y1 = y;
389 u16 y2 = y + h - 1;
390
391 u8 buf[5];
Archit Taneja7a7c48f2011-08-25 18:25:03 +0530392 buf[0] = MIPI_DCS_SET_COLUMN_ADDRESS;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200393 buf[1] = (x1 >> 8) & 0xff;
394 buf[2] = (x1 >> 0) & 0xff;
395 buf[3] = (x2 >> 8) & 0xff;
396 buf[4] = (x2 >> 0) & 0xff;
397
Archit Taneja1ffefe72011-05-12 17:26:24 +0530398 r = dsi_vc_dcs_write_nosync(td->dssdev, td->channel, buf, sizeof(buf));
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200399 if (r)
400 return r;
401
Archit Taneja7a7c48f2011-08-25 18:25:03 +0530402 buf[0] = MIPI_DCS_SET_PAGE_ADDRESS;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200403 buf[1] = (y1 >> 8) & 0xff;
404 buf[2] = (y1 >> 0) & 0xff;
405 buf[3] = (y2 >> 8) & 0xff;
406 buf[4] = (y2 >> 0) & 0xff;
407
Archit Taneja1ffefe72011-05-12 17:26:24 +0530408 r = dsi_vc_dcs_write_nosync(td->dssdev, td->channel, buf, sizeof(buf));
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200409 if (r)
410 return r;
411
Archit Taneja1ffefe72011-05-12 17:26:24 +0530412 dsi_vc_send_bta_sync(td->dssdev, td->channel);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200413
414 return r;
415}
416
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +0200417static void taal_queue_esd_work(struct omap_dss_device *dssdev)
418{
419 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
420
421 if (td->esd_interval > 0)
Tomi Valkeinen883b9ac2011-03-24 15:01:30 +0200422 queue_delayed_work(td->workqueue, &td->esd_work,
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +0200423 msecs_to_jiffies(td->esd_interval));
424}
425
426static void taal_cancel_esd_work(struct omap_dss_device *dssdev)
427{
428 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
429
430 cancel_delayed_work(&td->esd_work);
431}
432
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200433static void taal_queue_ulps_work(struct omap_dss_device *dssdev)
434{
435 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
436
437 if (td->ulps_timeout > 0)
438 queue_delayed_work(td->workqueue, &td->ulps_work,
439 msecs_to_jiffies(td->ulps_timeout));
440}
441
442static void taal_cancel_ulps_work(struct omap_dss_device *dssdev)
443{
444 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
445
446 cancel_delayed_work(&td->ulps_work);
447}
448
449static int taal_enter_ulps(struct omap_dss_device *dssdev)
450{
451 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
452 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
453 int r;
454
455 if (td->ulps_enabled)
456 return 0;
457
458 taal_cancel_ulps_work(dssdev);
459
460 r = _taal_enable_te(dssdev, false);
461 if (r)
462 goto err;
463
464 disable_irq(gpio_to_irq(panel_data->ext_te_gpio));
465
466 omapdss_dsi_display_disable(dssdev, false, true);
467
468 td->ulps_enabled = true;
469
470 return 0;
471
472err:
473 dev_err(&dssdev->dev, "enter ULPS failed");
474 taal_panel_reset(dssdev);
475
476 td->ulps_enabled = false;
477
478 taal_queue_ulps_work(dssdev);
479
480 return r;
481}
482
483static int taal_exit_ulps(struct omap_dss_device *dssdev)
484{
485 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
486 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
487 int r;
488
489 if (!td->ulps_enabled)
490 return 0;
491
492 r = omapdss_dsi_display_enable(dssdev);
Tomi Valkeinene8945672011-05-31 16:39:01 +0300493 if (r) {
494 dev_err(&dssdev->dev, "failed to enable DSI\n");
495 goto err1;
496 }
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200497
Archit Taneja1ffefe72011-05-12 17:26:24 +0530498 omapdss_dsi_vc_enable_hs(dssdev, td->channel, true);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200499
500 r = _taal_enable_te(dssdev, true);
Tomi Valkeinene8945672011-05-31 16:39:01 +0300501 if (r) {
502 dev_err(&dssdev->dev, "failed to re-enable TE");
503 goto err2;
504 }
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200505
506 enable_irq(gpio_to_irq(panel_data->ext_te_gpio));
507
508 taal_queue_ulps_work(dssdev);
509
510 td->ulps_enabled = false;
511
512 return 0;
513
Tomi Valkeinene8945672011-05-31 16:39:01 +0300514err2:
515 dev_err(&dssdev->dev, "failed to exit ULPS");
516
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200517 r = taal_panel_reset(dssdev);
Tomi Valkeinene8945672011-05-31 16:39:01 +0300518 if (!r) {
519 enable_irq(gpio_to_irq(panel_data->ext_te_gpio));
520 td->ulps_enabled = false;
521 }
522err1:
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200523 taal_queue_ulps_work(dssdev);
524
525 return r;
526}
527
528static int taal_wake_up(struct omap_dss_device *dssdev)
529{
530 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
531
532 if (td->ulps_enabled)
533 return taal_exit_ulps(dssdev);
534
535 taal_cancel_ulps_work(dssdev);
536 taal_queue_ulps_work(dssdev);
537 return 0;
538}
539
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200540static int taal_bl_update_status(struct backlight_device *dev)
541{
542 struct omap_dss_device *dssdev = dev_get_drvdata(&dev->dev);
543 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300544 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200545 int r;
546 int level;
547
548 if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
549 dev->props.power == FB_BLANK_UNBLANK)
550 level = dev->props.brightness;
551 else
552 level = 0;
553
554 dev_dbg(&dssdev->dev, "update brightness to %d\n", level);
555
Tomi Valkeinen1cbc8702010-04-15 16:41:07 +0300556 mutex_lock(&td->lock);
557
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200558 if (td->use_dsi_bl) {
559 if (td->enabled) {
Archit Taneja1ffefe72011-05-12 17:26:24 +0530560 dsi_bus_lock(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200561
562 r = taal_wake_up(dssdev);
563 if (!r)
564 r = taal_dcs_write_1(td, DCS_BRIGHTNESS, level);
565
Archit Taneja1ffefe72011-05-12 17:26:24 +0530566 dsi_bus_unlock(dssdev);
Tomi Valkeinen1cbc8702010-04-15 16:41:07 +0300567 } else {
568 r = 0;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200569 }
570 } else {
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300571 if (!panel_data->set_backlight)
Tomi Valkeinen1cbc8702010-04-15 16:41:07 +0300572 r = -EINVAL;
573 else
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300574 r = panel_data->set_backlight(dssdev, level);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200575 }
576
Tomi Valkeinen1cbc8702010-04-15 16:41:07 +0300577 mutex_unlock(&td->lock);
578
579 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200580}
581
582static int taal_bl_get_intensity(struct backlight_device *dev)
583{
584 if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
585 dev->props.power == FB_BLANK_UNBLANK)
586 return dev->props.brightness;
587
588 return 0;
589}
590
Lionel Debrouxacc24722010-11-16 14:14:02 +0100591static const struct backlight_ops taal_bl_ops = {
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200592 .get_brightness = taal_bl_get_intensity,
593 .update_status = taal_bl_update_status,
594};
595
596static void taal_get_timings(struct omap_dss_device *dssdev,
597 struct omap_video_timings *timings)
598{
599 *timings = dssdev->panel.timings;
600}
601
602static void taal_get_resolution(struct omap_dss_device *dssdev,
603 u16 *xres, u16 *yres)
604{
605 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
606
607 if (td->rotate == 0 || td->rotate == 2) {
608 *xres = dssdev->panel.timings.x_res;
609 *yres = dssdev->panel.timings.y_res;
610 } else {
611 *yres = dssdev->panel.timings.x_res;
612 *xres = dssdev->panel.timings.y_res;
613 }
614}
615
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200616static ssize_t taal_num_errors_show(struct device *dev,
617 struct device_attribute *attr, char *buf)
618{
619 struct omap_dss_device *dssdev = to_dss_device(dev);
620 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
621 u8 errors;
622 int r;
623
Jani Nikula6b316712010-04-28 11:15:18 +0300624 mutex_lock(&td->lock);
625
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200626 if (td->enabled) {
Archit Taneja1ffefe72011-05-12 17:26:24 +0530627 dsi_bus_lock(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200628
629 r = taal_wake_up(dssdev);
630 if (!r)
631 r = taal_dcs_read_1(td, DCS_READ_NUM_ERRORS, &errors);
632
Archit Taneja1ffefe72011-05-12 17:26:24 +0530633 dsi_bus_unlock(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200634 } else {
635 r = -ENODEV;
636 }
637
Jani Nikula6b316712010-04-28 11:15:18 +0300638 mutex_unlock(&td->lock);
639
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200640 if (r)
641 return r;
642
643 return snprintf(buf, PAGE_SIZE, "%d\n", errors);
644}
645
646static ssize_t taal_hw_revision_show(struct device *dev,
647 struct device_attribute *attr, char *buf)
648{
649 struct omap_dss_device *dssdev = to_dss_device(dev);
650 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
651 u8 id1, id2, id3;
652 int r;
653
Jani Nikula6b316712010-04-28 11:15:18 +0300654 mutex_lock(&td->lock);
655
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200656 if (td->enabled) {
Archit Taneja1ffefe72011-05-12 17:26:24 +0530657 dsi_bus_lock(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200658
659 r = taal_wake_up(dssdev);
660 if (!r)
661 r = taal_get_id(td, &id1, &id2, &id3);
662
Archit Taneja1ffefe72011-05-12 17:26:24 +0530663 dsi_bus_unlock(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200664 } else {
665 r = -ENODEV;
666 }
667
Jani Nikula6b316712010-04-28 11:15:18 +0300668 mutex_unlock(&td->lock);
669
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200670 if (r)
671 return r;
672
673 return snprintf(buf, PAGE_SIZE, "%02x.%02x.%02x\n", id1, id2, id3);
674}
675
676static const char *cabc_modes[] = {
677 "off", /* used also always when CABC is not supported */
678 "ui",
679 "still-image",
680 "moving-image",
681};
682
683static ssize_t show_cabc_mode(struct device *dev,
684 struct device_attribute *attr,
685 char *buf)
686{
687 struct omap_dss_device *dssdev = to_dss_device(dev);
688 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
689 const char *mode_str;
690 int mode;
691 int len;
692
693 mode = td->cabc_mode;
694
695 mode_str = "unknown";
696 if (mode >= 0 && mode < ARRAY_SIZE(cabc_modes))
697 mode_str = cabc_modes[mode];
698 len = snprintf(buf, PAGE_SIZE, "%s\n", mode_str);
699
700 return len < PAGE_SIZE - 1 ? len : PAGE_SIZE - 1;
701}
702
703static ssize_t store_cabc_mode(struct device *dev,
704 struct device_attribute *attr,
705 const char *buf, size_t count)
706{
707 struct omap_dss_device *dssdev = to_dss_device(dev);
708 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
709 int i;
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200710 int r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200711
712 for (i = 0; i < ARRAY_SIZE(cabc_modes); i++) {
713 if (sysfs_streq(cabc_modes[i], buf))
714 break;
715 }
716
717 if (i == ARRAY_SIZE(cabc_modes))
718 return -EINVAL;
719
Jani Nikula6b316712010-04-28 11:15:18 +0300720 mutex_lock(&td->lock);
721
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200722 if (td->enabled) {
Archit Taneja1ffefe72011-05-12 17:26:24 +0530723 dsi_bus_lock(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200724
725 if (!td->cabc_broken) {
726 r = taal_wake_up(dssdev);
727 if (r)
728 goto err;
729
730 r = taal_dcs_write_1(td, DCS_WRITE_CABC, i);
731 if (r)
732 goto err;
733 }
734
Archit Taneja1ffefe72011-05-12 17:26:24 +0530735 dsi_bus_unlock(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200736 }
737
738 td->cabc_mode = i;
739
Jani Nikula6b316712010-04-28 11:15:18 +0300740 mutex_unlock(&td->lock);
741
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200742 return count;
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200743err:
Archit Taneja1ffefe72011-05-12 17:26:24 +0530744 dsi_bus_unlock(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200745 mutex_unlock(&td->lock);
746 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200747}
748
749static ssize_t show_cabc_available_modes(struct device *dev,
750 struct device_attribute *attr,
751 char *buf)
752{
753 int len;
754 int i;
755
756 for (i = 0, len = 0;
757 len < PAGE_SIZE && i < ARRAY_SIZE(cabc_modes); i++)
758 len += snprintf(&buf[len], PAGE_SIZE - len, "%s%s%s",
759 i ? " " : "", cabc_modes[i],
760 i == ARRAY_SIZE(cabc_modes) - 1 ? "\n" : "");
761
762 return len < PAGE_SIZE ? len : PAGE_SIZE - 1;
763}
764
Tomi Valkeinen1f8fa452011-03-24 14:06:51 +0200765static ssize_t taal_store_esd_interval(struct device *dev,
766 struct device_attribute *attr,
767 const char *buf, size_t count)
768{
769 struct omap_dss_device *dssdev = to_dss_device(dev);
770 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
771
772 unsigned long t;
773 int r;
774
775 r = strict_strtoul(buf, 10, &t);
776 if (r)
777 return r;
778
779 mutex_lock(&td->lock);
780 taal_cancel_esd_work(dssdev);
781 td->esd_interval = t;
782 if (td->enabled)
783 taal_queue_esd_work(dssdev);
784 mutex_unlock(&td->lock);
785
786 return count;
787}
788
789static ssize_t taal_show_esd_interval(struct device *dev,
790 struct device_attribute *attr,
791 char *buf)
792{
793 struct omap_dss_device *dssdev = to_dss_device(dev);
794 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
795 unsigned t;
796
797 mutex_lock(&td->lock);
798 t = td->esd_interval;
799 mutex_unlock(&td->lock);
800
801 return snprintf(buf, PAGE_SIZE, "%u\n", t);
802}
803
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200804static ssize_t taal_store_ulps(struct device *dev,
805 struct device_attribute *attr,
806 const char *buf, size_t count)
807{
808 struct omap_dss_device *dssdev = to_dss_device(dev);
809 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
810 unsigned long t;
811 int r;
812
813 r = strict_strtoul(buf, 10, &t);
814 if (r)
815 return r;
816
817 mutex_lock(&td->lock);
818
819 if (td->enabled) {
Archit Taneja1ffefe72011-05-12 17:26:24 +0530820 dsi_bus_lock(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200821
822 if (t)
823 r = taal_enter_ulps(dssdev);
824 else
825 r = taal_wake_up(dssdev);
826
Archit Taneja1ffefe72011-05-12 17:26:24 +0530827 dsi_bus_unlock(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200828 }
829
830 mutex_unlock(&td->lock);
831
832 if (r)
833 return r;
834
835 return count;
836}
837
838static ssize_t taal_show_ulps(struct device *dev,
839 struct device_attribute *attr,
840 char *buf)
841{
842 struct omap_dss_device *dssdev = to_dss_device(dev);
843 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
844 unsigned t;
845
846 mutex_lock(&td->lock);
847 t = td->ulps_enabled;
848 mutex_unlock(&td->lock);
849
850 return snprintf(buf, PAGE_SIZE, "%u\n", t);
851}
852
853static ssize_t taal_store_ulps_timeout(struct device *dev,
854 struct device_attribute *attr,
855 const char *buf, size_t count)
856{
857 struct omap_dss_device *dssdev = to_dss_device(dev);
858 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
859 unsigned long t;
860 int r;
861
862 r = strict_strtoul(buf, 10, &t);
863 if (r)
864 return r;
865
866 mutex_lock(&td->lock);
867 td->ulps_timeout = t;
868
869 if (td->enabled) {
870 /* taal_wake_up will restart the timer */
Archit Taneja1ffefe72011-05-12 17:26:24 +0530871 dsi_bus_lock(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200872 r = taal_wake_up(dssdev);
Archit Taneja1ffefe72011-05-12 17:26:24 +0530873 dsi_bus_unlock(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200874 }
875
876 mutex_unlock(&td->lock);
877
878 if (r)
879 return r;
880
881 return count;
882}
883
884static ssize_t taal_show_ulps_timeout(struct device *dev,
885 struct device_attribute *attr,
886 char *buf)
887{
888 struct omap_dss_device *dssdev = to_dss_device(dev);
889 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
890 unsigned t;
891
892 mutex_lock(&td->lock);
893 t = td->ulps_timeout;
894 mutex_unlock(&td->lock);
895
896 return snprintf(buf, PAGE_SIZE, "%u\n", t);
897}
898
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200899static DEVICE_ATTR(num_dsi_errors, S_IRUGO, taal_num_errors_show, NULL);
900static DEVICE_ATTR(hw_revision, S_IRUGO, taal_hw_revision_show, NULL);
901static DEVICE_ATTR(cabc_mode, S_IRUGO | S_IWUSR,
902 show_cabc_mode, store_cabc_mode);
903static DEVICE_ATTR(cabc_available_modes, S_IRUGO,
904 show_cabc_available_modes, NULL);
Tomi Valkeinen1f8fa452011-03-24 14:06:51 +0200905static DEVICE_ATTR(esd_interval, S_IRUGO | S_IWUSR,
906 taal_show_esd_interval, taal_store_esd_interval);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200907static DEVICE_ATTR(ulps, S_IRUGO | S_IWUSR,
908 taal_show_ulps, taal_store_ulps);
909static DEVICE_ATTR(ulps_timeout, S_IRUGO | S_IWUSR,
910 taal_show_ulps_timeout, taal_store_ulps_timeout);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200911
912static struct attribute *taal_attrs[] = {
913 &dev_attr_num_dsi_errors.attr,
914 &dev_attr_hw_revision.attr,
915 &dev_attr_cabc_mode.attr,
916 &dev_attr_cabc_available_modes.attr,
Tomi Valkeinen1f8fa452011-03-24 14:06:51 +0200917 &dev_attr_esd_interval.attr,
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200918 &dev_attr_ulps.attr,
919 &dev_attr_ulps_timeout.attr,
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200920 NULL,
921};
922
923static struct attribute_group taal_attr_group = {
924 .attrs = taal_attrs,
925};
926
Jani Nikula006db7b2010-04-09 12:25:59 +0300927static void taal_hw_reset(struct omap_dss_device *dssdev)
928{
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300929 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300930 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
931
932 if (panel_data->reset_gpio == -1)
Jani Nikula006db7b2010-04-09 12:25:59 +0300933 return;
934
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300935 gpio_set_value(panel_data->reset_gpio, 1);
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300936 if (td->panel_config->reset_sequence.high)
937 udelay(td->panel_config->reset_sequence.high);
Jani Nikula006db7b2010-04-09 12:25:59 +0300938 /* reset the panel */
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300939 gpio_set_value(panel_data->reset_gpio, 0);
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300940 /* assert reset */
941 if (td->panel_config->reset_sequence.low)
942 udelay(td->panel_config->reset_sequence.low);
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300943 gpio_set_value(panel_data->reset_gpio, 1);
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300944 /* wait after releasing reset */
945 if (td->panel_config->sleep.hw_reset)
946 msleep(td->panel_config->sleep.hw_reset);
Jani Nikula006db7b2010-04-09 12:25:59 +0300947}
948
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200949static int taal_probe(struct omap_dss_device *dssdev)
950{
Matthew Garretta19a6ee2010-02-17 16:39:44 -0500951 struct backlight_properties props;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200952 struct taal_data *td;
953 struct backlight_device *bldev;
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300954 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300955 struct panel_config *panel_config = NULL;
956 int r, i;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200957
958 dev_dbg(&dssdev->dev, "probe\n");
959
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300960 if (!panel_data || !panel_data->name) {
961 r = -EINVAL;
962 goto err;
963 }
964
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300965 for (i = 0; i < ARRAY_SIZE(panel_configs); i++) {
966 if (strcmp(panel_data->name, panel_configs[i].name) == 0) {
967 panel_config = &panel_configs[i];
968 break;
969 }
970 }
971
972 if (!panel_config) {
973 r = -EINVAL;
974 goto err;
975 }
976
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200977 dssdev->panel.config = OMAP_DSS_LCD_TFT;
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300978 dssdev->panel.timings = panel_config->timings;
Archit Tanejaa3b3cc22011-09-08 18:42:16 +0530979 dssdev->panel.dsi_pix_fmt = OMAP_DSS_DSI_FMT_RGB888;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200980
981 td = kzalloc(sizeof(*td), GFP_KERNEL);
982 if (!td) {
983 r = -ENOMEM;
Jani Nikulad2b65782010-04-15 12:24:36 +0300984 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200985 }
986 td->dssdev = dssdev;
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300987 td->panel_config = panel_config;
Tomi Valkeinen33a410b2011-03-24 13:58:01 +0200988 td->esd_interval = panel_data->esd_interval;
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200989 td->ulps_enabled = false;
990 td->ulps_timeout = panel_data->ulps_timeout;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200991
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200992 mutex_init(&td->lock);
993
Jani Nikula7ae2fb12010-04-13 10:57:52 +0300994 atomic_set(&td->do_update, 0);
995
Tomi Valkeinenc8cd4542010-06-09 15:24:46 +0300996 r = init_regulators(dssdev, panel_config->regulators,
997 panel_config->num_regulators);
998 if (r)
999 goto err_reg;
1000
Tomi Valkeinen883b9ac2011-03-24 15:01:30 +02001001 td->workqueue = create_singlethread_workqueue("taal_esd");
1002 if (td->workqueue == NULL) {
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001003 dev_err(&dssdev->dev, "can't create ESD workqueue\n");
1004 r = -ENOMEM;
Jani Nikulad2b65782010-04-15 12:24:36 +03001005 goto err_wq;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001006 }
1007 INIT_DELAYED_WORK_DEFERRABLE(&td->esd_work, taal_esd_work);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001008 INIT_DELAYED_WORK(&td->ulps_work, taal_ulps_work);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001009
1010 dev_set_drvdata(&dssdev->dev, td);
1011
Jani Nikula006db7b2010-04-09 12:25:59 +03001012 taal_hw_reset(dssdev);
1013
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001014 /* if no platform set_backlight() defined, presume DSI backlight
1015 * control */
Matthew Garretta19a6ee2010-02-17 16:39:44 -05001016 memset(&props, 0, sizeof(struct backlight_properties));
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001017 if (!panel_data->set_backlight)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001018 td->use_dsi_bl = true;
1019
Matthew Garretta19a6ee2010-02-17 16:39:44 -05001020 if (td->use_dsi_bl)
1021 props.max_brightness = 255;
1022 else
1023 props.max_brightness = 127;
Matthew Garrettbb7ca742011-03-22 16:30:21 -07001024
1025 props.type = BACKLIGHT_RAW;
Archit Taneja634610a2011-05-12 17:26:30 +05301026 bldev = backlight_device_register(dev_name(&dssdev->dev), &dssdev->dev,
1027 dssdev, &taal_bl_ops, &props);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001028 if (IS_ERR(bldev)) {
1029 r = PTR_ERR(bldev);
Jani Nikulad2b65782010-04-15 12:24:36 +03001030 goto err_bl;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001031 }
1032
1033 td->bldev = bldev;
1034
1035 bldev->props.fb_blank = FB_BLANK_UNBLANK;
1036 bldev->props.power = FB_BLANK_UNBLANK;
Matthew Garretta19a6ee2010-02-17 16:39:44 -05001037 if (td->use_dsi_bl)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001038 bldev->props.brightness = 255;
Matthew Garretta19a6ee2010-02-17 16:39:44 -05001039 else
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001040 bldev->props.brightness = 127;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001041
1042 taal_bl_update_status(bldev);
1043
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001044 if (panel_data->use_ext_te) {
1045 int gpio = panel_data->ext_te_gpio;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001046
1047 r = gpio_request(gpio, "taal irq");
1048 if (r) {
1049 dev_err(&dssdev->dev, "GPIO request failed\n");
Jani Nikulad2b65782010-04-15 12:24:36 +03001050 goto err_gpio;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001051 }
1052
1053 gpio_direction_input(gpio);
1054
1055 r = request_irq(gpio_to_irq(gpio), taal_te_isr,
1056 IRQF_DISABLED | IRQF_TRIGGER_RISING,
1057 "taal vsync", dssdev);
1058
1059 if (r) {
1060 dev_err(&dssdev->dev, "IRQ request failed\n");
1061 gpio_free(gpio);
Jani Nikulad2b65782010-04-15 12:24:36 +03001062 goto err_irq;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001063 }
1064
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001065 INIT_DELAYED_WORK_DEFERRABLE(&td->te_timeout_work,
1066 taal_te_timeout_work_callback);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001067
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001068 dev_dbg(&dssdev->dev, "Using GPIO TE\n");
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001069 }
1070
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301071 r = omap_dsi_request_vc(dssdev, &td->channel);
1072 if (r) {
1073 dev_err(&dssdev->dev, "failed to get virtual channel\n");
1074 goto err_req_vc;
1075 }
1076
1077 r = omap_dsi_set_vc_id(dssdev, td->channel, TCH);
1078 if (r) {
1079 dev_err(&dssdev->dev, "failed to set VC_ID\n");
1080 goto err_vc_id;
1081 }
1082
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001083 r = sysfs_create_group(&dssdev->dev.kobj, &taal_attr_group);
1084 if (r) {
1085 dev_err(&dssdev->dev, "failed to create sysfs files\n");
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301086 goto err_vc_id;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001087 }
1088
1089 return 0;
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301090
1091err_vc_id:
1092 omap_dsi_release_vc(dssdev, td->channel);
1093err_req_vc:
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001094 if (panel_data->use_ext_te)
1095 free_irq(gpio_to_irq(panel_data->ext_te_gpio), dssdev);
Jani Nikulad2b65782010-04-15 12:24:36 +03001096err_irq:
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001097 if (panel_data->use_ext_te)
1098 gpio_free(panel_data->ext_te_gpio);
Jani Nikulad2b65782010-04-15 12:24:36 +03001099err_gpio:
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001100 backlight_device_unregister(bldev);
Jani Nikulad2b65782010-04-15 12:24:36 +03001101err_bl:
Tomi Valkeinen883b9ac2011-03-24 15:01:30 +02001102 destroy_workqueue(td->workqueue);
Jani Nikulad2b65782010-04-15 12:24:36 +03001103err_wq:
Tomi Valkeinenc8cd4542010-06-09 15:24:46 +03001104 free_regulators(panel_config->regulators, panel_config->num_regulators);
1105err_reg:
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001106 kfree(td);
Jani Nikulad2b65782010-04-15 12:24:36 +03001107err:
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001108 return r;
1109}
1110
Tomi Valkeinen14e4d782011-03-31 12:03:51 +03001111static void __exit taal_remove(struct omap_dss_device *dssdev)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001112{
1113 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001114 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001115 struct backlight_device *bldev;
1116
1117 dev_dbg(&dssdev->dev, "remove\n");
1118
1119 sysfs_remove_group(&dssdev->dev.kobj, &taal_attr_group);
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301120 omap_dsi_release_vc(dssdev, td->channel);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001121
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001122 if (panel_data->use_ext_te) {
1123 int gpio = panel_data->ext_te_gpio;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001124 free_irq(gpio_to_irq(gpio), dssdev);
1125 gpio_free(gpio);
1126 }
1127
1128 bldev = td->bldev;
1129 bldev->props.power = FB_BLANK_POWERDOWN;
1130 taal_bl_update_status(bldev);
1131 backlight_device_unregister(bldev);
1132
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001133 taal_cancel_ulps_work(dssdev);
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +02001134 taal_cancel_esd_work(dssdev);
Tomi Valkeinen883b9ac2011-03-24 15:01:30 +02001135 destroy_workqueue(td->workqueue);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001136
Jani Nikula006db7b2010-04-09 12:25:59 +03001137 /* reset, to be sure that the panel is in a valid state */
1138 taal_hw_reset(dssdev);
1139
Tomi Valkeinenc8cd4542010-06-09 15:24:46 +03001140 free_regulators(td->panel_config->regulators,
1141 td->panel_config->num_regulators);
1142
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001143 kfree(td);
1144}
1145
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001146static int taal_power_on(struct omap_dss_device *dssdev)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001147{
1148 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1149 u8 id1, id2, id3;
1150 int r;
1151
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001152 r = omapdss_dsi_display_enable(dssdev);
1153 if (r) {
1154 dev_err(&dssdev->dev, "failed to enable DSI\n");
1155 goto err0;
1156 }
1157
Jani Nikula006db7b2010-04-09 12:25:59 +03001158 taal_hw_reset(dssdev);
1159
Archit Taneja1ffefe72011-05-12 17:26:24 +05301160 omapdss_dsi_vc_enable_hs(dssdev, td->channel, false);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001161
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001162 r = taal_sleep_out(td);
1163 if (r)
1164 goto err;
1165
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301166 r = taal_get_id(td, &id1, &id2, &id3);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001167 if (r)
1168 goto err;
1169
Jani Nikula1e8943d2010-04-15 17:07:39 +03001170 /* on early Taal revisions CABC is broken */
1171 if (td->panel_config->type == PANEL_TAAL &&
1172 (id2 == 0x00 || id2 == 0xff || id2 == 0x81))
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001173 td->cabc_broken = true;
1174
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301175 r = taal_dcs_write_1(td, DCS_BRIGHTNESS, 0xff);
Jani Nikulaf2a8b752010-04-09 14:15:12 +03001176 if (r)
1177 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001178
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301179 r = taal_dcs_write_1(td, DCS_CTRL_DISPLAY,
Jani Nikulaf2a8b752010-04-09 14:15:12 +03001180 (1<<2) | (1<<5)); /* BL | BCTRL */
1181 if (r)
1182 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001183
Archit Taneja7a7c48f2011-08-25 18:25:03 +05301184 r = taal_dcs_write_1(td, MIPI_DCS_SET_PIXEL_FORMAT,
1185 MIPI_DCS_PIXEL_FMT_24BIT);
Jani Nikulaf2a8b752010-04-09 14:15:12 +03001186 if (r)
1187 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001188
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301189 r = taal_set_addr_mode(td, td->rotate, td->mirror);
Jani Nikulaf2a8b752010-04-09 14:15:12 +03001190 if (r)
1191 goto err;
1192
1193 if (!td->cabc_broken) {
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301194 r = taal_dcs_write_1(td, DCS_WRITE_CABC, td->cabc_mode);
Jani Nikulaf2a8b752010-04-09 14:15:12 +03001195 if (r)
1196 goto err;
1197 }
1198
Archit Taneja7a7c48f2011-08-25 18:25:03 +05301199 r = taal_dcs_write_0(td, MIPI_DCS_SET_DISPLAY_ON);
Jani Nikulaf2a8b752010-04-09 14:15:12 +03001200 if (r)
1201 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001202
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001203 r = _taal_enable_te(dssdev, td->te_enabled);
1204 if (r)
1205 goto err;
1206
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001207 td->enabled = 1;
1208
1209 if (!td->intro_printed) {
Jani Nikula0f45bdd2010-04-16 14:21:14 +03001210 dev_info(&dssdev->dev, "%s panel revision %02x.%02x.%02x\n",
1211 td->panel_config->name, id1, id2, id3);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001212 if (td->cabc_broken)
1213 dev_info(&dssdev->dev,
1214 "old Taal version, CABC disabled\n");
1215 td->intro_printed = true;
1216 }
1217
Archit Taneja1ffefe72011-05-12 17:26:24 +05301218 omapdss_dsi_vc_enable_hs(dssdev, td->channel, true);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001219
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001220 return 0;
1221err:
Jani Nikula006db7b2010-04-09 12:25:59 +03001222 dev_err(&dssdev->dev, "error while enabling panel, issuing HW reset\n");
1223
1224 taal_hw_reset(dssdev);
1225
Tomi Valkeinen22d6d672010-10-11 11:33:30 +03001226 omapdss_dsi_display_disable(dssdev, true, false);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001227err0:
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001228 return r;
1229}
1230
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001231static void taal_power_off(struct omap_dss_device *dssdev)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001232{
1233 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Jani Nikula006db7b2010-04-09 12:25:59 +03001234 int r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001235
Archit Taneja7a7c48f2011-08-25 18:25:03 +05301236 r = taal_dcs_write_0(td, MIPI_DCS_SET_DISPLAY_OFF);
Tomi Valkeinen15ffa1d2011-06-16 14:34:06 +03001237 if (!r)
Jani Nikula006db7b2010-04-09 12:25:59 +03001238 r = taal_sleep_in(td);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001239
Jani Nikula006db7b2010-04-09 12:25:59 +03001240 if (r) {
1241 dev_err(&dssdev->dev,
1242 "error disabling panel, issuing HW reset\n");
1243 taal_hw_reset(dssdev);
1244 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001245
Tomi Valkeinen22d6d672010-10-11 11:33:30 +03001246 omapdss_dsi_display_disable(dssdev, true, false);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001247
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001248 td->enabled = 0;
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001249}
1250
Tomi Valkeinenbb5476c2011-03-24 15:00:06 +02001251static int taal_panel_reset(struct omap_dss_device *dssdev)
1252{
1253 dev_err(&dssdev->dev, "performing LCD reset\n");
1254
1255 taal_power_off(dssdev);
1256 taal_hw_reset(dssdev);
1257 return taal_power_on(dssdev);
1258}
1259
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001260static int taal_enable(struct omap_dss_device *dssdev)
1261{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001262 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001263 int r;
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001264
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001265 dev_dbg(&dssdev->dev, "enable\n");
1266
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001267 mutex_lock(&td->lock);
1268
1269 if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) {
1270 r = -EINVAL;
1271 goto err;
1272 }
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001273
Archit Taneja1ffefe72011-05-12 17:26:24 +05301274 dsi_bus_lock(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001275
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001276 r = taal_power_on(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001277
Archit Taneja1ffefe72011-05-12 17:26:24 +05301278 dsi_bus_unlock(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001279
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001280 if (r)
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001281 goto err;
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001282
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +02001283 taal_queue_esd_work(dssdev);
Jani Nikula4571a022010-04-12 10:23:46 +03001284
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001285 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
1286
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001287 mutex_unlock(&td->lock);
1288
1289 return 0;
1290err:
1291 dev_dbg(&dssdev->dev, "enable failed\n");
1292 mutex_unlock(&td->lock);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001293 return r;
1294}
1295
1296static void taal_disable(struct omap_dss_device *dssdev)
1297{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001298 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1299
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001300 dev_dbg(&dssdev->dev, "disable\n");
1301
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001302 mutex_lock(&td->lock);
1303
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001304 taal_cancel_ulps_work(dssdev);
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +02001305 taal_cancel_esd_work(dssdev);
Jani Nikula4571a022010-04-12 10:23:46 +03001306
Archit Taneja1ffefe72011-05-12 17:26:24 +05301307 dsi_bus_lock(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001308
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001309 if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) {
Tomi Valkeinene8945672011-05-31 16:39:01 +03001310 int r;
1311
1312 r = taal_wake_up(dssdev);
1313 if (!r)
1314 taal_power_off(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001315 }
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001316
Archit Taneja1ffefe72011-05-12 17:26:24 +05301317 dsi_bus_unlock(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001318
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001319 dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001320
1321 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001322}
1323
1324static int taal_suspend(struct omap_dss_device *dssdev)
1325{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001326 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1327 int r;
1328
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001329 dev_dbg(&dssdev->dev, "suspend\n");
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001330
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001331 mutex_lock(&td->lock);
1332
1333 if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) {
1334 r = -EINVAL;
1335 goto err;
1336 }
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001337
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001338 taal_cancel_ulps_work(dssdev);
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +02001339 taal_cancel_esd_work(dssdev);
Jani Nikula4571a022010-04-12 10:23:46 +03001340
Archit Taneja1ffefe72011-05-12 17:26:24 +05301341 dsi_bus_lock(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001342
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001343 r = taal_wake_up(dssdev);
1344 if (!r)
1345 taal_power_off(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001346
Archit Taneja1ffefe72011-05-12 17:26:24 +05301347 dsi_bus_unlock(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001348
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001349 dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001350
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001351 mutex_unlock(&td->lock);
1352
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001353 return 0;
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001354err:
1355 mutex_unlock(&td->lock);
1356 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001357}
1358
1359static int taal_resume(struct omap_dss_device *dssdev)
1360{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001361 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001362 int r;
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001363
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001364 dev_dbg(&dssdev->dev, "resume\n");
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001365
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001366 mutex_lock(&td->lock);
1367
1368 if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) {
1369 r = -EINVAL;
1370 goto err;
1371 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001372
Archit Taneja1ffefe72011-05-12 17:26:24 +05301373 dsi_bus_lock(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001374
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001375 r = taal_power_on(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001376
Archit Taneja1ffefe72011-05-12 17:26:24 +05301377 dsi_bus_unlock(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001378
Jani Nikula4571a022010-04-12 10:23:46 +03001379 if (r) {
Jani Nikulafed44b72010-04-12 10:25:21 +03001380 dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
Jani Nikula4571a022010-04-12 10:23:46 +03001381 } else {
Jani Nikulafed44b72010-04-12 10:25:21 +03001382 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +02001383 taal_queue_esd_work(dssdev);
Jani Nikula4571a022010-04-12 10:23:46 +03001384 }
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001385
1386 mutex_unlock(&td->lock);
1387
1388 return r;
1389err:
1390 mutex_unlock(&td->lock);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001391 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001392}
1393
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001394static void taal_framedone_cb(int err, void *data)
1395{
1396 struct omap_dss_device *dssdev = data;
1397 dev_dbg(&dssdev->dev, "framedone, err %d\n", err);
Archit Taneja1ffefe72011-05-12 17:26:24 +05301398 dsi_bus_unlock(dssdev);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001399}
1400
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001401static irqreturn_t taal_te_isr(int irq, void *data)
1402{
1403 struct omap_dss_device *dssdev = data;
1404 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1405 int old;
1406 int r;
1407
1408 old = atomic_cmpxchg(&td->do_update, 1, 0);
1409
1410 if (old) {
1411 cancel_delayed_work(&td->te_timeout_work);
1412
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301413 r = omap_dsi_update(dssdev, td->channel,
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001414 td->update_region.x,
1415 td->update_region.y,
1416 td->update_region.w,
1417 td->update_region.h,
1418 taal_framedone_cb, dssdev);
1419 if (r)
1420 goto err;
1421 }
1422
1423 return IRQ_HANDLED;
1424err:
1425 dev_err(&dssdev->dev, "start update failed\n");
Archit Taneja1ffefe72011-05-12 17:26:24 +05301426 dsi_bus_unlock(dssdev);
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001427 return IRQ_HANDLED;
1428}
1429
1430static void taal_te_timeout_work_callback(struct work_struct *work)
1431{
1432 struct taal_data *td = container_of(work, struct taal_data,
1433 te_timeout_work.work);
1434 struct omap_dss_device *dssdev = td->dssdev;
1435
1436 dev_err(&dssdev->dev, "TE not received for 250ms!\n");
1437
1438 atomic_set(&td->do_update, 0);
Archit Taneja1ffefe72011-05-12 17:26:24 +05301439 dsi_bus_unlock(dssdev);
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001440}
1441
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001442static int taal_update(struct omap_dss_device *dssdev,
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001443 u16 x, u16 y, u16 w, u16 h)
1444{
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001445 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001446 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001447 int r;
1448
1449 dev_dbg(&dssdev->dev, "update %d, %d, %d x %d\n", x, y, w, h);
1450
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001451 mutex_lock(&td->lock);
Archit Taneja1ffefe72011-05-12 17:26:24 +05301452 dsi_bus_lock(dssdev);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001453
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001454 r = taal_wake_up(dssdev);
1455 if (r)
1456 goto err;
1457
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001458 if (!td->enabled) {
1459 r = 0;
1460 goto err;
1461 }
1462
Tomi Valkeinen26a8c252010-06-09 15:31:34 +03001463 r = omap_dsi_prepare_update(dssdev, &x, &y, &w, &h, true);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001464 if (r)
1465 goto err;
1466
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301467 r = taal_set_update_window(td, x, y, w, h);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001468 if (r)
1469 goto err;
1470
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001471 if (td->te_enabled && panel_data->use_ext_te) {
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001472 td->update_region.x = x;
1473 td->update_region.y = y;
1474 td->update_region.w = w;
1475 td->update_region.h = h;
1476 barrier();
1477 schedule_delayed_work(&td->te_timeout_work,
1478 msecs_to_jiffies(250));
1479 atomic_set(&td->do_update, 1);
1480 } else {
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301481 r = omap_dsi_update(dssdev, td->channel, x, y, w, h,
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001482 taal_framedone_cb, dssdev);
1483 if (r)
1484 goto err;
1485 }
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001486
1487 /* note: no bus_unlock here. unlock is in framedone_cb */
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001488 mutex_unlock(&td->lock);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001489 return 0;
1490err:
Archit Taneja1ffefe72011-05-12 17:26:24 +05301491 dsi_bus_unlock(dssdev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001492 mutex_unlock(&td->lock);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001493 return r;
1494}
1495
1496static int taal_sync(struct omap_dss_device *dssdev)
1497{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001498 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1499
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001500 dev_dbg(&dssdev->dev, "sync\n");
1501
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001502 mutex_lock(&td->lock);
Archit Taneja1ffefe72011-05-12 17:26:24 +05301503 dsi_bus_lock(dssdev);
1504 dsi_bus_unlock(dssdev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001505 mutex_unlock(&td->lock);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001506
1507 dev_dbg(&dssdev->dev, "sync done\n");
1508
1509 return 0;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001510}
1511
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001512static int _taal_enable_te(struct omap_dss_device *dssdev, bool enable)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001513{
Jani Nikulae7f6c3f2010-04-15 12:55:38 +03001514 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001515 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001516 int r;
1517
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001518 if (enable)
Archit Taneja7a7c48f2011-08-25 18:25:03 +05301519 r = taal_dcs_write_1(td, MIPI_DCS_SET_TEAR_ON, 0);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001520 else
Archit Taneja7a7c48f2011-08-25 18:25:03 +05301521 r = taal_dcs_write_0(td, MIPI_DCS_SET_TEAR_OFF);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001522
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001523 if (!panel_data->use_ext_te)
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001524 omapdss_dsi_enable_te(dssdev, enable);
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001525
Jani Nikulae7f6c3f2010-04-15 12:55:38 +03001526 if (td->panel_config->sleep.enable_te)
1527 msleep(td->panel_config->sleep.enable_te);
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001528
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001529 return r;
1530}
1531
1532static int taal_enable_te(struct omap_dss_device *dssdev, bool enable)
1533{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001534 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001535 int r;
1536
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001537 mutex_lock(&td->lock);
Tomi Valkeinenffb63c92010-05-21 10:09:18 +03001538
1539 if (td->te_enabled == enable)
1540 goto end;
1541
Archit Taneja1ffefe72011-05-12 17:26:24 +05301542 dsi_bus_lock(dssdev);
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001543
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001544 if (td->enabled) {
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001545 r = taal_wake_up(dssdev);
1546 if (r)
1547 goto err;
1548
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001549 r = _taal_enable_te(dssdev, enable);
1550 if (r)
1551 goto err;
1552 }
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001553
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001554 td->te_enabled = enable;
1555
Archit Taneja1ffefe72011-05-12 17:26:24 +05301556 dsi_bus_unlock(dssdev);
Tomi Valkeinenffb63c92010-05-21 10:09:18 +03001557end:
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001558 mutex_unlock(&td->lock);
1559
1560 return 0;
1561err:
Archit Taneja1ffefe72011-05-12 17:26:24 +05301562 dsi_bus_unlock(dssdev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001563 mutex_unlock(&td->lock);
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001564
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001565 return r;
1566}
1567
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001568static int taal_get_te(struct omap_dss_device *dssdev)
1569{
1570 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001571 int r;
1572
1573 mutex_lock(&td->lock);
1574 r = td->te_enabled;
1575 mutex_unlock(&td->lock);
1576
1577 return r;
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001578}
1579
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001580static int taal_rotate(struct omap_dss_device *dssdev, u8 rotate)
1581{
1582 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1583 int r;
1584
1585 dev_dbg(&dssdev->dev, "rotate %d\n", rotate);
1586
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001587 mutex_lock(&td->lock);
Tomi Valkeinenffb63c92010-05-21 10:09:18 +03001588
1589 if (td->rotate == rotate)
1590 goto end;
1591
Archit Taneja1ffefe72011-05-12 17:26:24 +05301592 dsi_bus_lock(dssdev);
Tomi Valkeinen87424e12010-01-08 16:52:48 +02001593
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001594 if (td->enabled) {
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001595 r = taal_wake_up(dssdev);
1596 if (r)
1597 goto err;
1598
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301599 r = taal_set_addr_mode(td, rotate, td->mirror);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001600 if (r)
Tomi Valkeinen87424e12010-01-08 16:52:48 +02001601 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001602 }
1603
1604 td->rotate = rotate;
1605
Archit Taneja1ffefe72011-05-12 17:26:24 +05301606 dsi_bus_unlock(dssdev);
Tomi Valkeinenffb63c92010-05-21 10:09:18 +03001607end:
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001608 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001609 return 0;
Tomi Valkeinen87424e12010-01-08 16:52:48 +02001610err:
Archit Taneja1ffefe72011-05-12 17:26:24 +05301611 dsi_bus_unlock(dssdev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001612 mutex_unlock(&td->lock);
Tomi Valkeinen87424e12010-01-08 16:52:48 +02001613 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001614}
1615
1616static u8 taal_get_rotate(struct omap_dss_device *dssdev)
1617{
1618 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001619 int r;
1620
1621 mutex_lock(&td->lock);
1622 r = td->rotate;
1623 mutex_unlock(&td->lock);
1624
1625 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001626}
1627
1628static int taal_mirror(struct omap_dss_device *dssdev, bool enable)
1629{
1630 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1631 int r;
1632
1633 dev_dbg(&dssdev->dev, "mirror %d\n", enable);
1634
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001635 mutex_lock(&td->lock);
Tomi Valkeinenffb63c92010-05-21 10:09:18 +03001636
1637 if (td->mirror == enable)
1638 goto end;
1639
Archit Taneja1ffefe72011-05-12 17:26:24 +05301640 dsi_bus_lock(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001641 if (td->enabled) {
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001642 r = taal_wake_up(dssdev);
1643 if (r)
1644 goto err;
1645
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301646 r = taal_set_addr_mode(td, td->rotate, enable);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001647 if (r)
Tomi Valkeinen8d8aa612010-01-08 16:30:33 +02001648 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001649 }
1650
1651 td->mirror = enable;
1652
Archit Taneja1ffefe72011-05-12 17:26:24 +05301653 dsi_bus_unlock(dssdev);
Tomi Valkeinenffb63c92010-05-21 10:09:18 +03001654end:
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001655 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001656 return 0;
Tomi Valkeinen8d8aa612010-01-08 16:30:33 +02001657err:
Archit Taneja1ffefe72011-05-12 17:26:24 +05301658 dsi_bus_unlock(dssdev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001659 mutex_unlock(&td->lock);
Tomi Valkeinen8d8aa612010-01-08 16:30:33 +02001660 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001661}
1662
1663static bool taal_get_mirror(struct omap_dss_device *dssdev)
1664{
1665 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001666 int r;
1667
1668 mutex_lock(&td->lock);
1669 r = td->mirror;
1670 mutex_unlock(&td->lock);
1671
1672 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001673}
1674
1675static int taal_run_test(struct omap_dss_device *dssdev, int test_num)
1676{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001677 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001678 u8 id1, id2, id3;
1679 int r;
1680
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001681 mutex_lock(&td->lock);
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001682
1683 if (!td->enabled) {
1684 r = -ENODEV;
1685 goto err1;
1686 }
1687
Archit Taneja1ffefe72011-05-12 17:26:24 +05301688 dsi_bus_lock(dssdev);
Tomi Valkeinen1a75ef42010-01-08 16:21:28 +02001689
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001690 r = taal_wake_up(dssdev);
1691 if (r)
1692 goto err2;
1693
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301694 r = taal_dcs_read_1(td, DCS_GET_ID1, &id1);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001695 if (r)
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001696 goto err2;
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301697 r = taal_dcs_read_1(td, DCS_GET_ID2, &id2);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001698 if (r)
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001699 goto err2;
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301700 r = taal_dcs_read_1(td, DCS_GET_ID3, &id3);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001701 if (r)
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001702 goto err2;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001703
Archit Taneja1ffefe72011-05-12 17:26:24 +05301704 dsi_bus_unlock(dssdev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001705 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001706 return 0;
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001707err2:
Archit Taneja1ffefe72011-05-12 17:26:24 +05301708 dsi_bus_unlock(dssdev);
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001709err1:
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001710 mutex_unlock(&td->lock);
Tomi Valkeinen1a75ef42010-01-08 16:21:28 +02001711 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001712}
1713
1714static int taal_memory_read(struct omap_dss_device *dssdev,
1715 void *buf, size_t size,
1716 u16 x, u16 y, u16 w, u16 h)
1717{
1718 int r;
1719 int first = 1;
1720 int plen;
1721 unsigned buf_used = 0;
Tomi Valkeinenc75d9462010-01-08 16:56:44 +02001722 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1723
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001724 if (size < w * h * 3)
1725 return -ENOMEM;
1726
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001727 mutex_lock(&td->lock);
1728
1729 if (!td->enabled) {
1730 r = -ENODEV;
1731 goto err1;
1732 }
1733
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001734 size = min(w * h * 3,
1735 dssdev->panel.timings.x_res *
1736 dssdev->panel.timings.y_res * 3);
1737
Archit Taneja1ffefe72011-05-12 17:26:24 +05301738 dsi_bus_lock(dssdev);
Tomi Valkeinenc75d9462010-01-08 16:56:44 +02001739
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001740 r = taal_wake_up(dssdev);
1741 if (r)
1742 goto err2;
1743
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001744 /* plen 1 or 2 goes into short packet. until checksum error is fixed,
1745 * use short packets. plen 32 works, but bigger packets seem to cause
1746 * an error. */
1747 if (size % 2)
1748 plen = 1;
1749 else
1750 plen = 2;
1751
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301752 taal_set_update_window(td, x, y, w, h);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001753
Archit Taneja1ffefe72011-05-12 17:26:24 +05301754 r = dsi_vc_set_max_rx_packet_size(dssdev, td->channel, plen);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001755 if (r)
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001756 goto err2;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001757
1758 while (buf_used < size) {
1759 u8 dcs_cmd = first ? 0x2e : 0x3e;
1760 first = 0;
1761
Archit Taneja1ffefe72011-05-12 17:26:24 +05301762 r = dsi_vc_dcs_read(dssdev, td->channel, dcs_cmd,
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001763 buf + buf_used, size - buf_used);
1764
1765 if (r < 0) {
1766 dev_err(&dssdev->dev, "read error\n");
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001767 goto err3;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001768 }
1769
1770 buf_used += r;
1771
1772 if (r < plen) {
1773 dev_err(&dssdev->dev, "short read\n");
1774 break;
1775 }
1776
1777 if (signal_pending(current)) {
1778 dev_err(&dssdev->dev, "signal pending, "
1779 "aborting memory read\n");
1780 r = -ERESTARTSYS;
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001781 goto err3;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001782 }
1783 }
1784
1785 r = buf_used;
1786
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001787err3:
Archit Taneja1ffefe72011-05-12 17:26:24 +05301788 dsi_vc_set_max_rx_packet_size(dssdev, td->channel, 1);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001789err2:
Archit Taneja1ffefe72011-05-12 17:26:24 +05301790 dsi_bus_unlock(dssdev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001791err1:
1792 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001793 return r;
1794}
1795
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001796static void taal_ulps_work(struct work_struct *work)
1797{
1798 struct taal_data *td = container_of(work, struct taal_data,
1799 ulps_work.work);
1800 struct omap_dss_device *dssdev = td->dssdev;
1801
1802 mutex_lock(&td->lock);
1803
1804 if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE || !td->enabled) {
1805 mutex_unlock(&td->lock);
1806 return;
1807 }
1808
Archit Taneja1ffefe72011-05-12 17:26:24 +05301809 dsi_bus_lock(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001810
1811 taal_enter_ulps(dssdev);
1812
Archit Taneja1ffefe72011-05-12 17:26:24 +05301813 dsi_bus_unlock(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001814 mutex_unlock(&td->lock);
1815}
1816
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001817static void taal_esd_work(struct work_struct *work)
1818{
1819 struct taal_data *td = container_of(work, struct taal_data,
1820 esd_work.work);
1821 struct omap_dss_device *dssdev = td->dssdev;
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001822 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001823 u8 state1, state2;
1824 int r;
1825
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001826 mutex_lock(&td->lock);
1827
1828 if (!td->enabled) {
1829 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001830 return;
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001831 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001832
Archit Taneja1ffefe72011-05-12 17:26:24 +05301833 dsi_bus_lock(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001834
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001835 r = taal_wake_up(dssdev);
1836 if (r) {
1837 dev_err(&dssdev->dev, "failed to exit ULPS\n");
1838 goto err;
1839 }
1840
Archit Taneja7a7c48f2011-08-25 18:25:03 +05301841 r = taal_dcs_read_1(td, MIPI_DCS_GET_DIAGNOSTIC_RESULT, &state1);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001842 if (r) {
1843 dev_err(&dssdev->dev, "failed to read Taal status\n");
1844 goto err;
1845 }
1846
1847 /* Run self diagnostics */
1848 r = taal_sleep_out(td);
1849 if (r) {
1850 dev_err(&dssdev->dev, "failed to run Taal self-diagnostics\n");
1851 goto err;
1852 }
1853
Archit Taneja7a7c48f2011-08-25 18:25:03 +05301854 r = taal_dcs_read_1(td, MIPI_DCS_GET_DIAGNOSTIC_RESULT, &state2);
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 /* Each sleep out command will trigger a self diagnostic and flip
1861 * Bit6 if the test passes.
1862 */
1863 if (!((state1 ^ state2) & (1 << 6))) {
1864 dev_err(&dssdev->dev, "LCD self diagnostics failed\n");
1865 goto err;
1866 }
1867 /* Self-diagnostics result is also shown on TE GPIO line. We need
1868 * to re-enable TE after self diagnostics */
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001869 if (td->te_enabled && panel_data->use_ext_te) {
Archit Taneja7a7c48f2011-08-25 18:25:03 +05301870 r = taal_dcs_write_1(td, MIPI_DCS_SET_TEAR_ON, 0);
Tomi Valkeinen1189b7f2010-03-01 13:52:10 +02001871 if (r)
1872 goto err;
1873 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001874
Archit Taneja1ffefe72011-05-12 17:26:24 +05301875 dsi_bus_unlock(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001876
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +02001877 taal_queue_esd_work(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001878
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001879 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001880 return;
1881err:
1882 dev_err(&dssdev->dev, "performing LCD reset\n");
1883
Tomi Valkeinenbb5476c2011-03-24 15:00:06 +02001884 taal_panel_reset(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001885
Archit Taneja1ffefe72011-05-12 17:26:24 +05301886 dsi_bus_unlock(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001887
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +02001888 taal_queue_esd_work(dssdev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001889
1890 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001891}
1892
1893static struct omap_dss_driver taal_driver = {
1894 .probe = taal_probe,
Tomi Valkeinen14e4d782011-03-31 12:03:51 +03001895 .remove = __exit_p(taal_remove),
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001896
1897 .enable = taal_enable,
1898 .disable = taal_disable,
1899 .suspend = taal_suspend,
1900 .resume = taal_resume,
1901
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001902 .update = taal_update,
1903 .sync = taal_sync,
1904
Tomi Valkeinen96adcec2010-01-11 13:54:33 +02001905 .get_resolution = taal_get_resolution,
Tomi Valkeinena2699502010-01-11 14:33:40 +02001906 .get_recommended_bpp = omapdss_default_get_recommended_bpp,
1907
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001908 .enable_te = taal_enable_te,
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001909 .get_te = taal_get_te,
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001910
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001911 .set_rotate = taal_rotate,
1912 .get_rotate = taal_get_rotate,
1913 .set_mirror = taal_mirror,
1914 .get_mirror = taal_get_mirror,
1915 .run_test = taal_run_test,
1916 .memory_read = taal_memory_read,
1917
Tomi Valkeinen69b20482010-01-20 12:11:25 +02001918 .get_timings = taal_get_timings,
1919
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001920 .driver = {
1921 .name = "taal",
1922 .owner = THIS_MODULE,
1923 },
1924};
1925
1926static int __init taal_init(void)
1927{
1928 omap_dss_register_driver(&taal_driver);
1929
1930 return 0;
1931}
1932
1933static void __exit taal_exit(void)
1934{
1935 omap_dss_unregister_driver(&taal_driver);
1936}
1937
1938module_init(taal_init);
1939module_exit(taal_exit);
1940
1941MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@nokia.com>");
1942MODULE_DESCRIPTION("Taal Driver");
1943MODULE_LICENSE("GPL");