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