Linux-libre 3.4.28-gnu1
[librecmc/linux-libre.git] / drivers / video / backlight / ld9040.c
1 /*
2  * ld9040 AMOLED LCD panel driver.
3  *
4  * Copyright (c) 2011 Samsung Electronics
5  * Author: Donghwa Lee  <dh09.lee@samsung.com>
6  * Derived from drivers/video/backlight/s6e63m0.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 #include <linux/regulator/consumer.h>
35
36 #include "ld9040_gamma.h"
37
38 #define SLEEPMSEC               0x1000
39 #define ENDDEF                  0x2000
40 #define DEFMASK                 0xFF00
41 #define COMMAND_ONLY            0xFE
42 #define DATA_ONLY               0xFF
43
44 #define MIN_BRIGHTNESS          0
45 #define MAX_BRIGHTNESS          24
46 #define power_is_on(pwr)        ((pwr) <= FB_BLANK_NORMAL)
47
48 struct ld9040 {
49         struct device                   *dev;
50         struct spi_device               *spi;
51         unsigned int                    power;
52         unsigned int                    current_brightness;
53
54         struct lcd_device               *ld;
55         struct backlight_device         *bd;
56         struct lcd_platform_data        *lcd_pd;
57
58         struct mutex                    lock;
59         bool  enabled;
60 };
61
62 static struct regulator_bulk_data supplies[] = {
63         { .supply = "vdd3", },
64         { .supply = "vci", },
65 };
66
67 static void ld9040_regulator_enable(struct ld9040 *lcd)
68 {
69         int ret = 0;
70         struct lcd_platform_data *pd = NULL;
71
72         pd = lcd->lcd_pd;
73         mutex_lock(&lcd->lock);
74         if (!lcd->enabled) {
75                 ret = regulator_bulk_enable(ARRAY_SIZE(supplies), supplies);
76                 if (ret)
77                         goto out;
78
79                 lcd->enabled = true;
80         }
81         mdelay(pd->power_on_delay);
82 out:
83         mutex_unlock(&lcd->lock);
84 }
85
86 static void ld9040_regulator_disable(struct ld9040 *lcd)
87 {
88         int ret = 0;
89
90         mutex_lock(&lcd->lock);
91         if (lcd->enabled) {
92                 ret = regulator_bulk_disable(ARRAY_SIZE(supplies), supplies);
93                 if (ret)
94                         goto out;
95
96                 lcd->enabled = false;
97         }
98 out:
99         mutex_unlock(&lcd->lock);
100 }
101
102 static const unsigned short seq_swreset[] = {
103         0x01, COMMAND_ONLY,
104         ENDDEF, 0x00
105 };
106
107 static const unsigned short seq_user_setting[] = {
108         0xF0, 0x5A,
109
110         DATA_ONLY, 0x5A,
111         ENDDEF, 0x00
112 };
113
114 static const unsigned short seq_elvss_on[] = {
115         0xB1, 0x0D,
116
117         DATA_ONLY, 0x00,
118         DATA_ONLY, 0x16,
119         ENDDEF, 0x00
120 };
121
122 static const unsigned short seq_gtcon[] = {
123         0xF7, 0x09,
124
125         DATA_ONLY, 0x00,
126         DATA_ONLY, 0x00,
127         ENDDEF, 0x00
128 };
129
130 static const unsigned short seq_panel_condition[] = {
131         0xF8, 0x05,
132
133         DATA_ONLY, 0x65,
134         DATA_ONLY, 0x96,
135         DATA_ONLY, 0x71,
136         DATA_ONLY, 0x7D,
137         DATA_ONLY, 0x19,
138         DATA_ONLY, 0x3B,
139         DATA_ONLY, 0x0D,
140         DATA_ONLY, 0x19,
141         DATA_ONLY, 0x7E,
142         DATA_ONLY, 0x0D,
143         DATA_ONLY, 0xE2,
144         DATA_ONLY, 0x00,
145         DATA_ONLY, 0x00,
146         DATA_ONLY, 0x7E,
147         DATA_ONLY, 0x7D,
148         DATA_ONLY, 0x07,
149         DATA_ONLY, 0x07,
150         DATA_ONLY, 0x20,
151         DATA_ONLY, 0x20,
152         DATA_ONLY, 0x20,
153         DATA_ONLY, 0x02,
154         DATA_ONLY, 0x02,
155         ENDDEF, 0x00
156 };
157
158 static const unsigned short seq_gamma_set1[] = {
159         0xF9, 0x00,
160
161         DATA_ONLY, 0xA7,
162         DATA_ONLY, 0xB4,
163         DATA_ONLY, 0xAE,
164         DATA_ONLY, 0xBF,
165         DATA_ONLY, 0x00,
166         DATA_ONLY, 0x91,
167         DATA_ONLY, 0x00,
168         DATA_ONLY, 0xB2,
169         DATA_ONLY, 0xB4,
170         DATA_ONLY, 0xAA,
171         DATA_ONLY, 0xBB,
172         DATA_ONLY, 0x00,
173         DATA_ONLY, 0xAC,
174         DATA_ONLY, 0x00,
175         DATA_ONLY, 0xB3,
176         DATA_ONLY, 0xB1,
177         DATA_ONLY, 0xAA,
178         DATA_ONLY, 0xBC,
179         DATA_ONLY, 0x00,
180         DATA_ONLY, 0xB3,
181         ENDDEF, 0x00
182 };
183
184 static const unsigned short seq_gamma_ctrl[] = {
185         0xFB, 0x02,
186
187         DATA_ONLY, 0x5A,
188         ENDDEF, 0x00
189 };
190
191 static const unsigned short seq_gamma_start[] = {
192         0xF9, COMMAND_ONLY,
193
194         ENDDEF, 0x00
195 };
196
197 static const unsigned short seq_apon[] = {
198         0xF3, 0x00,
199
200         DATA_ONLY, 0x00,
201         DATA_ONLY, 0x00,
202         DATA_ONLY, 0x0A,
203         DATA_ONLY, 0x02,
204         ENDDEF, 0x00
205 };
206
207 static const unsigned short seq_display_ctrl[] = {
208         0xF2, 0x02,
209
210         DATA_ONLY, 0x08,
211         DATA_ONLY, 0x08,
212         DATA_ONLY, 0x10,
213         DATA_ONLY, 0x10,
214         ENDDEF, 0x00
215 };
216
217 static const unsigned short seq_manual_pwr[] = {
218         0xB0, 0x04,
219         ENDDEF, 0x00
220 };
221
222 static const unsigned short seq_pwr_ctrl[] = {
223         0xF4, 0x0A,
224
225         DATA_ONLY, 0x87,
226         DATA_ONLY, 0x25,
227         DATA_ONLY, 0x6A,
228         DATA_ONLY, 0x44,
229         DATA_ONLY, 0x02,
230         DATA_ONLY, 0x88,
231         ENDDEF, 0x00
232 };
233
234 static const unsigned short seq_sleep_out[] = {
235         0x11, COMMAND_ONLY,
236         ENDDEF, 0x00
237 };
238
239 static const unsigned short seq_sleep_in[] = {
240         0x10, COMMAND_ONLY,
241         ENDDEF, 0x00
242 };
243
244 static const unsigned short seq_display_on[] = {
245         0x29, COMMAND_ONLY,
246         ENDDEF, 0x00
247 };
248
249 static const unsigned short seq_display_off[] = {
250         0x28, COMMAND_ONLY,
251         ENDDEF, 0x00
252 };
253
254 static const unsigned short seq_vci1_1st_en[] = {
255         0xF3, 0x10,
256
257         DATA_ONLY, 0x00,
258         DATA_ONLY, 0x00,
259         DATA_ONLY, 0x00,
260         DATA_ONLY, 0x02,
261         ENDDEF, 0x00
262 };
263
264 static const unsigned short seq_vl1_en[] = {
265         0xF3, 0x11,
266
267         DATA_ONLY, 0x00,
268         DATA_ONLY, 0x00,
269         DATA_ONLY, 0x00,
270         DATA_ONLY, 0x02,
271         ENDDEF, 0x00
272 };
273
274 static const unsigned short seq_vl2_en[] = {
275         0xF3, 0x13,
276
277         DATA_ONLY, 0x00,
278         DATA_ONLY, 0x00,
279         DATA_ONLY, 0x00,
280         DATA_ONLY, 0x02,
281         ENDDEF, 0x00
282 };
283
284 static const unsigned short seq_vci1_2nd_en[] = {
285         0xF3, 0x33,
286
287         DATA_ONLY, 0x00,
288         DATA_ONLY, 0x00,
289         DATA_ONLY, 0x00,
290         DATA_ONLY, 0x02,
291         ENDDEF, 0x00
292 };
293
294 static const unsigned short seq_vl3_en[] = {
295         0xF3, 0x37,
296
297         DATA_ONLY, 0x00,
298         DATA_ONLY, 0x00,
299         DATA_ONLY, 0x00,
300         DATA_ONLY, 0x02,
301         ENDDEF, 0x00
302 };
303
304 static const unsigned short seq_vreg1_amp_en[] = {
305         0xF3, 0x37,
306
307         DATA_ONLY, 0x01,
308         DATA_ONLY, 0x00,
309         DATA_ONLY, 0x00,
310         DATA_ONLY, 0x02,
311         ENDDEF, 0x00
312 };
313
314 static const unsigned short seq_vgh_amp_en[] = {
315         0xF3, 0x37,
316
317         DATA_ONLY, 0x11,
318         DATA_ONLY, 0x00,
319         DATA_ONLY, 0x00,
320         DATA_ONLY, 0x02,
321         ENDDEF, 0x00
322 };
323
324 static const unsigned short seq_vgl_amp_en[] = {
325         0xF3, 0x37,
326
327         DATA_ONLY, 0x31,
328         DATA_ONLY, 0x00,
329         DATA_ONLY, 0x00,
330         DATA_ONLY, 0x02,
331         ENDDEF, 0x00
332 };
333
334 static const unsigned short seq_vmos_amp_en[] = {
335         0xF3, 0x37,
336
337         DATA_ONLY, 0xB1,
338         DATA_ONLY, 0x00,
339         DATA_ONLY, 0x00,
340         DATA_ONLY, 0x03,
341         ENDDEF, 0x00
342 };
343
344 static const unsigned short seq_vint_amp_en[] = {
345         0xF3, 0x37,
346
347         DATA_ONLY, 0xF1,
348         /* DATA_ONLY, 0x71,     VMOS/VBL/VBH not used */
349         DATA_ONLY, 0x00,
350         DATA_ONLY, 0x00,
351         DATA_ONLY, 0x03,
352         /* DATA_ONLY, 0x02,     VMOS/VBL/VBH not used */
353         ENDDEF, 0x00
354 };
355
356 static const unsigned short seq_vbh_amp_en[] = {
357         0xF3, 0x37,
358
359         DATA_ONLY, 0xF9,
360         DATA_ONLY, 0x00,
361         DATA_ONLY, 0x00,
362         DATA_ONLY, 0x03,
363         ENDDEF, 0x00
364 };
365
366 static const unsigned short seq_vbl_amp_en[] = {
367         0xF3, 0x37,
368
369         DATA_ONLY, 0xFD,
370         DATA_ONLY, 0x00,
371         DATA_ONLY, 0x00,
372         DATA_ONLY, 0x03,
373         ENDDEF, 0x00
374 };
375
376 static const unsigned short seq_gam_amp_en[] = {
377         0xF3, 0x37,
378
379         DATA_ONLY, 0xFF,
380         /* DATA_ONLY, 0x73,     VMOS/VBL/VBH not used */
381         DATA_ONLY, 0x00,
382         DATA_ONLY, 0x00,
383         DATA_ONLY, 0x03,
384         /* DATA_ONLY, 0x02,     VMOS/VBL/VBH not used */
385         ENDDEF, 0x00
386 };
387
388 static const unsigned short seq_sd_amp_en[] = {
389         0xF3, 0x37,
390
391         DATA_ONLY, 0xFF,
392         /* DATA_ONLY, 0x73,     VMOS/VBL/VBH not used */
393         DATA_ONLY, 0x80,
394         DATA_ONLY, 0x00,
395         DATA_ONLY, 0x03,
396         /* DATA_ONLY, 0x02,     VMOS/VBL/VBH not used */
397         ENDDEF, 0x00
398 };
399
400 static const unsigned short seq_gls_en[] = {
401         0xF3, 0x37,
402
403         DATA_ONLY, 0xFF,
404         /* DATA_ONLY, 0x73,     VMOS/VBL/VBH not used */
405         DATA_ONLY, 0x81,
406         DATA_ONLY, 0x00,
407         DATA_ONLY, 0x03,
408         /* DATA_ONLY, 0x02,     VMOS/VBL/VBH not used */
409         ENDDEF, 0x00
410 };
411
412 static const unsigned short seq_els_en[] = {
413         0xF3, 0x37,
414
415         DATA_ONLY, 0xFF,
416         /* DATA_ONLY, 0x73,     VMOS/VBL/VBH not used */
417         DATA_ONLY, 0x83,
418         DATA_ONLY, 0x00,
419         DATA_ONLY, 0x03,
420         /* DATA_ONLY, 0x02,     VMOS/VBL/VBH not used */
421         ENDDEF, 0x00
422 };
423
424 static const unsigned short seq_el_on[] = {
425         0xF3, 0x37,
426
427         DATA_ONLY, 0xFF,
428         /* DATA_ONLY, 0x73,     VMOS/VBL/VBH not used */
429         DATA_ONLY, 0x87,
430         DATA_ONLY, 0x00,
431         DATA_ONLY, 0x03,
432         /* DATA_ONLY, 0x02,     VMOS/VBL/VBH not used */
433         ENDDEF, 0x00
434 };
435
436 static int ld9040_spi_write_byte(struct ld9040 *lcd, int addr, int data)
437 {
438         u16 buf[1];
439         struct spi_message msg;
440
441         struct spi_transfer xfer = {
442                 .len            = 2,
443                 .tx_buf         = buf,
444         };
445
446         buf[0] = (addr << 8) | data;
447
448         spi_message_init(&msg);
449         spi_message_add_tail(&xfer, &msg);
450
451         return spi_sync(lcd->spi, &msg);
452 }
453
454 static int ld9040_spi_write(struct ld9040 *lcd, unsigned char address,
455         unsigned char command)
456 {
457         int ret = 0;
458
459         if (address != DATA_ONLY)
460                 ret = ld9040_spi_write_byte(lcd, 0x0, address);
461         if (command != COMMAND_ONLY)
462                 ret = ld9040_spi_write_byte(lcd, 0x1, command);
463
464         return ret;
465 }
466
467 static int ld9040_panel_send_sequence(struct ld9040 *lcd,
468         const unsigned short *wbuf)
469 {
470         int ret = 0, i = 0;
471
472         while ((wbuf[i] & DEFMASK) != ENDDEF) {
473                 if ((wbuf[i] & DEFMASK) != SLEEPMSEC) {
474                         ret = ld9040_spi_write(lcd, wbuf[i], wbuf[i+1]);
475                         if (ret)
476                                 break;
477                 } else
478                         udelay(wbuf[i+1]*1000);
479                 i += 2;
480         }
481
482         return ret;
483 }
484
485 static int _ld9040_gamma_ctl(struct ld9040 *lcd, const unsigned int *gamma)
486 {
487         unsigned int i = 0;
488         int ret = 0;
489
490         /* start gamma table updating. */
491         ret = ld9040_panel_send_sequence(lcd, seq_gamma_start);
492         if (ret) {
493                 dev_err(lcd->dev, "failed to disable gamma table updating.\n");
494                 goto gamma_err;
495         }
496
497         for (i = 0 ; i < GAMMA_TABLE_COUNT; i++) {
498                 ret = ld9040_spi_write(lcd, DATA_ONLY, gamma[i]);
499                 if (ret) {
500                         dev_err(lcd->dev, "failed to set gamma table.\n");
501                         goto gamma_err;
502                 }
503         }
504
505         /* update gamma table. */
506         ret = ld9040_panel_send_sequence(lcd, seq_gamma_ctrl);
507         if (ret)
508                 dev_err(lcd->dev, "failed to update gamma table.\n");
509
510 gamma_err:
511         return ret;
512 }
513
514 static int ld9040_gamma_ctl(struct ld9040 *lcd, int gamma)
515 {
516         int ret = 0;
517
518         ret = _ld9040_gamma_ctl(lcd, gamma_table.gamma_22_table[gamma]);
519
520         return ret;
521 }
522
523
524 static int ld9040_ldi_init(struct ld9040 *lcd)
525 {
526         int ret, i;
527         static const unsigned short *init_seq[] = {
528                 seq_user_setting,
529                 seq_panel_condition,
530                 seq_display_ctrl,
531                 seq_manual_pwr,
532                 seq_elvss_on,
533                 seq_gtcon,
534                 seq_gamma_set1,
535                 seq_gamma_ctrl,
536                 seq_sleep_out,
537         };
538
539         for (i = 0; i < ARRAY_SIZE(init_seq); i++) {
540                 ret = ld9040_panel_send_sequence(lcd, init_seq[i]);
541                 /* workaround: minimum delay time for transferring CMD */
542                 udelay(300);
543                 if (ret)
544                         break;
545         }
546
547         return ret;
548 }
549
550 static int ld9040_ldi_enable(struct ld9040 *lcd)
551 {
552         int ret = 0;
553
554         ret = ld9040_panel_send_sequence(lcd, seq_display_on);
555
556         return ret;
557 }
558
559 static int ld9040_ldi_disable(struct ld9040 *lcd)
560 {
561         int ret;
562
563         ret = ld9040_panel_send_sequence(lcd, seq_display_off);
564         ret = ld9040_panel_send_sequence(lcd, seq_sleep_in);
565
566         return ret;
567 }
568
569 static int ld9040_power_on(struct ld9040 *lcd)
570 {
571         int ret = 0;
572         struct lcd_platform_data *pd = NULL;
573         pd = lcd->lcd_pd;
574         if (!pd) {
575                 dev_err(lcd->dev, "platform data is NULL.\n");
576                 return -EFAULT;
577         }
578
579         /* lcd power on */
580         ld9040_regulator_enable(lcd);
581
582         if (!pd->reset) {
583                 dev_err(lcd->dev, "reset is NULL.\n");
584                 return -EFAULT;
585         } else {
586                 pd->reset(lcd->ld);
587                 mdelay(pd->reset_delay);
588         }
589
590         ret = ld9040_ldi_init(lcd);
591         if (ret) {
592                 dev_err(lcd->dev, "failed to initialize ldi.\n");
593                 return ret;
594         }
595
596         ret = ld9040_ldi_enable(lcd);
597         if (ret) {
598                 dev_err(lcd->dev, "failed to enable ldi.\n");
599                 return ret;
600         }
601
602         return 0;
603 }
604
605 static int ld9040_power_off(struct ld9040 *lcd)
606 {
607         int ret = 0;
608         struct lcd_platform_data *pd = NULL;
609
610         pd = lcd->lcd_pd;
611         if (!pd) {
612                 dev_err(lcd->dev, "platform data is NULL.\n");
613                 return -EFAULT;
614         }
615
616         ret = ld9040_ldi_disable(lcd);
617         if (ret) {
618                 dev_err(lcd->dev, "lcd setting failed.\n");
619                 return -EIO;
620         }
621
622         mdelay(pd->power_off_delay);
623
624         /* lcd power off */
625         ld9040_regulator_disable(lcd);
626
627         return 0;
628 }
629
630 static int ld9040_power(struct ld9040 *lcd, int power)
631 {
632         int ret = 0;
633
634         if (power_is_on(power) && !power_is_on(lcd->power))
635                 ret = ld9040_power_on(lcd);
636         else if (!power_is_on(power) && power_is_on(lcd->power))
637                 ret = ld9040_power_off(lcd);
638
639         if (!ret)
640                 lcd->power = power;
641
642         return ret;
643 }
644
645 static int ld9040_set_power(struct lcd_device *ld, int power)
646 {
647         struct ld9040 *lcd = lcd_get_data(ld);
648
649         if (power != FB_BLANK_UNBLANK && power != FB_BLANK_POWERDOWN &&
650                 power != FB_BLANK_NORMAL) {
651                 dev_err(lcd->dev, "power value should be 0, 1 or 4.\n");
652                 return -EINVAL;
653         }
654
655         return ld9040_power(lcd, power);
656 }
657
658 static int ld9040_get_power(struct lcd_device *ld)
659 {
660         struct ld9040 *lcd = lcd_get_data(ld);
661
662         return lcd->power;
663 }
664
665 static int ld9040_get_brightness(struct backlight_device *bd)
666 {
667         return bd->props.brightness;
668 }
669
670 static int ld9040_set_brightness(struct backlight_device *bd)
671 {
672         int ret = 0, brightness = bd->props.brightness;
673         struct ld9040 *lcd = bl_get_data(bd);
674
675         if (brightness < MIN_BRIGHTNESS ||
676                 brightness > bd->props.max_brightness) {
677                 dev_err(&bd->dev, "lcd brightness should be %d to %d.\n",
678                         MIN_BRIGHTNESS, MAX_BRIGHTNESS);
679                 return -EINVAL;
680         }
681
682         ret = ld9040_gamma_ctl(lcd, bd->props.brightness);
683         if (ret) {
684                 dev_err(&bd->dev, "lcd brightness setting failed.\n");
685                 return -EIO;
686         }
687
688         return ret;
689 }
690
691 static struct lcd_ops ld9040_lcd_ops = {
692         .set_power = ld9040_set_power,
693         .get_power = ld9040_get_power,
694 };
695
696 static const struct backlight_ops ld9040_backlight_ops  = {
697         .get_brightness = ld9040_get_brightness,
698         .update_status = ld9040_set_brightness,
699 };
700
701
702 static int ld9040_probe(struct spi_device *spi)
703 {
704         int ret = 0;
705         struct ld9040 *lcd = NULL;
706         struct lcd_device *ld = NULL;
707         struct backlight_device *bd = NULL;
708         struct backlight_properties props;
709
710         lcd = kzalloc(sizeof(struct ld9040), GFP_KERNEL);
711         if (!lcd)
712                 return -ENOMEM;
713
714         /* ld9040 lcd panel uses 3-wire 9bits SPI Mode. */
715         spi->bits_per_word = 9;
716
717         ret = spi_setup(spi);
718         if (ret < 0) {
719                 dev_err(&spi->dev, "spi setup failed.\n");
720                 goto out_free_lcd;
721         }
722
723         lcd->spi = spi;
724         lcd->dev = &spi->dev;
725
726         lcd->lcd_pd = spi->dev.platform_data;
727         if (!lcd->lcd_pd) {
728                 dev_err(&spi->dev, "platform data is NULL.\n");
729                 goto out_free_lcd;
730         }
731
732         mutex_init(&lcd->lock);
733
734         ret = regulator_bulk_get(lcd->dev, ARRAY_SIZE(supplies), supplies);
735         if (ret) {
736                 dev_err(lcd->dev, "Failed to get regulators: %d\n", ret);
737                 goto out_free_lcd;
738         }
739
740         ld = lcd_device_register("ld9040", &spi->dev, lcd, &ld9040_lcd_ops);
741         if (IS_ERR(ld)) {
742                 ret = PTR_ERR(ld);
743                 goto out_free_lcd;
744         }
745
746         lcd->ld = ld;
747
748         memset(&props, 0, sizeof(struct backlight_properties));
749         props.type = BACKLIGHT_RAW;
750         props.max_brightness = MAX_BRIGHTNESS;
751
752         bd = backlight_device_register("ld9040-bl", &spi->dev,
753                 lcd, &ld9040_backlight_ops, &props);
754         if (IS_ERR(bd)) {
755                 ret = PTR_ERR(bd);
756                 goto out_unregister_lcd;
757         }
758
759         bd->props.brightness = MAX_BRIGHTNESS;
760         lcd->bd = bd;
761
762         /*
763          * if lcd panel was on from bootloader like u-boot then
764          * do not lcd on.
765          */
766         if (!lcd->lcd_pd->lcd_enabled) {
767                 /*
768                  * if lcd panel was off from bootloader then
769                  * current lcd status is powerdown and then
770                  * it enables lcd panel.
771                  */
772                 lcd->power = FB_BLANK_POWERDOWN;
773
774                 ld9040_power(lcd, FB_BLANK_UNBLANK);
775         } else
776                 lcd->power = FB_BLANK_UNBLANK;
777
778         dev_set_drvdata(&spi->dev, lcd);
779
780         dev_info(&spi->dev, "ld9040 panel driver has been probed.\n");
781         return 0;
782
783 out_unregister_lcd:
784         lcd_device_unregister(lcd->ld);
785 out_free_lcd:
786         regulator_bulk_free(ARRAY_SIZE(supplies), supplies);
787
788         kfree(lcd);
789         return ret;
790 }
791
792 static int __devexit ld9040_remove(struct spi_device *spi)
793 {
794         struct ld9040 *lcd = dev_get_drvdata(&spi->dev);
795
796         ld9040_power(lcd, FB_BLANK_POWERDOWN);
797         backlight_device_unregister(lcd->bd);
798         lcd_device_unregister(lcd->ld);
799         regulator_bulk_free(ARRAY_SIZE(supplies), supplies);
800         kfree(lcd);
801
802         return 0;
803 }
804
805 #if defined(CONFIG_PM)
806 static int ld9040_suspend(struct spi_device *spi, pm_message_t mesg)
807 {
808         int ret = 0;
809         struct ld9040 *lcd = dev_get_drvdata(&spi->dev);
810
811         dev_dbg(&spi->dev, "lcd->power = %d\n", lcd->power);
812
813         /*
814          * when lcd panel is suspend, lcd panel becomes off
815          * regardless of status.
816          */
817         ret = ld9040_power(lcd, FB_BLANK_POWERDOWN);
818
819         return ret;
820 }
821
822 static int ld9040_resume(struct spi_device *spi)
823 {
824         int ret = 0;
825         struct ld9040 *lcd = dev_get_drvdata(&spi->dev);
826
827         lcd->power = FB_BLANK_POWERDOWN;
828
829         ret = ld9040_power(lcd, FB_BLANK_UNBLANK);
830
831         return ret;
832 }
833 #else
834 #define ld9040_suspend          NULL
835 #define ld9040_resume           NULL
836 #endif
837
838 /* Power down all displays on reboot, poweroff or halt. */
839 static void ld9040_shutdown(struct spi_device *spi)
840 {
841         struct ld9040 *lcd = dev_get_drvdata(&spi->dev);
842
843         ld9040_power(lcd, FB_BLANK_POWERDOWN);
844 }
845
846 static struct spi_driver ld9040_driver = {
847         .driver = {
848                 .name   = "ld9040",
849                 .bus    = &spi_bus_type,
850                 .owner  = THIS_MODULE,
851         },
852         .probe          = ld9040_probe,
853         .remove         = __devexit_p(ld9040_remove),
854         .shutdown       = ld9040_shutdown,
855         .suspend        = ld9040_suspend,
856         .resume         = ld9040_resume,
857 };
858
859 module_spi_driver(ld9040_driver);
860
861 MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>");
862 MODULE_DESCRIPTION("ld9040 LCD Driver");
863 MODULE_LICENSE("GPL");