backlight: s6e63m0: use sleep instead of delay
[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                         msleep(wbuf[i+1]);
411                 }
412                 i += 2;
413         }
414
415         return ret;
416 }
417
418 static int _s6e63m0_gamma_ctl(struct s6e63m0 *lcd, const unsigned int *gamma)
419 {
420         unsigned int i = 0;
421         int ret = 0;
422
423         /* disable gamma table updating. */
424         ret = s6e63m0_spi_write(lcd, 0xfa, 0x00);
425         if (ret) {
426                 dev_err(lcd->dev, "failed to disable gamma table updating.\n");
427                 goto gamma_err;
428         }
429
430         for (i = 0 ; i < GAMMA_TABLE_COUNT; i++) {
431                 ret = s6e63m0_spi_write(lcd, DATA_ONLY, gamma[i]);
432                 if (ret) {
433                         dev_err(lcd->dev, "failed to set gamma table.\n");
434                         goto gamma_err;
435                 }
436         }
437
438         /* update gamma table. */
439         ret = s6e63m0_spi_write(lcd, 0xfa, 0x01);
440         if (ret)
441                 dev_err(lcd->dev, "failed to update gamma table.\n");
442
443 gamma_err:
444         return ret;
445 }
446
447 static int s6e63m0_gamma_ctl(struct s6e63m0 *lcd, int gamma)
448 {
449         int ret = 0;
450
451         ret = _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_22_table[gamma]);
452
453         return ret;
454 }
455
456
457 static int s6e63m0_ldi_init(struct s6e63m0 *lcd)
458 {
459         int ret, i;
460         const unsigned short *init_seq[] = {
461                 seq_panel_condition_set,
462                 seq_display_condition_set,
463                 seq_gamma_setting,
464                 seq_etc_condition_set,
465                 seq_acl_on,
466                 seq_elvss_on,
467         };
468
469         for (i = 0; i < ARRAY_SIZE(init_seq); i++) {
470                 ret = s6e63m0_panel_send_sequence(lcd, init_seq[i]);
471                 if (ret)
472                         break;
473         }
474
475         return ret;
476 }
477
478 static int s6e63m0_ldi_enable(struct s6e63m0 *lcd)
479 {
480         int ret = 0, i;
481         const unsigned short *enable_seq[] = {
482                 seq_stand_by_off,
483                 seq_display_on,
484         };
485
486         for (i = 0; i < ARRAY_SIZE(enable_seq); i++) {
487                 ret = s6e63m0_panel_send_sequence(lcd, enable_seq[i]);
488                 if (ret)
489                         break;
490         }
491
492         return ret;
493 }
494
495 static int s6e63m0_ldi_disable(struct s6e63m0 *lcd)
496 {
497         int ret;
498
499         ret = s6e63m0_panel_send_sequence(lcd, seq_stand_by_on);
500
501         return ret;
502 }
503
504 static int s6e63m0_power_on(struct s6e63m0 *lcd)
505 {
506         int ret = 0;
507         struct lcd_platform_data *pd = NULL;
508         struct backlight_device *bd = NULL;
509
510         pd = lcd->lcd_pd;
511         if (!pd) {
512                 dev_err(lcd->dev, "platform data is NULL.\n");
513                 return -EFAULT;
514         }
515
516         bd = lcd->bd;
517         if (!bd) {
518                 dev_err(lcd->dev, "backlight device is NULL.\n");
519                 return -EFAULT;
520         }
521
522         if (!pd->power_on) {
523                 dev_err(lcd->dev, "power_on is NULL.\n");
524                 return -EFAULT;
525         } else {
526                 pd->power_on(lcd->ld, 1);
527                 msleep(pd->power_on_delay);
528         }
529
530         if (!pd->reset) {
531                 dev_err(lcd->dev, "reset is NULL.\n");
532                 return -EFAULT;
533         } else {
534                 pd->reset(lcd->ld);
535                 msleep(pd->reset_delay);
536         }
537
538         ret = s6e63m0_ldi_init(lcd);
539         if (ret) {
540                 dev_err(lcd->dev, "failed to initialize ldi.\n");
541                 return ret;
542         }
543
544         ret = s6e63m0_ldi_enable(lcd);
545         if (ret) {
546                 dev_err(lcd->dev, "failed to enable ldi.\n");
547                 return ret;
548         }
549
550         /* set brightness to current value after power on or resume. */
551         ret = s6e63m0_gamma_ctl(lcd, bd->props.brightness);
552         if (ret) {
553                 dev_err(lcd->dev, "lcd gamma setting failed.\n");
554                 return ret;
555         }
556
557         return 0;
558 }
559
560 static int s6e63m0_power_off(struct s6e63m0 *lcd)
561 {
562         int ret = 0;
563         struct lcd_platform_data *pd = NULL;
564
565         pd = lcd->lcd_pd;
566         if (!pd) {
567                 dev_err(lcd->dev, "platform data is NULL.\n");
568                 return -EFAULT;
569         }
570
571         ret = s6e63m0_ldi_disable(lcd);
572         if (ret) {
573                 dev_err(lcd->dev, "lcd setting failed.\n");
574                 return -EIO;
575         }
576
577         msleep(pd->power_off_delay);
578
579         if (!pd->power_on) {
580                 dev_err(lcd->dev, "power_on is NULL.\n");
581                 return -EFAULT;
582         } else
583                 pd->power_on(lcd->ld, 0);
584
585         return 0;
586 }
587
588 static int s6e63m0_power(struct s6e63m0 *lcd, int power)
589 {
590         int ret = 0;
591
592         if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power))
593                 ret = s6e63m0_power_on(lcd);
594         else if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->power))
595                 ret = s6e63m0_power_off(lcd);
596
597         if (!ret)
598                 lcd->power = power;
599
600         return ret;
601 }
602
603 static int s6e63m0_set_power(struct lcd_device *ld, int power)
604 {
605         struct s6e63m0 *lcd = lcd_get_data(ld);
606
607         if (power != FB_BLANK_UNBLANK && power != FB_BLANK_POWERDOWN &&
608                 power != FB_BLANK_NORMAL) {
609                 dev_err(lcd->dev, "power value should be 0, 1 or 4.\n");
610                 return -EINVAL;
611         }
612
613         return s6e63m0_power(lcd, power);
614 }
615
616 static int s6e63m0_get_power(struct lcd_device *ld)
617 {
618         struct s6e63m0 *lcd = lcd_get_data(ld);
619
620         return lcd->power;
621 }
622
623 static int s6e63m0_get_brightness(struct backlight_device *bd)
624 {
625         return bd->props.brightness;
626 }
627
628 static int s6e63m0_set_brightness(struct backlight_device *bd)
629 {
630         int ret = 0, brightness = bd->props.brightness;
631         struct s6e63m0 *lcd = bl_get_data(bd);
632
633         if (brightness < MIN_BRIGHTNESS ||
634                 brightness > bd->props.max_brightness) {
635                 dev_err(&bd->dev, "lcd brightness should be %d to %d.\n",
636                         MIN_BRIGHTNESS, MAX_BRIGHTNESS);
637                 return -EINVAL;
638         }
639
640         ret = s6e63m0_gamma_ctl(lcd, bd->props.brightness);
641         if (ret) {
642                 dev_err(&bd->dev, "lcd brightness setting failed.\n");
643                 return -EIO;
644         }
645
646         return ret;
647 }
648
649 static struct lcd_ops s6e63m0_lcd_ops = {
650         .set_power = s6e63m0_set_power,
651         .get_power = s6e63m0_get_power,
652 };
653
654 static const struct backlight_ops s6e63m0_backlight_ops  = {
655         .get_brightness = s6e63m0_get_brightness,
656         .update_status = s6e63m0_set_brightness,
657 };
658
659 static ssize_t s6e63m0_sysfs_show_gamma_mode(struct device *dev,
660                                       struct device_attribute *attr, char *buf)
661 {
662         struct s6e63m0 *lcd = dev_get_drvdata(dev);
663         char temp[10];
664
665         switch (lcd->gamma_mode) {
666         case 0:
667                 sprintf(temp, "2.2 mode\n");
668                 strcat(buf, temp);
669                 break;
670         case 1:
671                 sprintf(temp, "1.9 mode\n");
672                 strcat(buf, temp);
673                 break;
674         case 2:
675                 sprintf(temp, "1.7 mode\n");
676                 strcat(buf, temp);
677                 break;
678         default:
679                 dev_info(dev, "gamma mode could be 0:2.2, 1:1.9 or 2:1.7)n");
680                 break;
681         }
682
683         return strlen(buf);
684 }
685
686 static ssize_t s6e63m0_sysfs_store_gamma_mode(struct device *dev,
687                                        struct device_attribute *attr,
688                                        const char *buf, size_t len)
689 {
690         struct s6e63m0 *lcd = dev_get_drvdata(dev);
691         struct backlight_device *bd = NULL;
692         int brightness, rc;
693
694         rc = kstrtouint(buf, 0, &lcd->gamma_mode);
695         if (rc < 0)
696                 return rc;
697
698         bd = lcd->bd;
699
700         brightness = bd->props.brightness;
701
702         switch (lcd->gamma_mode) {
703         case 0:
704                 _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_22_table[brightness]);
705                 break;
706         case 1:
707                 _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_19_table[brightness]);
708                 break;
709         case 2:
710                 _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_17_table[brightness]);
711                 break;
712         default:
713                 dev_info(dev, "gamma mode could be 0:2.2, 1:1.9 or 2:1.7\n");
714                 _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_22_table[brightness]);
715                 break;
716         }
717         return len;
718 }
719
720 static DEVICE_ATTR(gamma_mode, 0644,
721                 s6e63m0_sysfs_show_gamma_mode, s6e63m0_sysfs_store_gamma_mode);
722
723 static ssize_t s6e63m0_sysfs_show_gamma_table(struct device *dev,
724                                       struct device_attribute *attr, char *buf)
725 {
726         struct s6e63m0 *lcd = dev_get_drvdata(dev);
727         char temp[3];
728
729         sprintf(temp, "%d\n", lcd->gamma_table_count);
730         strcpy(buf, temp);
731
732         return strlen(buf);
733 }
734 static DEVICE_ATTR(gamma_table, 0444,
735                 s6e63m0_sysfs_show_gamma_table, NULL);
736
737 static int s6e63m0_probe(struct spi_device *spi)
738 {
739         int ret = 0;
740         struct s6e63m0 *lcd = NULL;
741         struct lcd_device *ld = NULL;
742         struct backlight_device *bd = NULL;
743         struct backlight_properties props;
744
745         lcd = devm_kzalloc(&spi->dev, sizeof(struct s6e63m0), GFP_KERNEL);
746         if (!lcd)
747                 return -ENOMEM;
748
749         /* s6e63m0 lcd panel uses 3-wire 9bits SPI Mode. */
750         spi->bits_per_word = 9;
751
752         ret = spi_setup(spi);
753         if (ret < 0) {
754                 dev_err(&spi->dev, "spi setup failed.\n");
755                 return ret;
756         }
757
758         lcd->spi = spi;
759         lcd->dev = &spi->dev;
760
761         lcd->lcd_pd = spi->dev.platform_data;
762         if (!lcd->lcd_pd) {
763                 dev_err(&spi->dev, "platform data is NULL.\n");
764                 return -EFAULT;
765         }
766
767         ld = lcd_device_register("s6e63m0", &spi->dev, lcd, &s6e63m0_lcd_ops);
768         if (IS_ERR(ld))
769                 return PTR_ERR(ld);
770
771         lcd->ld = ld;
772
773         memset(&props, 0, sizeof(struct backlight_properties));
774         props.type = BACKLIGHT_RAW;
775         props.max_brightness = MAX_BRIGHTNESS;
776
777         bd = backlight_device_register("s6e63m0bl-bl", &spi->dev, lcd,
778                 &s6e63m0_backlight_ops, &props);
779         if (IS_ERR(bd)) {
780                 ret =  PTR_ERR(bd);
781                 goto out_lcd_unregister;
782         }
783
784         bd->props.brightness = MAX_BRIGHTNESS;
785         lcd->bd = bd;
786
787         /*
788          * it gets gamma table count available so it gets user
789          * know that.
790          */
791         lcd->gamma_table_count =
792             sizeof(gamma_table) / (MAX_GAMMA_LEVEL * sizeof(int));
793
794         ret = device_create_file(&(spi->dev), &dev_attr_gamma_mode);
795         if (ret < 0)
796                 dev_err(&(spi->dev), "failed to add sysfs entries\n");
797
798         ret = device_create_file(&(spi->dev), &dev_attr_gamma_table);
799         if (ret < 0)
800                 dev_err(&(spi->dev), "failed to add sysfs entries\n");
801
802         /*
803          * if lcd panel was on from bootloader like u-boot then
804          * do not lcd on.
805          */
806         if (!lcd->lcd_pd->lcd_enabled) {
807                 /*
808                  * if lcd panel was off from bootloader then
809                  * current lcd status is powerdown and then
810                  * it enables lcd panel.
811                  */
812                 lcd->power = FB_BLANK_POWERDOWN;
813
814                 s6e63m0_power(lcd, FB_BLANK_UNBLANK);
815         } else
816                 lcd->power = FB_BLANK_UNBLANK;
817
818         dev_set_drvdata(&spi->dev, lcd);
819
820         dev_info(&spi->dev, "s6e63m0 panel driver has been probed.\n");
821
822         return 0;
823
824 out_lcd_unregister:
825         lcd_device_unregister(ld);
826         return ret;
827 }
828
829 static int s6e63m0_remove(struct spi_device *spi)
830 {
831         struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
832
833         s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
834         device_remove_file(&spi->dev, &dev_attr_gamma_table);
835         device_remove_file(&spi->dev, &dev_attr_gamma_mode);
836         backlight_device_unregister(lcd->bd);
837         lcd_device_unregister(lcd->ld);
838
839         return 0;
840 }
841
842 #if defined(CONFIG_PM)
843 static unsigned int before_power;
844
845 static int s6e63m0_suspend(struct spi_device *spi, pm_message_t mesg)
846 {
847         int ret = 0;
848         struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
849
850         dev_dbg(&spi->dev, "lcd->power = %d\n", lcd->power);
851
852         before_power = lcd->power;
853
854         /*
855          * when lcd panel is suspend, lcd panel becomes off
856          * regardless of status.
857          */
858         ret = s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
859
860         return ret;
861 }
862
863 static int s6e63m0_resume(struct spi_device *spi)
864 {
865         int ret = 0;
866         struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
867
868         /*
869          * after suspended, if lcd panel status is FB_BLANK_UNBLANK
870          * (at that time, before_power is FB_BLANK_UNBLANK) then
871          * it changes that status to FB_BLANK_POWERDOWN to get lcd on.
872          */
873         if (before_power == FB_BLANK_UNBLANK)
874                 lcd->power = FB_BLANK_POWERDOWN;
875
876         dev_dbg(&spi->dev, "before_power = %d\n", before_power);
877
878         ret = s6e63m0_power(lcd, before_power);
879
880         return ret;
881 }
882 #else
883 #define s6e63m0_suspend         NULL
884 #define s6e63m0_resume          NULL
885 #endif
886
887 /* Power down all displays on reboot, poweroff or halt. */
888 static void s6e63m0_shutdown(struct spi_device *spi)
889 {
890         struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
891
892         s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
893 }
894
895 static struct spi_driver s6e63m0_driver = {
896         .driver = {
897                 .name   = "s6e63m0",
898                 .owner  = THIS_MODULE,
899         },
900         .probe          = s6e63m0_probe,
901         .remove         = s6e63m0_remove,
902         .shutdown       = s6e63m0_shutdown,
903         .suspend        = s6e63m0_suspend,
904         .resume         = s6e63m0_resume,
905 };
906
907 module_spi_driver(s6e63m0_driver);
908
909 MODULE_AUTHOR("InKi Dae <inki.dae@samsung.com>");
910 MODULE_DESCRIPTION("S6E63M0 LCD Driver");
911 MODULE_LICENSE("GPL");
912