packaging: release out (3.8.3)
[profile/ivi/kernel-adaptation-intel-automotive.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                         mdelay(wbuf[i+1]);
215                 i += 2;
216         }
217
218         return ret;
219 }
220
221 static int _ams369fg06_gamma_ctl(struct ams369fg06 *lcd,
222         const unsigned int *gamma)
223 {
224         unsigned int i = 0;
225         int ret = 0;
226
227         for (i = 0 ; i < GAMMA_TABLE_COUNT / 3; i++) {
228                 ret = ams369fg06_spi_write(lcd, 0x40 + i, gamma[i]);
229                 ret = ams369fg06_spi_write(lcd, 0x50 + i, gamma[i+7*1]);
230                 ret = ams369fg06_spi_write(lcd, 0x60 + i, gamma[i+7*2]);
231                 if (ret) {
232                         dev_err(lcd->dev, "failed to set gamma table.\n");
233                         goto gamma_err;
234                 }
235         }
236
237 gamma_err:
238         return ret;
239 }
240
241 static int ams369fg06_gamma_ctl(struct ams369fg06 *lcd, int brightness)
242 {
243         int ret = 0;
244         int gamma = 0;
245
246         if ((brightness >= 0) && (brightness <= 50))
247                 gamma = 0;
248         else if ((brightness > 50) && (brightness <= 100))
249                 gamma = 1;
250         else if ((brightness > 100) && (brightness <= 150))
251                 gamma = 2;
252         else if ((brightness > 150) && (brightness <= 200))
253                 gamma = 3;
254         else if ((brightness > 200) && (brightness <= 255))
255                 gamma = 4;
256
257         ret = _ams369fg06_gamma_ctl(lcd, gamma_table.gamma_22_table[gamma]);
258
259         return ret;
260 }
261
262 static int ams369fg06_ldi_init(struct ams369fg06 *lcd)
263 {
264         int ret, i;
265         static const unsigned short *init_seq[] = {
266                 seq_setting,
267                 seq_stand_by_off,
268         };
269
270         for (i = 0; i < ARRAY_SIZE(init_seq); i++) {
271                 ret = ams369fg06_panel_send_sequence(lcd, init_seq[i]);
272                 if (ret)
273                         break;
274         }
275
276         return ret;
277 }
278
279 static int ams369fg06_ldi_enable(struct ams369fg06 *lcd)
280 {
281         int ret, i;
282         static const unsigned short *init_seq[] = {
283                 seq_stand_by_off,
284                 seq_display_on,
285         };
286
287         for (i = 0; i < ARRAY_SIZE(init_seq); i++) {
288                 ret = ams369fg06_panel_send_sequence(lcd, init_seq[i]);
289                 if (ret)
290                         break;
291         }
292
293         return ret;
294 }
295
296 static int ams369fg06_ldi_disable(struct ams369fg06 *lcd)
297 {
298         int ret, i;
299
300         static const unsigned short *init_seq[] = {
301                 seq_display_off,
302                 seq_stand_by_on,
303         };
304
305         for (i = 0; i < ARRAY_SIZE(init_seq); i++) {
306                 ret = ams369fg06_panel_send_sequence(lcd, init_seq[i]);
307                 if (ret)
308                         break;
309         }
310
311         return ret;
312 }
313
314 static int ams369fg06_power_is_on(int power)
315 {
316         return ((power) <= FB_BLANK_NORMAL);
317 }
318
319 static int ams369fg06_power_on(struct ams369fg06 *lcd)
320 {
321         int ret = 0;
322         struct lcd_platform_data *pd = NULL;
323         struct backlight_device *bd = NULL;
324
325         pd = lcd->lcd_pd;
326         if (!pd) {
327                 dev_err(lcd->dev, "platform data is NULL.\n");
328                 return -EFAULT;
329         }
330
331         bd = lcd->bd;
332         if (!bd) {
333                 dev_err(lcd->dev, "backlight device is NULL.\n");
334                 return -EFAULT;
335         }
336
337         if (!pd->power_on) {
338                 dev_err(lcd->dev, "power_on is NULL.\n");
339                 return -EFAULT;
340         } else {
341                 pd->power_on(lcd->ld, 1);
342                 mdelay(pd->power_on_delay);
343         }
344
345         if (!pd->reset) {
346                 dev_err(lcd->dev, "reset is NULL.\n");
347                 return -EFAULT;
348         } else {
349                 pd->reset(lcd->ld);
350                 mdelay(pd->reset_delay);
351         }
352
353         ret = ams369fg06_ldi_init(lcd);
354         if (ret) {
355                 dev_err(lcd->dev, "failed to initialize ldi.\n");
356                 return ret;
357         }
358
359         ret = ams369fg06_ldi_enable(lcd);
360         if (ret) {
361                 dev_err(lcd->dev, "failed to enable ldi.\n");
362                 return ret;
363         }
364
365         /* set brightness to current value after power on or resume. */
366         ret = ams369fg06_gamma_ctl(lcd, bd->props.brightness);
367         if (ret) {
368                 dev_err(lcd->dev, "lcd gamma setting failed.\n");
369                 return ret;
370         }
371
372         return 0;
373 }
374
375 static int ams369fg06_power_off(struct ams369fg06 *lcd)
376 {
377         int ret = 0;
378         struct lcd_platform_data *pd = NULL;
379
380         pd = lcd->lcd_pd;
381         if (!pd) {
382                 dev_err(lcd->dev, "platform data is NULL\n");
383                 return -EFAULT;
384         }
385
386         ret = ams369fg06_ldi_disable(lcd);
387         if (ret) {
388                 dev_err(lcd->dev, "lcd setting failed.\n");
389                 return -EIO;
390         }
391
392         mdelay(pd->power_off_delay);
393
394         if (!pd->power_on) {
395                 dev_err(lcd->dev, "power_on is NULL.\n");
396                 return -EFAULT;
397         } else
398                 pd->power_on(lcd->ld, 0);
399
400         return 0;
401 }
402
403 static int ams369fg06_power(struct ams369fg06 *lcd, int power)
404 {
405         int ret = 0;
406
407         if (ams369fg06_power_is_on(power) &&
408                 !ams369fg06_power_is_on(lcd->power))
409                 ret = ams369fg06_power_on(lcd);
410         else if (!ams369fg06_power_is_on(power) &&
411                 ams369fg06_power_is_on(lcd->power))
412                 ret = ams369fg06_power_off(lcd);
413
414         if (!ret)
415                 lcd->power = power;
416
417         return ret;
418 }
419
420 static int ams369fg06_get_power(struct lcd_device *ld)
421 {
422         struct ams369fg06 *lcd = lcd_get_data(ld);
423
424         return lcd->power;
425 }
426
427 static int ams369fg06_set_power(struct lcd_device *ld, int power)
428 {
429         struct ams369fg06 *lcd = lcd_get_data(ld);
430
431         if (power != FB_BLANK_UNBLANK && power != FB_BLANK_POWERDOWN &&
432                 power != FB_BLANK_NORMAL) {
433                 dev_err(lcd->dev, "power value should be 0, 1 or 4.\n");
434                 return -EINVAL;
435         }
436
437         return ams369fg06_power(lcd, power);
438 }
439
440 static int ams369fg06_get_brightness(struct backlight_device *bd)
441 {
442         return bd->props.brightness;
443 }
444
445 static int ams369fg06_set_brightness(struct backlight_device *bd)
446 {
447         int ret = 0;
448         int brightness = bd->props.brightness;
449         struct ams369fg06 *lcd = dev_get_drvdata(&bd->dev);
450
451         if (brightness < MIN_BRIGHTNESS ||
452                 brightness > bd->props.max_brightness) {
453                 dev_err(&bd->dev, "lcd brightness should be %d to %d.\n",
454                         MIN_BRIGHTNESS, MAX_BRIGHTNESS);
455                 return -EINVAL;
456         }
457
458         ret = ams369fg06_gamma_ctl(lcd, bd->props.brightness);
459         if (ret) {
460                 dev_err(&bd->dev, "lcd brightness setting failed.\n");
461                 return -EIO;
462         }
463
464         return ret;
465 }
466
467 static struct lcd_ops ams369fg06_lcd_ops = {
468         .get_power = ams369fg06_get_power,
469         .set_power = ams369fg06_set_power,
470 };
471
472 static const struct backlight_ops ams369fg06_backlight_ops = {
473         .get_brightness = ams369fg06_get_brightness,
474         .update_status = ams369fg06_set_brightness,
475 };
476
477 static int ams369fg06_probe(struct spi_device *spi)
478 {
479         int ret = 0;
480         struct ams369fg06 *lcd = NULL;
481         struct lcd_device *ld = NULL;
482         struct backlight_device *bd = NULL;
483         struct backlight_properties props;
484
485         lcd = devm_kzalloc(&spi->dev, sizeof(struct ams369fg06), GFP_KERNEL);
486         if (!lcd)
487                 return -ENOMEM;
488
489         /* ams369fg06 lcd panel uses 3-wire 16bits SPI Mode. */
490         spi->bits_per_word = 16;
491
492         ret = spi_setup(spi);
493         if (ret < 0) {
494                 dev_err(&spi->dev, "spi setup failed.\n");
495                 return ret;
496         }
497
498         lcd->spi = spi;
499         lcd->dev = &spi->dev;
500
501         lcd->lcd_pd = spi->dev.platform_data;
502         if (!lcd->lcd_pd) {
503                 dev_err(&spi->dev, "platform data is NULL\n");
504                 return -EFAULT;
505         }
506
507         ld = lcd_device_register("ams369fg06", &spi->dev, lcd,
508                 &ams369fg06_lcd_ops);
509         if (IS_ERR(ld))
510                 return PTR_ERR(ld);
511
512         lcd->ld = ld;
513
514         memset(&props, 0, sizeof(struct backlight_properties));
515         props.type = BACKLIGHT_RAW;
516         props.max_brightness = MAX_BRIGHTNESS;
517
518         bd = backlight_device_register("ams369fg06-bl", &spi->dev, lcd,
519                 &ams369fg06_backlight_ops, &props);
520         if (IS_ERR(bd)) {
521                 ret =  PTR_ERR(bd);
522                 goto out_lcd_unregister;
523         }
524
525         bd->props.brightness = DEFAULT_BRIGHTNESS;
526         lcd->bd = bd;
527
528         if (!lcd->lcd_pd->lcd_enabled) {
529                 /*
530                  * if lcd panel was off from bootloader then
531                  * current lcd status is powerdown and then
532                  * it enables lcd panel.
533                  */
534                 lcd->power = FB_BLANK_POWERDOWN;
535
536                 ams369fg06_power(lcd, FB_BLANK_UNBLANK);
537         } else
538                 lcd->power = FB_BLANK_UNBLANK;
539
540         dev_set_drvdata(&spi->dev, lcd);
541
542         dev_info(&spi->dev, "ams369fg06 panel driver has been probed.\n");
543
544         return 0;
545
546 out_lcd_unregister:
547         lcd_device_unregister(ld);
548         return ret;
549 }
550
551 static int ams369fg06_remove(struct spi_device *spi)
552 {
553         struct ams369fg06 *lcd = dev_get_drvdata(&spi->dev);
554
555         ams369fg06_power(lcd, FB_BLANK_POWERDOWN);
556         backlight_device_unregister(lcd->bd);
557         lcd_device_unregister(lcd->ld);
558
559         return 0;
560 }
561
562 #if defined(CONFIG_PM)
563 static unsigned int before_power;
564
565 static int ams369fg06_suspend(struct spi_device *spi, pm_message_t mesg)
566 {
567         int ret = 0;
568         struct ams369fg06 *lcd = dev_get_drvdata(&spi->dev);
569
570         dev_dbg(&spi->dev, "lcd->power = %d\n", lcd->power);
571
572         before_power = lcd->power;
573
574         /*
575          * when lcd panel is suspend, lcd panel becomes off
576          * regardless of status.
577          */
578         ret = ams369fg06_power(lcd, FB_BLANK_POWERDOWN);
579
580         return ret;
581 }
582
583 static int ams369fg06_resume(struct spi_device *spi)
584 {
585         int ret = 0;
586         struct ams369fg06 *lcd = dev_get_drvdata(&spi->dev);
587
588         /*
589          * after suspended, if lcd panel status is FB_BLANK_UNBLANK
590          * (at that time, before_power is FB_BLANK_UNBLANK) then
591          * it changes that status to FB_BLANK_POWERDOWN to get lcd on.
592          */
593         if (before_power == FB_BLANK_UNBLANK)
594                 lcd->power = FB_BLANK_POWERDOWN;
595
596         dev_dbg(&spi->dev, "before_power = %d\n", before_power);
597
598         ret = ams369fg06_power(lcd, before_power);
599
600         return ret;
601 }
602 #else
603 #define ams369fg06_suspend      NULL
604 #define ams369fg06_resume       NULL
605 #endif
606
607 static void ams369fg06_shutdown(struct spi_device *spi)
608 {
609         struct ams369fg06 *lcd = dev_get_drvdata(&spi->dev);
610
611         ams369fg06_power(lcd, FB_BLANK_POWERDOWN);
612 }
613
614 static struct spi_driver ams369fg06_driver = {
615         .driver = {
616                 .name   = "ams369fg06",
617                 .owner  = THIS_MODULE,
618         },
619         .probe          = ams369fg06_probe,
620         .remove         = ams369fg06_remove,
621         .shutdown       = ams369fg06_shutdown,
622         .suspend        = ams369fg06_suspend,
623         .resume         = ams369fg06_resume,
624 };
625
626 module_spi_driver(ams369fg06_driver);
627
628 MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
629 MODULE_DESCRIPTION("ams369fg06 LCD Driver");
630 MODULE_LICENSE("GPL");