blob: 0aa6c5d5a695206da959cfb1c8754c4b12841324 [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
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200211 bool cabc_broken;
212 unsigned cabc_mode;
213
214 bool intro_printed;
215
Tomi Valkeinen883b9ac2011-03-24 15:01:30 +0200216 struct workqueue_struct *workqueue;
217
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200218 struct delayed_work esd_work;
Tomi Valkeinen33a410b2011-03-24 13:58:01 +0200219 unsigned esd_interval;
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300220
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200221 bool ulps_enabled;
222 unsigned ulps_timeout;
223 struct delayed_work ulps_work;
224
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300225 struct panel_config *panel_config;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200226};
227
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300228static inline struct nokia_dsi_panel_data
229*get_panel_data(const struct omap_dss_device *dssdev)
230{
231 return (struct nokia_dsi_panel_data *) dssdev->data;
232}
233
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200234static void taal_esd_work(struct work_struct *work);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200235static void taal_ulps_work(struct work_struct *work);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200236
237static void hw_guard_start(struct taal_data *td, int guard_msec)
238{
239 td->hw_guard_wait = msecs_to_jiffies(guard_msec);
240 td->hw_guard_end = jiffies + td->hw_guard_wait;
241}
242
243static void hw_guard_wait(struct taal_data *td)
244{
245 unsigned long wait = td->hw_guard_end - jiffies;
246
247 if ((long)wait > 0 && wait <= td->hw_guard_wait) {
248 set_current_state(TASK_UNINTERRUPTIBLE);
249 schedule_timeout(wait);
250 }
251}
252
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530253static int taal_dcs_read_1(struct taal_data *td, u8 dcs_cmd, u8 *data)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200254{
255 int r;
256 u8 buf[1];
257
Archit Taneja1ffefe72011-05-12 17:26:24 +0530258 r = dsi_vc_dcs_read(td->dssdev, td->channel, dcs_cmd, buf, 1);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200259
260 if (r < 0)
261 return r;
262
263 *data = buf[0];
264
265 return 0;
266}
267
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530268static int taal_dcs_write_0(struct taal_data *td, u8 dcs_cmd)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200269{
Archit Taneja1ffefe72011-05-12 17:26:24 +0530270 return dsi_vc_dcs_write(td->dssdev, td->channel, &dcs_cmd, 1);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200271}
272
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530273static int taal_dcs_write_1(struct taal_data *td, u8 dcs_cmd, u8 param)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200274{
275 u8 buf[2];
276 buf[0] = dcs_cmd;
277 buf[1] = param;
Archit Taneja1ffefe72011-05-12 17:26:24 +0530278 return dsi_vc_dcs_write(td->dssdev, td->channel, buf, 2);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200279}
280
281static int taal_sleep_in(struct taal_data *td)
282
283{
284 u8 cmd;
285 int r;
286
287 hw_guard_wait(td);
288
Archit Taneja7a7c48f2011-08-25 18:25:03 +0530289 cmd = MIPI_DCS_ENTER_SLEEP_MODE;
Archit Taneja1ffefe72011-05-12 17:26:24 +0530290 r = dsi_vc_dcs_write_nosync(td->dssdev, td->channel, &cmd, 1);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200291 if (r)
292 return r;
293
294 hw_guard_start(td, 120);
295
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300296 if (td->panel_config->sleep.sleep_in)
297 msleep(td->panel_config->sleep.sleep_in);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200298
299 return 0;
300}
301
302static int taal_sleep_out(struct taal_data *td)
303{
304 int r;
305
306 hw_guard_wait(td);
307
Archit Taneja7a7c48f2011-08-25 18:25:03 +0530308 r = taal_dcs_write_0(td, MIPI_DCS_EXIT_SLEEP_MODE);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200309 if (r)
310 return r;
311
312 hw_guard_start(td, 120);
313
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300314 if (td->panel_config->sleep.sleep_out)
315 msleep(td->panel_config->sleep.sleep_out);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200316
317 return 0;
318}
319
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530320static int taal_get_id(struct taal_data *td, u8 *id1, u8 *id2, u8 *id3)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200321{
322 int r;
323
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530324 r = taal_dcs_read_1(td, DCS_GET_ID1, id1);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200325 if (r)
326 return r;
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530327 r = taal_dcs_read_1(td, DCS_GET_ID2, id2);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200328 if (r)
329 return r;
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530330 r = taal_dcs_read_1(td, DCS_GET_ID3, id3);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200331 if (r)
332 return r;
333
334 return 0;
335}
336
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530337static int taal_set_addr_mode(struct taal_data *td, u8 rotate, bool mirror)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200338{
339 int r;
340 u8 mode;
341 int b5, b6, b7;
342
Archit Taneja7a7c48f2011-08-25 18:25:03 +0530343 r = taal_dcs_read_1(td, MIPI_DCS_GET_ADDRESS_MODE, &mode);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200344 if (r)
345 return r;
346
347 switch (rotate) {
348 default:
349 case 0:
350 b7 = 0;
351 b6 = 0;
352 b5 = 0;
353 break;
354 case 1:
355 b7 = 0;
356 b6 = 1;
357 b5 = 1;
358 break;
359 case 2:
360 b7 = 1;
361 b6 = 1;
362 b5 = 0;
363 break;
364 case 3:
365 b7 = 1;
366 b6 = 0;
367 b5 = 1;
368 break;
369 }
370
371 if (mirror)
372 b6 = !b6;
373
374 mode &= ~((1<<7) | (1<<6) | (1<<5));
375 mode |= (b7 << 7) | (b6 << 6) | (b5 << 5);
376
Archit Taneja7a7c48f2011-08-25 18:25:03 +0530377 return taal_dcs_write_1(td, MIPI_DCS_SET_ADDRESS_MODE, mode);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200378}
379
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530380static int taal_set_update_window(struct taal_data *td,
381 u16 x, u16 y, u16 w, u16 h)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200382{
383 int r;
384 u16 x1 = x;
385 u16 x2 = x + w - 1;
386 u16 y1 = y;
387 u16 y2 = y + h - 1;
388
389 u8 buf[5];
Archit Taneja7a7c48f2011-08-25 18:25:03 +0530390 buf[0] = MIPI_DCS_SET_COLUMN_ADDRESS;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200391 buf[1] = (x1 >> 8) & 0xff;
392 buf[2] = (x1 >> 0) & 0xff;
393 buf[3] = (x2 >> 8) & 0xff;
394 buf[4] = (x2 >> 0) & 0xff;
395
Archit Taneja1ffefe72011-05-12 17:26:24 +0530396 r = dsi_vc_dcs_write_nosync(td->dssdev, td->channel, buf, sizeof(buf));
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200397 if (r)
398 return r;
399
Archit Taneja7a7c48f2011-08-25 18:25:03 +0530400 buf[0] = MIPI_DCS_SET_PAGE_ADDRESS;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200401 buf[1] = (y1 >> 8) & 0xff;
402 buf[2] = (y1 >> 0) & 0xff;
403 buf[3] = (y2 >> 8) & 0xff;
404 buf[4] = (y2 >> 0) & 0xff;
405
Archit Taneja1ffefe72011-05-12 17:26:24 +0530406 r = dsi_vc_dcs_write_nosync(td->dssdev, td->channel, buf, sizeof(buf));
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200407 if (r)
408 return r;
409
Archit Taneja1ffefe72011-05-12 17:26:24 +0530410 dsi_vc_send_bta_sync(td->dssdev, td->channel);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200411
412 return r;
413}
414
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +0200415static void taal_queue_esd_work(struct omap_dss_device *dssdev)
416{
417 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
418
419 if (td->esd_interval > 0)
Tomi Valkeinen883b9ac2011-03-24 15:01:30 +0200420 queue_delayed_work(td->workqueue, &td->esd_work,
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +0200421 msecs_to_jiffies(td->esd_interval));
422}
423
424static void taal_cancel_esd_work(struct omap_dss_device *dssdev)
425{
426 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
427
428 cancel_delayed_work(&td->esd_work);
429}
430
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200431static void taal_queue_ulps_work(struct omap_dss_device *dssdev)
432{
433 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
434
435 if (td->ulps_timeout > 0)
436 queue_delayed_work(td->workqueue, &td->ulps_work,
437 msecs_to_jiffies(td->ulps_timeout));
438}
439
440static void taal_cancel_ulps_work(struct omap_dss_device *dssdev)
441{
442 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
443
444 cancel_delayed_work(&td->ulps_work);
445}
446
447static int taal_enter_ulps(struct omap_dss_device *dssdev)
448{
449 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
450 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
451 int r;
452
453 if (td->ulps_enabled)
454 return 0;
455
456 taal_cancel_ulps_work(dssdev);
457
458 r = _taal_enable_te(dssdev, false);
459 if (r)
460 goto err;
461
462 disable_irq(gpio_to_irq(panel_data->ext_te_gpio));
463
464 omapdss_dsi_display_disable(dssdev, false, true);
465
466 td->ulps_enabled = true;
467
468 return 0;
469
470err:
471 dev_err(&dssdev->dev, "enter ULPS failed");
472 taal_panel_reset(dssdev);
473
474 td->ulps_enabled = false;
475
476 taal_queue_ulps_work(dssdev);
477
478 return r;
479}
480
481static int taal_exit_ulps(struct omap_dss_device *dssdev)
482{
483 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
484 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
485 int r;
486
487 if (!td->ulps_enabled)
488 return 0;
489
490 r = omapdss_dsi_display_enable(dssdev);
Tomi Valkeinene8945672011-05-31 16:39:01 +0300491 if (r) {
492 dev_err(&dssdev->dev, "failed to enable DSI\n");
493 goto err1;
494 }
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200495
Archit Taneja1ffefe72011-05-12 17:26:24 +0530496 omapdss_dsi_vc_enable_hs(dssdev, td->channel, true);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200497
498 r = _taal_enable_te(dssdev, true);
Tomi Valkeinene8945672011-05-31 16:39:01 +0300499 if (r) {
500 dev_err(&dssdev->dev, "failed to re-enable TE");
501 goto err2;
502 }
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200503
504 enable_irq(gpio_to_irq(panel_data->ext_te_gpio));
505
506 taal_queue_ulps_work(dssdev);
507
508 td->ulps_enabled = false;
509
510 return 0;
511
Tomi Valkeinene8945672011-05-31 16:39:01 +0300512err2:
513 dev_err(&dssdev->dev, "failed to exit ULPS");
514
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200515 r = taal_panel_reset(dssdev);
Tomi Valkeinene8945672011-05-31 16:39:01 +0300516 if (!r) {
517 enable_irq(gpio_to_irq(panel_data->ext_te_gpio));
518 td->ulps_enabled = false;
519 }
520err1:
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200521 taal_queue_ulps_work(dssdev);
522
523 return r;
524}
525
526static int taal_wake_up(struct omap_dss_device *dssdev)
527{
528 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
529
530 if (td->ulps_enabled)
531 return taal_exit_ulps(dssdev);
532
533 taal_cancel_ulps_work(dssdev);
534 taal_queue_ulps_work(dssdev);
535 return 0;
536}
537
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200538static int taal_bl_update_status(struct backlight_device *dev)
539{
540 struct omap_dss_device *dssdev = dev_get_drvdata(&dev->dev);
541 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
542 int r;
543 int level;
544
545 if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
546 dev->props.power == FB_BLANK_UNBLANK)
547 level = dev->props.brightness;
548 else
549 level = 0;
550
551 dev_dbg(&dssdev->dev, "update brightness to %d\n", level);
552
Tomi Valkeinen1cbc8702010-04-15 16:41:07 +0300553 mutex_lock(&td->lock);
554
Tomi Valkeinenbb36dbf2011-08-09 11:39:27 +0300555 if (td->enabled) {
556 dsi_bus_lock(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200557
Tomi Valkeinenbb36dbf2011-08-09 11:39:27 +0300558 r = taal_wake_up(dssdev);
559 if (!r)
560 r = taal_dcs_write_1(td, DCS_BRIGHTNESS, level);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200561
Tomi Valkeinenbb36dbf2011-08-09 11:39:27 +0300562 dsi_bus_unlock(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200563 } else {
Tomi Valkeinenbb36dbf2011-08-09 11:39:27 +0300564 r = 0;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200565 }
566
Tomi Valkeinen1cbc8702010-04-15 16:41:07 +0300567 mutex_unlock(&td->lock);
568
569 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200570}
571
572static int taal_bl_get_intensity(struct backlight_device *dev)
573{
574 if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
575 dev->props.power == FB_BLANK_UNBLANK)
576 return dev->props.brightness;
577
578 return 0;
579}
580
Lionel Debrouxacc24722010-11-16 14:14:02 +0100581static const struct backlight_ops taal_bl_ops = {
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200582 .get_brightness = taal_bl_get_intensity,
583 .update_status = taal_bl_update_status,
584};
585
586static void taal_get_timings(struct omap_dss_device *dssdev,
587 struct omap_video_timings *timings)
588{
589 *timings = dssdev->panel.timings;
590}
591
592static void taal_get_resolution(struct omap_dss_device *dssdev,
593 u16 *xres, u16 *yres)
594{
595 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
596
597 if (td->rotate == 0 || td->rotate == 2) {
598 *xres = dssdev->panel.timings.x_res;
599 *yres = dssdev->panel.timings.y_res;
600 } else {
601 *yres = dssdev->panel.timings.x_res;
602 *xres = dssdev->panel.timings.y_res;
603 }
604}
605
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200606static ssize_t taal_num_errors_show(struct device *dev,
607 struct device_attribute *attr, char *buf)
608{
609 struct omap_dss_device *dssdev = to_dss_device(dev);
610 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
611 u8 errors;
612 int r;
613
Jani Nikula6b316712010-04-28 11:15:18 +0300614 mutex_lock(&td->lock);
615
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200616 if (td->enabled) {
Archit Taneja1ffefe72011-05-12 17:26:24 +0530617 dsi_bus_lock(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200618
619 r = taal_wake_up(dssdev);
620 if (!r)
621 r = taal_dcs_read_1(td, DCS_READ_NUM_ERRORS, &errors);
622
Archit Taneja1ffefe72011-05-12 17:26:24 +0530623 dsi_bus_unlock(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200624 } else {
625 r = -ENODEV;
626 }
627
Jani Nikula6b316712010-04-28 11:15:18 +0300628 mutex_unlock(&td->lock);
629
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200630 if (r)
631 return r;
632
633 return snprintf(buf, PAGE_SIZE, "%d\n", errors);
634}
635
636static ssize_t taal_hw_revision_show(struct device *dev,
637 struct device_attribute *attr, char *buf)
638{
639 struct omap_dss_device *dssdev = to_dss_device(dev);
640 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
641 u8 id1, id2, id3;
642 int r;
643
Jani Nikula6b316712010-04-28 11:15:18 +0300644 mutex_lock(&td->lock);
645
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200646 if (td->enabled) {
Archit Taneja1ffefe72011-05-12 17:26:24 +0530647 dsi_bus_lock(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200648
649 r = taal_wake_up(dssdev);
650 if (!r)
651 r = taal_get_id(td, &id1, &id2, &id3);
652
Archit Taneja1ffefe72011-05-12 17:26:24 +0530653 dsi_bus_unlock(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200654 } else {
655 r = -ENODEV;
656 }
657
Jani Nikula6b316712010-04-28 11:15:18 +0300658 mutex_unlock(&td->lock);
659
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200660 if (r)
661 return r;
662
663 return snprintf(buf, PAGE_SIZE, "%02x.%02x.%02x\n", id1, id2, id3);
664}
665
666static const char *cabc_modes[] = {
667 "off", /* used also always when CABC is not supported */
668 "ui",
669 "still-image",
670 "moving-image",
671};
672
673static ssize_t show_cabc_mode(struct device *dev,
674 struct device_attribute *attr,
675 char *buf)
676{
677 struct omap_dss_device *dssdev = to_dss_device(dev);
678 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
679 const char *mode_str;
680 int mode;
681 int len;
682
683 mode = td->cabc_mode;
684
685 mode_str = "unknown";
686 if (mode >= 0 && mode < ARRAY_SIZE(cabc_modes))
687 mode_str = cabc_modes[mode];
688 len = snprintf(buf, PAGE_SIZE, "%s\n", mode_str);
689
690 return len < PAGE_SIZE - 1 ? len : PAGE_SIZE - 1;
691}
692
693static ssize_t store_cabc_mode(struct device *dev,
694 struct device_attribute *attr,
695 const char *buf, size_t count)
696{
697 struct omap_dss_device *dssdev = to_dss_device(dev);
698 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
699 int i;
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200700 int r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200701
702 for (i = 0; i < ARRAY_SIZE(cabc_modes); i++) {
703 if (sysfs_streq(cabc_modes[i], buf))
704 break;
705 }
706
707 if (i == ARRAY_SIZE(cabc_modes))
708 return -EINVAL;
709
Jani Nikula6b316712010-04-28 11:15:18 +0300710 mutex_lock(&td->lock);
711
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200712 if (td->enabled) {
Archit Taneja1ffefe72011-05-12 17:26:24 +0530713 dsi_bus_lock(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200714
715 if (!td->cabc_broken) {
716 r = taal_wake_up(dssdev);
717 if (r)
718 goto err;
719
720 r = taal_dcs_write_1(td, DCS_WRITE_CABC, i);
721 if (r)
722 goto err;
723 }
724
Archit Taneja1ffefe72011-05-12 17:26:24 +0530725 dsi_bus_unlock(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200726 }
727
728 td->cabc_mode = i;
729
Jani Nikula6b316712010-04-28 11:15:18 +0300730 mutex_unlock(&td->lock);
731
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200732 return count;
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200733err:
Archit Taneja1ffefe72011-05-12 17:26:24 +0530734 dsi_bus_unlock(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200735 mutex_unlock(&td->lock);
736 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200737}
738
739static ssize_t show_cabc_available_modes(struct device *dev,
740 struct device_attribute *attr,
741 char *buf)
742{
743 int len;
744 int i;
745
746 for (i = 0, len = 0;
747 len < PAGE_SIZE && i < ARRAY_SIZE(cabc_modes); i++)
748 len += snprintf(&buf[len], PAGE_SIZE - len, "%s%s%s",
749 i ? " " : "", cabc_modes[i],
750 i == ARRAY_SIZE(cabc_modes) - 1 ? "\n" : "");
751
752 return len < PAGE_SIZE ? len : PAGE_SIZE - 1;
753}
754
Tomi Valkeinen1f8fa452011-03-24 14:06:51 +0200755static ssize_t taal_store_esd_interval(struct device *dev,
756 struct device_attribute *attr,
757 const char *buf, size_t count)
758{
759 struct omap_dss_device *dssdev = to_dss_device(dev);
760 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
761
762 unsigned long t;
763 int r;
764
765 r = strict_strtoul(buf, 10, &t);
766 if (r)
767 return r;
768
769 mutex_lock(&td->lock);
770 taal_cancel_esd_work(dssdev);
771 td->esd_interval = t;
772 if (td->enabled)
773 taal_queue_esd_work(dssdev);
774 mutex_unlock(&td->lock);
775
776 return count;
777}
778
779static ssize_t taal_show_esd_interval(struct device *dev,
780 struct device_attribute *attr,
781 char *buf)
782{
783 struct omap_dss_device *dssdev = to_dss_device(dev);
784 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
785 unsigned t;
786
787 mutex_lock(&td->lock);
788 t = td->esd_interval;
789 mutex_unlock(&td->lock);
790
791 return snprintf(buf, PAGE_SIZE, "%u\n", t);
792}
793
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200794static ssize_t taal_store_ulps(struct device *dev,
795 struct device_attribute *attr,
796 const char *buf, size_t count)
797{
798 struct omap_dss_device *dssdev = to_dss_device(dev);
799 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
800 unsigned long t;
801 int r;
802
803 r = strict_strtoul(buf, 10, &t);
804 if (r)
805 return r;
806
807 mutex_lock(&td->lock);
808
809 if (td->enabled) {
Archit Taneja1ffefe72011-05-12 17:26:24 +0530810 dsi_bus_lock(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200811
812 if (t)
813 r = taal_enter_ulps(dssdev);
814 else
815 r = taal_wake_up(dssdev);
816
Archit Taneja1ffefe72011-05-12 17:26:24 +0530817 dsi_bus_unlock(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200818 }
819
820 mutex_unlock(&td->lock);
821
822 if (r)
823 return r;
824
825 return count;
826}
827
828static ssize_t taal_show_ulps(struct device *dev,
829 struct device_attribute *attr,
830 char *buf)
831{
832 struct omap_dss_device *dssdev = to_dss_device(dev);
833 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
834 unsigned t;
835
836 mutex_lock(&td->lock);
837 t = td->ulps_enabled;
838 mutex_unlock(&td->lock);
839
840 return snprintf(buf, PAGE_SIZE, "%u\n", t);
841}
842
843static ssize_t taal_store_ulps_timeout(struct device *dev,
844 struct device_attribute *attr,
845 const char *buf, size_t count)
846{
847 struct omap_dss_device *dssdev = to_dss_device(dev);
848 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
849 unsigned long t;
850 int r;
851
852 r = strict_strtoul(buf, 10, &t);
853 if (r)
854 return r;
855
856 mutex_lock(&td->lock);
857 td->ulps_timeout = t;
858
859 if (td->enabled) {
860 /* taal_wake_up will restart the timer */
Archit Taneja1ffefe72011-05-12 17:26:24 +0530861 dsi_bus_lock(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200862 r = taal_wake_up(dssdev);
Archit Taneja1ffefe72011-05-12 17:26:24 +0530863 dsi_bus_unlock(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200864 }
865
866 mutex_unlock(&td->lock);
867
868 if (r)
869 return r;
870
871 return count;
872}
873
874static ssize_t taal_show_ulps_timeout(struct device *dev,
875 struct device_attribute *attr,
876 char *buf)
877{
878 struct omap_dss_device *dssdev = to_dss_device(dev);
879 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
880 unsigned t;
881
882 mutex_lock(&td->lock);
883 t = td->ulps_timeout;
884 mutex_unlock(&td->lock);
885
886 return snprintf(buf, PAGE_SIZE, "%u\n", t);
887}
888
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200889static DEVICE_ATTR(num_dsi_errors, S_IRUGO, taal_num_errors_show, NULL);
890static DEVICE_ATTR(hw_revision, S_IRUGO, taal_hw_revision_show, NULL);
891static DEVICE_ATTR(cabc_mode, S_IRUGO | S_IWUSR,
892 show_cabc_mode, store_cabc_mode);
893static DEVICE_ATTR(cabc_available_modes, S_IRUGO,
894 show_cabc_available_modes, NULL);
Tomi Valkeinen1f8fa452011-03-24 14:06:51 +0200895static DEVICE_ATTR(esd_interval, S_IRUGO | S_IWUSR,
896 taal_show_esd_interval, taal_store_esd_interval);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200897static DEVICE_ATTR(ulps, S_IRUGO | S_IWUSR,
898 taal_show_ulps, taal_store_ulps);
899static DEVICE_ATTR(ulps_timeout, S_IRUGO | S_IWUSR,
900 taal_show_ulps_timeout, taal_store_ulps_timeout);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200901
902static struct attribute *taal_attrs[] = {
903 &dev_attr_num_dsi_errors.attr,
904 &dev_attr_hw_revision.attr,
905 &dev_attr_cabc_mode.attr,
906 &dev_attr_cabc_available_modes.attr,
Tomi Valkeinen1f8fa452011-03-24 14:06:51 +0200907 &dev_attr_esd_interval.attr,
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200908 &dev_attr_ulps.attr,
909 &dev_attr_ulps_timeout.attr,
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200910 NULL,
911};
912
913static struct attribute_group taal_attr_group = {
914 .attrs = taal_attrs,
915};
916
Jani Nikula006db7b2010-04-09 12:25:59 +0300917static void taal_hw_reset(struct omap_dss_device *dssdev)
918{
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300919 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300920 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
921
922 if (panel_data->reset_gpio == -1)
Jani Nikula006db7b2010-04-09 12:25:59 +0300923 return;
924
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300925 gpio_set_value(panel_data->reset_gpio, 1);
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300926 if (td->panel_config->reset_sequence.high)
927 udelay(td->panel_config->reset_sequence.high);
Jani Nikula006db7b2010-04-09 12:25:59 +0300928 /* reset the panel */
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300929 gpio_set_value(panel_data->reset_gpio, 0);
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300930 /* assert reset */
931 if (td->panel_config->reset_sequence.low)
932 udelay(td->panel_config->reset_sequence.low);
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300933 gpio_set_value(panel_data->reset_gpio, 1);
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300934 /* wait after releasing reset */
935 if (td->panel_config->sleep.hw_reset)
936 msleep(td->panel_config->sleep.hw_reset);
Jani Nikula006db7b2010-04-09 12:25:59 +0300937}
938
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200939static int taal_probe(struct omap_dss_device *dssdev)
940{
Matthew Garretta19a6ee2010-02-17 16:39:44 -0500941 struct backlight_properties props;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200942 struct taal_data *td;
Tomi Valkeinenbb36dbf2011-08-09 11:39:27 +0300943 struct backlight_device *bldev = NULL;
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300944 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300945 struct panel_config *panel_config = NULL;
946 int r, i;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200947
948 dev_dbg(&dssdev->dev, "probe\n");
949
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300950 if (!panel_data || !panel_data->name) {
951 r = -EINVAL;
952 goto err;
953 }
954
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300955 for (i = 0; i < ARRAY_SIZE(panel_configs); i++) {
956 if (strcmp(panel_data->name, panel_configs[i].name) == 0) {
957 panel_config = &panel_configs[i];
958 break;
959 }
960 }
961
962 if (!panel_config) {
963 r = -EINVAL;
964 goto err;
965 }
966
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200967 dssdev->panel.config = OMAP_DSS_LCD_TFT;
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300968 dssdev->panel.timings = panel_config->timings;
Archit Tanejaa3b3cc22011-09-08 18:42:16 +0530969 dssdev->panel.dsi_pix_fmt = OMAP_DSS_DSI_FMT_RGB888;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200970
971 td = kzalloc(sizeof(*td), GFP_KERNEL);
972 if (!td) {
973 r = -ENOMEM;
Jani Nikulad2b65782010-04-15 12:24:36 +0300974 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200975 }
976 td->dssdev = dssdev;
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300977 td->panel_config = panel_config;
Tomi Valkeinen33a410b2011-03-24 13:58:01 +0200978 td->esd_interval = panel_data->esd_interval;
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200979 td->ulps_enabled = false;
980 td->ulps_timeout = panel_data->ulps_timeout;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200981
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200982 mutex_init(&td->lock);
983
Jani Nikula7ae2fb12010-04-13 10:57:52 +0300984 atomic_set(&td->do_update, 0);
985
Tomi Valkeinenc8cd4542010-06-09 15:24:46 +0300986 r = init_regulators(dssdev, panel_config->regulators,
987 panel_config->num_regulators);
988 if (r)
989 goto err_reg;
990
Tomi Valkeinen883b9ac2011-03-24 15:01:30 +0200991 td->workqueue = create_singlethread_workqueue("taal_esd");
992 if (td->workqueue == NULL) {
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200993 dev_err(&dssdev->dev, "can't create ESD workqueue\n");
994 r = -ENOMEM;
Jani Nikulad2b65782010-04-15 12:24:36 +0300995 goto err_wq;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200996 }
997 INIT_DELAYED_WORK_DEFERRABLE(&td->esd_work, taal_esd_work);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200998 INIT_DELAYED_WORK(&td->ulps_work, taal_ulps_work);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200999
1000 dev_set_drvdata(&dssdev->dev, td);
1001
Jani Nikula006db7b2010-04-09 12:25:59 +03001002 taal_hw_reset(dssdev);
1003
Tomi Valkeinenbb36dbf2011-08-09 11:39:27 +03001004 if (panel_data->use_dsi_backlight) {
1005 memset(&props, 0, sizeof(struct backlight_properties));
Matthew Garretta19a6ee2010-02-17 16:39:44 -05001006 props.max_brightness = 255;
Matthew Garrettbb7ca742011-03-22 16:30:21 -07001007
Tomi Valkeinenbb36dbf2011-08-09 11:39:27 +03001008 props.type = BACKLIGHT_RAW;
1009 bldev = backlight_device_register(dev_name(&dssdev->dev),
1010 &dssdev->dev, dssdev, &taal_bl_ops, &props);
1011 if (IS_ERR(bldev)) {
1012 r = PTR_ERR(bldev);
1013 goto err_bl;
1014 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001015
Tomi Valkeinenbb36dbf2011-08-09 11:39:27 +03001016 td->bldev = bldev;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001017
Tomi Valkeinenbb36dbf2011-08-09 11:39:27 +03001018 bldev->props.fb_blank = FB_BLANK_UNBLANK;
1019 bldev->props.power = FB_BLANK_UNBLANK;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001020 bldev->props.brightness = 255;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001021
Tomi Valkeinenbb36dbf2011-08-09 11:39:27 +03001022 taal_bl_update_status(bldev);
1023 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001024
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001025 if (panel_data->use_ext_te) {
1026 int gpio = panel_data->ext_te_gpio;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001027
1028 r = gpio_request(gpio, "taal irq");
1029 if (r) {
1030 dev_err(&dssdev->dev, "GPIO request failed\n");
Jani Nikulad2b65782010-04-15 12:24:36 +03001031 goto err_gpio;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001032 }
1033
1034 gpio_direction_input(gpio);
1035
1036 r = request_irq(gpio_to_irq(gpio), taal_te_isr,
Yong Zhangf8798cc2011-09-22 16:59:16 +08001037 IRQF_TRIGGER_RISING,
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001038 "taal vsync", dssdev);
1039
1040 if (r) {
1041 dev_err(&dssdev->dev, "IRQ request failed\n");
1042 gpio_free(gpio);
Jani Nikulad2b65782010-04-15 12:24:36 +03001043 goto err_irq;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001044 }
1045
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001046 INIT_DELAYED_WORK_DEFERRABLE(&td->te_timeout_work,
1047 taal_te_timeout_work_callback);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001048
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001049 dev_dbg(&dssdev->dev, "Using GPIO TE\n");
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001050 }
1051
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301052 r = omap_dsi_request_vc(dssdev, &td->channel);
1053 if (r) {
1054 dev_err(&dssdev->dev, "failed to get virtual channel\n");
1055 goto err_req_vc;
1056 }
1057
1058 r = omap_dsi_set_vc_id(dssdev, td->channel, TCH);
1059 if (r) {
1060 dev_err(&dssdev->dev, "failed to set VC_ID\n");
1061 goto err_vc_id;
1062 }
1063
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001064 r = sysfs_create_group(&dssdev->dev.kobj, &taal_attr_group);
1065 if (r) {
1066 dev_err(&dssdev->dev, "failed to create sysfs files\n");
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301067 goto err_vc_id;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001068 }
1069
1070 return 0;
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301071
1072err_vc_id:
1073 omap_dsi_release_vc(dssdev, td->channel);
1074err_req_vc:
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001075 if (panel_data->use_ext_te)
1076 free_irq(gpio_to_irq(panel_data->ext_te_gpio), dssdev);
Jani Nikulad2b65782010-04-15 12:24:36 +03001077err_irq:
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001078 if (panel_data->use_ext_te)
1079 gpio_free(panel_data->ext_te_gpio);
Jani Nikulad2b65782010-04-15 12:24:36 +03001080err_gpio:
Tomi Valkeinenbb36dbf2011-08-09 11:39:27 +03001081 if (bldev != NULL)
1082 backlight_device_unregister(bldev);
Jani Nikulad2b65782010-04-15 12:24:36 +03001083err_bl:
Tomi Valkeinen883b9ac2011-03-24 15:01:30 +02001084 destroy_workqueue(td->workqueue);
Jani Nikulad2b65782010-04-15 12:24:36 +03001085err_wq:
Tomi Valkeinenc8cd4542010-06-09 15:24:46 +03001086 free_regulators(panel_config->regulators, panel_config->num_regulators);
1087err_reg:
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001088 kfree(td);
Jani Nikulad2b65782010-04-15 12:24:36 +03001089err:
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001090 return r;
1091}
1092
Tomi Valkeinen14e4d782011-03-31 12:03:51 +03001093static void __exit taal_remove(struct omap_dss_device *dssdev)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001094{
1095 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001096 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001097 struct backlight_device *bldev;
1098
1099 dev_dbg(&dssdev->dev, "remove\n");
1100
1101 sysfs_remove_group(&dssdev->dev.kobj, &taal_attr_group);
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301102 omap_dsi_release_vc(dssdev, td->channel);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001103
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001104 if (panel_data->use_ext_te) {
1105 int gpio = panel_data->ext_te_gpio;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001106 free_irq(gpio_to_irq(gpio), dssdev);
1107 gpio_free(gpio);
1108 }
1109
1110 bldev = td->bldev;
Tomi Valkeinenbb36dbf2011-08-09 11:39:27 +03001111 if (bldev != NULL) {
1112 bldev->props.power = FB_BLANK_POWERDOWN;
1113 taal_bl_update_status(bldev);
1114 backlight_device_unregister(bldev);
1115 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001116
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001117 taal_cancel_ulps_work(dssdev);
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +02001118 taal_cancel_esd_work(dssdev);
Tomi Valkeinen883b9ac2011-03-24 15:01:30 +02001119 destroy_workqueue(td->workqueue);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001120
Jani Nikula006db7b2010-04-09 12:25:59 +03001121 /* reset, to be sure that the panel is in a valid state */
1122 taal_hw_reset(dssdev);
1123
Tomi Valkeinenc8cd4542010-06-09 15:24:46 +03001124 free_regulators(td->panel_config->regulators,
1125 td->panel_config->num_regulators);
1126
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001127 kfree(td);
1128}
1129
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001130static int taal_power_on(struct omap_dss_device *dssdev)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001131{
1132 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1133 u8 id1, id2, id3;
1134 int r;
1135
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001136 r = omapdss_dsi_display_enable(dssdev);
1137 if (r) {
1138 dev_err(&dssdev->dev, "failed to enable DSI\n");
1139 goto err0;
1140 }
1141
Jani Nikula006db7b2010-04-09 12:25:59 +03001142 taal_hw_reset(dssdev);
1143
Archit Taneja1ffefe72011-05-12 17:26:24 +05301144 omapdss_dsi_vc_enable_hs(dssdev, td->channel, false);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001145
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001146 r = taal_sleep_out(td);
1147 if (r)
1148 goto err;
1149
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301150 r = taal_get_id(td, &id1, &id2, &id3);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001151 if (r)
1152 goto err;
1153
Jani Nikula1e8943d2010-04-15 17:07:39 +03001154 /* on early Taal revisions CABC is broken */
1155 if (td->panel_config->type == PANEL_TAAL &&
1156 (id2 == 0x00 || id2 == 0xff || id2 == 0x81))
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001157 td->cabc_broken = true;
1158
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301159 r = taal_dcs_write_1(td, DCS_BRIGHTNESS, 0xff);
Jani Nikulaf2a8b752010-04-09 14:15:12 +03001160 if (r)
1161 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001162
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301163 r = taal_dcs_write_1(td, DCS_CTRL_DISPLAY,
Jani Nikulaf2a8b752010-04-09 14:15:12 +03001164 (1<<2) | (1<<5)); /* BL | BCTRL */
1165 if (r)
1166 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001167
Archit Taneja7a7c48f2011-08-25 18:25:03 +05301168 r = taal_dcs_write_1(td, MIPI_DCS_SET_PIXEL_FORMAT,
1169 MIPI_DCS_PIXEL_FMT_24BIT);
Jani Nikulaf2a8b752010-04-09 14:15:12 +03001170 if (r)
1171 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001172
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301173 r = taal_set_addr_mode(td, td->rotate, td->mirror);
Jani Nikulaf2a8b752010-04-09 14:15:12 +03001174 if (r)
1175 goto err;
1176
1177 if (!td->cabc_broken) {
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301178 r = taal_dcs_write_1(td, DCS_WRITE_CABC, td->cabc_mode);
Jani Nikulaf2a8b752010-04-09 14:15:12 +03001179 if (r)
1180 goto err;
1181 }
1182
Archit Taneja7a7c48f2011-08-25 18:25:03 +05301183 r = taal_dcs_write_0(td, MIPI_DCS_SET_DISPLAY_ON);
Jani Nikulaf2a8b752010-04-09 14:15:12 +03001184 if (r)
1185 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001186
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001187 r = _taal_enable_te(dssdev, td->te_enabled);
1188 if (r)
1189 goto err;
1190
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001191 td->enabled = 1;
1192
1193 if (!td->intro_printed) {
Jani Nikula0f45bdd2010-04-16 14:21:14 +03001194 dev_info(&dssdev->dev, "%s panel revision %02x.%02x.%02x\n",
1195 td->panel_config->name, id1, id2, id3);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001196 if (td->cabc_broken)
1197 dev_info(&dssdev->dev,
1198 "old Taal version, CABC disabled\n");
1199 td->intro_printed = true;
1200 }
1201
Archit Taneja1ffefe72011-05-12 17:26:24 +05301202 omapdss_dsi_vc_enable_hs(dssdev, td->channel, true);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001203
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001204 return 0;
1205err:
Jani Nikula006db7b2010-04-09 12:25:59 +03001206 dev_err(&dssdev->dev, "error while enabling panel, issuing HW reset\n");
1207
1208 taal_hw_reset(dssdev);
1209
Tomi Valkeinen22d6d672010-10-11 11:33:30 +03001210 omapdss_dsi_display_disable(dssdev, true, false);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001211err0:
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001212 return r;
1213}
1214
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001215static void taal_power_off(struct omap_dss_device *dssdev)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001216{
1217 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Jani Nikula006db7b2010-04-09 12:25:59 +03001218 int r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001219
Archit Taneja7a7c48f2011-08-25 18:25:03 +05301220 r = taal_dcs_write_0(td, MIPI_DCS_SET_DISPLAY_OFF);
Tomi Valkeinen15ffa1d2011-06-16 14:34:06 +03001221 if (!r)
Jani Nikula006db7b2010-04-09 12:25:59 +03001222 r = taal_sleep_in(td);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001223
Jani Nikula006db7b2010-04-09 12:25:59 +03001224 if (r) {
1225 dev_err(&dssdev->dev,
1226 "error disabling panel, issuing HW reset\n");
1227 taal_hw_reset(dssdev);
1228 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001229
Tomi Valkeinen22d6d672010-10-11 11:33:30 +03001230 omapdss_dsi_display_disable(dssdev, true, false);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001231
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001232 td->enabled = 0;
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001233}
1234
Tomi Valkeinenbb5476c2011-03-24 15:00:06 +02001235static int taal_panel_reset(struct omap_dss_device *dssdev)
1236{
1237 dev_err(&dssdev->dev, "performing LCD reset\n");
1238
1239 taal_power_off(dssdev);
1240 taal_hw_reset(dssdev);
1241 return taal_power_on(dssdev);
1242}
1243
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001244static int taal_enable(struct omap_dss_device *dssdev)
1245{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001246 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001247 int r;
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001248
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001249 dev_dbg(&dssdev->dev, "enable\n");
1250
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001251 mutex_lock(&td->lock);
1252
1253 if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) {
1254 r = -EINVAL;
1255 goto err;
1256 }
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001257
Archit Taneja1ffefe72011-05-12 17:26:24 +05301258 dsi_bus_lock(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001259
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001260 r = taal_power_on(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001261
Archit Taneja1ffefe72011-05-12 17:26:24 +05301262 dsi_bus_unlock(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001263
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001264 if (r)
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001265 goto err;
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001266
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +02001267 taal_queue_esd_work(dssdev);
Jani Nikula4571a022010-04-12 10:23:46 +03001268
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001269 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
1270
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001271 mutex_unlock(&td->lock);
1272
1273 return 0;
1274err:
1275 dev_dbg(&dssdev->dev, "enable failed\n");
1276 mutex_unlock(&td->lock);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001277 return r;
1278}
1279
1280static void taal_disable(struct omap_dss_device *dssdev)
1281{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001282 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1283
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001284 dev_dbg(&dssdev->dev, "disable\n");
1285
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001286 mutex_lock(&td->lock);
1287
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001288 taal_cancel_ulps_work(dssdev);
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +02001289 taal_cancel_esd_work(dssdev);
Jani Nikula4571a022010-04-12 10:23:46 +03001290
Archit Taneja1ffefe72011-05-12 17:26:24 +05301291 dsi_bus_lock(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001292
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001293 if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) {
Tomi Valkeinene8945672011-05-31 16:39:01 +03001294 int r;
1295
1296 r = taal_wake_up(dssdev);
1297 if (!r)
1298 taal_power_off(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001299 }
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001300
Archit Taneja1ffefe72011-05-12 17:26:24 +05301301 dsi_bus_unlock(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001302
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001303 dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001304
1305 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001306}
1307
1308static int taal_suspend(struct omap_dss_device *dssdev)
1309{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001310 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1311 int r;
1312
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001313 dev_dbg(&dssdev->dev, "suspend\n");
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001314
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001315 mutex_lock(&td->lock);
1316
1317 if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) {
1318 r = -EINVAL;
1319 goto err;
1320 }
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001321
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001322 taal_cancel_ulps_work(dssdev);
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +02001323 taal_cancel_esd_work(dssdev);
Jani Nikula4571a022010-04-12 10:23:46 +03001324
Archit Taneja1ffefe72011-05-12 17:26:24 +05301325 dsi_bus_lock(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001326
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001327 r = taal_wake_up(dssdev);
1328 if (!r)
1329 taal_power_off(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001330
Archit Taneja1ffefe72011-05-12 17:26:24 +05301331 dsi_bus_unlock(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001332
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001333 dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001334
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001335 mutex_unlock(&td->lock);
1336
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001337 return 0;
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001338err:
1339 mutex_unlock(&td->lock);
1340 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001341}
1342
1343static int taal_resume(struct omap_dss_device *dssdev)
1344{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001345 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001346 int r;
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001347
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001348 dev_dbg(&dssdev->dev, "resume\n");
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001349
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001350 mutex_lock(&td->lock);
1351
1352 if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) {
1353 r = -EINVAL;
1354 goto err;
1355 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001356
Archit Taneja1ffefe72011-05-12 17:26:24 +05301357 dsi_bus_lock(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001358
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001359 r = taal_power_on(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001360
Archit Taneja1ffefe72011-05-12 17:26:24 +05301361 dsi_bus_unlock(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001362
Jani Nikula4571a022010-04-12 10:23:46 +03001363 if (r) {
Jani Nikulafed44b72010-04-12 10:25:21 +03001364 dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
Jani Nikula4571a022010-04-12 10:23:46 +03001365 } else {
Jani Nikulafed44b72010-04-12 10:25:21 +03001366 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +02001367 taal_queue_esd_work(dssdev);
Jani Nikula4571a022010-04-12 10:23:46 +03001368 }
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001369
1370 mutex_unlock(&td->lock);
1371
1372 return r;
1373err:
1374 mutex_unlock(&td->lock);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001375 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001376}
1377
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001378static void taal_framedone_cb(int err, void *data)
1379{
1380 struct omap_dss_device *dssdev = data;
1381 dev_dbg(&dssdev->dev, "framedone, err %d\n", err);
Archit Taneja1ffefe72011-05-12 17:26:24 +05301382 dsi_bus_unlock(dssdev);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001383}
1384
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001385static irqreturn_t taal_te_isr(int irq, void *data)
1386{
1387 struct omap_dss_device *dssdev = data;
1388 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1389 int old;
1390 int r;
1391
1392 old = atomic_cmpxchg(&td->do_update, 1, 0);
1393
1394 if (old) {
1395 cancel_delayed_work(&td->te_timeout_work);
1396
Tomi Valkeinen5476e742011-11-03 16:34:20 +02001397 r = omap_dsi_update(dssdev, td->channel, taal_framedone_cb,
1398 dssdev);
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001399 if (r)
1400 goto err;
1401 }
1402
1403 return IRQ_HANDLED;
1404err:
1405 dev_err(&dssdev->dev, "start update failed\n");
Archit Taneja1ffefe72011-05-12 17:26:24 +05301406 dsi_bus_unlock(dssdev);
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001407 return IRQ_HANDLED;
1408}
1409
1410static void taal_te_timeout_work_callback(struct work_struct *work)
1411{
1412 struct taal_data *td = container_of(work, struct taal_data,
1413 te_timeout_work.work);
1414 struct omap_dss_device *dssdev = td->dssdev;
1415
1416 dev_err(&dssdev->dev, "TE not received for 250ms!\n");
1417
1418 atomic_set(&td->do_update, 0);
Archit Taneja1ffefe72011-05-12 17:26:24 +05301419 dsi_bus_unlock(dssdev);
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001420}
1421
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001422static int taal_update(struct omap_dss_device *dssdev,
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001423 u16 x, u16 y, u16 w, u16 h)
1424{
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001425 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001426 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001427 int r;
1428
1429 dev_dbg(&dssdev->dev, "update %d, %d, %d x %d\n", x, y, w, h);
1430
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001431 mutex_lock(&td->lock);
Archit Taneja1ffefe72011-05-12 17:26:24 +05301432 dsi_bus_lock(dssdev);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001433
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001434 r = taal_wake_up(dssdev);
1435 if (r)
1436 goto err;
1437
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001438 if (!td->enabled) {
1439 r = 0;
1440 goto err;
1441 }
1442
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301443 r = taal_set_update_window(td, x, y, w, h);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001444 if (r)
1445 goto err;
1446
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001447 if (td->te_enabled && panel_data->use_ext_te) {
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001448 td->update_region.x = x;
1449 td->update_region.y = y;
1450 td->update_region.w = w;
1451 td->update_region.h = h;
1452 barrier();
1453 schedule_delayed_work(&td->te_timeout_work,
1454 msecs_to_jiffies(250));
1455 atomic_set(&td->do_update, 1);
1456 } else {
Tomi Valkeinen5476e742011-11-03 16:34:20 +02001457 r = omap_dsi_update(dssdev, td->channel, taal_framedone_cb,
1458 dssdev);
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001459 if (r)
1460 goto err;
1461 }
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001462
1463 /* note: no bus_unlock here. unlock is in framedone_cb */
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001464 mutex_unlock(&td->lock);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001465 return 0;
1466err:
Archit Taneja1ffefe72011-05-12 17:26:24 +05301467 dsi_bus_unlock(dssdev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001468 mutex_unlock(&td->lock);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001469 return r;
1470}
1471
1472static int taal_sync(struct omap_dss_device *dssdev)
1473{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001474 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1475
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001476 dev_dbg(&dssdev->dev, "sync\n");
1477
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001478 mutex_lock(&td->lock);
Archit Taneja1ffefe72011-05-12 17:26:24 +05301479 dsi_bus_lock(dssdev);
1480 dsi_bus_unlock(dssdev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001481 mutex_unlock(&td->lock);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001482
1483 dev_dbg(&dssdev->dev, "sync done\n");
1484
1485 return 0;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001486}
1487
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001488static int _taal_enable_te(struct omap_dss_device *dssdev, bool enable)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001489{
Jani Nikulae7f6c3f2010-04-15 12:55:38 +03001490 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001491 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001492 int r;
1493
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001494 if (enable)
Archit Taneja7a7c48f2011-08-25 18:25:03 +05301495 r = taal_dcs_write_1(td, MIPI_DCS_SET_TEAR_ON, 0);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001496 else
Archit Taneja7a7c48f2011-08-25 18:25:03 +05301497 r = taal_dcs_write_0(td, MIPI_DCS_SET_TEAR_OFF);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001498
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001499 if (!panel_data->use_ext_te)
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001500 omapdss_dsi_enable_te(dssdev, enable);
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001501
Jani Nikulae7f6c3f2010-04-15 12:55:38 +03001502 if (td->panel_config->sleep.enable_te)
1503 msleep(td->panel_config->sleep.enable_te);
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001504
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001505 return r;
1506}
1507
1508static int taal_enable_te(struct omap_dss_device *dssdev, bool enable)
1509{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001510 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001511 int r;
1512
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001513 mutex_lock(&td->lock);
Tomi Valkeinenffb63c92010-05-21 10:09:18 +03001514
1515 if (td->te_enabled == enable)
1516 goto end;
1517
Archit Taneja1ffefe72011-05-12 17:26:24 +05301518 dsi_bus_lock(dssdev);
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001519
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001520 if (td->enabled) {
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001521 r = taal_wake_up(dssdev);
1522 if (r)
1523 goto err;
1524
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001525 r = _taal_enable_te(dssdev, enable);
1526 if (r)
1527 goto err;
1528 }
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001529
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001530 td->te_enabled = enable;
1531
Archit Taneja1ffefe72011-05-12 17:26:24 +05301532 dsi_bus_unlock(dssdev);
Tomi Valkeinenffb63c92010-05-21 10:09:18 +03001533end:
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001534 mutex_unlock(&td->lock);
1535
1536 return 0;
1537err:
Archit Taneja1ffefe72011-05-12 17:26:24 +05301538 dsi_bus_unlock(dssdev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001539 mutex_unlock(&td->lock);
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001540
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001541 return r;
1542}
1543
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001544static int taal_get_te(struct omap_dss_device *dssdev)
1545{
1546 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001547 int r;
1548
1549 mutex_lock(&td->lock);
1550 r = td->te_enabled;
1551 mutex_unlock(&td->lock);
1552
1553 return r;
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001554}
1555
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001556static int taal_rotate(struct omap_dss_device *dssdev, u8 rotate)
1557{
1558 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1559 int r;
1560
1561 dev_dbg(&dssdev->dev, "rotate %d\n", rotate);
1562
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001563 mutex_lock(&td->lock);
Tomi Valkeinenffb63c92010-05-21 10:09:18 +03001564
1565 if (td->rotate == rotate)
1566 goto end;
1567
Archit Taneja1ffefe72011-05-12 17:26:24 +05301568 dsi_bus_lock(dssdev);
Tomi Valkeinen87424e12010-01-08 16:52:48 +02001569
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001570 if (td->enabled) {
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001571 r = taal_wake_up(dssdev);
1572 if (r)
1573 goto err;
1574
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301575 r = taal_set_addr_mode(td, rotate, td->mirror);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001576 if (r)
Tomi Valkeinen87424e12010-01-08 16:52:48 +02001577 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001578 }
1579
1580 td->rotate = rotate;
1581
Archit Taneja1ffefe72011-05-12 17:26:24 +05301582 dsi_bus_unlock(dssdev);
Tomi Valkeinenffb63c92010-05-21 10:09:18 +03001583end:
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001584 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001585 return 0;
Tomi Valkeinen87424e12010-01-08 16:52:48 +02001586err:
Archit Taneja1ffefe72011-05-12 17:26:24 +05301587 dsi_bus_unlock(dssdev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001588 mutex_unlock(&td->lock);
Tomi Valkeinen87424e12010-01-08 16:52:48 +02001589 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001590}
1591
1592static u8 taal_get_rotate(struct omap_dss_device *dssdev)
1593{
1594 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001595 int r;
1596
1597 mutex_lock(&td->lock);
1598 r = td->rotate;
1599 mutex_unlock(&td->lock);
1600
1601 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001602}
1603
1604static int taal_mirror(struct omap_dss_device *dssdev, bool enable)
1605{
1606 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1607 int r;
1608
1609 dev_dbg(&dssdev->dev, "mirror %d\n", enable);
1610
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001611 mutex_lock(&td->lock);
Tomi Valkeinenffb63c92010-05-21 10:09:18 +03001612
1613 if (td->mirror == enable)
1614 goto end;
1615
Archit Taneja1ffefe72011-05-12 17:26:24 +05301616 dsi_bus_lock(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001617 if (td->enabled) {
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001618 r = taal_wake_up(dssdev);
1619 if (r)
1620 goto err;
1621
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301622 r = taal_set_addr_mode(td, td->rotate, enable);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001623 if (r)
Tomi Valkeinen8d8aa612010-01-08 16:30:33 +02001624 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001625 }
1626
1627 td->mirror = enable;
1628
Archit Taneja1ffefe72011-05-12 17:26:24 +05301629 dsi_bus_unlock(dssdev);
Tomi Valkeinenffb63c92010-05-21 10:09:18 +03001630end:
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001631 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001632 return 0;
Tomi Valkeinen8d8aa612010-01-08 16:30:33 +02001633err:
Archit Taneja1ffefe72011-05-12 17:26:24 +05301634 dsi_bus_unlock(dssdev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001635 mutex_unlock(&td->lock);
Tomi Valkeinen8d8aa612010-01-08 16:30:33 +02001636 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001637}
1638
1639static bool taal_get_mirror(struct omap_dss_device *dssdev)
1640{
1641 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001642 int r;
1643
1644 mutex_lock(&td->lock);
1645 r = td->mirror;
1646 mutex_unlock(&td->lock);
1647
1648 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001649}
1650
1651static int taal_run_test(struct omap_dss_device *dssdev, int test_num)
1652{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001653 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001654 u8 id1, id2, id3;
1655 int r;
1656
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001657 mutex_lock(&td->lock);
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001658
1659 if (!td->enabled) {
1660 r = -ENODEV;
1661 goto err1;
1662 }
1663
Archit Taneja1ffefe72011-05-12 17:26:24 +05301664 dsi_bus_lock(dssdev);
Tomi Valkeinen1a75ef42010-01-08 16:21:28 +02001665
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001666 r = taal_wake_up(dssdev);
1667 if (r)
1668 goto err2;
1669
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301670 r = taal_dcs_read_1(td, DCS_GET_ID1, &id1);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001671 if (r)
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001672 goto err2;
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301673 r = taal_dcs_read_1(td, DCS_GET_ID2, &id2);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001674 if (r)
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001675 goto err2;
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301676 r = taal_dcs_read_1(td, DCS_GET_ID3, &id3);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001677 if (r)
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001678 goto err2;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001679
Archit Taneja1ffefe72011-05-12 17:26:24 +05301680 dsi_bus_unlock(dssdev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001681 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001682 return 0;
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001683err2:
Archit Taneja1ffefe72011-05-12 17:26:24 +05301684 dsi_bus_unlock(dssdev);
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001685err1:
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001686 mutex_unlock(&td->lock);
Tomi Valkeinen1a75ef42010-01-08 16:21:28 +02001687 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001688}
1689
1690static int taal_memory_read(struct omap_dss_device *dssdev,
1691 void *buf, size_t size,
1692 u16 x, u16 y, u16 w, u16 h)
1693{
1694 int r;
1695 int first = 1;
1696 int plen;
1697 unsigned buf_used = 0;
Tomi Valkeinenc75d9462010-01-08 16:56:44 +02001698 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1699
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001700 if (size < w * h * 3)
1701 return -ENOMEM;
1702
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001703 mutex_lock(&td->lock);
1704
1705 if (!td->enabled) {
1706 r = -ENODEV;
1707 goto err1;
1708 }
1709
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001710 size = min(w * h * 3,
1711 dssdev->panel.timings.x_res *
1712 dssdev->panel.timings.y_res * 3);
1713
Archit Taneja1ffefe72011-05-12 17:26:24 +05301714 dsi_bus_lock(dssdev);
Tomi Valkeinenc75d9462010-01-08 16:56:44 +02001715
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001716 r = taal_wake_up(dssdev);
1717 if (r)
1718 goto err2;
1719
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001720 /* plen 1 or 2 goes into short packet. until checksum error is fixed,
1721 * use short packets. plen 32 works, but bigger packets seem to cause
1722 * an error. */
1723 if (size % 2)
1724 plen = 1;
1725 else
1726 plen = 2;
1727
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301728 taal_set_update_window(td, x, y, w, h);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001729
Archit Taneja1ffefe72011-05-12 17:26:24 +05301730 r = dsi_vc_set_max_rx_packet_size(dssdev, td->channel, plen);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001731 if (r)
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001732 goto err2;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001733
1734 while (buf_used < size) {
1735 u8 dcs_cmd = first ? 0x2e : 0x3e;
1736 first = 0;
1737
Archit Taneja1ffefe72011-05-12 17:26:24 +05301738 r = dsi_vc_dcs_read(dssdev, td->channel, dcs_cmd,
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001739 buf + buf_used, size - buf_used);
1740
1741 if (r < 0) {
1742 dev_err(&dssdev->dev, "read error\n");
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001743 goto err3;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001744 }
1745
1746 buf_used += r;
1747
1748 if (r < plen) {
1749 dev_err(&dssdev->dev, "short read\n");
1750 break;
1751 }
1752
1753 if (signal_pending(current)) {
1754 dev_err(&dssdev->dev, "signal pending, "
1755 "aborting memory read\n");
1756 r = -ERESTARTSYS;
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001757 goto err3;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001758 }
1759 }
1760
1761 r = buf_used;
1762
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001763err3:
Archit Taneja1ffefe72011-05-12 17:26:24 +05301764 dsi_vc_set_max_rx_packet_size(dssdev, td->channel, 1);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001765err2:
Archit Taneja1ffefe72011-05-12 17:26:24 +05301766 dsi_bus_unlock(dssdev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001767err1:
1768 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001769 return r;
1770}
1771
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001772static void taal_ulps_work(struct work_struct *work)
1773{
1774 struct taal_data *td = container_of(work, struct taal_data,
1775 ulps_work.work);
1776 struct omap_dss_device *dssdev = td->dssdev;
1777
1778 mutex_lock(&td->lock);
1779
1780 if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE || !td->enabled) {
1781 mutex_unlock(&td->lock);
1782 return;
1783 }
1784
Archit Taneja1ffefe72011-05-12 17:26:24 +05301785 dsi_bus_lock(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001786
1787 taal_enter_ulps(dssdev);
1788
Archit Taneja1ffefe72011-05-12 17:26:24 +05301789 dsi_bus_unlock(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001790 mutex_unlock(&td->lock);
1791}
1792
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001793static void taal_esd_work(struct work_struct *work)
1794{
1795 struct taal_data *td = container_of(work, struct taal_data,
1796 esd_work.work);
1797 struct omap_dss_device *dssdev = td->dssdev;
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001798 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001799 u8 state1, state2;
1800 int r;
1801
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001802 mutex_lock(&td->lock);
1803
1804 if (!td->enabled) {
1805 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001806 return;
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001807 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001808
Archit Taneja1ffefe72011-05-12 17:26:24 +05301809 dsi_bus_lock(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001810
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001811 r = taal_wake_up(dssdev);
1812 if (r) {
1813 dev_err(&dssdev->dev, "failed to exit ULPS\n");
1814 goto err;
1815 }
1816
Archit Taneja7a7c48f2011-08-25 18:25:03 +05301817 r = taal_dcs_read_1(td, MIPI_DCS_GET_DIAGNOSTIC_RESULT, &state1);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001818 if (r) {
1819 dev_err(&dssdev->dev, "failed to read Taal status\n");
1820 goto err;
1821 }
1822
1823 /* Run self diagnostics */
1824 r = taal_sleep_out(td);
1825 if (r) {
1826 dev_err(&dssdev->dev, "failed to run Taal self-diagnostics\n");
1827 goto err;
1828 }
1829
Archit Taneja7a7c48f2011-08-25 18:25:03 +05301830 r = taal_dcs_read_1(td, MIPI_DCS_GET_DIAGNOSTIC_RESULT, &state2);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001831 if (r) {
1832 dev_err(&dssdev->dev, "failed to read Taal status\n");
1833 goto err;
1834 }
1835
1836 /* Each sleep out command will trigger a self diagnostic and flip
1837 * Bit6 if the test passes.
1838 */
1839 if (!((state1 ^ state2) & (1 << 6))) {
1840 dev_err(&dssdev->dev, "LCD self diagnostics failed\n");
1841 goto err;
1842 }
1843 /* Self-diagnostics result is also shown on TE GPIO line. We need
1844 * to re-enable TE after self diagnostics */
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001845 if (td->te_enabled && panel_data->use_ext_te) {
Archit Taneja7a7c48f2011-08-25 18:25:03 +05301846 r = taal_dcs_write_1(td, MIPI_DCS_SET_TEAR_ON, 0);
Tomi Valkeinen1189b7f2010-03-01 13:52:10 +02001847 if (r)
1848 goto err;
1849 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001850
Archit Taneja1ffefe72011-05-12 17:26:24 +05301851 dsi_bus_unlock(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001852
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +02001853 taal_queue_esd_work(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001854
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001855 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001856 return;
1857err:
1858 dev_err(&dssdev->dev, "performing LCD reset\n");
1859
Tomi Valkeinenbb5476c2011-03-24 15:00:06 +02001860 taal_panel_reset(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001861
Archit Taneja1ffefe72011-05-12 17:26:24 +05301862 dsi_bus_unlock(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001863
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +02001864 taal_queue_esd_work(dssdev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001865
1866 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001867}
1868
1869static struct omap_dss_driver taal_driver = {
1870 .probe = taal_probe,
Tomi Valkeinen14e4d782011-03-31 12:03:51 +03001871 .remove = __exit_p(taal_remove),
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001872
1873 .enable = taal_enable,
1874 .disable = taal_disable,
1875 .suspend = taal_suspend,
1876 .resume = taal_resume,
1877
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001878 .update = taal_update,
1879 .sync = taal_sync,
1880
Tomi Valkeinen96adcec2010-01-11 13:54:33 +02001881 .get_resolution = taal_get_resolution,
Tomi Valkeinena2699502010-01-11 14:33:40 +02001882 .get_recommended_bpp = omapdss_default_get_recommended_bpp,
1883
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001884 .enable_te = taal_enable_te,
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001885 .get_te = taal_get_te,
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001886
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001887 .set_rotate = taal_rotate,
1888 .get_rotate = taal_get_rotate,
1889 .set_mirror = taal_mirror,
1890 .get_mirror = taal_get_mirror,
1891 .run_test = taal_run_test,
1892 .memory_read = taal_memory_read,
1893
Tomi Valkeinen69b20482010-01-20 12:11:25 +02001894 .get_timings = taal_get_timings,
1895
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001896 .driver = {
1897 .name = "taal",
1898 .owner = THIS_MODULE,
1899 },
1900};
1901
1902static int __init taal_init(void)
1903{
1904 omap_dss_register_driver(&taal_driver);
1905
1906 return 0;
1907}
1908
1909static void __exit taal_exit(void)
1910{
1911 omap_dss_unregister_driver(&taal_driver);
1912}
1913
1914module_init(taal_init);
1915module_exit(taal_exit);
1916
1917MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@nokia.com>");
1918MODULE_DESCRIPTION("Taal Driver");
1919MODULE_LICENSE("GPL");