backlight: ams369fg06: use sleep instead of delay
[pandora-kernel.git] / drivers / video / backlight / ams369fg06.c
1 /*
2  * ams369fg06 AMOLED LCD panel driver.
3  *
4  * Copyright (c) 2011 Samsung Electronics Co., Ltd.
5  * Author: Jingoo Han  <jg1.han@samsung.com>
6  *
7  * Derived from drivers/video/s6e63m0.c
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU General Public License as published by the
11  * Free Software Foundation; either version 2 of the License, or (at your
12  * option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License along
20  * with this program; if not, write to the Free Software Foundation, Inc.,
21  * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  */
23
24 #include <linux/wait.h>
25 #include <linux/module.h>
26 #include <linux/fb.h>
27 #include <linux/delay.h>
28 #include <linux/gpio.h>
29 #include <linux/spi/spi.h>
30 #include <linux/lcd.h>
31 #include <linux/backlight.h>
32
33 #define SLEEPMSEC               0x1000
34 #define ENDDEF                  0x2000
35 #define DEFMASK                 0xFF00
36 #define COMMAND_ONLY            0xFE
37 #define DATA_ONLY               0xFF
38
39 #define MAX_GAMMA_LEVEL         5
40 #define GAMMA_TABLE_COUNT       21
41
42 #define MIN_BRIGHTNESS          0
43 #define MAX_BRIGHTNESS          255
44 #define DEFAULT_BRIGHTNESS      150
45
46 struct ams369fg06 {
47         struct device                   *dev;
48         struct spi_device               *spi;
49         unsigned int                    power;
50         struct lcd_device               *ld;
51         struct backlight_device         *bd;
52         struct lcd_platform_data        *lcd_pd;
53 };
54
55 static const unsigned short seq_display_on[] = {
56         0x14, 0x03,
57         ENDDEF, 0x0000
58 };
59
60 static const unsigned short seq_display_off[] = {
61         0x14, 0x00,
62         ENDDEF, 0x0000
63 };
64
65 static const unsigned short seq_stand_by_on[] = {
66         0x1D, 0xA1,
67         SLEEPMSEC, 200,
68         ENDDEF, 0x0000
69 };
70
71 static const unsigned short seq_stand_by_off[] = {
72         0x1D, 0xA0,
73         SLEEPMSEC, 250,
74         ENDDEF, 0x0000
75 };
76
77 static const unsigned short seq_setting[] = {
78         0x31, 0x08,
79         0x32, 0x14,
80         0x30, 0x02,
81         0x27, 0x01,
82         0x12, 0x08,
83         0x13, 0x08,
84         0x15, 0x00,
85         0x16, 0x00,
86
87         0xef, 0xd0,
88         DATA_ONLY, 0xe8,
89
90         0x39, 0x44,
91         0x40, 0x00,
92         0x41, 0x3f,
93         0x42, 0x2a,
94         0x43, 0x27,
95         0x44, 0x27,
96         0x45, 0x1f,
97         0x46, 0x44,
98         0x50, 0x00,
99         0x51, 0x00,
100         0x52, 0x17,
101         0x53, 0x24,
102         0x54, 0x26,
103         0x55, 0x1f,
104         0x56, 0x43,
105         0x60, 0x00,
106         0x61, 0x3f,
107         0x62, 0x2a,
108         0x63, 0x25,
109         0x64, 0x24,
110         0x65, 0x1b,
111         0x66, 0x5c,
112
113         0x17, 0x22,
114         0x18, 0x33,
115         0x19, 0x03,
116         0x1a, 0x01,
117         0x22, 0xa4,
118         0x23, 0x00,
119         0x26, 0xa0,
120
121         0x1d, 0xa0,
122         SLEEPMSEC, 300,
123
124         0x14, 0x03,
125
126         ENDDEF, 0x0000
127 };
128
129 /* gamma value: 2.2 */
130 static const unsigned int ams369fg06_22_250[] = {
131         0x00, 0x3f, 0x2a, 0x27, 0x27, 0x1f, 0x44,
132         0x00, 0x00, 0x17, 0x24, 0x26, 0x1f, 0x43,
133         0x00, 0x3f, 0x2a, 0x25, 0x24, 0x1b, 0x5c,
134 };
135
136 static const unsigned int ams369fg06_22_200[] = {
137         0x00, 0x3f, 0x28, 0x29, 0x27, 0x21, 0x3e,
138         0x00, 0x00, 0x10, 0x25, 0x27, 0x20, 0x3d,
139         0x00, 0x3f, 0x28, 0x27, 0x25, 0x1d, 0x53,
140 };
141
142 static const unsigned int ams369fg06_22_150[] = {
143         0x00, 0x3f, 0x2d, 0x29, 0x28, 0x23, 0x37,
144         0x00, 0x00, 0x0b, 0x25, 0x28, 0x22, 0x36,
145         0x00, 0x3f, 0x2b, 0x28, 0x26, 0x1f, 0x4a,
146 };
147
148 static const unsigned int ams369fg06_22_100[] = {
149         0x00, 0x3f, 0x30, 0x2a, 0x2b, 0x24, 0x2f,
150         0x00, 0x00, 0x00, 0x25, 0x29, 0x24, 0x2e,
151         0x00, 0x3f, 0x2f, 0x29, 0x29, 0x21, 0x3f,
152 };
153
154 static const unsigned int ams369fg06_22_50[] = {
155         0x00, 0x3f, 0x3c, 0x2c, 0x2d, 0x27, 0x24,
156         0x00, 0x00, 0x00, 0x22, 0x2a, 0x27, 0x23,
157         0x00, 0x3f, 0x3b, 0x2c, 0x2b, 0x24, 0x31,
158 };
159
160 struct ams369fg06_gamma {
161         unsigned int *gamma_22_table[MAX_GAMMA_LEVEL];
162 };
163
164 static struct ams369fg06_gamma gamma_table = {
165         .gamma_22_table[0] = (unsigned int *)&ams369fg06_22_50,
166         .gamma_22_table[1] = (unsigned int *)&ams369fg06_22_100,
167         .gamma_22_table[2] = (unsigned int *)&ams369fg06_22_150,
168         .gamma_22_table[3] = (unsigned int *)&ams369fg06_22_200,
169         .gamma_22_table[4] = (unsigned int *)&ams369fg06_22_250,
170 };
171
172 static int ams369fg06_spi_write_byte(struct ams369fg06 *lcd, int addr, int data)
173 {
174         u16 buf[1];
175         struct spi_message msg;
176
177         struct spi_transfer xfer = {
178                 .len            = 2,
179                 .tx_buf         = buf,
180         };
181
182         buf[0] = (addr << 8) | data;
183
184         spi_message_init(&msg);
185         spi_message_add_tail(&xfer, &msg);
186
187         return spi_sync(lcd->spi, &msg);
188 }
189
190 static int ams369fg06_spi_write(struct ams369fg06 *lcd, unsigned char address,
191         unsigned char command)
192 {
193         int ret = 0;
194
195         if (address != DATA_ONLY)
196                 ret = ams369fg06_spi_write_byte(lcd, 0x70, address);
197         if (command != COMMAND_ONLY)
198                 ret = ams369fg06_spi_write_byte(lcd, 0x72, command);
199
200         return ret;
201 }
202
203 static int ams369fg06_panel_send_sequence(struct ams369fg06 *lcd,
204         const unsigned short *wbuf)
205 {
206         int ret = 0, i = 0;
207
208         while ((wbuf[i] & DEFMASK) != ENDDEF) {
209                 if ((wbuf[i] & DEFMASK) != SLEEPMSEC) {
210                         ret = ams369fg06_spi_write(lcd, wbuf[i], wbuf[i+1]);
211                         if (ret)
212                                 break;
213                 } else {
214                         msleep(wbuf[i+1]);
215                 }
216                 i += 2;
217         }
218
219         return ret;
220 }
221
222 static int _ams369fg06_gamma_ctl(struct ams369fg06 *lcd,
223         const unsigned int *gamma)
224 {
225         unsigned int i = 0;
226         int ret = 0;
227
228         for (i = 0 ; i < GAMMA_TABLE_COUNT / 3; i++) {
229                 ret = ams369fg06_spi_write(lcd, 0x40 + i, gamma[i]);
230                 ret = ams369fg06_spi_write(lcd, 0x50 + i, gamma[i+7*1]);
231                 ret = ams369fg06_spi_write(lcd, 0x60 + i, gamma[i+7*2]);
232                 if (ret) {
233                         dev_err(lcd->dev, "failed to set gamma table.\n");
234                         goto gamma_err;
235                 }
236         }
237
238 gamma_err:
239         return ret;
240 }
241
242 static int ams369fg06_gamma_ctl(struct ams369fg06 *lcd, int brightness)
243 {
244         int ret = 0;
245         int gamma = 0;
246
247         if ((brightness >= 0) && (brightness <= 50))
248                 gamma = 0;
249         else if ((brightness > 50) && (brightness <= 100))
250                 gamma = 1;
251         else if ((brightness > 100) && (brightness <= 150))
252                 gamma = 2;
253         else if ((brightness > 150) && (brightness <= 200))
254                 gamma = 3;
255         else if ((brightness > 200) && (brightness <= 255))
256                 gamma = 4;
257
258         ret = _ams369fg06_gamma_ctl(lcd, gamma_table.gamma_22_table[gamma]);
259
260         return ret;
261 }
262
263 static int ams369fg06_ldi_init(struct ams369fg06 *lcd)
264 {
265         int ret, i;
266         static const unsigned short *init_seq[] = {
267                 seq_setting,
268                 seq_stand_by_off,
269         };
270
271         for (i = 0; i < ARRAY_SIZE(init_seq); i++) {
272                 ret = ams369fg06_panel_send_sequence(lcd, init_seq[i]);
273                 if (ret)
274                         break;
275         }
276
277         return ret;
278 }
279
280 static int ams369fg06_ldi_enable(struct ams369fg06 *lcd)
281 {
282         int ret, i;
283         static const unsigned short *init_seq[] = {
284                 seq_stand_by_off,
285                 seq_display_on,
286         };
287
288         for (i = 0; i < ARRAY_SIZE(init_seq); i++) {
289                 ret = ams369fg06_panel_send_sequence(lcd, init_seq[i]);
290                 if (ret)
291                         break;
292         }
293
294         return ret;
295 }
296
297 static int ams369fg06_ldi_disable(struct ams369fg06 *lcd)
298 {
299         int ret, i;
300
301         static const unsigned short *init_seq[] = {
302                 seq_display_off,
303                 seq_stand_by_on,
304         };
305
306         for (i = 0; i < ARRAY_SIZE(init_seq); i++) {
307                 ret = ams369fg06_panel_send_sequence(lcd, init_seq[i]);
308                 if (ret)
309                         break;
310         }
311
312         return ret;
313 }
314
315 static int ams369fg06_power_is_on(int power)
316 {
317         return ((power) <= FB_BLANK_NORMAL);
318 }
319
320 static int ams369fg06_power_on(struct ams369fg06 *lcd)
321 {
322         int ret = 0;
323         struct lcd_platform_data *pd = NULL;
324         struct backlight_device *bd = NULL;
325
326         pd = lcd->lcd_pd;
327         if (!pd) {
328                 dev_err(lcd->dev, "platform data is NULL.\n");
329                 return -EFAULT;
330         }
331
332         bd = lcd->bd;
333         if (!bd) {
334                 dev_err(lcd->dev, "backlight device is NULL.\n");
335                 return -EFAULT;
336         }
337
338         if (!pd->power_on) {
339                 dev_err(lcd->dev, "power_on is NULL.\n");
340                 return -EFAULT;
341         } else {
342                 pd->power_on(lcd->ld, 1);
343                 msleep(pd->power_on_delay);
344         }
345
346         if (!pd->reset) {
347                 dev_err(lcd->dev, "reset is NULL.\n");
348                 return -EFAULT;
349         } else {
350                 pd->reset(lcd->ld);
351                 msleep(pd->reset_delay);
352         }
353
354         ret = ams369fg06_ldi_init(lcd);
355         if (ret) {
356                 dev_err(lcd->dev, "failed to initialize ldi.\n");
357                 return ret;
358         }
359
360         ret = ams369fg06_ldi_enable(lcd);
361         if (ret) {
362                 dev_err(lcd->dev, "failed to enable ldi.\n");
363                 return ret;
364         }
365
366         /* set brightness to current value after power on or resume. */
367         ret = ams369fg06_gamma_ctl(lcd, bd->props.brightness);
368         if (ret) {
369                 dev_err(lcd->dev, "lcd gamma setting failed.\n");
370                 return ret;
371         }
372
373         return 0;
374 }
375
376 static int ams369fg06_power_off(struct ams369fg06 *lcd)
377 {
378         int ret = 0;
379         struct lcd_platform_data *pd = NULL;
380
381         pd = lcd->lcd_pd;
382         if (!pd) {
383                 dev_err(lcd->dev, "platform data is NULL\n");
384                 return -EFAULT;
385         }
386
387         ret = ams369fg06_ldi_disable(lcd);
388         if (ret) {
389                 dev_err(lcd->dev, "lcd setting failed.\n");
390                 return -EIO;
391         }
392
393         msleep(pd->power_off_delay);
394
395         if (!pd->power_on) {
396                 dev_err(lcd->dev, "power_on is NULL.\n");
397                 return -EFAULT;
398         } else
399                 pd->power_on(lcd->ld, 0);
400
401         return 0;
402 }
403
404 static int ams369fg06_power(struct ams369fg06 *lcd, int power)
405 {
406         int ret = 0;
407
408         if (ams369fg06_power_is_on(power) &&
409                 !ams369fg06_power_is_on(lcd->power))
410                 ret = ams369fg06_power_on(lcd);
411         else if (!ams369fg06_power_is_on(power) &&
412                 ams369fg06_power_is_on(lcd->power))
413                 ret = ams369fg06_power_off(lcd);
414
415         if (!ret)
416                 lcd->power = power;
417
418         return ret;
419 }
420
421 static int ams369fg06_get_power(struct lcd_device *ld)
422 {
423         struct ams369fg06 *lcd = lcd_get_data(ld);
424
425         return lcd->power;
426 }
427
428 static int ams369fg06_set_power(struct lcd_device *ld, int power)
429 {
430         struct ams369fg06 *lcd = lcd_get_data(ld);
431
432         if (power != FB_BLANK_UNBLANK && power != FB_BLANK_POWERDOWN &&
433                 power != FB_BLANK_NORMAL) {
434                 dev_err(lcd->dev, "power value should be 0, 1 or 4.\n");
435                 return -EINVAL;
436         }
437
438         return ams369fg06_power(lcd, power);
439 }
440
441 static int ams369fg06_get_brightness(struct backlight_device *bd)
442 {
443         return bd->props.brightness;
444 }
445
446 static int ams369fg06_set_brightness(struct backlight_device *bd)
447 {
448         int ret = 0;
449         int brightness = bd->props.brightness;
450         struct ams369fg06 *lcd = dev_get_drvdata(&bd->dev);
451
452         if (brightness < MIN_BRIGHTNESS ||
453                 brightness > bd->props.max_brightness) {
454                 dev_err(&bd->dev, "lcd brightness should be %d to %d.\n",
455                         MIN_BRIGHTNESS, MAX_BRIGHTNESS);
456                 return -EINVAL;
457         }
458
459         ret = ams369fg06_gamma_ctl(lcd, bd->props.brightness);
460         if (ret) {
461                 dev_err(&bd->dev, "lcd brightness setting failed.\n");
462                 return -EIO;
463         }
464
465         return ret;
466 }
467
468 static struct lcd_ops ams369fg06_lcd_ops = {
469         .get_power = ams369fg06_get_power,
470         .set_power = ams369fg06_set_power,
471 };
472
473 static const struct backlight_ops ams369fg06_backlight_ops = {
474         .get_brightness = ams369fg06_get_brightness,
475         .update_status = ams369fg06_set_brightness,
476 };
477
478 static int ams369fg06_probe(struct spi_device *spi)
479 {
480         int ret = 0;
481         struct ams369fg06 *lcd = NULL;
482         struct lcd_device *ld = NULL;
483         struct backlight_device *bd = NULL;
484         struct backlight_properties props;
485
486         lcd = devm_kzalloc(&spi->dev, sizeof(struct ams369fg06), GFP_KERNEL);
487         if (!lcd)
488                 return -ENOMEM;
489
490         /* ams369fg06 lcd panel uses 3-wire 16bits SPI Mode. */
491         spi->bits_per_word = 16;
492
493         ret = spi_setup(spi);
494         if (ret < 0) {
495                 dev_err(&spi->dev, "spi setup failed.\n");
496                 return ret;
497         }
498
499         lcd->spi = spi;
500         lcd->dev = &spi->dev;
501
502         lcd->lcd_pd = spi->dev.platform_data;
503         if (!lcd->lcd_pd) {
504                 dev_err(&spi->dev, "platform data is NULL\n");
505                 return -EFAULT;
506         }
507
508         ld = lcd_device_register("ams369fg06", &spi->dev, lcd,
509                 &ams369fg06_lcd_ops);
510         if (IS_ERR(ld))
511                 return PTR_ERR(ld);
512
513         lcd->ld = ld;
514
515         memset(&props, 0, sizeof(struct backlight_properties));
516         props.type = BACKLIGHT_RAW;
517         props.max_brightness = MAX_BRIGHTNESS;
518
519         bd = backlight_device_register("ams369fg06-bl", &spi->dev, lcd,
520                 &ams369fg06_backlight_ops, &props);
521         if (IS_ERR(bd)) {
522                 ret =  PTR_ERR(bd);
523                 goto out_lcd_unregister;
524         }
525
526         bd->props.brightness = DEFAULT_BRIGHTNESS;
527         lcd->bd = bd;
528
529         if (!lcd->lcd_pd->lcd_enabled) {
530                 /*
531                  * if lcd panel was off from bootloader then
532                  * current lcd status is powerdown and then
533                  * it enables lcd panel.
534                  */
535                 lcd->power = FB_BLANK_POWERDOWN;
536
537                 ams369fg06_power(lcd, FB_BLANK_UNBLANK);
538         } else
539                 lcd->power = FB_BLANK_UNBLANK;
540
541         dev_set_drvdata(&spi->dev, lcd);
542
543         dev_info(&spi->dev, "ams369fg06 panel driver has been probed.\n");
544
545         return 0;
546
547 out_lcd_unregister:
548         lcd_device_unregister(ld);
549         return ret;
550 }
551
552 static int ams369fg06_remove(struct spi_device *spi)
553 {
554         struct ams369fg06 *lcd = dev_get_drvdata(&spi->dev);
555
556         ams369fg06_power(lcd, FB_BLANK_POWERDOWN);
557         backlight_device_unregister(lcd->bd);
558         lcd_device_unregister(lcd->ld);
559
560         return 0;
561 }
562
563 #if defined(CONFIG_PM)
564 static unsigned int before_power;
565
566 static int ams369fg06_suspend(struct spi_device *spi, pm_message_t mesg)
567 {
568         int ret = 0;
569         struct ams369fg06 *lcd = dev_get_drvdata(&spi->dev);
570
571         dev_dbg(&spi->dev, "lcd->power = %d\n", lcd->power);
572
573         before_power = lcd->power;
574
575         /*
576          * when lcd panel is suspend, lcd panel becomes off
577          * regardless of status.
578          */
579         ret = ams369fg06_power(lcd, FB_BLANK_POWERDOWN);
580
581         return ret;
582 }
583
584 static int ams369fg06_resume(struct spi_device *spi)
585 {
586         int ret = 0;
587         struct ams369fg06 *lcd = dev_get_drvdata(&spi->dev);
588
589         /*
590          * after suspended, if lcd panel status is FB_BLANK_UNBLANK
591          * (at that time, before_power is FB_BLANK_UNBLANK) then
592          * it changes that status to FB_BLANK_POWERDOWN to get lcd on.
593          */
594         if (before_power == FB_BLANK_UNBLANK)
595                 lcd->power = FB_BLANK_POWERDOWN;
596
597         dev_dbg(&spi->dev, "before_power = %d\n", before_power);
598
599         ret = ams369fg06_power(lcd, before_power);
600
601         return ret;
602 }
603 #else
604 #define ams369fg06_suspend      NULL
605 #define ams369fg06_resume       NULL
606 #endif
607
608 static void ams369fg06_shutdown(struct spi_device *spi)
609 {
610         struct ams369fg06 *lcd = dev_get_drvdata(&spi->dev);
611
612         ams369fg06_power(lcd, FB_BLANK_POWERDOWN);
613 }
614
615 static struct spi_driver ams369fg06_driver = {
616         .driver = {
617                 .name   = "ams369fg06",
618                 .owner  = THIS_MODULE,
619         },
620         .probe          = ams369fg06_probe,
621         .remove         = ams369fg06_remove,
622         .shutdown       = ams369fg06_shutdown,
623         .suspend        = ams369fg06_suspend,
624         .resume         = ams369fg06_resume,
625 };
626
627 module_spi_driver(ams369fg06_driver);
628
629 MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
630 MODULE_DESCRIPTION("ams369fg06 LCD Driver");
631 MODULE_LICENSE("GPL");