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