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