blob: a743b7cee7a5e33da85cc2bfe389d89901efe2cc [file] [log] [blame]
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001/*
2 * Taal DSI command mode panel
3 *
4 * Copyright (C) 2009 Nokia Corporation
5 * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License version 2 as published by
9 * the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20/*#define DEBUG*/
21
22#include <linux/module.h>
23#include <linux/delay.h>
24#include <linux/err.h>
25#include <linux/jiffies.h>
26#include <linux/sched.h>
27#include <linux/backlight.h>
28#include <linux/fb.h>
29#include <linux/interrupt.h>
30#include <linux/gpio.h>
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +020031#include <linux/workqueue.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090032#include <linux/slab.h>
Tomi Valkeinenc8cd4542010-06-09 15:24:46 +030033#include <linux/regulator/consumer.h>
Tomi Valkeinena3201a02010-03-03 14:31:45 +020034#include <linux/mutex.h>
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +020035
Tomi Valkeinena0b38cc2011-05-11 14:05:07 +030036#include <video/omapdss.h>
Tomi Valkeinen4e9f99d2011-05-11 14:10:07 +030037#include <video/omap-panel-nokia-dsi.h>
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +020038
39/* DSI Virtual channel. Hardcoded for now. */
40#define TCH 0
41
42#define DCS_READ_NUM_ERRORS 0x05
43#define DCS_READ_POWER_MODE 0x0a
44#define DCS_READ_MADCTL 0x0b
45#define DCS_READ_PIXEL_FORMAT 0x0c
46#define DCS_RDDSDR 0x0f
47#define DCS_SLEEP_IN 0x10
48#define DCS_SLEEP_OUT 0x11
49#define DCS_DISPLAY_OFF 0x28
50#define DCS_DISPLAY_ON 0x29
51#define DCS_COLUMN_ADDR 0x2a
52#define DCS_PAGE_ADDR 0x2b
53#define DCS_MEMORY_WRITE 0x2c
54#define DCS_TEAR_OFF 0x34
55#define DCS_TEAR_ON 0x35
56#define DCS_MEM_ACC_CTRL 0x36
57#define DCS_PIXEL_FORMAT 0x3a
58#define DCS_BRIGHTNESS 0x51
59#define DCS_CTRL_DISPLAY 0x53
60#define DCS_WRITE_CABC 0x55
61#define DCS_READ_CABC 0x56
62#define DCS_GET_ID1 0xda
63#define DCS_GET_ID2 0xdb
64#define DCS_GET_ID3 0xdc
65
Jani Nikula7ae2fb12010-04-13 10:57:52 +030066static irqreturn_t taal_te_isr(int irq, void *data);
67static void taal_te_timeout_work_callback(struct work_struct *work);
Tomi Valkeinen21df20f2010-03-02 12:13:55 +020068static int _taal_enable_te(struct omap_dss_device *dssdev, bool enable);
69
Tomi Valkeinenc8cd4542010-06-09 15:24:46 +030070struct panel_regulator {
71 struct regulator *regulator;
72 const char *name;
73 int min_uV;
74 int max_uV;
75};
76
77static void free_regulators(struct panel_regulator *regulators, int n)
78{
79 int i;
80
81 for (i = 0; i < n; i++) {
82 /* disable/put in reverse order */
83 regulator_disable(regulators[n - i - 1].regulator);
84 regulator_put(regulators[n - i - 1].regulator);
85 }
86}
87
88static int init_regulators(struct omap_dss_device *dssdev,
89 struct panel_regulator *regulators, int n)
90{
91 int r, i, v;
92
93 for (i = 0; i < n; i++) {
94 struct regulator *reg;
95
96 reg = regulator_get(&dssdev->dev, regulators[i].name);
97 if (IS_ERR(reg)) {
98 dev_err(&dssdev->dev, "failed to get regulator %s\n",
99 regulators[i].name);
100 r = PTR_ERR(reg);
101 goto err;
102 }
103
104 /* FIXME: better handling of fixed vs. variable regulators */
105 v = regulator_get_voltage(reg);
106 if (v < regulators[i].min_uV || v > regulators[i].max_uV) {
107 r = regulator_set_voltage(reg, regulators[i].min_uV,
108 regulators[i].max_uV);
109 if (r) {
110 dev_err(&dssdev->dev,
111 "failed to set regulator %s voltage\n",
112 regulators[i].name);
113 regulator_put(reg);
114 goto err;
115 }
116 }
117
118 r = regulator_enable(reg);
119 if (r) {
120 dev_err(&dssdev->dev, "failed to enable regulator %s\n",
121 regulators[i].name);
122 regulator_put(reg);
123 goto err;
124 }
125
126 regulators[i].regulator = reg;
127 }
128
129 return 0;
130
131err:
132 free_regulators(regulators, i);
133
134 return r;
135}
136
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300137/**
138 * struct panel_config - panel configuration
139 * @name: panel name
140 * @type: panel type
141 * @timings: panel resolution
142 * @sleep: various panel specific delays, passed to msleep() if non-zero
143 * @reset_sequence: reset sequence timings, passed to udelay() if non-zero
Tomi Valkeinenc8cd4542010-06-09 15:24:46 +0300144 * @regulators: array of panel regulators
145 * @num_regulators: number of regulators in the array
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300146 */
147struct panel_config {
148 const char *name;
149 int type;
150
151 struct omap_video_timings timings;
152
153 struct {
154 unsigned int sleep_in;
155 unsigned int sleep_out;
156 unsigned int hw_reset;
157 unsigned int enable_te;
158 } sleep;
159
160 struct {
161 unsigned int high;
162 unsigned int low;
163 } reset_sequence;
Tomi Valkeinenc8cd4542010-06-09 15:24:46 +0300164
165 struct panel_regulator *regulators;
166 int num_regulators;
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300167};
168
169enum {
170 PANEL_TAAL,
171};
172
173static struct panel_config panel_configs[] = {
174 {
175 .name = "taal",
176 .type = PANEL_TAAL,
177 .timings = {
178 .x_res = 864,
179 .y_res = 480,
180 },
181 .sleep = {
182 .sleep_in = 5,
183 .sleep_out = 5,
184 .hw_reset = 5,
185 .enable_te = 100, /* possible panel bug */
186 },
187 .reset_sequence = {
188 .high = 10,
189 .low = 10,
190 },
191 },
192};
193
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200194struct taal_data {
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200195 struct mutex lock;
196
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200197 struct backlight_device *bldev;
198
199 unsigned long hw_guard_end; /* next value of jiffies when we can
200 * issue the next sleep in/out command
201 */
202 unsigned long hw_guard_wait; /* max guard time in jiffies */
203
204 struct omap_dss_device *dssdev;
205
206 bool enabled;
207 u8 rotate;
208 bool mirror;
209
210 bool te_enabled;
Jani Nikula7ae2fb12010-04-13 10:57:52 +0300211
212 atomic_t do_update;
213 struct {
214 u16 x;
215 u16 y;
216 u16 w;
217 u16 h;
218 } update_region;
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530219 int channel;
220
Jani Nikula7ae2fb12010-04-13 10:57:52 +0300221 struct delayed_work te_timeout_work;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200222
223 bool use_dsi_bl;
224
225 bool cabc_broken;
226 unsigned cabc_mode;
227
228 bool intro_printed;
229
Tomi Valkeinen883b9ac2011-03-24 15:01:30 +0200230 struct workqueue_struct *workqueue;
231
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200232 struct delayed_work esd_work;
Tomi Valkeinen33a410b2011-03-24 13:58:01 +0200233 unsigned esd_interval;
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300234
235 struct panel_config *panel_config;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200236};
237
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300238static inline struct nokia_dsi_panel_data
239*get_panel_data(const struct omap_dss_device *dssdev)
240{
241 return (struct nokia_dsi_panel_data *) dssdev->data;
242}
243
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200244static void taal_esd_work(struct work_struct *work);
245
246static void hw_guard_start(struct taal_data *td, int guard_msec)
247{
248 td->hw_guard_wait = msecs_to_jiffies(guard_msec);
249 td->hw_guard_end = jiffies + td->hw_guard_wait;
250}
251
252static void hw_guard_wait(struct taal_data *td)
253{
254 unsigned long wait = td->hw_guard_end - jiffies;
255
256 if ((long)wait > 0 && wait <= td->hw_guard_wait) {
257 set_current_state(TASK_UNINTERRUPTIBLE);
258 schedule_timeout(wait);
259 }
260}
261
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530262static int taal_dcs_read_1(struct taal_data *td, u8 dcs_cmd, u8 *data)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200263{
264 int r;
265 u8 buf[1];
266
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530267 r = dsi_vc_dcs_read(td->channel, dcs_cmd, buf, 1);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200268
269 if (r < 0)
270 return r;
271
272 *data = buf[0];
273
274 return 0;
275}
276
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530277static int taal_dcs_write_0(struct taal_data *td, u8 dcs_cmd)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200278{
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530279 return dsi_vc_dcs_write(td->channel, &dcs_cmd, 1);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200280}
281
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530282static int taal_dcs_write_1(struct taal_data *td, u8 dcs_cmd, u8 param)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200283{
284 u8 buf[2];
285 buf[0] = dcs_cmd;
286 buf[1] = param;
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530287 return dsi_vc_dcs_write(td->channel, buf, 2);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200288}
289
290static int taal_sleep_in(struct taal_data *td)
291
292{
293 u8 cmd;
294 int r;
295
296 hw_guard_wait(td);
297
298 cmd = DCS_SLEEP_IN;
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530299 r = dsi_vc_dcs_write_nosync(td->channel, &cmd, 1);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200300 if (r)
301 return r;
302
303 hw_guard_start(td, 120);
304
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300305 if (td->panel_config->sleep.sleep_in)
306 msleep(td->panel_config->sleep.sleep_in);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200307
308 return 0;
309}
310
311static int taal_sleep_out(struct taal_data *td)
312{
313 int r;
314
315 hw_guard_wait(td);
316
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530317 r = taal_dcs_write_0(td, DCS_SLEEP_OUT);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200318 if (r)
319 return r;
320
321 hw_guard_start(td, 120);
322
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300323 if (td->panel_config->sleep.sleep_out)
324 msleep(td->panel_config->sleep.sleep_out);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200325
326 return 0;
327}
328
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530329static int taal_get_id(struct taal_data *td, u8 *id1, u8 *id2, u8 *id3)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200330{
331 int r;
332
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530333 r = taal_dcs_read_1(td, DCS_GET_ID1, id1);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200334 if (r)
335 return r;
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530336 r = taal_dcs_read_1(td, DCS_GET_ID2, id2);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200337 if (r)
338 return r;
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530339 r = taal_dcs_read_1(td, DCS_GET_ID3, id3);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200340 if (r)
341 return r;
342
343 return 0;
344}
345
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530346static int taal_set_addr_mode(struct taal_data *td, u8 rotate, bool mirror)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200347{
348 int r;
349 u8 mode;
350 int b5, b6, b7;
351
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530352 r = taal_dcs_read_1(td, DCS_READ_MADCTL, &mode);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200353 if (r)
354 return r;
355
356 switch (rotate) {
357 default:
358 case 0:
359 b7 = 0;
360 b6 = 0;
361 b5 = 0;
362 break;
363 case 1:
364 b7 = 0;
365 b6 = 1;
366 b5 = 1;
367 break;
368 case 2:
369 b7 = 1;
370 b6 = 1;
371 b5 = 0;
372 break;
373 case 3:
374 b7 = 1;
375 b6 = 0;
376 b5 = 1;
377 break;
378 }
379
380 if (mirror)
381 b6 = !b6;
382
383 mode &= ~((1<<7) | (1<<6) | (1<<5));
384 mode |= (b7 << 7) | (b6 << 6) | (b5 << 5);
385
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530386 return taal_dcs_write_1(td, DCS_MEM_ACC_CTRL, mode);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200387}
388
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530389static int taal_set_update_window(struct taal_data *td,
390 u16 x, u16 y, u16 w, u16 h)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200391{
392 int r;
393 u16 x1 = x;
394 u16 x2 = x + w - 1;
395 u16 y1 = y;
396 u16 y2 = y + h - 1;
397
398 u8 buf[5];
399 buf[0] = DCS_COLUMN_ADDR;
400 buf[1] = (x1 >> 8) & 0xff;
401 buf[2] = (x1 >> 0) & 0xff;
402 buf[3] = (x2 >> 8) & 0xff;
403 buf[4] = (x2 >> 0) & 0xff;
404
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530405 r = dsi_vc_dcs_write_nosync(td->channel, buf, sizeof(buf));
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200406 if (r)
407 return r;
408
409 buf[0] = DCS_PAGE_ADDR;
410 buf[1] = (y1 >> 8) & 0xff;
411 buf[2] = (y1 >> 0) & 0xff;
412 buf[3] = (y2 >> 8) & 0xff;
413 buf[4] = (y2 >> 0) & 0xff;
414
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530415 r = dsi_vc_dcs_write_nosync(td->channel, buf, sizeof(buf));
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200416 if (r)
417 return r;
418
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530419 dsi_vc_send_bta_sync(td->channel);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200420
421 return r;
422}
423
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +0200424static void taal_queue_esd_work(struct omap_dss_device *dssdev)
425{
426 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
427
428 if (td->esd_interval > 0)
Tomi Valkeinen883b9ac2011-03-24 15:01:30 +0200429 queue_delayed_work(td->workqueue, &td->esd_work,
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +0200430 msecs_to_jiffies(td->esd_interval));
431}
432
433static void taal_cancel_esd_work(struct omap_dss_device *dssdev)
434{
435 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
436
437 cancel_delayed_work(&td->esd_work);
438}
439
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200440static int taal_bl_update_status(struct backlight_device *dev)
441{
442 struct omap_dss_device *dssdev = dev_get_drvdata(&dev->dev);
443 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300444 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200445 int r;
446 int level;
447
448 if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
449 dev->props.power == FB_BLANK_UNBLANK)
450 level = dev->props.brightness;
451 else
452 level = 0;
453
454 dev_dbg(&dssdev->dev, "update brightness to %d\n", level);
455
Tomi Valkeinen1cbc8702010-04-15 16:41:07 +0300456 mutex_lock(&td->lock);
457
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200458 if (td->use_dsi_bl) {
459 if (td->enabled) {
460 dsi_bus_lock();
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530461 r = taal_dcs_write_1(td, DCS_BRIGHTNESS, level);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200462 dsi_bus_unlock();
Tomi Valkeinen1cbc8702010-04-15 16:41:07 +0300463 } else {
464 r = 0;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200465 }
466 } else {
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300467 if (!panel_data->set_backlight)
Tomi Valkeinen1cbc8702010-04-15 16:41:07 +0300468 r = -EINVAL;
469 else
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300470 r = panel_data->set_backlight(dssdev, level);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200471 }
472
Tomi Valkeinen1cbc8702010-04-15 16:41:07 +0300473 mutex_unlock(&td->lock);
474
475 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200476}
477
478static int taal_bl_get_intensity(struct backlight_device *dev)
479{
480 if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
481 dev->props.power == FB_BLANK_UNBLANK)
482 return dev->props.brightness;
483
484 return 0;
485}
486
Lionel Debrouxacc24722010-11-16 14:14:02 +0100487static const struct backlight_ops taal_bl_ops = {
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200488 .get_brightness = taal_bl_get_intensity,
489 .update_status = taal_bl_update_status,
490};
491
492static void taal_get_timings(struct omap_dss_device *dssdev,
493 struct omap_video_timings *timings)
494{
495 *timings = dssdev->panel.timings;
496}
497
498static void taal_get_resolution(struct omap_dss_device *dssdev,
499 u16 *xres, u16 *yres)
500{
501 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
502
503 if (td->rotate == 0 || td->rotate == 2) {
504 *xres = dssdev->panel.timings.x_res;
505 *yres = dssdev->panel.timings.y_res;
506 } else {
507 *yres = dssdev->panel.timings.x_res;
508 *xres = dssdev->panel.timings.y_res;
509 }
510}
511
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200512static ssize_t taal_num_errors_show(struct device *dev,
513 struct device_attribute *attr, char *buf)
514{
515 struct omap_dss_device *dssdev = to_dss_device(dev);
516 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
517 u8 errors;
518 int r;
519
Jani Nikula6b316712010-04-28 11:15:18 +0300520 mutex_lock(&td->lock);
521
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200522 if (td->enabled) {
523 dsi_bus_lock();
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530524 r = taal_dcs_read_1(td, DCS_READ_NUM_ERRORS, &errors);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200525 dsi_bus_unlock();
526 } else {
527 r = -ENODEV;
528 }
529
Jani Nikula6b316712010-04-28 11:15:18 +0300530 mutex_unlock(&td->lock);
531
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200532 if (r)
533 return r;
534
535 return snprintf(buf, PAGE_SIZE, "%d\n", errors);
536}
537
538static ssize_t taal_hw_revision_show(struct device *dev,
539 struct device_attribute *attr, char *buf)
540{
541 struct omap_dss_device *dssdev = to_dss_device(dev);
542 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
543 u8 id1, id2, id3;
544 int r;
545
Jani Nikula6b316712010-04-28 11:15:18 +0300546 mutex_lock(&td->lock);
547
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200548 if (td->enabled) {
549 dsi_bus_lock();
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530550 r = taal_get_id(td, &id1, &id2, &id3);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200551 dsi_bus_unlock();
552 } else {
553 r = -ENODEV;
554 }
555
Jani Nikula6b316712010-04-28 11:15:18 +0300556 mutex_unlock(&td->lock);
557
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200558 if (r)
559 return r;
560
561 return snprintf(buf, PAGE_SIZE, "%02x.%02x.%02x\n", id1, id2, id3);
562}
563
564static const char *cabc_modes[] = {
565 "off", /* used also always when CABC is not supported */
566 "ui",
567 "still-image",
568 "moving-image",
569};
570
571static ssize_t show_cabc_mode(struct device *dev,
572 struct device_attribute *attr,
573 char *buf)
574{
575 struct omap_dss_device *dssdev = to_dss_device(dev);
576 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
577 const char *mode_str;
578 int mode;
579 int len;
580
581 mode = td->cabc_mode;
582
583 mode_str = "unknown";
584 if (mode >= 0 && mode < ARRAY_SIZE(cabc_modes))
585 mode_str = cabc_modes[mode];
586 len = snprintf(buf, PAGE_SIZE, "%s\n", mode_str);
587
588 return len < PAGE_SIZE - 1 ? len : PAGE_SIZE - 1;
589}
590
591static ssize_t store_cabc_mode(struct device *dev,
592 struct device_attribute *attr,
593 const char *buf, size_t count)
594{
595 struct omap_dss_device *dssdev = to_dss_device(dev);
596 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
597 int i;
598
599 for (i = 0; i < ARRAY_SIZE(cabc_modes); i++) {
600 if (sysfs_streq(cabc_modes[i], buf))
601 break;
602 }
603
604 if (i == ARRAY_SIZE(cabc_modes))
605 return -EINVAL;
606
Jani Nikula6b316712010-04-28 11:15:18 +0300607 mutex_lock(&td->lock);
608
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200609 if (td->enabled) {
610 dsi_bus_lock();
611 if (!td->cabc_broken)
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530612 taal_dcs_write_1(td, DCS_WRITE_CABC, i);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200613 dsi_bus_unlock();
614 }
615
616 td->cabc_mode = i;
617
Jani Nikula6b316712010-04-28 11:15:18 +0300618 mutex_unlock(&td->lock);
619
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200620 return count;
621}
622
623static ssize_t show_cabc_available_modes(struct device *dev,
624 struct device_attribute *attr,
625 char *buf)
626{
627 int len;
628 int i;
629
630 for (i = 0, len = 0;
631 len < PAGE_SIZE && i < ARRAY_SIZE(cabc_modes); i++)
632 len += snprintf(&buf[len], PAGE_SIZE - len, "%s%s%s",
633 i ? " " : "", cabc_modes[i],
634 i == ARRAY_SIZE(cabc_modes) - 1 ? "\n" : "");
635
636 return len < PAGE_SIZE ? len : PAGE_SIZE - 1;
637}
638
Tomi Valkeinen1f8fa452011-03-24 14:06:51 +0200639static ssize_t taal_store_esd_interval(struct device *dev,
640 struct device_attribute *attr,
641 const char *buf, size_t count)
642{
643 struct omap_dss_device *dssdev = to_dss_device(dev);
644 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
645
646 unsigned long t;
647 int r;
648
649 r = strict_strtoul(buf, 10, &t);
650 if (r)
651 return r;
652
653 mutex_lock(&td->lock);
654 taal_cancel_esd_work(dssdev);
655 td->esd_interval = t;
656 if (td->enabled)
657 taal_queue_esd_work(dssdev);
658 mutex_unlock(&td->lock);
659
660 return count;
661}
662
663static ssize_t taal_show_esd_interval(struct device *dev,
664 struct device_attribute *attr,
665 char *buf)
666{
667 struct omap_dss_device *dssdev = to_dss_device(dev);
668 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
669 unsigned t;
670
671 mutex_lock(&td->lock);
672 t = td->esd_interval;
673 mutex_unlock(&td->lock);
674
675 return snprintf(buf, PAGE_SIZE, "%u\n", t);
676}
677
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200678static DEVICE_ATTR(num_dsi_errors, S_IRUGO, taal_num_errors_show, NULL);
679static DEVICE_ATTR(hw_revision, S_IRUGO, taal_hw_revision_show, NULL);
680static DEVICE_ATTR(cabc_mode, S_IRUGO | S_IWUSR,
681 show_cabc_mode, store_cabc_mode);
682static DEVICE_ATTR(cabc_available_modes, S_IRUGO,
683 show_cabc_available_modes, NULL);
Tomi Valkeinen1f8fa452011-03-24 14:06:51 +0200684static DEVICE_ATTR(esd_interval, S_IRUGO | S_IWUSR,
685 taal_show_esd_interval, taal_store_esd_interval);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200686
687static struct attribute *taal_attrs[] = {
688 &dev_attr_num_dsi_errors.attr,
689 &dev_attr_hw_revision.attr,
690 &dev_attr_cabc_mode.attr,
691 &dev_attr_cabc_available_modes.attr,
Tomi Valkeinen1f8fa452011-03-24 14:06:51 +0200692 &dev_attr_esd_interval.attr,
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200693 NULL,
694};
695
696static struct attribute_group taal_attr_group = {
697 .attrs = taal_attrs,
698};
699
Jani Nikula006db7b2010-04-09 12:25:59 +0300700static void taal_hw_reset(struct omap_dss_device *dssdev)
701{
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300702 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300703 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
704
705 if (panel_data->reset_gpio == -1)
Jani Nikula006db7b2010-04-09 12:25:59 +0300706 return;
707
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300708 gpio_set_value(panel_data->reset_gpio, 1);
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300709 if (td->panel_config->reset_sequence.high)
710 udelay(td->panel_config->reset_sequence.high);
Jani Nikula006db7b2010-04-09 12:25:59 +0300711 /* reset the panel */
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300712 gpio_set_value(panel_data->reset_gpio, 0);
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300713 /* assert reset */
714 if (td->panel_config->reset_sequence.low)
715 udelay(td->panel_config->reset_sequence.low);
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300716 gpio_set_value(panel_data->reset_gpio, 1);
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300717 /* wait after releasing reset */
718 if (td->panel_config->sleep.hw_reset)
719 msleep(td->panel_config->sleep.hw_reset);
Jani Nikula006db7b2010-04-09 12:25:59 +0300720}
721
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200722static int taal_probe(struct omap_dss_device *dssdev)
723{
Matthew Garretta19a6ee2010-02-17 16:39:44 -0500724 struct backlight_properties props;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200725 struct taal_data *td;
726 struct backlight_device *bldev;
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300727 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300728 struct panel_config *panel_config = NULL;
729 int r, i;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200730
731 dev_dbg(&dssdev->dev, "probe\n");
732
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300733 if (!panel_data || !panel_data->name) {
734 r = -EINVAL;
735 goto err;
736 }
737
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300738 for (i = 0; i < ARRAY_SIZE(panel_configs); i++) {
739 if (strcmp(panel_data->name, panel_configs[i].name) == 0) {
740 panel_config = &panel_configs[i];
741 break;
742 }
743 }
744
745 if (!panel_config) {
746 r = -EINVAL;
747 goto err;
748 }
749
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200750 dssdev->panel.config = OMAP_DSS_LCD_TFT;
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300751 dssdev->panel.timings = panel_config->timings;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200752 dssdev->ctrl.pixel_size = 24;
753
754 td = kzalloc(sizeof(*td), GFP_KERNEL);
755 if (!td) {
756 r = -ENOMEM;
Jani Nikulad2b65782010-04-15 12:24:36 +0300757 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200758 }
759 td->dssdev = dssdev;
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300760 td->panel_config = panel_config;
Tomi Valkeinen33a410b2011-03-24 13:58:01 +0200761 td->esd_interval = panel_data->esd_interval;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200762
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200763 mutex_init(&td->lock);
764
Jani Nikula7ae2fb12010-04-13 10:57:52 +0300765 atomic_set(&td->do_update, 0);
766
Tomi Valkeinenc8cd4542010-06-09 15:24:46 +0300767 r = init_regulators(dssdev, panel_config->regulators,
768 panel_config->num_regulators);
769 if (r)
770 goto err_reg;
771
Tomi Valkeinen883b9ac2011-03-24 15:01:30 +0200772 td->workqueue = create_singlethread_workqueue("taal_esd");
773 if (td->workqueue == NULL) {
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200774 dev_err(&dssdev->dev, "can't create ESD workqueue\n");
775 r = -ENOMEM;
Jani Nikulad2b65782010-04-15 12:24:36 +0300776 goto err_wq;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200777 }
778 INIT_DELAYED_WORK_DEFERRABLE(&td->esd_work, taal_esd_work);
779
780 dev_set_drvdata(&dssdev->dev, td);
781
Jani Nikula006db7b2010-04-09 12:25:59 +0300782 taal_hw_reset(dssdev);
783
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200784 /* if no platform set_backlight() defined, presume DSI backlight
785 * control */
Matthew Garretta19a6ee2010-02-17 16:39:44 -0500786 memset(&props, 0, sizeof(struct backlight_properties));
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300787 if (!panel_data->set_backlight)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200788 td->use_dsi_bl = true;
789
Matthew Garretta19a6ee2010-02-17 16:39:44 -0500790 if (td->use_dsi_bl)
791 props.max_brightness = 255;
792 else
793 props.max_brightness = 127;
Matthew Garrettbb7ca742011-03-22 16:30:21 -0700794
795 props.type = BACKLIGHT_RAW;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200796 bldev = backlight_device_register("taal", &dssdev->dev, dssdev,
Matthew Garretta19a6ee2010-02-17 16:39:44 -0500797 &taal_bl_ops, &props);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200798 if (IS_ERR(bldev)) {
799 r = PTR_ERR(bldev);
Jani Nikulad2b65782010-04-15 12:24:36 +0300800 goto err_bl;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200801 }
802
803 td->bldev = bldev;
804
805 bldev->props.fb_blank = FB_BLANK_UNBLANK;
806 bldev->props.power = FB_BLANK_UNBLANK;
Matthew Garretta19a6ee2010-02-17 16:39:44 -0500807 if (td->use_dsi_bl)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200808 bldev->props.brightness = 255;
Matthew Garretta19a6ee2010-02-17 16:39:44 -0500809 else
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200810 bldev->props.brightness = 127;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200811
812 taal_bl_update_status(bldev);
813
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300814 if (panel_data->use_ext_te) {
815 int gpio = panel_data->ext_te_gpio;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200816
817 r = gpio_request(gpio, "taal irq");
818 if (r) {
819 dev_err(&dssdev->dev, "GPIO request failed\n");
Jani Nikulad2b65782010-04-15 12:24:36 +0300820 goto err_gpio;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200821 }
822
823 gpio_direction_input(gpio);
824
825 r = request_irq(gpio_to_irq(gpio), taal_te_isr,
826 IRQF_DISABLED | IRQF_TRIGGER_RISING,
827 "taal vsync", dssdev);
828
829 if (r) {
830 dev_err(&dssdev->dev, "IRQ request failed\n");
831 gpio_free(gpio);
Jani Nikulad2b65782010-04-15 12:24:36 +0300832 goto err_irq;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200833 }
834
Jani Nikula7ae2fb12010-04-13 10:57:52 +0300835 INIT_DELAYED_WORK_DEFERRABLE(&td->te_timeout_work,
836 taal_te_timeout_work_callback);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200837
Jani Nikula7ae2fb12010-04-13 10:57:52 +0300838 dev_dbg(&dssdev->dev, "Using GPIO TE\n");
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200839 }
840
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530841 r = omap_dsi_request_vc(dssdev, &td->channel);
842 if (r) {
843 dev_err(&dssdev->dev, "failed to get virtual channel\n");
844 goto err_req_vc;
845 }
846
847 r = omap_dsi_set_vc_id(dssdev, td->channel, TCH);
848 if (r) {
849 dev_err(&dssdev->dev, "failed to set VC_ID\n");
850 goto err_vc_id;
851 }
852
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200853 r = sysfs_create_group(&dssdev->dev.kobj, &taal_attr_group);
854 if (r) {
855 dev_err(&dssdev->dev, "failed to create sysfs files\n");
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530856 goto err_vc_id;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200857 }
858
859 return 0;
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530860
861err_vc_id:
862 omap_dsi_release_vc(dssdev, td->channel);
863err_req_vc:
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300864 if (panel_data->use_ext_te)
865 free_irq(gpio_to_irq(panel_data->ext_te_gpio), dssdev);
Jani Nikulad2b65782010-04-15 12:24:36 +0300866err_irq:
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300867 if (panel_data->use_ext_te)
868 gpio_free(panel_data->ext_te_gpio);
Jani Nikulad2b65782010-04-15 12:24:36 +0300869err_gpio:
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200870 backlight_device_unregister(bldev);
Jani Nikulad2b65782010-04-15 12:24:36 +0300871err_bl:
Tomi Valkeinen883b9ac2011-03-24 15:01:30 +0200872 destroy_workqueue(td->workqueue);
Jani Nikulad2b65782010-04-15 12:24:36 +0300873err_wq:
Tomi Valkeinenc8cd4542010-06-09 15:24:46 +0300874 free_regulators(panel_config->regulators, panel_config->num_regulators);
875err_reg:
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200876 kfree(td);
Jani Nikulad2b65782010-04-15 12:24:36 +0300877err:
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200878 return r;
879}
880
Tomi Valkeinen14e4d782011-03-31 12:03:51 +0300881static void __exit taal_remove(struct omap_dss_device *dssdev)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200882{
883 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300884 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200885 struct backlight_device *bldev;
886
887 dev_dbg(&dssdev->dev, "remove\n");
888
889 sysfs_remove_group(&dssdev->dev.kobj, &taal_attr_group);
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530890 omap_dsi_release_vc(dssdev, td->channel);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200891
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300892 if (panel_data->use_ext_te) {
893 int gpio = panel_data->ext_te_gpio;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200894 free_irq(gpio_to_irq(gpio), dssdev);
895 gpio_free(gpio);
896 }
897
898 bldev = td->bldev;
899 bldev->props.power = FB_BLANK_POWERDOWN;
900 taal_bl_update_status(bldev);
901 backlight_device_unregister(bldev);
902
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +0200903 taal_cancel_esd_work(dssdev);
Tomi Valkeinen883b9ac2011-03-24 15:01:30 +0200904 destroy_workqueue(td->workqueue);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200905
Jani Nikula006db7b2010-04-09 12:25:59 +0300906 /* reset, to be sure that the panel is in a valid state */
907 taal_hw_reset(dssdev);
908
Tomi Valkeinenc8cd4542010-06-09 15:24:46 +0300909 free_regulators(td->panel_config->regulators,
910 td->panel_config->num_regulators);
911
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200912 kfree(td);
913}
914
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200915static int taal_power_on(struct omap_dss_device *dssdev)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200916{
917 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
918 u8 id1, id2, id3;
919 int r;
920
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200921 r = omapdss_dsi_display_enable(dssdev);
922 if (r) {
923 dev_err(&dssdev->dev, "failed to enable DSI\n");
924 goto err0;
925 }
926
Jani Nikula006db7b2010-04-09 12:25:59 +0300927 taal_hw_reset(dssdev);
928
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530929 omapdss_dsi_vc_enable_hs(td->channel, false);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200930
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200931 r = taal_sleep_out(td);
932 if (r)
933 goto err;
934
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530935 r = taal_get_id(td, &id1, &id2, &id3);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200936 if (r)
937 goto err;
938
Jani Nikula1e8943d2010-04-15 17:07:39 +0300939 /* on early Taal revisions CABC is broken */
940 if (td->panel_config->type == PANEL_TAAL &&
941 (id2 == 0x00 || id2 == 0xff || id2 == 0x81))
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200942 td->cabc_broken = true;
943
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530944 r = taal_dcs_write_1(td, DCS_BRIGHTNESS, 0xff);
Jani Nikulaf2a8b752010-04-09 14:15:12 +0300945 if (r)
946 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200947
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530948 r = taal_dcs_write_1(td, DCS_CTRL_DISPLAY,
Jani Nikulaf2a8b752010-04-09 14:15:12 +0300949 (1<<2) | (1<<5)); /* BL | BCTRL */
950 if (r)
951 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200952
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530953 r = taal_dcs_write_1(td, DCS_PIXEL_FORMAT, 0x7); /* 24bit/pixel */
Jani Nikulaf2a8b752010-04-09 14:15:12 +0300954 if (r)
955 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200956
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530957 r = taal_set_addr_mode(td, td->rotate, td->mirror);
Jani Nikulaf2a8b752010-04-09 14:15:12 +0300958 if (r)
959 goto err;
960
961 if (!td->cabc_broken) {
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530962 r = taal_dcs_write_1(td, DCS_WRITE_CABC, td->cabc_mode);
Jani Nikulaf2a8b752010-04-09 14:15:12 +0300963 if (r)
964 goto err;
965 }
966
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530967 r = taal_dcs_write_0(td, DCS_DISPLAY_ON);
Jani Nikulaf2a8b752010-04-09 14:15:12 +0300968 if (r)
969 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200970
Tomi Valkeinen21df20f2010-03-02 12:13:55 +0200971 r = _taal_enable_te(dssdev, td->te_enabled);
972 if (r)
973 goto err;
974
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200975 td->enabled = 1;
976
977 if (!td->intro_printed) {
Jani Nikula0f45bdd2010-04-16 14:21:14 +0300978 dev_info(&dssdev->dev, "%s panel revision %02x.%02x.%02x\n",
979 td->panel_config->name, id1, id2, id3);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200980 if (td->cabc_broken)
981 dev_info(&dssdev->dev,
982 "old Taal version, CABC disabled\n");
983 td->intro_printed = true;
984 }
985
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530986 omapdss_dsi_vc_enable_hs(td->channel, true);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200987
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200988 return 0;
989err:
Jani Nikula006db7b2010-04-09 12:25:59 +0300990 dev_err(&dssdev->dev, "error while enabling panel, issuing HW reset\n");
991
992 taal_hw_reset(dssdev);
993
Tomi Valkeinen22d6d672010-10-11 11:33:30 +0300994 omapdss_dsi_display_disable(dssdev, true, false);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200995err0:
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200996 return r;
997}
998
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200999static void taal_power_off(struct omap_dss_device *dssdev)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001000{
1001 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Jani Nikula006db7b2010-04-09 12:25:59 +03001002 int r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001003
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301004 r = taal_dcs_write_0(td, DCS_DISPLAY_OFF);
Jani Nikula006db7b2010-04-09 12:25:59 +03001005 if (!r) {
1006 r = taal_sleep_in(td);
Jani Nikulae7f6c3f2010-04-15 12:55:38 +03001007 /* HACK: wait a bit so that the message goes through */
Jani Nikula006db7b2010-04-09 12:25:59 +03001008 msleep(10);
1009 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001010
Jani Nikula006db7b2010-04-09 12:25:59 +03001011 if (r) {
1012 dev_err(&dssdev->dev,
1013 "error disabling panel, issuing HW reset\n");
1014 taal_hw_reset(dssdev);
1015 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001016
Tomi Valkeinen22d6d672010-10-11 11:33:30 +03001017 omapdss_dsi_display_disable(dssdev, true, false);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001018
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001019 td->enabled = 0;
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001020}
1021
Tomi Valkeinenbb5476c2011-03-24 15:00:06 +02001022static int taal_panel_reset(struct omap_dss_device *dssdev)
1023{
1024 dev_err(&dssdev->dev, "performing LCD reset\n");
1025
1026 taal_power_off(dssdev);
1027 taal_hw_reset(dssdev);
1028 return taal_power_on(dssdev);
1029}
1030
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001031static int taal_enable(struct omap_dss_device *dssdev)
1032{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001033 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001034 int r;
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001035
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001036 dev_dbg(&dssdev->dev, "enable\n");
1037
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001038 mutex_lock(&td->lock);
1039
1040 if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) {
1041 r = -EINVAL;
1042 goto err;
1043 }
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001044
Jani Nikula2c2fc152010-04-12 10:06:14 +03001045 dsi_bus_lock();
1046
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001047 r = taal_power_on(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001048
1049 dsi_bus_unlock();
1050
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001051 if (r)
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001052 goto err;
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001053
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +02001054 taal_queue_esd_work(dssdev);
Jani Nikula4571a022010-04-12 10:23:46 +03001055
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001056 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
1057
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001058 mutex_unlock(&td->lock);
1059
1060 return 0;
1061err:
1062 dev_dbg(&dssdev->dev, "enable failed\n");
1063 mutex_unlock(&td->lock);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001064 return r;
1065}
1066
1067static void taal_disable(struct omap_dss_device *dssdev)
1068{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001069 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1070
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001071 dev_dbg(&dssdev->dev, "disable\n");
1072
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001073 mutex_lock(&td->lock);
1074
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +02001075 taal_cancel_esd_work(dssdev);
Jani Nikula4571a022010-04-12 10:23:46 +03001076
Jani Nikula2c2fc152010-04-12 10:06:14 +03001077 dsi_bus_lock();
1078
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001079 if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
1080 taal_power_off(dssdev);
1081
Jani Nikula2c2fc152010-04-12 10:06:14 +03001082 dsi_bus_unlock();
1083
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001084 dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001085
1086 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001087}
1088
1089static int taal_suspend(struct omap_dss_device *dssdev)
1090{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001091 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1092 int r;
1093
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001094 dev_dbg(&dssdev->dev, "suspend\n");
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001095
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001096 mutex_lock(&td->lock);
1097
1098 if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) {
1099 r = -EINVAL;
1100 goto err;
1101 }
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001102
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +02001103 taal_cancel_esd_work(dssdev);
Jani Nikula4571a022010-04-12 10:23:46 +03001104
Jani Nikula2c2fc152010-04-12 10:06:14 +03001105 dsi_bus_lock();
1106
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001107 taal_power_off(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001108
1109 dsi_bus_unlock();
1110
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001111 dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001112
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001113 mutex_unlock(&td->lock);
1114
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001115 return 0;
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001116err:
1117 mutex_unlock(&td->lock);
1118 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001119}
1120
1121static int taal_resume(struct omap_dss_device *dssdev)
1122{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001123 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001124 int r;
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001125
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001126 dev_dbg(&dssdev->dev, "resume\n");
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001127
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001128 mutex_lock(&td->lock);
1129
1130 if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) {
1131 r = -EINVAL;
1132 goto err;
1133 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001134
Jani Nikula2c2fc152010-04-12 10:06:14 +03001135 dsi_bus_lock();
1136
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001137 r = taal_power_on(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001138
1139 dsi_bus_unlock();
1140
Jani Nikula4571a022010-04-12 10:23:46 +03001141 if (r) {
Jani Nikulafed44b72010-04-12 10:25:21 +03001142 dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
Jani Nikula4571a022010-04-12 10:23:46 +03001143 } else {
Jani Nikulafed44b72010-04-12 10:25:21 +03001144 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +02001145 taal_queue_esd_work(dssdev);
Jani Nikula4571a022010-04-12 10:23:46 +03001146 }
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001147
1148 mutex_unlock(&td->lock);
1149
1150 return r;
1151err:
1152 mutex_unlock(&td->lock);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001153 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001154}
1155
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001156static void taal_framedone_cb(int err, void *data)
1157{
1158 struct omap_dss_device *dssdev = data;
1159 dev_dbg(&dssdev->dev, "framedone, err %d\n", err);
1160 dsi_bus_unlock();
1161}
1162
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001163static irqreturn_t taal_te_isr(int irq, void *data)
1164{
1165 struct omap_dss_device *dssdev = data;
1166 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1167 int old;
1168 int r;
1169
1170 old = atomic_cmpxchg(&td->do_update, 1, 0);
1171
1172 if (old) {
1173 cancel_delayed_work(&td->te_timeout_work);
1174
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301175 r = omap_dsi_update(dssdev, td->channel,
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001176 td->update_region.x,
1177 td->update_region.y,
1178 td->update_region.w,
1179 td->update_region.h,
1180 taal_framedone_cb, dssdev);
1181 if (r)
1182 goto err;
1183 }
1184
1185 return IRQ_HANDLED;
1186err:
1187 dev_err(&dssdev->dev, "start update failed\n");
1188 dsi_bus_unlock();
1189 return IRQ_HANDLED;
1190}
1191
1192static void taal_te_timeout_work_callback(struct work_struct *work)
1193{
1194 struct taal_data *td = container_of(work, struct taal_data,
1195 te_timeout_work.work);
1196 struct omap_dss_device *dssdev = td->dssdev;
1197
1198 dev_err(&dssdev->dev, "TE not received for 250ms!\n");
1199
1200 atomic_set(&td->do_update, 0);
1201 dsi_bus_unlock();
1202}
1203
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001204static int taal_update(struct omap_dss_device *dssdev,
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001205 u16 x, u16 y, u16 w, u16 h)
1206{
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001207 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001208 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001209 int r;
1210
1211 dev_dbg(&dssdev->dev, "update %d, %d, %d x %d\n", x, y, w, h);
1212
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001213 mutex_lock(&td->lock);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001214 dsi_bus_lock();
1215
1216 if (!td->enabled) {
1217 r = 0;
1218 goto err;
1219 }
1220
Tomi Valkeinen26a8c252010-06-09 15:31:34 +03001221 r = omap_dsi_prepare_update(dssdev, &x, &y, &w, &h, true);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001222 if (r)
1223 goto err;
1224
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301225 r = taal_set_update_window(td, x, y, w, h);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001226 if (r)
1227 goto err;
1228
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001229 if (td->te_enabled && panel_data->use_ext_te) {
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001230 td->update_region.x = x;
1231 td->update_region.y = y;
1232 td->update_region.w = w;
1233 td->update_region.h = h;
1234 barrier();
1235 schedule_delayed_work(&td->te_timeout_work,
1236 msecs_to_jiffies(250));
1237 atomic_set(&td->do_update, 1);
1238 } else {
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301239 r = omap_dsi_update(dssdev, td->channel, x, y, w, h,
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001240 taal_framedone_cb, dssdev);
1241 if (r)
1242 goto err;
1243 }
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001244
1245 /* note: no bus_unlock here. unlock is in framedone_cb */
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001246 mutex_unlock(&td->lock);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001247 return 0;
1248err:
1249 dsi_bus_unlock();
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001250 mutex_unlock(&td->lock);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001251 return r;
1252}
1253
1254static int taal_sync(struct omap_dss_device *dssdev)
1255{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001256 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1257
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001258 dev_dbg(&dssdev->dev, "sync\n");
1259
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001260 mutex_lock(&td->lock);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001261 dsi_bus_lock();
1262 dsi_bus_unlock();
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001263 mutex_unlock(&td->lock);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001264
1265 dev_dbg(&dssdev->dev, "sync done\n");
1266
1267 return 0;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001268}
1269
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001270static int _taal_enable_te(struct omap_dss_device *dssdev, bool enable)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001271{
Jani Nikulae7f6c3f2010-04-15 12:55:38 +03001272 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001273 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001274 int r;
1275
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001276 if (enable)
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301277 r = taal_dcs_write_1(td, DCS_TEAR_ON, 0);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001278 else
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301279 r = taal_dcs_write_0(td, DCS_TEAR_OFF);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001280
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001281 if (!panel_data->use_ext_te)
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001282 omapdss_dsi_enable_te(dssdev, enable);
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001283
Jani Nikulae7f6c3f2010-04-15 12:55:38 +03001284 if (td->panel_config->sleep.enable_te)
1285 msleep(td->panel_config->sleep.enable_te);
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001286
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001287 return r;
1288}
1289
1290static int taal_enable_te(struct omap_dss_device *dssdev, bool enable)
1291{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001292 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001293 int r;
1294
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001295 mutex_lock(&td->lock);
Tomi Valkeinenffb63c92010-05-21 10:09:18 +03001296
1297 if (td->te_enabled == enable)
1298 goto end;
1299
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001300 dsi_bus_lock();
1301
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001302 if (td->enabled) {
1303 r = _taal_enable_te(dssdev, enable);
1304 if (r)
1305 goto err;
1306 }
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001307
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001308 td->te_enabled = enable;
1309
1310 dsi_bus_unlock();
Tomi Valkeinenffb63c92010-05-21 10:09:18 +03001311end:
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001312 mutex_unlock(&td->lock);
1313
1314 return 0;
1315err:
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001316 dsi_bus_unlock();
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001317 mutex_unlock(&td->lock);
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001318
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001319 return r;
1320}
1321
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001322static int taal_get_te(struct omap_dss_device *dssdev)
1323{
1324 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001325 int r;
1326
1327 mutex_lock(&td->lock);
1328 r = td->te_enabled;
1329 mutex_unlock(&td->lock);
1330
1331 return r;
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001332}
1333
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001334static int taal_rotate(struct omap_dss_device *dssdev, u8 rotate)
1335{
1336 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1337 int r;
1338
1339 dev_dbg(&dssdev->dev, "rotate %d\n", rotate);
1340
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001341 mutex_lock(&td->lock);
Tomi Valkeinenffb63c92010-05-21 10:09:18 +03001342
1343 if (td->rotate == rotate)
1344 goto end;
1345
Tomi Valkeinen87424e12010-01-08 16:52:48 +02001346 dsi_bus_lock();
1347
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001348 if (td->enabled) {
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301349 r = taal_set_addr_mode(td, rotate, td->mirror);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001350 if (r)
Tomi Valkeinen87424e12010-01-08 16:52:48 +02001351 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001352 }
1353
1354 td->rotate = rotate;
1355
Tomi Valkeinen87424e12010-01-08 16:52:48 +02001356 dsi_bus_unlock();
Tomi Valkeinenffb63c92010-05-21 10:09:18 +03001357end:
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001358 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001359 return 0;
Tomi Valkeinen87424e12010-01-08 16:52:48 +02001360err:
1361 dsi_bus_unlock();
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001362 mutex_unlock(&td->lock);
Tomi Valkeinen87424e12010-01-08 16:52:48 +02001363 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001364}
1365
1366static u8 taal_get_rotate(struct omap_dss_device *dssdev)
1367{
1368 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001369 int r;
1370
1371 mutex_lock(&td->lock);
1372 r = td->rotate;
1373 mutex_unlock(&td->lock);
1374
1375 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001376}
1377
1378static int taal_mirror(struct omap_dss_device *dssdev, bool enable)
1379{
1380 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1381 int r;
1382
1383 dev_dbg(&dssdev->dev, "mirror %d\n", enable);
1384
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001385 mutex_lock(&td->lock);
Tomi Valkeinenffb63c92010-05-21 10:09:18 +03001386
1387 if (td->mirror == enable)
1388 goto end;
1389
Tomi Valkeinen8d8aa612010-01-08 16:30:33 +02001390 dsi_bus_lock();
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001391 if (td->enabled) {
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301392 r = taal_set_addr_mode(td, td->rotate, enable);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001393 if (r)
Tomi Valkeinen8d8aa612010-01-08 16:30:33 +02001394 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001395 }
1396
1397 td->mirror = enable;
1398
Tomi Valkeinen8d8aa612010-01-08 16:30:33 +02001399 dsi_bus_unlock();
Tomi Valkeinenffb63c92010-05-21 10:09:18 +03001400end:
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001401 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001402 return 0;
Tomi Valkeinen8d8aa612010-01-08 16:30:33 +02001403err:
1404 dsi_bus_unlock();
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001405 mutex_unlock(&td->lock);
Tomi Valkeinen8d8aa612010-01-08 16:30:33 +02001406 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001407}
1408
1409static bool taal_get_mirror(struct omap_dss_device *dssdev)
1410{
1411 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001412 int r;
1413
1414 mutex_lock(&td->lock);
1415 r = td->mirror;
1416 mutex_unlock(&td->lock);
1417
1418 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001419}
1420
1421static int taal_run_test(struct omap_dss_device *dssdev, int test_num)
1422{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001423 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001424 u8 id1, id2, id3;
1425 int r;
1426
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001427 mutex_lock(&td->lock);
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001428
1429 if (!td->enabled) {
1430 r = -ENODEV;
1431 goto err1;
1432 }
1433
Tomi Valkeinen1a75ef42010-01-08 16:21:28 +02001434 dsi_bus_lock();
1435
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301436 r = taal_dcs_read_1(td, DCS_GET_ID1, &id1);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001437 if (r)
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001438 goto err2;
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301439 r = taal_dcs_read_1(td, DCS_GET_ID2, &id2);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001440 if (r)
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001441 goto err2;
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301442 r = taal_dcs_read_1(td, DCS_GET_ID3, &id3);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001443 if (r)
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001444 goto err2;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001445
Tomi Valkeinen1a75ef42010-01-08 16:21:28 +02001446 dsi_bus_unlock();
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001447 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001448 return 0;
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001449err2:
Tomi Valkeinen1a75ef42010-01-08 16:21:28 +02001450 dsi_bus_unlock();
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001451err1:
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001452 mutex_unlock(&td->lock);
Tomi Valkeinen1a75ef42010-01-08 16:21:28 +02001453 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001454}
1455
1456static int taal_memory_read(struct omap_dss_device *dssdev,
1457 void *buf, size_t size,
1458 u16 x, u16 y, u16 w, u16 h)
1459{
1460 int r;
1461 int first = 1;
1462 int plen;
1463 unsigned buf_used = 0;
Tomi Valkeinenc75d9462010-01-08 16:56:44 +02001464 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1465
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001466 if (size < w * h * 3)
1467 return -ENOMEM;
1468
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001469 mutex_lock(&td->lock);
1470
1471 if (!td->enabled) {
1472 r = -ENODEV;
1473 goto err1;
1474 }
1475
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001476 size = min(w * h * 3,
1477 dssdev->panel.timings.x_res *
1478 dssdev->panel.timings.y_res * 3);
1479
Tomi Valkeinenc75d9462010-01-08 16:56:44 +02001480 dsi_bus_lock();
1481
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001482 /* plen 1 or 2 goes into short packet. until checksum error is fixed,
1483 * use short packets. plen 32 works, but bigger packets seem to cause
1484 * an error. */
1485 if (size % 2)
1486 plen = 1;
1487 else
1488 plen = 2;
1489
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301490 taal_set_update_window(td, x, y, w, h);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001491
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301492 r = dsi_vc_set_max_rx_packet_size(td->channel, plen);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001493 if (r)
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001494 goto err2;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001495
1496 while (buf_used < size) {
1497 u8 dcs_cmd = first ? 0x2e : 0x3e;
1498 first = 0;
1499
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301500 r = dsi_vc_dcs_read(td->channel, dcs_cmd,
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001501 buf + buf_used, size - buf_used);
1502
1503 if (r < 0) {
1504 dev_err(&dssdev->dev, "read error\n");
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001505 goto err3;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001506 }
1507
1508 buf_used += r;
1509
1510 if (r < plen) {
1511 dev_err(&dssdev->dev, "short read\n");
1512 break;
1513 }
1514
1515 if (signal_pending(current)) {
1516 dev_err(&dssdev->dev, "signal pending, "
1517 "aborting memory read\n");
1518 r = -ERESTARTSYS;
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001519 goto err3;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001520 }
1521 }
1522
1523 r = buf_used;
1524
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001525err3:
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301526 dsi_vc_set_max_rx_packet_size(td->channel, 1);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001527err2:
Tomi Valkeinenc75d9462010-01-08 16:56:44 +02001528 dsi_bus_unlock();
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001529err1:
1530 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001531 return r;
1532}
1533
1534static void taal_esd_work(struct work_struct *work)
1535{
1536 struct taal_data *td = container_of(work, struct taal_data,
1537 esd_work.work);
1538 struct omap_dss_device *dssdev = td->dssdev;
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001539 struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001540 u8 state1, state2;
1541 int r;
1542
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001543 mutex_lock(&td->lock);
1544
1545 if (!td->enabled) {
1546 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001547 return;
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001548 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001549
1550 dsi_bus_lock();
1551
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301552 r = taal_dcs_read_1(td, DCS_RDDSDR, &state1);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001553 if (r) {
1554 dev_err(&dssdev->dev, "failed to read Taal status\n");
1555 goto err;
1556 }
1557
1558 /* Run self diagnostics */
1559 r = taal_sleep_out(td);
1560 if (r) {
1561 dev_err(&dssdev->dev, "failed to run Taal self-diagnostics\n");
1562 goto err;
1563 }
1564
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301565 r = taal_dcs_read_1(td, DCS_RDDSDR, &state2);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001566 if (r) {
1567 dev_err(&dssdev->dev, "failed to read Taal status\n");
1568 goto err;
1569 }
1570
1571 /* Each sleep out command will trigger a self diagnostic and flip
1572 * Bit6 if the test passes.
1573 */
1574 if (!((state1 ^ state2) & (1 << 6))) {
1575 dev_err(&dssdev->dev, "LCD self diagnostics failed\n");
1576 goto err;
1577 }
1578 /* Self-diagnostics result is also shown on TE GPIO line. We need
1579 * to re-enable TE after self diagnostics */
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +03001580 if (td->te_enabled && panel_data->use_ext_te) {
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301581 r = taal_dcs_write_1(td, DCS_TEAR_ON, 0);
Tomi Valkeinen1189b7f2010-03-01 13:52:10 +02001582 if (r)
1583 goto err;
1584 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001585
1586 dsi_bus_unlock();
1587
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +02001588 taal_queue_esd_work(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001589
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001590 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001591 return;
1592err:
1593 dev_err(&dssdev->dev, "performing LCD reset\n");
1594
Tomi Valkeinenbb5476c2011-03-24 15:00:06 +02001595 taal_panel_reset(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001596
1597 dsi_bus_unlock();
1598
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +02001599 taal_queue_esd_work(dssdev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001600
1601 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001602}
1603
Tomi Valkeinen446f7bf2010-01-11 16:12:31 +02001604static int taal_set_update_mode(struct omap_dss_device *dssdev,
1605 enum omap_dss_update_mode mode)
1606{
1607 if (mode != OMAP_DSS_UPDATE_MANUAL)
1608 return -EINVAL;
1609 return 0;
1610}
1611
1612static enum omap_dss_update_mode taal_get_update_mode(
1613 struct omap_dss_device *dssdev)
1614{
1615 return OMAP_DSS_UPDATE_MANUAL;
1616}
1617
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001618static struct omap_dss_driver taal_driver = {
1619 .probe = taal_probe,
Tomi Valkeinen14e4d782011-03-31 12:03:51 +03001620 .remove = __exit_p(taal_remove),
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001621
1622 .enable = taal_enable,
1623 .disable = taal_disable,
1624 .suspend = taal_suspend,
1625 .resume = taal_resume,
1626
Tomi Valkeinen446f7bf2010-01-11 16:12:31 +02001627 .set_update_mode = taal_set_update_mode,
1628 .get_update_mode = taal_get_update_mode,
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001629
1630 .update = taal_update,
1631 .sync = taal_sync,
1632
Tomi Valkeinen96adcec2010-01-11 13:54:33 +02001633 .get_resolution = taal_get_resolution,
Tomi Valkeinena2699502010-01-11 14:33:40 +02001634 .get_recommended_bpp = omapdss_default_get_recommended_bpp,
1635
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001636 .enable_te = taal_enable_te,
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001637 .get_te = taal_get_te,
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001638
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001639 .set_rotate = taal_rotate,
1640 .get_rotate = taal_get_rotate,
1641 .set_mirror = taal_mirror,
1642 .get_mirror = taal_get_mirror,
1643 .run_test = taal_run_test,
1644 .memory_read = taal_memory_read,
1645
Tomi Valkeinen69b20482010-01-20 12:11:25 +02001646 .get_timings = taal_get_timings,
1647
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001648 .driver = {
1649 .name = "taal",
1650 .owner = THIS_MODULE,
1651 },
1652};
1653
1654static int __init taal_init(void)
1655{
1656 omap_dss_register_driver(&taal_driver);
1657
1658 return 0;
1659}
1660
1661static void __exit taal_exit(void)
1662{
1663 omap_dss_unregister_driver(&taal_driver);
1664}
1665
1666module_init(taal_init);
1667module_exit(taal_exit);
1668
1669MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@nokia.com>");
1670MODULE_DESCRIPTION("Taal Driver");
1671MODULE_LICENSE("GPL");