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