Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / drivers / video / backlight / s6e63m0.c
1 /*
2  * S6E63M0 AMOLED LCD panel driver.
3  *
4  * Author: InKi Dae  <inki.dae@samsung.com>
5  *
6  * Derived from drivers/video/omap/lcd-apollon.c
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU General Public License as published by the
10  * Free Software Foundation; either version 2 of the License, or (at your
11  * option) any later version.
12  */
13
14 #include <linux/backlight.h>
15 #include <linux/delay.h>
16 #include <linux/fb.h>
17 #include <linux/gpio.h>
18 #include <linux/interrupt.h>
19 #include <linux/irq.h>
20 #include <linux/kernel.h>
21 #include <linux/lcd.h>
22 #include <linux/module.h>
23 #include <linux/spi/spi.h>
24 #include <linux/wait.h>
25
26 #include "s6e63m0_gamma.h"
27
28 #define SLEEPMSEC               0x1000
29 #define ENDDEF                  0x2000
30 #define DEFMASK                 0xFF00
31 #define COMMAND_ONLY            0xFE
32 #define DATA_ONLY               0xFF
33
34 #define MIN_BRIGHTNESS          0
35 #define MAX_BRIGHTNESS          10
36
37 struct s6e63m0 {
38         struct device                   *dev;
39         struct spi_device               *spi;
40         unsigned int                    power;
41         unsigned int                    current_brightness;
42         unsigned int                    gamma_mode;
43         unsigned int                    gamma_table_count;
44         struct lcd_device               *ld;
45         struct backlight_device         *bd;
46         struct lcd_platform_data        *lcd_pd;
47 };
48
49 static const unsigned short seq_panel_condition_set[] = {
50         0xF8, 0x01,
51         DATA_ONLY, 0x27,
52         DATA_ONLY, 0x27,
53         DATA_ONLY, 0x07,
54         DATA_ONLY, 0x07,
55         DATA_ONLY, 0x54,
56         DATA_ONLY, 0x9f,
57         DATA_ONLY, 0x63,
58         DATA_ONLY, 0x86,
59         DATA_ONLY, 0x1a,
60         DATA_ONLY, 0x33,
61         DATA_ONLY, 0x0d,
62         DATA_ONLY, 0x00,
63         DATA_ONLY, 0x00,
64
65         ENDDEF, 0x0000
66 };
67
68 static const unsigned short seq_display_condition_set[] = {
69         0xf2, 0x02,
70         DATA_ONLY, 0x03,
71         DATA_ONLY, 0x1c,
72         DATA_ONLY, 0x10,
73         DATA_ONLY, 0x10,
74
75         0xf7, 0x03,
76         DATA_ONLY, 0x00,
77         DATA_ONLY, 0x00,
78
79         ENDDEF, 0x0000
80 };
81
82 static const unsigned short seq_gamma_setting[] = {
83         0xfa, 0x00,
84         DATA_ONLY, 0x18,
85         DATA_ONLY, 0x08,
86         DATA_ONLY, 0x24,
87         DATA_ONLY, 0x64,
88         DATA_ONLY, 0x56,
89         DATA_ONLY, 0x33,
90         DATA_ONLY, 0xb6,
91         DATA_ONLY, 0xba,
92         DATA_ONLY, 0xa8,
93         DATA_ONLY, 0xac,
94         DATA_ONLY, 0xb1,
95         DATA_ONLY, 0x9d,
96         DATA_ONLY, 0xc1,
97         DATA_ONLY, 0xc1,
98         DATA_ONLY, 0xb7,
99         DATA_ONLY, 0x00,
100         DATA_ONLY, 0x9c,
101         DATA_ONLY, 0x00,
102         DATA_ONLY, 0x9f,
103         DATA_ONLY, 0x00,
104         DATA_ONLY, 0xd6,
105
106         0xfa, 0x01,
107
108         ENDDEF, 0x0000
109 };
110
111 static const unsigned short seq_etc_condition_set[] = {
112         0xf6, 0x00,
113         DATA_ONLY, 0x8c,
114         DATA_ONLY, 0x07,
115
116         0xb3, 0xc,
117
118         0xb5, 0x2c,
119         DATA_ONLY, 0x12,
120         DATA_ONLY, 0x0c,
121         DATA_ONLY, 0x0a,
122         DATA_ONLY, 0x10,
123         DATA_ONLY, 0x0e,
124         DATA_ONLY, 0x17,
125         DATA_ONLY, 0x13,
126         DATA_ONLY, 0x1f,
127         DATA_ONLY, 0x1a,
128         DATA_ONLY, 0x2a,
129         DATA_ONLY, 0x24,
130         DATA_ONLY, 0x1f,
131         DATA_ONLY, 0x1b,
132         DATA_ONLY, 0x1a,
133         DATA_ONLY, 0x17,
134
135         DATA_ONLY, 0x2b,
136         DATA_ONLY, 0x26,
137         DATA_ONLY, 0x22,
138         DATA_ONLY, 0x20,
139         DATA_ONLY, 0x3a,
140         DATA_ONLY, 0x34,
141         DATA_ONLY, 0x30,
142         DATA_ONLY, 0x2c,
143         DATA_ONLY, 0x29,
144         DATA_ONLY, 0x26,
145         DATA_ONLY, 0x25,
146         DATA_ONLY, 0x23,
147         DATA_ONLY, 0x21,
148         DATA_ONLY, 0x20,
149         DATA_ONLY, 0x1e,
150         DATA_ONLY, 0x1e,
151
152         0xb6, 0x00,
153         DATA_ONLY, 0x00,
154         DATA_ONLY, 0x11,
155         DATA_ONLY, 0x22,
156         DATA_ONLY, 0x33,
157         DATA_ONLY, 0x44,
158         DATA_ONLY, 0x44,
159         DATA_ONLY, 0x44,
160
161         DATA_ONLY, 0x55,
162         DATA_ONLY, 0x55,
163         DATA_ONLY, 0x66,
164         DATA_ONLY, 0x66,
165         DATA_ONLY, 0x66,
166         DATA_ONLY, 0x66,
167         DATA_ONLY, 0x66,
168         DATA_ONLY, 0x66,
169
170         0xb7, 0x2c,
171         DATA_ONLY, 0x12,
172         DATA_ONLY, 0x0c,
173         DATA_ONLY, 0x0a,
174         DATA_ONLY, 0x10,
175         DATA_ONLY, 0x0e,
176         DATA_ONLY, 0x17,
177         DATA_ONLY, 0x13,
178         DATA_ONLY, 0x1f,
179         DATA_ONLY, 0x1a,
180         DATA_ONLY, 0x2a,
181         DATA_ONLY, 0x24,
182         DATA_ONLY, 0x1f,
183         DATA_ONLY, 0x1b,
184         DATA_ONLY, 0x1a,
185         DATA_ONLY, 0x17,
186
187         DATA_ONLY, 0x2b,
188         DATA_ONLY, 0x26,
189         DATA_ONLY, 0x22,
190         DATA_ONLY, 0x20,
191         DATA_ONLY, 0x3a,
192         DATA_ONLY, 0x34,
193         DATA_ONLY, 0x30,
194         DATA_ONLY, 0x2c,
195         DATA_ONLY, 0x29,
196         DATA_ONLY, 0x26,
197         DATA_ONLY, 0x25,
198         DATA_ONLY, 0x23,
199         DATA_ONLY, 0x21,
200         DATA_ONLY, 0x20,
201         DATA_ONLY, 0x1e,
202         DATA_ONLY, 0x1e,
203
204         0xb8, 0x00,
205         DATA_ONLY, 0x00,
206         DATA_ONLY, 0x11,
207         DATA_ONLY, 0x22,
208         DATA_ONLY, 0x33,
209         DATA_ONLY, 0x44,
210         DATA_ONLY, 0x44,
211         DATA_ONLY, 0x44,
212
213         DATA_ONLY, 0x55,
214         DATA_ONLY, 0x55,
215         DATA_ONLY, 0x66,
216         DATA_ONLY, 0x66,
217         DATA_ONLY, 0x66,
218         DATA_ONLY, 0x66,
219         DATA_ONLY, 0x66,
220         DATA_ONLY, 0x66,
221
222         0xb9, 0x2c,
223         DATA_ONLY, 0x12,
224         DATA_ONLY, 0x0c,
225         DATA_ONLY, 0x0a,
226         DATA_ONLY, 0x10,
227         DATA_ONLY, 0x0e,
228         DATA_ONLY, 0x17,
229         DATA_ONLY, 0x13,
230         DATA_ONLY, 0x1f,
231         DATA_ONLY, 0x1a,
232         DATA_ONLY, 0x2a,
233         DATA_ONLY, 0x24,
234         DATA_ONLY, 0x1f,
235         DATA_ONLY, 0x1b,
236         DATA_ONLY, 0x1a,
237         DATA_ONLY, 0x17,
238
239         DATA_ONLY, 0x2b,
240         DATA_ONLY, 0x26,
241         DATA_ONLY, 0x22,
242         DATA_ONLY, 0x20,
243         DATA_ONLY, 0x3a,
244         DATA_ONLY, 0x34,
245         DATA_ONLY, 0x30,
246         DATA_ONLY, 0x2c,
247         DATA_ONLY, 0x29,
248         DATA_ONLY, 0x26,
249         DATA_ONLY, 0x25,
250         DATA_ONLY, 0x23,
251         DATA_ONLY, 0x21,
252         DATA_ONLY, 0x20,
253         DATA_ONLY, 0x1e,
254         DATA_ONLY, 0x1e,
255
256         0xba, 0x00,
257         DATA_ONLY, 0x00,
258         DATA_ONLY, 0x11,
259         DATA_ONLY, 0x22,
260         DATA_ONLY, 0x33,
261         DATA_ONLY, 0x44,
262         DATA_ONLY, 0x44,
263         DATA_ONLY, 0x44,
264
265         DATA_ONLY, 0x55,
266         DATA_ONLY, 0x55,
267         DATA_ONLY, 0x66,
268         DATA_ONLY, 0x66,
269         DATA_ONLY, 0x66,
270         DATA_ONLY, 0x66,
271         DATA_ONLY, 0x66,
272         DATA_ONLY, 0x66,
273
274         0xc1, 0x4d,
275         DATA_ONLY, 0x96,
276         DATA_ONLY, 0x1d,
277         DATA_ONLY, 0x00,
278         DATA_ONLY, 0x00,
279         DATA_ONLY, 0x01,
280         DATA_ONLY, 0xdf,
281         DATA_ONLY, 0x00,
282         DATA_ONLY, 0x00,
283         DATA_ONLY, 0x03,
284         DATA_ONLY, 0x1f,
285         DATA_ONLY, 0x00,
286         DATA_ONLY, 0x00,
287         DATA_ONLY, 0x00,
288         DATA_ONLY, 0x00,
289         DATA_ONLY, 0x00,
290         DATA_ONLY, 0x00,
291         DATA_ONLY, 0x00,
292         DATA_ONLY, 0x00,
293         DATA_ONLY, 0x03,
294         DATA_ONLY, 0x06,
295         DATA_ONLY, 0x09,
296         DATA_ONLY, 0x0d,
297         DATA_ONLY, 0x0f,
298         DATA_ONLY, 0x12,
299         DATA_ONLY, 0x15,
300         DATA_ONLY, 0x18,
301
302         0xb2, 0x10,
303         DATA_ONLY, 0x10,
304         DATA_ONLY, 0x0b,
305         DATA_ONLY, 0x05,
306
307         ENDDEF, 0x0000
308 };
309
310 static const unsigned short seq_acl_on[] = {
311         /* ACL on */
312         0xc0, 0x01,
313
314         ENDDEF, 0x0000
315 };
316
317 static const unsigned short seq_acl_off[] = {
318         /* ACL off */
319         0xc0, 0x00,
320
321         ENDDEF, 0x0000
322 };
323
324 static const unsigned short seq_elvss_on[] = {
325         /* ELVSS on */
326         0xb1, 0x0b,
327
328         ENDDEF, 0x0000
329 };
330
331 static const unsigned short seq_elvss_off[] = {
332         /* ELVSS off */
333         0xb1, 0x0a,
334
335         ENDDEF, 0x0000
336 };
337
338 static const unsigned short seq_stand_by_off[] = {
339         0x11, COMMAND_ONLY,
340
341         ENDDEF, 0x0000
342 };
343
344 static const unsigned short seq_stand_by_on[] = {
345         0x10, COMMAND_ONLY,
346
347         ENDDEF, 0x0000
348 };
349
350 static const unsigned short seq_display_on[] = {
351         0x29, COMMAND_ONLY,
352
353         ENDDEF, 0x0000
354 };
355
356
357 static int s6e63m0_spi_write_byte(struct s6e63m0 *lcd, int addr, int data)
358 {
359         u16 buf[1];
360         struct spi_message msg;
361
362         struct spi_transfer xfer = {
363                 .len            = 2,
364                 .tx_buf         = buf,
365         };
366
367         buf[0] = (addr << 8) | data;
368
369         spi_message_init(&msg);
370         spi_message_add_tail(&xfer, &msg);
371
372         return spi_sync(lcd->spi, &msg);
373 }
374
375 static int s6e63m0_spi_write(struct s6e63m0 *lcd, unsigned char address,
376         unsigned char command)
377 {
378         int ret = 0;
379
380         if (address != DATA_ONLY)
381                 ret = s6e63m0_spi_write_byte(lcd, 0x0, address);
382         if (command != COMMAND_ONLY)
383                 ret = s6e63m0_spi_write_byte(lcd, 0x1, command);
384
385         return ret;
386 }
387
388 static int s6e63m0_panel_send_sequence(struct s6e63m0 *lcd,
389         const unsigned short *wbuf)
390 {
391         int ret = 0, i = 0;
392
393         while ((wbuf[i] & DEFMASK) != ENDDEF) {
394                 if ((wbuf[i] & DEFMASK) != SLEEPMSEC) {
395                         ret = s6e63m0_spi_write(lcd, wbuf[i], wbuf[i+1]);
396                         if (ret)
397                                 break;
398                 } else {
399                         msleep(wbuf[i+1]);
400                 }
401                 i += 2;
402         }
403
404         return ret;
405 }
406
407 static int _s6e63m0_gamma_ctl(struct s6e63m0 *lcd, const unsigned int *gamma)
408 {
409         unsigned int i = 0;
410         int ret = 0;
411
412         /* disable gamma table updating. */
413         ret = s6e63m0_spi_write(lcd, 0xfa, 0x00);
414         if (ret) {
415                 dev_err(lcd->dev, "failed to disable gamma table updating.\n");
416                 goto gamma_err;
417         }
418
419         for (i = 0 ; i < GAMMA_TABLE_COUNT; i++) {
420                 ret = s6e63m0_spi_write(lcd, DATA_ONLY, gamma[i]);
421                 if (ret) {
422                         dev_err(lcd->dev, "failed to set gamma table.\n");
423                         goto gamma_err;
424                 }
425         }
426
427         /* update gamma table. */
428         ret = s6e63m0_spi_write(lcd, 0xfa, 0x01);
429         if (ret)
430                 dev_err(lcd->dev, "failed to update gamma table.\n");
431
432 gamma_err:
433         return ret;
434 }
435
436 static int s6e63m0_gamma_ctl(struct s6e63m0 *lcd, int gamma)
437 {
438         int ret = 0;
439
440         ret = _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_22_table[gamma]);
441
442         return ret;
443 }
444
445
446 static int s6e63m0_ldi_init(struct s6e63m0 *lcd)
447 {
448         int ret, i;
449         const unsigned short *init_seq[] = {
450                 seq_panel_condition_set,
451                 seq_display_condition_set,
452                 seq_gamma_setting,
453                 seq_etc_condition_set,
454                 seq_acl_on,
455                 seq_elvss_on,
456         };
457
458         for (i = 0; i < ARRAY_SIZE(init_seq); i++) {
459                 ret = s6e63m0_panel_send_sequence(lcd, init_seq[i]);
460                 if (ret)
461                         break;
462         }
463
464         return ret;
465 }
466
467 static int s6e63m0_ldi_enable(struct s6e63m0 *lcd)
468 {
469         int ret = 0, i;
470         const unsigned short *enable_seq[] = {
471                 seq_stand_by_off,
472                 seq_display_on,
473         };
474
475         for (i = 0; i < ARRAY_SIZE(enable_seq); i++) {
476                 ret = s6e63m0_panel_send_sequence(lcd, enable_seq[i]);
477                 if (ret)
478                         break;
479         }
480
481         return ret;
482 }
483
484 static int s6e63m0_ldi_disable(struct s6e63m0 *lcd)
485 {
486         int ret;
487
488         ret = s6e63m0_panel_send_sequence(lcd, seq_stand_by_on);
489
490         return ret;
491 }
492
493 static int s6e63m0_power_is_on(int power)
494 {
495         return power <= FB_BLANK_NORMAL;
496 }
497
498 static int s6e63m0_power_on(struct s6e63m0 *lcd)
499 {
500         int ret = 0;
501         struct lcd_platform_data *pd;
502         struct backlight_device *bd;
503
504         pd = lcd->lcd_pd;
505         bd = lcd->bd;
506
507         if (!pd->power_on) {
508                 dev_err(lcd->dev, "power_on is NULL.\n");
509                 return -EINVAL;
510         }
511
512         pd->power_on(lcd->ld, 1);
513         msleep(pd->power_on_delay);
514
515         if (!pd->reset) {
516                 dev_err(lcd->dev, "reset is NULL.\n");
517                 return -EINVAL;
518         }
519
520         pd->reset(lcd->ld);
521         msleep(pd->reset_delay);
522
523         ret = s6e63m0_ldi_init(lcd);
524         if (ret) {
525                 dev_err(lcd->dev, "failed to initialize ldi.\n");
526                 return ret;
527         }
528
529         ret = s6e63m0_ldi_enable(lcd);
530         if (ret) {
531                 dev_err(lcd->dev, "failed to enable ldi.\n");
532                 return ret;
533         }
534
535         /* set brightness to current value after power on or resume. */
536         ret = s6e63m0_gamma_ctl(lcd, bd->props.brightness);
537         if (ret) {
538                 dev_err(lcd->dev, "lcd gamma setting failed.\n");
539                 return ret;
540         }
541
542         return 0;
543 }
544
545 static int s6e63m0_power_off(struct s6e63m0 *lcd)
546 {
547         int ret;
548         struct lcd_platform_data *pd;
549
550         pd = lcd->lcd_pd;
551
552         ret = s6e63m0_ldi_disable(lcd);
553         if (ret) {
554                 dev_err(lcd->dev, "lcd setting failed.\n");
555                 return -EIO;
556         }
557
558         msleep(pd->power_off_delay);
559
560         pd->power_on(lcd->ld, 0);
561
562         return 0;
563 }
564
565 static int s6e63m0_power(struct s6e63m0 *lcd, int power)
566 {
567         int ret = 0;
568
569         if (s6e63m0_power_is_on(power) && !s6e63m0_power_is_on(lcd->power))
570                 ret = s6e63m0_power_on(lcd);
571         else if (!s6e63m0_power_is_on(power) && s6e63m0_power_is_on(lcd->power))
572                 ret = s6e63m0_power_off(lcd);
573
574         if (!ret)
575                 lcd->power = power;
576
577         return ret;
578 }
579
580 static int s6e63m0_set_power(struct lcd_device *ld, int power)
581 {
582         struct s6e63m0 *lcd = lcd_get_data(ld);
583
584         if (power != FB_BLANK_UNBLANK && power != FB_BLANK_POWERDOWN &&
585                 power != FB_BLANK_NORMAL) {
586                 dev_err(lcd->dev, "power value should be 0, 1 or 4.\n");
587                 return -EINVAL;
588         }
589
590         return s6e63m0_power(lcd, power);
591 }
592
593 static int s6e63m0_get_power(struct lcd_device *ld)
594 {
595         struct s6e63m0 *lcd = lcd_get_data(ld);
596
597         return lcd->power;
598 }
599
600 static int s6e63m0_set_brightness(struct backlight_device *bd)
601 {
602         int ret = 0, brightness = bd->props.brightness;
603         struct s6e63m0 *lcd = bl_get_data(bd);
604
605         if (brightness < MIN_BRIGHTNESS ||
606                 brightness > bd->props.max_brightness) {
607                 dev_err(&bd->dev, "lcd brightness should be %d to %d.\n",
608                         MIN_BRIGHTNESS, MAX_BRIGHTNESS);
609                 return -EINVAL;
610         }
611
612         ret = s6e63m0_gamma_ctl(lcd, bd->props.brightness);
613         if (ret) {
614                 dev_err(&bd->dev, "lcd brightness setting failed.\n");
615                 return -EIO;
616         }
617
618         return ret;
619 }
620
621 static struct lcd_ops s6e63m0_lcd_ops = {
622         .set_power = s6e63m0_set_power,
623         .get_power = s6e63m0_get_power,
624 };
625
626 static const struct backlight_ops s6e63m0_backlight_ops  = {
627         .update_status = s6e63m0_set_brightness,
628 };
629
630 static ssize_t s6e63m0_sysfs_show_gamma_mode(struct device *dev,
631                                       struct device_attribute *attr, char *buf)
632 {
633         struct s6e63m0 *lcd = dev_get_drvdata(dev);
634         char temp[10];
635
636         switch (lcd->gamma_mode) {
637         case 0:
638                 sprintf(temp, "2.2 mode\n");
639                 strcat(buf, temp);
640                 break;
641         case 1:
642                 sprintf(temp, "1.9 mode\n");
643                 strcat(buf, temp);
644                 break;
645         case 2:
646                 sprintf(temp, "1.7 mode\n");
647                 strcat(buf, temp);
648                 break;
649         default:
650                 dev_info(dev, "gamma mode could be 0:2.2, 1:1.9 or 2:1.7)n");
651                 break;
652         }
653
654         return strlen(buf);
655 }
656
657 static ssize_t s6e63m0_sysfs_store_gamma_mode(struct device *dev,
658                                        struct device_attribute *attr,
659                                        const char *buf, size_t len)
660 {
661         struct s6e63m0 *lcd = dev_get_drvdata(dev);
662         struct backlight_device *bd = NULL;
663         int brightness, rc;
664
665         rc = kstrtouint(buf, 0, &lcd->gamma_mode);
666         if (rc < 0)
667                 return rc;
668
669         bd = lcd->bd;
670
671         brightness = bd->props.brightness;
672
673         switch (lcd->gamma_mode) {
674         case 0:
675                 _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_22_table[brightness]);
676                 break;
677         case 1:
678                 _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_19_table[brightness]);
679                 break;
680         case 2:
681                 _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_17_table[brightness]);
682                 break;
683         default:
684                 dev_info(dev, "gamma mode could be 0:2.2, 1:1.9 or 2:1.7\n");
685                 _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_22_table[brightness]);
686                 break;
687         }
688         return len;
689 }
690
691 static DEVICE_ATTR(gamma_mode, 0644,
692                 s6e63m0_sysfs_show_gamma_mode, s6e63m0_sysfs_store_gamma_mode);
693
694 static ssize_t s6e63m0_sysfs_show_gamma_table(struct device *dev,
695                                       struct device_attribute *attr, char *buf)
696 {
697         struct s6e63m0 *lcd = dev_get_drvdata(dev);
698         char temp[3];
699
700         sprintf(temp, "%u\n", lcd->gamma_table_count);
701         strcpy(buf, temp);
702
703         return strlen(buf);
704 }
705 static DEVICE_ATTR(gamma_table, 0444,
706                 s6e63m0_sysfs_show_gamma_table, NULL);
707
708 static int s6e63m0_probe(struct spi_device *spi)
709 {
710         int ret = 0;
711         struct s6e63m0 *lcd = NULL;
712         struct lcd_device *ld = NULL;
713         struct backlight_device *bd = NULL;
714         struct backlight_properties props;
715
716         lcd = devm_kzalloc(&spi->dev, sizeof(struct s6e63m0), GFP_KERNEL);
717         if (!lcd)
718                 return -ENOMEM;
719
720         /* s6e63m0 lcd panel uses 3-wire 9bits SPI Mode. */
721         spi->bits_per_word = 9;
722
723         ret = spi_setup(spi);
724         if (ret < 0) {
725                 dev_err(&spi->dev, "spi setup failed.\n");
726                 return ret;
727         }
728
729         lcd->spi = spi;
730         lcd->dev = &spi->dev;
731
732         lcd->lcd_pd = dev_get_platdata(&spi->dev);
733         if (!lcd->lcd_pd) {
734                 dev_err(&spi->dev, "platform data is NULL.\n");
735                 return -EINVAL;
736         }
737
738         ld = devm_lcd_device_register(&spi->dev, "s6e63m0", &spi->dev, lcd,
739                                 &s6e63m0_lcd_ops);
740         if (IS_ERR(ld))
741                 return PTR_ERR(ld);
742
743         lcd->ld = ld;
744
745         memset(&props, 0, sizeof(struct backlight_properties));
746         props.type = BACKLIGHT_RAW;
747         props.max_brightness = MAX_BRIGHTNESS;
748
749         bd = devm_backlight_device_register(&spi->dev, "s6e63m0bl-bl",
750                                         &spi->dev, lcd, &s6e63m0_backlight_ops,
751                                         &props);
752         if (IS_ERR(bd))
753                 return PTR_ERR(bd);
754
755         bd->props.brightness = MAX_BRIGHTNESS;
756         lcd->bd = bd;
757
758         /*
759          * it gets gamma table count available so it gets user
760          * know that.
761          */
762         lcd->gamma_table_count =
763             sizeof(gamma_table) / (MAX_GAMMA_LEVEL * sizeof(int *));
764
765         ret = device_create_file(&(spi->dev), &dev_attr_gamma_mode);
766         if (ret < 0)
767                 dev_err(&(spi->dev), "failed to add sysfs entries\n");
768
769         ret = device_create_file(&(spi->dev), &dev_attr_gamma_table);
770         if (ret < 0)
771                 dev_err(&(spi->dev), "failed to add sysfs entries\n");
772
773         /*
774          * if lcd panel was on from bootloader like u-boot then
775          * do not lcd on.
776          */
777         if (!lcd->lcd_pd->lcd_enabled) {
778                 /*
779                  * if lcd panel was off from bootloader then
780                  * current lcd status is powerdown and then
781                  * it enables lcd panel.
782                  */
783                 lcd->power = FB_BLANK_POWERDOWN;
784
785                 s6e63m0_power(lcd, FB_BLANK_UNBLANK);
786         } else {
787                 lcd->power = FB_BLANK_UNBLANK;
788         }
789
790         spi_set_drvdata(spi, lcd);
791
792         dev_info(&spi->dev, "s6e63m0 panel driver has been probed.\n");
793
794         return 0;
795 }
796
797 static int s6e63m0_remove(struct spi_device *spi)
798 {
799         struct s6e63m0 *lcd = spi_get_drvdata(spi);
800
801         s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
802         device_remove_file(&spi->dev, &dev_attr_gamma_table);
803         device_remove_file(&spi->dev, &dev_attr_gamma_mode);
804
805         return 0;
806 }
807
808 #ifdef CONFIG_PM_SLEEP
809 static int s6e63m0_suspend(struct device *dev)
810 {
811         struct s6e63m0 *lcd = dev_get_drvdata(dev);
812
813         dev_dbg(dev, "lcd->power = %d\n", lcd->power);
814
815         /*
816          * when lcd panel is suspend, lcd panel becomes off
817          * regardless of status.
818          */
819         return s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
820 }
821
822 static int s6e63m0_resume(struct device *dev)
823 {
824         struct s6e63m0 *lcd = dev_get_drvdata(dev);
825
826         lcd->power = FB_BLANK_POWERDOWN;
827
828         return s6e63m0_power(lcd, FB_BLANK_UNBLANK);
829 }
830 #endif
831
832 static SIMPLE_DEV_PM_OPS(s6e63m0_pm_ops, s6e63m0_suspend, s6e63m0_resume);
833
834 /* Power down all displays on reboot, poweroff or halt. */
835 static void s6e63m0_shutdown(struct spi_device *spi)
836 {
837         struct s6e63m0 *lcd = spi_get_drvdata(spi);
838
839         s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
840 }
841
842 static struct spi_driver s6e63m0_driver = {
843         .driver = {
844                 .name   = "s6e63m0",
845                 .owner  = THIS_MODULE,
846                 .pm     = &s6e63m0_pm_ops,
847         },
848         .probe          = s6e63m0_probe,
849         .remove         = s6e63m0_remove,
850         .shutdown       = s6e63m0_shutdown,
851 };
852
853 module_spi_driver(s6e63m0_driver);
854
855 MODULE_AUTHOR("InKi Dae <inki.dae@samsung.com>");
856 MODULE_DESCRIPTION("S6E63M0 LCD Driver");
857 MODULE_LICENSE("GPL");
858