blob: 038a815ba6c8629dfa938e4ad2b7ac76b1585d30 [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 Valkeinena3201a02010-03-03 14:31:45 +020033#include <linux/mutex.h>
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +020034
Tomi Valkeinena0b38cc2011-05-11 14:05:07 +030035#include <video/omapdss.h>
Tomi Valkeinen4e9f99d2011-05-11 14:10:07 +030036#include <video/omap-panel-nokia-dsi.h>
Archit Taneja7a7c48f2011-08-25 18:25:03 +053037#include <video/mipi_display.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
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +020043#define DCS_BRIGHTNESS 0x51
44#define DCS_CTRL_DISPLAY 0x53
45#define DCS_WRITE_CABC 0x55
46#define DCS_READ_CABC 0x56
47#define DCS_GET_ID1 0xda
48#define DCS_GET_ID2 0xdb
49#define DCS_GET_ID3 0xdc
50
Jani Nikula7ae2fb12010-04-13 10:57:52 +030051static irqreturn_t taal_te_isr(int irq, void *data);
52static void taal_te_timeout_work_callback(struct work_struct *work);
Tomi Valkeinen21df20f2010-03-02 12:13:55 +020053static int _taal_enable_te(struct omap_dss_device *dssdev, bool enable);
54
Tomi Valkeinen1abf7812011-03-24 14:53:27 +020055static int taal_panel_reset(struct omap_dss_device *dssdev);
56
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +020057struct taal_data {
Tomi Valkeinena3201a02010-03-03 14:31:45 +020058 struct mutex lock;
59
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +020060 struct backlight_device *bldev;
61
62 unsigned long hw_guard_end; /* next value of jiffies when we can
63 * issue the next sleep in/out command
64 */
65 unsigned long hw_guard_wait; /* max guard time in jiffies */
66
67 struct omap_dss_device *dssdev;
68
Tomi Valkeinenf075a592012-09-03 10:42:50 +030069 /* panel HW configuration from DT or platform data */
70 int reset_gpio;
71 int ext_te_gpio;
72
73 bool use_dsi_backlight;
74
75 struct omap_dsi_pin_config pin_config;
76
77 /* runtime variables */
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +020078 bool enabled;
79 u8 rotate;
80 bool mirror;
81
82 bool te_enabled;
Jani Nikula7ae2fb12010-04-13 10:57:52 +030083
84 atomic_t do_update;
Archit Tanejabc6d4b12011-03-01 13:59:46 +053085 int channel;
86
Jani Nikula7ae2fb12010-04-13 10:57:52 +030087 struct delayed_work te_timeout_work;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +020088
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +020089 bool cabc_broken;
90 unsigned cabc_mode;
91
92 bool intro_printed;
93
Tomi Valkeinen883b9ac2011-03-24 15:01:30 +020094 struct workqueue_struct *workqueue;
95
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +020096 struct delayed_work esd_work;
Tomi Valkeinen33a410b2011-03-24 13:58:01 +020097 unsigned esd_interval;
Jani Nikulae7f6c3f2010-04-15 12:55:38 +030098
Tomi Valkeinen1abf7812011-03-24 14:53:27 +020099 bool ulps_enabled;
100 unsigned ulps_timeout;
101 struct delayed_work ulps_work;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200102};
103
104static void taal_esd_work(struct work_struct *work);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200105static void taal_ulps_work(struct work_struct *work);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200106
107static void hw_guard_start(struct taal_data *td, int guard_msec)
108{
109 td->hw_guard_wait = msecs_to_jiffies(guard_msec);
110 td->hw_guard_end = jiffies + td->hw_guard_wait;
111}
112
113static void hw_guard_wait(struct taal_data *td)
114{
115 unsigned long wait = td->hw_guard_end - jiffies;
116
117 if ((long)wait > 0 && wait <= td->hw_guard_wait) {
118 set_current_state(TASK_UNINTERRUPTIBLE);
119 schedule_timeout(wait);
120 }
121}
122
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530123static int taal_dcs_read_1(struct taal_data *td, u8 dcs_cmd, u8 *data)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200124{
125 int r;
126 u8 buf[1];
127
Archit Taneja1ffefe72011-05-12 17:26:24 +0530128 r = dsi_vc_dcs_read(td->dssdev, td->channel, dcs_cmd, buf, 1);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200129
130 if (r < 0)
131 return r;
132
133 *data = buf[0];
134
135 return 0;
136}
137
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530138static int taal_dcs_write_0(struct taal_data *td, u8 dcs_cmd)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200139{
Archit Taneja1ffefe72011-05-12 17:26:24 +0530140 return dsi_vc_dcs_write(td->dssdev, td->channel, &dcs_cmd, 1);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200141}
142
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530143static int taal_dcs_write_1(struct taal_data *td, u8 dcs_cmd, u8 param)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200144{
145 u8 buf[2];
146 buf[0] = dcs_cmd;
147 buf[1] = param;
Archit Taneja1ffefe72011-05-12 17:26:24 +0530148 return dsi_vc_dcs_write(td->dssdev, td->channel, buf, 2);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200149}
150
151static int taal_sleep_in(struct taal_data *td)
152
153{
154 u8 cmd;
155 int r;
156
157 hw_guard_wait(td);
158
Archit Taneja7a7c48f2011-08-25 18:25:03 +0530159 cmd = MIPI_DCS_ENTER_SLEEP_MODE;
Archit Taneja1ffefe72011-05-12 17:26:24 +0530160 r = dsi_vc_dcs_write_nosync(td->dssdev, td->channel, &cmd, 1);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200161 if (r)
162 return r;
163
164 hw_guard_start(td, 120);
165
Tomi Valkeinene087cc212012-11-16 14:48:51 +0200166 msleep(5);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200167
168 return 0;
169}
170
171static int taal_sleep_out(struct taal_data *td)
172{
173 int r;
174
175 hw_guard_wait(td);
176
Archit Taneja7a7c48f2011-08-25 18:25:03 +0530177 r = taal_dcs_write_0(td, MIPI_DCS_EXIT_SLEEP_MODE);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200178 if (r)
179 return r;
180
181 hw_guard_start(td, 120);
182
Tomi Valkeinene087cc212012-11-16 14:48:51 +0200183 msleep(5);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200184
185 return 0;
186}
187
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530188static int taal_get_id(struct taal_data *td, u8 *id1, u8 *id2, u8 *id3)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200189{
190 int r;
191
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530192 r = taal_dcs_read_1(td, DCS_GET_ID1, id1);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200193 if (r)
194 return r;
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530195 r = taal_dcs_read_1(td, DCS_GET_ID2, id2);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200196 if (r)
197 return r;
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530198 r = taal_dcs_read_1(td, DCS_GET_ID3, id3);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200199 if (r)
200 return r;
201
202 return 0;
203}
204
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530205static int taal_set_addr_mode(struct taal_data *td, u8 rotate, bool mirror)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200206{
207 int r;
208 u8 mode;
209 int b5, b6, b7;
210
Archit Taneja7a7c48f2011-08-25 18:25:03 +0530211 r = taal_dcs_read_1(td, MIPI_DCS_GET_ADDRESS_MODE, &mode);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200212 if (r)
213 return r;
214
215 switch (rotate) {
216 default:
217 case 0:
218 b7 = 0;
219 b6 = 0;
220 b5 = 0;
221 break;
222 case 1:
223 b7 = 0;
224 b6 = 1;
225 b5 = 1;
226 break;
227 case 2:
228 b7 = 1;
229 b6 = 1;
230 b5 = 0;
231 break;
232 case 3:
233 b7 = 1;
234 b6 = 0;
235 b5 = 1;
236 break;
237 }
238
239 if (mirror)
240 b6 = !b6;
241
242 mode &= ~((1<<7) | (1<<6) | (1<<5));
243 mode |= (b7 << 7) | (b6 << 6) | (b5 << 5);
244
Archit Taneja7a7c48f2011-08-25 18:25:03 +0530245 return taal_dcs_write_1(td, MIPI_DCS_SET_ADDRESS_MODE, mode);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200246}
247
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530248static int taal_set_update_window(struct taal_data *td,
249 u16 x, u16 y, u16 w, u16 h)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200250{
251 int r;
252 u16 x1 = x;
253 u16 x2 = x + w - 1;
254 u16 y1 = y;
255 u16 y2 = y + h - 1;
256
257 u8 buf[5];
Archit Taneja7a7c48f2011-08-25 18:25:03 +0530258 buf[0] = MIPI_DCS_SET_COLUMN_ADDRESS;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200259 buf[1] = (x1 >> 8) & 0xff;
260 buf[2] = (x1 >> 0) & 0xff;
261 buf[3] = (x2 >> 8) & 0xff;
262 buf[4] = (x2 >> 0) & 0xff;
263
Archit Taneja1ffefe72011-05-12 17:26:24 +0530264 r = dsi_vc_dcs_write_nosync(td->dssdev, td->channel, buf, sizeof(buf));
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200265 if (r)
266 return r;
267
Archit Taneja7a7c48f2011-08-25 18:25:03 +0530268 buf[0] = MIPI_DCS_SET_PAGE_ADDRESS;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200269 buf[1] = (y1 >> 8) & 0xff;
270 buf[2] = (y1 >> 0) & 0xff;
271 buf[3] = (y2 >> 8) & 0xff;
272 buf[4] = (y2 >> 0) & 0xff;
273
Archit Taneja1ffefe72011-05-12 17:26:24 +0530274 r = dsi_vc_dcs_write_nosync(td->dssdev, td->channel, buf, sizeof(buf));
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200275 if (r)
276 return r;
277
Archit Taneja1ffefe72011-05-12 17:26:24 +0530278 dsi_vc_send_bta_sync(td->dssdev, td->channel);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200279
280 return r;
281}
282
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +0200283static void taal_queue_esd_work(struct omap_dss_device *dssdev)
284{
285 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
286
287 if (td->esd_interval > 0)
Tomi Valkeinen883b9ac2011-03-24 15:01:30 +0200288 queue_delayed_work(td->workqueue, &td->esd_work,
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +0200289 msecs_to_jiffies(td->esd_interval));
290}
291
292static void taal_cancel_esd_work(struct omap_dss_device *dssdev)
293{
294 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
295
296 cancel_delayed_work(&td->esd_work);
297}
298
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200299static void taal_queue_ulps_work(struct omap_dss_device *dssdev)
300{
301 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
302
303 if (td->ulps_timeout > 0)
304 queue_delayed_work(td->workqueue, &td->ulps_work,
305 msecs_to_jiffies(td->ulps_timeout));
306}
307
308static void taal_cancel_ulps_work(struct omap_dss_device *dssdev)
309{
310 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
311
312 cancel_delayed_work(&td->ulps_work);
313}
314
315static int taal_enter_ulps(struct omap_dss_device *dssdev)
316{
317 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200318 int r;
319
320 if (td->ulps_enabled)
321 return 0;
322
323 taal_cancel_ulps_work(dssdev);
324
325 r = _taal_enable_te(dssdev, false);
326 if (r)
327 goto err;
328
Tomi Valkeinenf075a592012-09-03 10:42:50 +0300329 if (gpio_is_valid(td->ext_te_gpio))
330 disable_irq(gpio_to_irq(td->ext_te_gpio));
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200331
332 omapdss_dsi_display_disable(dssdev, false, true);
333
334 td->ulps_enabled = true;
335
336 return 0;
337
338err:
339 dev_err(&dssdev->dev, "enter ULPS failed");
340 taal_panel_reset(dssdev);
341
342 td->ulps_enabled = false;
343
344 taal_queue_ulps_work(dssdev);
345
346 return r;
347}
348
349static int taal_exit_ulps(struct omap_dss_device *dssdev)
350{
351 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200352 int r;
353
354 if (!td->ulps_enabled)
355 return 0;
356
357 r = omapdss_dsi_display_enable(dssdev);
Tomi Valkeinene8945672011-05-31 16:39:01 +0300358 if (r) {
359 dev_err(&dssdev->dev, "failed to enable DSI\n");
360 goto err1;
361 }
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200362
Archit Taneja1ffefe72011-05-12 17:26:24 +0530363 omapdss_dsi_vc_enable_hs(dssdev, td->channel, true);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200364
365 r = _taal_enable_te(dssdev, true);
Tomi Valkeinene8945672011-05-31 16:39:01 +0300366 if (r) {
367 dev_err(&dssdev->dev, "failed to re-enable TE");
368 goto err2;
369 }
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200370
Tomi Valkeinenf075a592012-09-03 10:42:50 +0300371 if (gpio_is_valid(td->ext_te_gpio))
372 enable_irq(gpio_to_irq(td->ext_te_gpio));
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200373
374 taal_queue_ulps_work(dssdev);
375
376 td->ulps_enabled = false;
377
378 return 0;
379
Tomi Valkeinene8945672011-05-31 16:39:01 +0300380err2:
381 dev_err(&dssdev->dev, "failed to exit ULPS");
382
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200383 r = taal_panel_reset(dssdev);
Tomi Valkeinene8945672011-05-31 16:39:01 +0300384 if (!r) {
Tomi Valkeinenf075a592012-09-03 10:42:50 +0300385 if (gpio_is_valid(td->ext_te_gpio))
386 enable_irq(gpio_to_irq(td->ext_te_gpio));
Tomi Valkeinene8945672011-05-31 16:39:01 +0300387 td->ulps_enabled = false;
388 }
389err1:
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200390 taal_queue_ulps_work(dssdev);
391
392 return r;
393}
394
395static int taal_wake_up(struct omap_dss_device *dssdev)
396{
397 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
398
399 if (td->ulps_enabled)
400 return taal_exit_ulps(dssdev);
401
402 taal_cancel_ulps_work(dssdev);
403 taal_queue_ulps_work(dssdev);
404 return 0;
405}
406
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200407static int taal_bl_update_status(struct backlight_device *dev)
408{
409 struct omap_dss_device *dssdev = dev_get_drvdata(&dev->dev);
410 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
411 int r;
412 int level;
413
414 if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
415 dev->props.power == FB_BLANK_UNBLANK)
416 level = dev->props.brightness;
417 else
418 level = 0;
419
420 dev_dbg(&dssdev->dev, "update brightness to %d\n", level);
421
Tomi Valkeinen1cbc8702010-04-15 16:41:07 +0300422 mutex_lock(&td->lock);
423
Tomi Valkeinenbb36dbf2011-08-09 11:39:27 +0300424 if (td->enabled) {
425 dsi_bus_lock(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200426
Tomi Valkeinenbb36dbf2011-08-09 11:39:27 +0300427 r = taal_wake_up(dssdev);
428 if (!r)
429 r = taal_dcs_write_1(td, DCS_BRIGHTNESS, level);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200430
Tomi Valkeinenbb36dbf2011-08-09 11:39:27 +0300431 dsi_bus_unlock(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200432 } else {
Tomi Valkeinenbb36dbf2011-08-09 11:39:27 +0300433 r = 0;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200434 }
435
Tomi Valkeinen1cbc8702010-04-15 16:41:07 +0300436 mutex_unlock(&td->lock);
437
438 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200439}
440
441static int taal_bl_get_intensity(struct backlight_device *dev)
442{
443 if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
444 dev->props.power == FB_BLANK_UNBLANK)
445 return dev->props.brightness;
446
447 return 0;
448}
449
Lionel Debrouxacc24722010-11-16 14:14:02 +0100450static const struct backlight_ops taal_bl_ops = {
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200451 .get_brightness = taal_bl_get_intensity,
452 .update_status = taal_bl_update_status,
453};
454
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200455static void taal_get_resolution(struct omap_dss_device *dssdev,
456 u16 *xres, u16 *yres)
457{
458 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
459
460 if (td->rotate == 0 || td->rotate == 2) {
461 *xres = dssdev->panel.timings.x_res;
462 *yres = dssdev->panel.timings.y_res;
463 } else {
464 *yres = dssdev->panel.timings.x_res;
465 *xres = dssdev->panel.timings.y_res;
466 }
467}
468
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200469static ssize_t taal_num_errors_show(struct device *dev,
470 struct device_attribute *attr, char *buf)
471{
472 struct omap_dss_device *dssdev = to_dss_device(dev);
473 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinend1700f92012-05-23 16:56:09 +0300474 u8 errors = 0;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200475 int r;
476
Jani Nikula6b316712010-04-28 11:15:18 +0300477 mutex_lock(&td->lock);
478
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200479 if (td->enabled) {
Archit Taneja1ffefe72011-05-12 17:26:24 +0530480 dsi_bus_lock(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200481
482 r = taal_wake_up(dssdev);
483 if (!r)
484 r = taal_dcs_read_1(td, DCS_READ_NUM_ERRORS, &errors);
485
Archit Taneja1ffefe72011-05-12 17:26:24 +0530486 dsi_bus_unlock(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200487 } else {
488 r = -ENODEV;
489 }
490
Jani Nikula6b316712010-04-28 11:15:18 +0300491 mutex_unlock(&td->lock);
492
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200493 if (r)
494 return r;
495
496 return snprintf(buf, PAGE_SIZE, "%d\n", errors);
497}
498
499static ssize_t taal_hw_revision_show(struct device *dev,
500 struct device_attribute *attr, char *buf)
501{
502 struct omap_dss_device *dssdev = to_dss_device(dev);
503 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
504 u8 id1, id2, id3;
505 int r;
506
Jani Nikula6b316712010-04-28 11:15:18 +0300507 mutex_lock(&td->lock);
508
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200509 if (td->enabled) {
Archit Taneja1ffefe72011-05-12 17:26:24 +0530510 dsi_bus_lock(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200511
512 r = taal_wake_up(dssdev);
513 if (!r)
514 r = taal_get_id(td, &id1, &id2, &id3);
515
Archit Taneja1ffefe72011-05-12 17:26:24 +0530516 dsi_bus_unlock(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200517 } else {
518 r = -ENODEV;
519 }
520
Jani Nikula6b316712010-04-28 11:15:18 +0300521 mutex_unlock(&td->lock);
522
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200523 if (r)
524 return r;
525
526 return snprintf(buf, PAGE_SIZE, "%02x.%02x.%02x\n", id1, id2, id3);
527}
528
529static const char *cabc_modes[] = {
530 "off", /* used also always when CABC is not supported */
531 "ui",
532 "still-image",
533 "moving-image",
534};
535
536static ssize_t show_cabc_mode(struct device *dev,
537 struct device_attribute *attr,
538 char *buf)
539{
540 struct omap_dss_device *dssdev = to_dss_device(dev);
541 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
542 const char *mode_str;
543 int mode;
544 int len;
545
546 mode = td->cabc_mode;
547
548 mode_str = "unknown";
549 if (mode >= 0 && mode < ARRAY_SIZE(cabc_modes))
550 mode_str = cabc_modes[mode];
551 len = snprintf(buf, PAGE_SIZE, "%s\n", mode_str);
552
553 return len < PAGE_SIZE - 1 ? len : PAGE_SIZE - 1;
554}
555
556static ssize_t store_cabc_mode(struct device *dev,
557 struct device_attribute *attr,
558 const char *buf, size_t count)
559{
560 struct omap_dss_device *dssdev = to_dss_device(dev);
561 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
562 int i;
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200563 int r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200564
565 for (i = 0; i < ARRAY_SIZE(cabc_modes); i++) {
566 if (sysfs_streq(cabc_modes[i], buf))
567 break;
568 }
569
570 if (i == ARRAY_SIZE(cabc_modes))
571 return -EINVAL;
572
Jani Nikula6b316712010-04-28 11:15:18 +0300573 mutex_lock(&td->lock);
574
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200575 if (td->enabled) {
Archit Taneja1ffefe72011-05-12 17:26:24 +0530576 dsi_bus_lock(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200577
578 if (!td->cabc_broken) {
579 r = taal_wake_up(dssdev);
580 if (r)
581 goto err;
582
583 r = taal_dcs_write_1(td, DCS_WRITE_CABC, i);
584 if (r)
585 goto err;
586 }
587
Archit Taneja1ffefe72011-05-12 17:26:24 +0530588 dsi_bus_unlock(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200589 }
590
591 td->cabc_mode = i;
592
Jani Nikula6b316712010-04-28 11:15:18 +0300593 mutex_unlock(&td->lock);
594
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200595 return count;
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200596err:
Archit Taneja1ffefe72011-05-12 17:26:24 +0530597 dsi_bus_unlock(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200598 mutex_unlock(&td->lock);
599 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200600}
601
602static ssize_t show_cabc_available_modes(struct device *dev,
603 struct device_attribute *attr,
604 char *buf)
605{
606 int len;
607 int i;
608
609 for (i = 0, len = 0;
610 len < PAGE_SIZE && i < ARRAY_SIZE(cabc_modes); i++)
611 len += snprintf(&buf[len], PAGE_SIZE - len, "%s%s%s",
612 i ? " " : "", cabc_modes[i],
613 i == ARRAY_SIZE(cabc_modes) - 1 ? "\n" : "");
614
615 return len < PAGE_SIZE ? len : PAGE_SIZE - 1;
616}
617
Tomi Valkeinen1f8fa452011-03-24 14:06:51 +0200618static ssize_t taal_store_esd_interval(struct device *dev,
619 struct device_attribute *attr,
620 const char *buf, size_t count)
621{
622 struct omap_dss_device *dssdev = to_dss_device(dev);
623 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
624
625 unsigned long t;
626 int r;
627
628 r = strict_strtoul(buf, 10, &t);
629 if (r)
630 return r;
631
632 mutex_lock(&td->lock);
633 taal_cancel_esd_work(dssdev);
634 td->esd_interval = t;
635 if (td->enabled)
636 taal_queue_esd_work(dssdev);
637 mutex_unlock(&td->lock);
638
639 return count;
640}
641
642static ssize_t taal_show_esd_interval(struct device *dev,
643 struct device_attribute *attr,
644 char *buf)
645{
646 struct omap_dss_device *dssdev = to_dss_device(dev);
647 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
648 unsigned t;
649
650 mutex_lock(&td->lock);
651 t = td->esd_interval;
652 mutex_unlock(&td->lock);
653
654 return snprintf(buf, PAGE_SIZE, "%u\n", t);
655}
656
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200657static ssize_t taal_store_ulps(struct device *dev,
658 struct device_attribute *attr,
659 const char *buf, size_t count)
660{
661 struct omap_dss_device *dssdev = to_dss_device(dev);
662 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
663 unsigned long t;
664 int r;
665
666 r = strict_strtoul(buf, 10, &t);
667 if (r)
668 return r;
669
670 mutex_lock(&td->lock);
671
672 if (td->enabled) {
Archit Taneja1ffefe72011-05-12 17:26:24 +0530673 dsi_bus_lock(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200674
675 if (t)
676 r = taal_enter_ulps(dssdev);
677 else
678 r = taal_wake_up(dssdev);
679
Archit Taneja1ffefe72011-05-12 17:26:24 +0530680 dsi_bus_unlock(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200681 }
682
683 mutex_unlock(&td->lock);
684
685 if (r)
686 return r;
687
688 return count;
689}
690
691static ssize_t taal_show_ulps(struct device *dev,
692 struct device_attribute *attr,
693 char *buf)
694{
695 struct omap_dss_device *dssdev = to_dss_device(dev);
696 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
697 unsigned t;
698
699 mutex_lock(&td->lock);
700 t = td->ulps_enabled;
701 mutex_unlock(&td->lock);
702
703 return snprintf(buf, PAGE_SIZE, "%u\n", t);
704}
705
706static ssize_t taal_store_ulps_timeout(struct device *dev,
707 struct device_attribute *attr,
708 const char *buf, size_t count)
709{
710 struct omap_dss_device *dssdev = to_dss_device(dev);
711 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
712 unsigned long t;
713 int r;
714
715 r = strict_strtoul(buf, 10, &t);
716 if (r)
717 return r;
718
719 mutex_lock(&td->lock);
720 td->ulps_timeout = t;
721
722 if (td->enabled) {
723 /* taal_wake_up will restart the timer */
Archit Taneja1ffefe72011-05-12 17:26:24 +0530724 dsi_bus_lock(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200725 r = taal_wake_up(dssdev);
Archit Taneja1ffefe72011-05-12 17:26:24 +0530726 dsi_bus_unlock(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200727 }
728
729 mutex_unlock(&td->lock);
730
731 if (r)
732 return r;
733
734 return count;
735}
736
737static ssize_t taal_show_ulps_timeout(struct device *dev,
738 struct device_attribute *attr,
739 char *buf)
740{
741 struct omap_dss_device *dssdev = to_dss_device(dev);
742 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
743 unsigned t;
744
745 mutex_lock(&td->lock);
746 t = td->ulps_timeout;
747 mutex_unlock(&td->lock);
748
749 return snprintf(buf, PAGE_SIZE, "%u\n", t);
750}
751
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200752static DEVICE_ATTR(num_dsi_errors, S_IRUGO, taal_num_errors_show, NULL);
753static DEVICE_ATTR(hw_revision, S_IRUGO, taal_hw_revision_show, NULL);
754static DEVICE_ATTR(cabc_mode, S_IRUGO | S_IWUSR,
755 show_cabc_mode, store_cabc_mode);
756static DEVICE_ATTR(cabc_available_modes, S_IRUGO,
757 show_cabc_available_modes, NULL);
Tomi Valkeinen1f8fa452011-03-24 14:06:51 +0200758static DEVICE_ATTR(esd_interval, S_IRUGO | S_IWUSR,
759 taal_show_esd_interval, taal_store_esd_interval);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200760static DEVICE_ATTR(ulps, S_IRUGO | S_IWUSR,
761 taal_show_ulps, taal_store_ulps);
762static DEVICE_ATTR(ulps_timeout, S_IRUGO | S_IWUSR,
763 taal_show_ulps_timeout, taal_store_ulps_timeout);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200764
765static struct attribute *taal_attrs[] = {
766 &dev_attr_num_dsi_errors.attr,
767 &dev_attr_hw_revision.attr,
768 &dev_attr_cabc_mode.attr,
769 &dev_attr_cabc_available_modes.attr,
Tomi Valkeinen1f8fa452011-03-24 14:06:51 +0200770 &dev_attr_esd_interval.attr,
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200771 &dev_attr_ulps.attr,
772 &dev_attr_ulps_timeout.attr,
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200773 NULL,
774};
775
776static struct attribute_group taal_attr_group = {
777 .attrs = taal_attrs,
778};
779
Jani Nikula006db7b2010-04-09 12:25:59 +0300780static void taal_hw_reset(struct omap_dss_device *dssdev)
781{
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300782 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen8d3573c2010-06-09 15:23:44 +0300783
Tomi Valkeinenf075a592012-09-03 10:42:50 +0300784 if (!gpio_is_valid(td->reset_gpio))
Jani Nikula006db7b2010-04-09 12:25:59 +0300785 return;
786
Tomi Valkeinenf075a592012-09-03 10:42:50 +0300787 gpio_set_value(td->reset_gpio, 1);
Tomi Valkeinene087cc212012-11-16 14:48:51 +0200788 udelay(10);
Jani Nikula006db7b2010-04-09 12:25:59 +0300789 /* reset the panel */
Tomi Valkeinenf075a592012-09-03 10:42:50 +0300790 gpio_set_value(td->reset_gpio, 0);
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300791 /* assert reset */
Tomi Valkeinene087cc212012-11-16 14:48:51 +0200792 udelay(10);
Tomi Valkeinenf075a592012-09-03 10:42:50 +0300793 gpio_set_value(td->reset_gpio, 1);
Jani Nikulae7f6c3f2010-04-15 12:55:38 +0300794 /* wait after releasing reset */
Tomi Valkeinene087cc212012-11-16 14:48:51 +0200795 msleep(5);
Jani Nikula006db7b2010-04-09 12:25:59 +0300796}
797
Tomi Valkeinenf075a592012-09-03 10:42:50 +0300798static void taal_probe_pdata(struct taal_data *td,
799 const struct nokia_dsi_panel_data *pdata)
800{
801 td->reset_gpio = pdata->reset_gpio;
802
803 if (pdata->use_ext_te)
804 td->ext_te_gpio = pdata->ext_te_gpio;
805 else
806 td->ext_te_gpio = -1;
807
808 td->esd_interval = pdata->esd_interval;
809 td->ulps_timeout = pdata->ulps_timeout;
810
811 td->use_dsi_backlight = pdata->use_dsi_backlight;
812
813 td->pin_config = pdata->pin_config;
814}
815
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200816static int taal_probe(struct omap_dss_device *dssdev)
817{
Matthew Garretta19a6ee2010-02-17 16:39:44 -0500818 struct backlight_properties props;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200819 struct taal_data *td;
Tomi Valkeinenbb36dbf2011-08-09 11:39:27 +0300820 struct backlight_device *bldev = NULL;
Tomi Valkeinene087cc212012-11-16 14:48:51 +0200821 int r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200822
823 dev_dbg(&dssdev->dev, "probe\n");
824
Tomi Valkeinen5e56ad42012-08-29 16:31:29 +0300825 td = devm_kzalloc(&dssdev->dev, sizeof(*td), GFP_KERNEL);
826 if (!td)
827 return -ENOMEM;
Tomi Valkeinenf075a592012-09-03 10:42:50 +0300828
829 dev_set_drvdata(&dssdev->dev, td);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200830 td->dssdev = dssdev;
Tomi Valkeinenf075a592012-09-03 10:42:50 +0300831
832 if (dssdev->data) {
833 const struct nokia_dsi_panel_data *pdata = dssdev->data;
834
835 taal_probe_pdata(td, pdata);
Tomi Valkeinenf075a592012-09-03 10:42:50 +0300836 } else {
837 return -ENODEV;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200838 }
839
Tomi Valkeinene087cc212012-11-16 14:48:51 +0200840 dssdev->panel.timings.x_res = 864;
841 dssdev->panel.timings.y_res = 480;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200842 dssdev->panel.dsi_pix_fmt = OMAP_DSS_DSI_FMT_RGB888;
Tomi Valkeinenf075a592012-09-03 10:42:50 +0300843 dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE |
844 OMAP_DSS_DISPLAY_CAP_TEAR_ELIM;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200845
Tomi Valkeinena3201a02010-03-03 14:31:45 +0200846 mutex_init(&td->lock);
847
Jani Nikula7ae2fb12010-04-13 10:57:52 +0300848 atomic_set(&td->do_update, 0);
849
Tomi Valkeinenf075a592012-09-03 10:42:50 +0300850 if (gpio_is_valid(td->reset_gpio)) {
851 r = devm_gpio_request_one(&dssdev->dev, td->reset_gpio,
Tomi Valkeinen5e56ad42012-08-29 16:31:29 +0300852 GPIOF_OUT_INIT_LOW, "taal rst");
Tomi Valkeinen3acc7972012-02-20 12:18:52 +0200853 if (r) {
854 dev_err(&dssdev->dev, "failed to request reset gpio\n");
Tomi Valkeinen5e56ad42012-08-29 16:31:29 +0300855 return r;
Tomi Valkeinen3acc7972012-02-20 12:18:52 +0200856 }
857 }
858
Tomi Valkeinenf075a592012-09-03 10:42:50 +0300859 if (gpio_is_valid(td->ext_te_gpio)) {
860 r = devm_gpio_request_one(&dssdev->dev, td->ext_te_gpio,
861 GPIOF_IN, "taal irq");
Tomi Valkeinen5e56ad42012-08-29 16:31:29 +0300862 if (r) {
863 dev_err(&dssdev->dev, "GPIO request failed\n");
864 return r;
865 }
866
Tomi Valkeinenf075a592012-09-03 10:42:50 +0300867 r = devm_request_irq(&dssdev->dev, gpio_to_irq(td->ext_te_gpio),
Tomi Valkeinen5e56ad42012-08-29 16:31:29 +0300868 taal_te_isr,
869 IRQF_TRIGGER_RISING,
870 "taal vsync", dssdev);
871
872 if (r) {
873 dev_err(&dssdev->dev, "IRQ request failed\n");
874 return r;
875 }
876
Linus Torvalds5f769452012-10-12 10:21:02 +0900877 INIT_DEFERRABLE_WORK(&td->te_timeout_work,
Tomi Valkeinen5e56ad42012-08-29 16:31:29 +0300878 taal_te_timeout_work_callback);
879
880 dev_dbg(&dssdev->dev, "Using GPIO TE\n");
881 }
882
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200883 td->workqueue = create_singlethread_workqueue("taal_esd");
884 if (td->workqueue == NULL) {
885 dev_err(&dssdev->dev, "can't create ESD workqueue\n");
Tomi Valkeinen5e56ad42012-08-29 16:31:29 +0300886 return -ENOMEM;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200887 }
Tejun Heo203b42f2012-08-21 13:18:23 -0700888 INIT_DEFERRABLE_WORK(&td->esd_work, taal_esd_work);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200889 INIT_DELAYED_WORK(&td->ulps_work, taal_ulps_work);
890
Jani Nikula006db7b2010-04-09 12:25:59 +0300891 taal_hw_reset(dssdev);
892
Tomi Valkeinenf075a592012-09-03 10:42:50 +0300893 if (td->use_dsi_backlight) {
Tomi Valkeinenbb36dbf2011-08-09 11:39:27 +0300894 memset(&props, 0, sizeof(struct backlight_properties));
Matthew Garretta19a6ee2010-02-17 16:39:44 -0500895 props.max_brightness = 255;
Matthew Garrettbb7ca742011-03-22 16:30:21 -0700896
Tomi Valkeinenbb36dbf2011-08-09 11:39:27 +0300897 props.type = BACKLIGHT_RAW;
898 bldev = backlight_device_register(dev_name(&dssdev->dev),
899 &dssdev->dev, dssdev, &taal_bl_ops, &props);
900 if (IS_ERR(bldev)) {
901 r = PTR_ERR(bldev);
902 goto err_bl;
903 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200904
Tomi Valkeinenbb36dbf2011-08-09 11:39:27 +0300905 td->bldev = bldev;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200906
Tomi Valkeinenbb36dbf2011-08-09 11:39:27 +0300907 bldev->props.fb_blank = FB_BLANK_UNBLANK;
908 bldev->props.power = FB_BLANK_UNBLANK;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200909 bldev->props.brightness = 255;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200910
Tomi Valkeinenbb36dbf2011-08-09 11:39:27 +0300911 taal_bl_update_status(bldev);
912 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200913
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530914 r = omap_dsi_request_vc(dssdev, &td->channel);
915 if (r) {
916 dev_err(&dssdev->dev, "failed to get virtual channel\n");
917 goto err_req_vc;
918 }
919
920 r = omap_dsi_set_vc_id(dssdev, td->channel, TCH);
921 if (r) {
922 dev_err(&dssdev->dev, "failed to set VC_ID\n");
923 goto err_vc_id;
924 }
925
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200926 r = sysfs_create_group(&dssdev->dev.kobj, &taal_attr_group);
927 if (r) {
928 dev_err(&dssdev->dev, "failed to create sysfs files\n");
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530929 goto err_vc_id;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200930 }
931
932 return 0;
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530933
934err_vc_id:
935 omap_dsi_release_vc(dssdev, td->channel);
936err_req_vc:
Tomi Valkeinenbb36dbf2011-08-09 11:39:27 +0300937 if (bldev != NULL)
938 backlight_device_unregister(bldev);
Jani Nikulad2b65782010-04-15 12:24:36 +0300939err_bl:
Tomi Valkeinen883b9ac2011-03-24 15:01:30 +0200940 destroy_workqueue(td->workqueue);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200941 return r;
942}
943
Tomi Valkeinen14e4d782011-03-31 12:03:51 +0300944static void __exit taal_remove(struct omap_dss_device *dssdev)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200945{
946 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
947 struct backlight_device *bldev;
948
949 dev_dbg(&dssdev->dev, "remove\n");
950
951 sysfs_remove_group(&dssdev->dev.kobj, &taal_attr_group);
Archit Tanejabc6d4b12011-03-01 13:59:46 +0530952 omap_dsi_release_vc(dssdev, td->channel);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200953
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200954 bldev = td->bldev;
Tomi Valkeinenbb36dbf2011-08-09 11:39:27 +0300955 if (bldev != NULL) {
956 bldev->props.power = FB_BLANK_POWERDOWN;
957 taal_bl_update_status(bldev);
958 backlight_device_unregister(bldev);
959 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200960
Tomi Valkeinen1abf7812011-03-24 14:53:27 +0200961 taal_cancel_ulps_work(dssdev);
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +0200962 taal_cancel_esd_work(dssdev);
Tomi Valkeinen883b9ac2011-03-24 15:01:30 +0200963 destroy_workqueue(td->workqueue);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200964
Jani Nikula006db7b2010-04-09 12:25:59 +0300965 /* reset, to be sure that the panel is in a valid state */
966 taal_hw_reset(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200967}
968
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200969static int taal_power_on(struct omap_dss_device *dssdev)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +0200970{
971 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
972 u8 id1, id2, id3;
973 int r;
974
Tomi Valkeinenf075a592012-09-03 10:42:50 +0300975 r = omapdss_dsi_configure_pins(dssdev, &td->pin_config);
Tomi Valkeinene4a9e942012-03-28 15:58:56 +0300976 if (r) {
977 dev_err(&dssdev->dev, "failed to configure DSI pins\n");
978 goto err0;
979 };
980
Archit Tanejae3525742012-08-09 15:23:43 +0530981 omapdss_dsi_set_size(dssdev, dssdev->panel.timings.x_res,
982 dssdev->panel.timings.y_res);
Archit Taneja02c39602012-08-10 15:01:33 +0530983 omapdss_dsi_set_pixel_format(dssdev, OMAP_DSS_DSI_FMT_RGB888);
Archit Tanejadca2b152012-08-16 18:02:00 +0530984 omapdss_dsi_set_operation_mode(dssdev, OMAP_DSS_DSI_CMD_MODE);
Archit Tanejae3525742012-08-09 15:23:43 +0530985
Tomi Valkeinenee144e62012-08-10 16:50:51 +0300986 r = omapdss_dsi_set_clocks(dssdev, 216000000, 10000000);
987 if (r) {
988 dev_err(&dssdev->dev, "failed to set HS and LP clocks\n");
989 goto err0;
990 }
991
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +0200992 r = omapdss_dsi_display_enable(dssdev);
993 if (r) {
994 dev_err(&dssdev->dev, "failed to enable DSI\n");
995 goto err0;
996 }
997
Jani Nikula006db7b2010-04-09 12:25:59 +0300998 taal_hw_reset(dssdev);
999
Archit Taneja1ffefe72011-05-12 17:26:24 +05301000 omapdss_dsi_vc_enable_hs(dssdev, td->channel, false);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001001
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001002 r = taal_sleep_out(td);
1003 if (r)
1004 goto err;
1005
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301006 r = taal_get_id(td, &id1, &id2, &id3);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001007 if (r)
1008 goto err;
1009
Jani Nikula1e8943d2010-04-15 17:07:39 +03001010 /* on early Taal revisions CABC is broken */
Tomi Valkeinene087cc212012-11-16 14:48:51 +02001011 if (id2 == 0x00 || id2 == 0xff || id2 == 0x81)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001012 td->cabc_broken = true;
1013
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301014 r = taal_dcs_write_1(td, DCS_BRIGHTNESS, 0xff);
Jani Nikulaf2a8b752010-04-09 14:15:12 +03001015 if (r)
1016 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001017
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301018 r = taal_dcs_write_1(td, DCS_CTRL_DISPLAY,
Jani Nikulaf2a8b752010-04-09 14:15:12 +03001019 (1<<2) | (1<<5)); /* BL | BCTRL */
1020 if (r)
1021 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001022
Archit Taneja7a7c48f2011-08-25 18:25:03 +05301023 r = taal_dcs_write_1(td, MIPI_DCS_SET_PIXEL_FORMAT,
1024 MIPI_DCS_PIXEL_FMT_24BIT);
Jani Nikulaf2a8b752010-04-09 14:15:12 +03001025 if (r)
1026 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001027
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301028 r = taal_set_addr_mode(td, td->rotate, td->mirror);
Jani Nikulaf2a8b752010-04-09 14:15:12 +03001029 if (r)
1030 goto err;
1031
1032 if (!td->cabc_broken) {
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301033 r = taal_dcs_write_1(td, DCS_WRITE_CABC, td->cabc_mode);
Jani Nikulaf2a8b752010-04-09 14:15:12 +03001034 if (r)
1035 goto err;
1036 }
1037
Archit Taneja7a7c48f2011-08-25 18:25:03 +05301038 r = taal_dcs_write_0(td, MIPI_DCS_SET_DISPLAY_ON);
Jani Nikulaf2a8b752010-04-09 14:15:12 +03001039 if (r)
1040 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001041
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001042 r = _taal_enable_te(dssdev, td->te_enabled);
1043 if (r)
1044 goto err;
1045
Tomi Valkeinen9a147a62011-11-09 15:30:11 +02001046 r = dsi_enable_video_output(dssdev, td->channel);
1047 if (r)
1048 goto err;
1049
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001050 td->enabled = 1;
1051
1052 if (!td->intro_printed) {
Tomi Valkeinene087cc212012-11-16 14:48:51 +02001053 dev_info(&dssdev->dev, "panel revision %02x.%02x.%02x\n",
1054 id1, id2, id3);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001055 if (td->cabc_broken)
1056 dev_info(&dssdev->dev,
1057 "old Taal version, CABC disabled\n");
1058 td->intro_printed = true;
1059 }
1060
Archit Taneja1ffefe72011-05-12 17:26:24 +05301061 omapdss_dsi_vc_enable_hs(dssdev, td->channel, true);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001062
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001063 return 0;
1064err:
Jani Nikula006db7b2010-04-09 12:25:59 +03001065 dev_err(&dssdev->dev, "error while enabling panel, issuing HW reset\n");
1066
1067 taal_hw_reset(dssdev);
1068
Tomi Valkeinen22d6d672010-10-11 11:33:30 +03001069 omapdss_dsi_display_disable(dssdev, true, false);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001070err0:
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001071 return r;
1072}
1073
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001074static void taal_power_off(struct omap_dss_device *dssdev)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001075{
1076 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Jani Nikula006db7b2010-04-09 12:25:59 +03001077 int r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001078
Tomi Valkeinen9a147a62011-11-09 15:30:11 +02001079 dsi_disable_video_output(dssdev, td->channel);
1080
Archit Taneja7a7c48f2011-08-25 18:25:03 +05301081 r = taal_dcs_write_0(td, MIPI_DCS_SET_DISPLAY_OFF);
Tomi Valkeinen15ffa1d2011-06-16 14:34:06 +03001082 if (!r)
Jani Nikula006db7b2010-04-09 12:25:59 +03001083 r = taal_sleep_in(td);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001084
Jani Nikula006db7b2010-04-09 12:25:59 +03001085 if (r) {
1086 dev_err(&dssdev->dev,
1087 "error disabling panel, issuing HW reset\n");
1088 taal_hw_reset(dssdev);
1089 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001090
Tomi Valkeinen22d6d672010-10-11 11:33:30 +03001091 omapdss_dsi_display_disable(dssdev, true, false);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001092
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001093 td->enabled = 0;
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001094}
1095
Tomi Valkeinenbb5476c2011-03-24 15:00:06 +02001096static int taal_panel_reset(struct omap_dss_device *dssdev)
1097{
1098 dev_err(&dssdev->dev, "performing LCD reset\n");
1099
1100 taal_power_off(dssdev);
1101 taal_hw_reset(dssdev);
1102 return taal_power_on(dssdev);
1103}
1104
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001105static int taal_enable(struct omap_dss_device *dssdev)
1106{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001107 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001108 int r;
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001109
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001110 dev_dbg(&dssdev->dev, "enable\n");
1111
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001112 mutex_lock(&td->lock);
1113
1114 if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) {
1115 r = -EINVAL;
1116 goto err;
1117 }
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001118
Archit Taneja1ffefe72011-05-12 17:26:24 +05301119 dsi_bus_lock(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001120
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001121 r = taal_power_on(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001122
Archit Taneja1ffefe72011-05-12 17:26:24 +05301123 dsi_bus_unlock(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001124
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001125 if (r)
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001126 goto err;
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001127
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +02001128 taal_queue_esd_work(dssdev);
Jani Nikula4571a022010-04-12 10:23:46 +03001129
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001130 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
1131
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001132 mutex_unlock(&td->lock);
1133
1134 return 0;
1135err:
1136 dev_dbg(&dssdev->dev, "enable failed\n");
1137 mutex_unlock(&td->lock);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001138 return r;
1139}
1140
1141static void taal_disable(struct omap_dss_device *dssdev)
1142{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001143 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1144
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001145 dev_dbg(&dssdev->dev, "disable\n");
1146
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001147 mutex_lock(&td->lock);
1148
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001149 taal_cancel_ulps_work(dssdev);
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +02001150 taal_cancel_esd_work(dssdev);
Jani Nikula4571a022010-04-12 10:23:46 +03001151
Archit Taneja1ffefe72011-05-12 17:26:24 +05301152 dsi_bus_lock(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001153
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001154 if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) {
Tomi Valkeinene8945672011-05-31 16:39:01 +03001155 int r;
1156
1157 r = taal_wake_up(dssdev);
1158 if (!r)
1159 taal_power_off(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001160 }
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001161
Archit Taneja1ffefe72011-05-12 17:26:24 +05301162 dsi_bus_unlock(dssdev);
Jani Nikula2c2fc152010-04-12 10:06:14 +03001163
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02001164 dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001165
1166 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001167}
1168
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001169static void taal_framedone_cb(int err, void *data)
1170{
1171 struct omap_dss_device *dssdev = data;
1172 dev_dbg(&dssdev->dev, "framedone, err %d\n", err);
Archit Taneja1ffefe72011-05-12 17:26:24 +05301173 dsi_bus_unlock(dssdev);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001174}
1175
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001176static irqreturn_t taal_te_isr(int irq, void *data)
1177{
1178 struct omap_dss_device *dssdev = data;
1179 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1180 int old;
1181 int r;
1182
1183 old = atomic_cmpxchg(&td->do_update, 1, 0);
1184
1185 if (old) {
1186 cancel_delayed_work(&td->te_timeout_work);
1187
Tomi Valkeinen5476e742011-11-03 16:34:20 +02001188 r = omap_dsi_update(dssdev, td->channel, taal_framedone_cb,
1189 dssdev);
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001190 if (r)
1191 goto err;
1192 }
1193
1194 return IRQ_HANDLED;
1195err:
1196 dev_err(&dssdev->dev, "start update failed\n");
Archit Taneja1ffefe72011-05-12 17:26:24 +05301197 dsi_bus_unlock(dssdev);
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001198 return IRQ_HANDLED;
1199}
1200
1201static void taal_te_timeout_work_callback(struct work_struct *work)
1202{
1203 struct taal_data *td = container_of(work, struct taal_data,
1204 te_timeout_work.work);
1205 struct omap_dss_device *dssdev = td->dssdev;
1206
1207 dev_err(&dssdev->dev, "TE not received for 250ms!\n");
1208
1209 atomic_set(&td->do_update, 0);
Archit Taneja1ffefe72011-05-12 17:26:24 +05301210 dsi_bus_unlock(dssdev);
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001211}
1212
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001213static int taal_update(struct omap_dss_device *dssdev,
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001214 u16 x, u16 y, u16 w, u16 h)
1215{
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001216 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1217 int r;
1218
1219 dev_dbg(&dssdev->dev, "update %d, %d, %d x %d\n", x, y, w, h);
1220
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001221 mutex_lock(&td->lock);
Archit Taneja1ffefe72011-05-12 17:26:24 +05301222 dsi_bus_lock(dssdev);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001223
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001224 r = taal_wake_up(dssdev);
1225 if (r)
1226 goto err;
1227
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001228 if (!td->enabled) {
1229 r = 0;
1230 goto err;
1231 }
1232
Tomi Valkeinen63317092011-11-03 16:45:07 +02001233 /* XXX no need to send this every frame, but dsi break if not done */
1234 r = taal_set_update_window(td, 0, 0,
Tomi Valkeinene087cc212012-11-16 14:48:51 +02001235 dssdev->panel.timings.x_res,
1236 dssdev->panel.timings.y_res);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001237 if (r)
1238 goto err;
1239
Tomi Valkeinenf075a592012-09-03 10:42:50 +03001240 if (td->te_enabled && gpio_is_valid(td->ext_te_gpio)) {
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001241 schedule_delayed_work(&td->te_timeout_work,
1242 msecs_to_jiffies(250));
1243 atomic_set(&td->do_update, 1);
1244 } else {
Tomi Valkeinen5476e742011-11-03 16:34:20 +02001245 r = omap_dsi_update(dssdev, td->channel, taal_framedone_cb,
1246 dssdev);
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001247 if (r)
1248 goto err;
1249 }
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001250
1251 /* note: no bus_unlock here. unlock is in framedone_cb */
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001252 mutex_unlock(&td->lock);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001253 return 0;
1254err:
Archit Taneja1ffefe72011-05-12 17:26:24 +05301255 dsi_bus_unlock(dssdev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001256 mutex_unlock(&td->lock);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001257 return r;
1258}
1259
1260static int taal_sync(struct omap_dss_device *dssdev)
1261{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001262 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1263
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001264 dev_dbg(&dssdev->dev, "sync\n");
1265
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001266 mutex_lock(&td->lock);
Archit Taneja1ffefe72011-05-12 17:26:24 +05301267 dsi_bus_lock(dssdev);
1268 dsi_bus_unlock(dssdev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001269 mutex_unlock(&td->lock);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001270
1271 dev_dbg(&dssdev->dev, "sync done\n");
1272
1273 return 0;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001274}
1275
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001276static int _taal_enable_te(struct omap_dss_device *dssdev, bool enable)
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001277{
Jani Nikulae7f6c3f2010-04-15 12:55:38 +03001278 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001279 int r;
1280
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001281 if (enable)
Archit Taneja7a7c48f2011-08-25 18:25:03 +05301282 r = taal_dcs_write_1(td, MIPI_DCS_SET_TEAR_ON, 0);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001283 else
Archit Taneja7a7c48f2011-08-25 18:25:03 +05301284 r = taal_dcs_write_0(td, MIPI_DCS_SET_TEAR_OFF);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001285
Tomi Valkeinenf075a592012-09-03 10:42:50 +03001286 if (!gpio_is_valid(td->ext_te_gpio))
Jani Nikula7ae2fb12010-04-13 10:57:52 +03001287 omapdss_dsi_enable_te(dssdev, enable);
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001288
Tomi Valkeinene087cc212012-11-16 14:48:51 +02001289 /* possible panel bug */
1290 msleep(100);
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001291
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001292 return r;
1293}
1294
1295static int taal_enable_te(struct omap_dss_device *dssdev, bool enable)
1296{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001297 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001298 int r;
1299
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001300 mutex_lock(&td->lock);
Tomi Valkeinenffb63c92010-05-21 10:09:18 +03001301
1302 if (td->te_enabled == enable)
1303 goto end;
1304
Archit Taneja1ffefe72011-05-12 17:26:24 +05301305 dsi_bus_lock(dssdev);
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001306
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001307 if (td->enabled) {
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001308 r = taal_wake_up(dssdev);
1309 if (r)
1310 goto err;
1311
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001312 r = _taal_enable_te(dssdev, enable);
1313 if (r)
1314 goto err;
1315 }
Tomi Valkeinen21df20f2010-03-02 12:13:55 +02001316
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001317 td->te_enabled = enable;
1318
Archit Taneja1ffefe72011-05-12 17:26:24 +05301319 dsi_bus_unlock(dssdev);
Tomi Valkeinenffb63c92010-05-21 10:09:18 +03001320end:
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001321 mutex_unlock(&td->lock);
1322
1323 return 0;
1324err:
Archit Taneja1ffefe72011-05-12 17:26:24 +05301325 dsi_bus_unlock(dssdev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001326 mutex_unlock(&td->lock);
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001327
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001328 return r;
1329}
1330
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001331static int taal_get_te(struct omap_dss_device *dssdev)
1332{
1333 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001334 int r;
1335
1336 mutex_lock(&td->lock);
1337 r = td->te_enabled;
1338 mutex_unlock(&td->lock);
1339
1340 return r;
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001341}
1342
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001343static int taal_rotate(struct omap_dss_device *dssdev, u8 rotate)
1344{
1345 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Archit Tanejae3525742012-08-09 15:23:43 +05301346 u16 dw, dh;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001347 int r;
1348
1349 dev_dbg(&dssdev->dev, "rotate %d\n", rotate);
1350
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001351 mutex_lock(&td->lock);
Tomi Valkeinenffb63c92010-05-21 10:09:18 +03001352
1353 if (td->rotate == rotate)
1354 goto end;
1355
Archit Taneja1ffefe72011-05-12 17:26:24 +05301356 dsi_bus_lock(dssdev);
Tomi Valkeinen87424e12010-01-08 16:52:48 +02001357
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001358 if (td->enabled) {
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001359 r = taal_wake_up(dssdev);
1360 if (r)
1361 goto err;
1362
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301363 r = taal_set_addr_mode(td, rotate, td->mirror);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001364 if (r)
Tomi Valkeinen87424e12010-01-08 16:52:48 +02001365 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001366 }
1367
Archit Tanejae3525742012-08-09 15:23:43 +05301368 if (rotate == 0 || rotate == 2) {
1369 dw = dssdev->panel.timings.x_res;
1370 dh = dssdev->panel.timings.y_res;
1371 } else {
1372 dw = dssdev->panel.timings.y_res;
1373 dh = dssdev->panel.timings.x_res;
1374 }
1375
1376 omapdss_dsi_set_size(dssdev, dw, dh);
1377
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001378 td->rotate = rotate;
1379
Archit Taneja1ffefe72011-05-12 17:26:24 +05301380 dsi_bus_unlock(dssdev);
Tomi Valkeinenffb63c92010-05-21 10:09:18 +03001381end:
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001382 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001383 return 0;
Tomi Valkeinen87424e12010-01-08 16:52:48 +02001384err:
Archit Taneja1ffefe72011-05-12 17:26:24 +05301385 dsi_bus_unlock(dssdev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001386 mutex_unlock(&td->lock);
Tomi Valkeinen87424e12010-01-08 16:52:48 +02001387 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001388}
1389
1390static u8 taal_get_rotate(struct omap_dss_device *dssdev)
1391{
1392 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001393 int r;
1394
1395 mutex_lock(&td->lock);
1396 r = td->rotate;
1397 mutex_unlock(&td->lock);
1398
1399 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001400}
1401
1402static int taal_mirror(struct omap_dss_device *dssdev, bool enable)
1403{
1404 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1405 int r;
1406
1407 dev_dbg(&dssdev->dev, "mirror %d\n", enable);
1408
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001409 mutex_lock(&td->lock);
Tomi Valkeinenffb63c92010-05-21 10:09:18 +03001410
1411 if (td->mirror == enable)
1412 goto end;
1413
Archit Taneja1ffefe72011-05-12 17:26:24 +05301414 dsi_bus_lock(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001415 if (td->enabled) {
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001416 r = taal_wake_up(dssdev);
1417 if (r)
1418 goto err;
1419
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301420 r = taal_set_addr_mode(td, td->rotate, enable);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001421 if (r)
Tomi Valkeinen8d8aa612010-01-08 16:30:33 +02001422 goto err;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001423 }
1424
1425 td->mirror = enable;
1426
Archit Taneja1ffefe72011-05-12 17:26:24 +05301427 dsi_bus_unlock(dssdev);
Tomi Valkeinenffb63c92010-05-21 10:09:18 +03001428end:
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001429 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001430 return 0;
Tomi Valkeinen8d8aa612010-01-08 16:30:33 +02001431err:
Archit Taneja1ffefe72011-05-12 17:26:24 +05301432 dsi_bus_unlock(dssdev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001433 mutex_unlock(&td->lock);
Tomi Valkeinen8d8aa612010-01-08 16:30:33 +02001434 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001435}
1436
1437static bool taal_get_mirror(struct omap_dss_device *dssdev)
1438{
1439 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001440 int r;
1441
1442 mutex_lock(&td->lock);
1443 r = td->mirror;
1444 mutex_unlock(&td->lock);
1445
1446 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001447}
1448
1449static int taal_run_test(struct omap_dss_device *dssdev, int test_num)
1450{
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001451 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001452 u8 id1, id2, id3;
1453 int r;
1454
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001455 mutex_lock(&td->lock);
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001456
1457 if (!td->enabled) {
1458 r = -ENODEV;
1459 goto err1;
1460 }
1461
Archit Taneja1ffefe72011-05-12 17:26:24 +05301462 dsi_bus_lock(dssdev);
Tomi Valkeinen1a75ef42010-01-08 16:21:28 +02001463
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001464 r = taal_wake_up(dssdev);
1465 if (r)
1466 goto err2;
1467
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301468 r = taal_dcs_read_1(td, DCS_GET_ID1, &id1);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001469 if (r)
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001470 goto err2;
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301471 r = taal_dcs_read_1(td, DCS_GET_ID2, &id2);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001472 if (r)
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001473 goto err2;
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301474 r = taal_dcs_read_1(td, DCS_GET_ID3, &id3);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001475 if (r)
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001476 goto err2;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001477
Archit Taneja1ffefe72011-05-12 17:26:24 +05301478 dsi_bus_unlock(dssdev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001479 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001480 return 0;
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001481err2:
Archit Taneja1ffefe72011-05-12 17:26:24 +05301482 dsi_bus_unlock(dssdev);
Jani Nikulaee52c0a2010-04-12 09:36:05 +03001483err1:
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001484 mutex_unlock(&td->lock);
Tomi Valkeinen1a75ef42010-01-08 16:21:28 +02001485 return r;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001486}
1487
1488static int taal_memory_read(struct omap_dss_device *dssdev,
1489 void *buf, size_t size,
1490 u16 x, u16 y, u16 w, u16 h)
1491{
1492 int r;
1493 int first = 1;
1494 int plen;
1495 unsigned buf_used = 0;
Tomi Valkeinenc75d9462010-01-08 16:56:44 +02001496 struct taal_data *td = dev_get_drvdata(&dssdev->dev);
1497
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001498 if (size < w * h * 3)
1499 return -ENOMEM;
1500
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001501 mutex_lock(&td->lock);
1502
1503 if (!td->enabled) {
1504 r = -ENODEV;
1505 goto err1;
1506 }
1507
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001508 size = min(w * h * 3,
1509 dssdev->panel.timings.x_res *
1510 dssdev->panel.timings.y_res * 3);
1511
Archit Taneja1ffefe72011-05-12 17:26:24 +05301512 dsi_bus_lock(dssdev);
Tomi Valkeinenc75d9462010-01-08 16:56:44 +02001513
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001514 r = taal_wake_up(dssdev);
1515 if (r)
1516 goto err2;
1517
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001518 /* plen 1 or 2 goes into short packet. until checksum error is fixed,
1519 * use short packets. plen 32 works, but bigger packets seem to cause
1520 * an error. */
1521 if (size % 2)
1522 plen = 1;
1523 else
1524 plen = 2;
1525
Archit Tanejabc6d4b12011-03-01 13:59:46 +05301526 taal_set_update_window(td, x, y, w, h);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001527
Archit Taneja1ffefe72011-05-12 17:26:24 +05301528 r = dsi_vc_set_max_rx_packet_size(dssdev, td->channel, plen);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001529 if (r)
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001530 goto err2;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001531
1532 while (buf_used < size) {
1533 u8 dcs_cmd = first ? 0x2e : 0x3e;
1534 first = 0;
1535
Archit Taneja1ffefe72011-05-12 17:26:24 +05301536 r = dsi_vc_dcs_read(dssdev, td->channel, dcs_cmd,
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001537 buf + buf_used, size - buf_used);
1538
1539 if (r < 0) {
1540 dev_err(&dssdev->dev, "read error\n");
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001541 goto err3;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001542 }
1543
1544 buf_used += r;
1545
1546 if (r < plen) {
1547 dev_err(&dssdev->dev, "short read\n");
1548 break;
1549 }
1550
1551 if (signal_pending(current)) {
1552 dev_err(&dssdev->dev, "signal pending, "
1553 "aborting memory read\n");
1554 r = -ERESTARTSYS;
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001555 goto err3;
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001556 }
1557 }
1558
1559 r = buf_used;
1560
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001561err3:
Archit Taneja1ffefe72011-05-12 17:26:24 +05301562 dsi_vc_set_max_rx_packet_size(dssdev, td->channel, 1);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001563err2:
Archit Taneja1ffefe72011-05-12 17:26:24 +05301564 dsi_bus_unlock(dssdev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001565err1:
1566 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001567 return r;
1568}
1569
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001570static void taal_ulps_work(struct work_struct *work)
1571{
1572 struct taal_data *td = container_of(work, struct taal_data,
1573 ulps_work.work);
1574 struct omap_dss_device *dssdev = td->dssdev;
1575
1576 mutex_lock(&td->lock);
1577
1578 if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE || !td->enabled) {
1579 mutex_unlock(&td->lock);
1580 return;
1581 }
1582
Archit Taneja1ffefe72011-05-12 17:26:24 +05301583 dsi_bus_lock(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001584
1585 taal_enter_ulps(dssdev);
1586
Archit Taneja1ffefe72011-05-12 17:26:24 +05301587 dsi_bus_unlock(dssdev);
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001588 mutex_unlock(&td->lock);
1589}
1590
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001591static void taal_esd_work(struct work_struct *work)
1592{
1593 struct taal_data *td = container_of(work, struct taal_data,
1594 esd_work.work);
1595 struct omap_dss_device *dssdev = td->dssdev;
1596 u8 state1, state2;
1597 int r;
1598
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001599 mutex_lock(&td->lock);
1600
1601 if (!td->enabled) {
1602 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001603 return;
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001604 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001605
Archit Taneja1ffefe72011-05-12 17:26:24 +05301606 dsi_bus_lock(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001607
Tomi Valkeinen1abf7812011-03-24 14:53:27 +02001608 r = taal_wake_up(dssdev);
1609 if (r) {
1610 dev_err(&dssdev->dev, "failed to exit ULPS\n");
1611 goto err;
1612 }
1613
Archit Taneja7a7c48f2011-08-25 18:25:03 +05301614 r = taal_dcs_read_1(td, MIPI_DCS_GET_DIAGNOSTIC_RESULT, &state1);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001615 if (r) {
1616 dev_err(&dssdev->dev, "failed to read Taal status\n");
1617 goto err;
1618 }
1619
1620 /* Run self diagnostics */
1621 r = taal_sleep_out(td);
1622 if (r) {
1623 dev_err(&dssdev->dev, "failed to run Taal self-diagnostics\n");
1624 goto err;
1625 }
1626
Archit Taneja7a7c48f2011-08-25 18:25:03 +05301627 r = taal_dcs_read_1(td, MIPI_DCS_GET_DIAGNOSTIC_RESULT, &state2);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001628 if (r) {
1629 dev_err(&dssdev->dev, "failed to read Taal status\n");
1630 goto err;
1631 }
1632
1633 /* Each sleep out command will trigger a self diagnostic and flip
1634 * Bit6 if the test passes.
1635 */
1636 if (!((state1 ^ state2) & (1 << 6))) {
1637 dev_err(&dssdev->dev, "LCD self diagnostics failed\n");
1638 goto err;
1639 }
1640 /* Self-diagnostics result is also shown on TE GPIO line. We need
1641 * to re-enable TE after self diagnostics */
Tomi Valkeinenf075a592012-09-03 10:42:50 +03001642 if (td->te_enabled && gpio_is_valid(td->ext_te_gpio)) {
Archit Taneja7a7c48f2011-08-25 18:25:03 +05301643 r = taal_dcs_write_1(td, MIPI_DCS_SET_TEAR_ON, 0);
Tomi Valkeinen1189b7f2010-03-01 13:52:10 +02001644 if (r)
1645 goto err;
1646 }
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001647
Archit Taneja1ffefe72011-05-12 17:26:24 +05301648 dsi_bus_unlock(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001649
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +02001650 taal_queue_esd_work(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001651
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001652 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001653 return;
1654err:
1655 dev_err(&dssdev->dev, "performing LCD reset\n");
1656
Tomi Valkeinenbb5476c2011-03-24 15:00:06 +02001657 taal_panel_reset(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001658
Archit Taneja1ffefe72011-05-12 17:26:24 +05301659 dsi_bus_unlock(dssdev);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001660
Tomi Valkeinen1663d2f2011-03-24 14:01:49 +02001661 taal_queue_esd_work(dssdev);
Tomi Valkeinena3201a02010-03-03 14:31:45 +02001662
1663 mutex_unlock(&td->lock);
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001664}
1665
1666static struct omap_dss_driver taal_driver = {
1667 .probe = taal_probe,
Tomi Valkeinen14e4d782011-03-31 12:03:51 +03001668 .remove = __exit_p(taal_remove),
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001669
1670 .enable = taal_enable,
1671 .disable = taal_disable,
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001672
Tomi Valkeinen18946f62010-01-12 14:16:41 +02001673 .update = taal_update,
1674 .sync = taal_sync,
1675
Tomi Valkeinen96adcec2010-01-11 13:54:33 +02001676 .get_resolution = taal_get_resolution,
Tomi Valkeinena2699502010-01-11 14:33:40 +02001677 .get_recommended_bpp = omapdss_default_get_recommended_bpp,
1678
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001679 .enable_te = taal_enable_te,
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001680 .get_te = taal_get_te,
Tomi Valkeinen225b6502010-01-11 15:11:01 +02001681
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001682 .set_rotate = taal_rotate,
1683 .get_rotate = taal_get_rotate,
1684 .set_mirror = taal_mirror,
1685 .get_mirror = taal_get_mirror,
1686 .run_test = taal_run_test,
1687 .memory_read = taal_memory_read,
1688
Tomi Valkeinenf133a9d2009-10-28 11:31:05 +02001689 .driver = {
1690 .name = "taal",
1691 .owner = THIS_MODULE,
1692 },
1693};
1694
1695static int __init taal_init(void)
1696{
1697 omap_dss_register_driver(&taal_driver);
1698
1699 return 0;
1700}
1701
1702static void __exit taal_exit(void)
1703{
1704 omap_dss_unregister_driver(&taal_driver);
1705}
1706
1707module_init(taal_init);
1708module_exit(taal_exit);
1709
1710MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@nokia.com>");
1711MODULE_DESCRIPTION("Taal Driver");
1712MODULE_LICENSE("GPL");