GUI: Fix Tomato RAF theme for all builds. Compilation typo.
[tomato.git] / release / src-rt-6.x.4708 / linux / linux-2.6.36 / drivers / video / backlight / s6e63m0.c
blob5265ea5f7a3485f077e49b07c15af6f6591e1cff
1 /*
2 * S6E63M0 AMOLED LCD panel driver.
4 * Author: InKi Dae <inki.dae@samsung.com>
6 * Derived from drivers/video/omap/lcd-apollon.c
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.
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.
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.
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>
34 #include "s6e63m0_gamma.h"
36 #define SLEEPMSEC 0x1000
37 #define ENDDEF 0x2000
38 #define DEFMASK 0xFF00
39 #define COMMAND_ONLY 0xFE
40 #define DATA_ONLY 0xFF
42 #define MIN_BRIGHTNESS 0
43 #define MAX_BRIGHTNESS 10
45 #define POWER_IS_ON(pwr) ((pwr) <= FB_BLANK_NORMAL)
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;
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,
75 ENDDEF, 0x0000
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,
85 0xf7, 0x03,
86 DATA_ONLY, 0x00,
87 DATA_ONLY, 0x00,
89 ENDDEF, 0x0000
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,
116 0xfa, 0x01,
118 ENDDEF, 0x0000
121 static const unsigned short SEQ_ETC_CONDITION_SET[] = {
122 0xf6, 0x00,
123 DATA_ONLY, 0x8c,
124 DATA_ONLY, 0x07,
126 0xb3, 0xc,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
312 0xb2, 0x10,
313 DATA_ONLY, 0x10,
314 DATA_ONLY, 0x0b,
315 DATA_ONLY, 0x05,
317 ENDDEF, 0x0000
320 static const unsigned short SEQ_ACL_ON[] = {
321 /* ACL on */
322 0xc0, 0x01,
324 ENDDEF, 0x0000
327 static const unsigned short SEQ_ACL_OFF[] = {
328 /* ACL off */
329 0xc0, 0x00,
331 ENDDEF, 0x0000
334 static const unsigned short SEQ_ELVSS_ON[] = {
335 /* ELVSS on */
336 0xb1, 0x0b,
338 ENDDEF, 0x0000
341 static const unsigned short SEQ_ELVSS_OFF[] = {
342 /* ELVSS off */
343 0xb1, 0x0a,
345 ENDDEF, 0x0000
348 static const unsigned short SEQ_STAND_BY_OFF[] = {
349 0x11, COMMAND_ONLY,
351 ENDDEF, 0x0000
354 static const unsigned short SEQ_STAND_BY_ON[] = {
355 0x10, COMMAND_ONLY,
357 ENDDEF, 0x0000
360 static const unsigned short SEQ_DISPLAY_ON[] = {
361 0x29, COMMAND_ONLY,
363 ENDDEF, 0x0000
367 static int s6e63m0_spi_write_byte(struct s6e63m0 *lcd, int addr, int data)
369 u16 buf[1];
370 struct spi_message msg;
372 struct spi_transfer xfer = {
373 .len = 2,
374 .tx_buf = buf,
377 buf[0] = (addr << 8) | data;
379 spi_message_init(&msg);
380 spi_message_add_tail(&xfer, &msg);
382 return spi_sync(lcd->spi, &msg);
385 static int s6e63m0_spi_write(struct s6e63m0 *lcd, unsigned char address,
386 unsigned char command)
388 int ret = 0;
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);
395 return ret;
398 static int s6e63m0_panel_send_sequence(struct s6e63m0 *lcd,
399 const unsigned short *wbuf)
401 int ret = 0, i = 0;
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;
413 return ret;
416 static int _s6e63m0_gamma_ctl(struct s6e63m0 *lcd, const unsigned int *gamma)
418 unsigned int i = 0;
419 int ret = 0;
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;
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;
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");
441 gamma_err:
442 return ret;
445 static int s6e63m0_gamma_ctl(struct s6e63m0 *lcd, int gamma)
447 int ret = 0;
449 ret = _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_22_table[gamma]);
451 return ret;
455 static int s6e63m0_ldi_init(struct s6e63m0 *lcd)
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,
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;
473 return ret;
476 static int s6e63m0_ldi_enable(struct s6e63m0 *lcd)
478 int ret = 0, i;
479 const unsigned short *enable_seq[] = {
480 SEQ_STAND_BY_OFF,
481 SEQ_DISPLAY_ON,
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;
490 return ret;
493 static int s6e63m0_ldi_disable(struct s6e63m0 *lcd)
495 int ret;
497 ret = s6e63m0_panel_send_sequence(lcd, SEQ_STAND_BY_ON);
499 return ret;
502 static int s6e63m0_power_on(struct s6e63m0 *lcd)
504 int ret = 0;
505 struct lcd_platform_data *pd = NULL;
506 struct backlight_device *bd = NULL;
508 pd = lcd->lcd_pd;
509 if (!pd) {
510 dev_err(lcd->dev, "platform data is NULL.\n");
511 return -EFAULT;
514 bd = lcd->bd;
515 if (!bd) {
516 dev_err(lcd->dev, "backlight device is NULL.\n");
517 return -EFAULT;
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);
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);
536 ret = s6e63m0_ldi_init(lcd);
537 if (ret) {
538 dev_err(lcd->dev, "failed to initialize ldi.\n");
539 return ret;
542 ret = s6e63m0_ldi_enable(lcd);
543 if (ret) {
544 dev_err(lcd->dev, "failed to enable ldi.\n");
545 return ret;
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;
555 return 0;
558 static int s6e63m0_power_off(struct s6e63m0 *lcd)
560 int ret = 0;
561 struct lcd_platform_data *pd = NULL;
563 pd = lcd->lcd_pd;
564 if (!pd) {
565 dev_err(lcd->dev, "platform data is NULL.\n");
566 return -EFAULT;
569 ret = s6e63m0_ldi_disable(lcd);
570 if (ret) {
571 dev_err(lcd->dev, "lcd setting failed.\n");
572 return -EIO;
575 mdelay(pd->power_off_delay);
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);
583 return 0;
586 static int s6e63m0_power(struct s6e63m0 *lcd, int power)
588 int ret = 0;
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);
595 if (!ret)
596 lcd->power = power;
598 return ret;
601 static int s6e63m0_set_power(struct lcd_device *ld, int power)
603 struct s6e63m0 *lcd = lcd_get_data(ld);
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;
611 return s6e63m0_power(lcd, power);
614 static int s6e63m0_get_power(struct lcd_device *ld)
616 struct s6e63m0 *lcd = lcd_get_data(ld);
618 return lcd->power;
621 static int s6e63m0_get_brightness(struct backlight_device *bd)
623 return bd->props.brightness;
626 static int s6e63m0_set_brightness(struct backlight_device *bd)
628 int ret = 0, brightness = bd->props.brightness;
629 struct s6e63m0 *lcd = bl_get_data(bd);
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;
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;
644 return ret;
647 static struct lcd_ops s6e63m0_lcd_ops = {
648 .set_power = s6e63m0_set_power,
649 .get_power = s6e63m0_get_power,
652 static const struct backlight_ops s6e63m0_backlight_ops = {
653 .get_brightness = s6e63m0_get_brightness,
654 .update_status = s6e63m0_set_brightness,
657 static ssize_t s6e63m0_sysfs_show_gamma_mode(struct device *dev,
658 struct device_attribute *attr, char *buf)
660 struct s6e63m0 *lcd = dev_get_drvdata(dev);
661 char temp[10];
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;
681 return strlen(buf);
684 static ssize_t s6e63m0_sysfs_store_gamma_mode(struct device *dev,
685 struct device_attribute *attr,
686 const char *buf, size_t len)
688 struct s6e63m0 *lcd = dev_get_drvdata(dev);
689 struct backlight_device *bd = NULL;
690 int brightness, rc;
692 rc = strict_strtoul(buf, 0, (unsigned long *)&lcd->gamma_mode);
693 if (rc < 0)
694 return rc;
696 bd = lcd->bd;
698 brightness = bd->props.brightness;
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;
715 return len;
718 static DEVICE_ATTR(gamma_mode, 0644,
719 s6e63m0_sysfs_show_gamma_mode, s6e63m0_sysfs_store_gamma_mode);
721 static ssize_t s6e63m0_sysfs_show_gamma_table(struct device *dev,
722 struct device_attribute *attr, char *buf)
724 struct s6e63m0 *lcd = dev_get_drvdata(dev);
725 char temp[3];
727 sprintf(temp, "%d\n", lcd->gamma_table_count);
728 strcpy(buf, temp);
730 return strlen(buf);
732 static DEVICE_ATTR(gamma_table, 0644,
733 s6e63m0_sysfs_show_gamma_table, NULL);
735 static int __init s6e63m0_probe(struct spi_device *spi)
737 int ret = 0;
738 struct s6e63m0 *lcd = NULL;
739 struct lcd_device *ld = NULL;
740 struct backlight_device *bd = NULL;
742 lcd = kzalloc(sizeof(struct s6e63m0), GFP_KERNEL);
743 if (!lcd)
744 return -ENOMEM;
746 /* s6e63m0 lcd panel uses 3-wire 9bits SPI Mode. */
747 spi->bits_per_word = 9;
749 ret = spi_setup(spi);
750 if (ret < 0) {
751 dev_err(&spi->dev, "spi setup failed.\n");
752 goto out_free_lcd;
755 lcd->spi = spi;
756 lcd->dev = &spi->dev;
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;
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;
770 lcd->ld = ld;
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;
779 bd->props.max_brightness = MAX_BRIGHTNESS;
780 bd->props.brightness = MAX_BRIGHTNESS;
781 lcd->bd = bd;
784 * it gets gamma table count available so it gets user
785 * know that.
787 lcd->gamma_table_count =
788 sizeof(gamma_table) / (MAX_GAMMA_LEVEL * sizeof(int));
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");
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");
799 * if lcd panel was on from bootloader like u-boot then
800 * do not lcd on.
802 if (!lcd->lcd_pd->lcd_enabled) {
804 * if lcd panel was off from bootloader then
805 * current lcd status is powerdown and then
806 * it enables lcd panel.
808 lcd->power = FB_BLANK_POWERDOWN;
810 s6e63m0_power(lcd, FB_BLANK_UNBLANK);
811 } else
812 lcd->power = FB_BLANK_UNBLANK;
814 dev_set_drvdata(&spi->dev, lcd);
816 dev_info(&spi->dev, "s6e63m0 panel driver has been probed.\n");
818 return 0;
820 out_lcd_unregister:
821 lcd_device_unregister(ld);
822 out_free_lcd:
823 kfree(lcd);
824 return ret;
827 static int __devexit s6e63m0_remove(struct spi_device *spi)
829 struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
831 s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
832 lcd_device_unregister(lcd->ld);
833 kfree(lcd);
835 return 0;
838 #if defined(CONFIG_PM)
839 unsigned int before_power;
841 static int s6e63m0_suspend(struct spi_device *spi, pm_message_t mesg)
843 int ret = 0;
844 struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
846 dev_dbg(&spi->dev, "lcd->power = %d\n", lcd->power);
848 before_power = lcd->power;
851 * when lcd panel is suspend, lcd panel becomes off
852 * regardless of status.
854 ret = s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
856 return ret;
859 static int s6e63m0_resume(struct spi_device *spi)
861 int ret = 0;
862 struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
865 * after suspended, if lcd panel status is FB_BLANK_UNBLANK
866 * (at that time, before_power is FB_BLANK_UNBLANK) then
867 * it changes that status to FB_BLANK_POWERDOWN to get lcd on.
869 if (before_power == FB_BLANK_UNBLANK)
870 lcd->power = FB_BLANK_POWERDOWN;
872 dev_dbg(&spi->dev, "before_power = %d\n", before_power);
874 ret = s6e63m0_power(lcd, before_power);
876 return ret;
878 #else
879 #define s6e63m0_suspend NULL
880 #define s6e63m0_resume NULL
881 #endif
883 /* Power down all displays on reboot, poweroff or halt. */
884 static void s6e63m0_shutdown(struct spi_device *spi)
886 struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
888 s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
891 static struct spi_driver s6e63m0_driver = {
892 .driver = {
893 .name = "s6e63m0",
894 .bus = &spi_bus_type,
895 .owner = THIS_MODULE,
897 .probe = s6e63m0_probe,
898 .remove = __devexit_p(s6e63m0_remove),
899 .shutdown = s6e63m0_shutdown,
900 .suspend = s6e63m0_suspend,
901 .resume = s6e63m0_resume,
904 static int __init s6e63m0_init(void)
906 return spi_register_driver(&s6e63m0_driver);
909 static void __exit s6e63m0_exit(void)
911 spi_unregister_driver(&s6e63m0_driver);
914 module_init(s6e63m0_init);
915 module_exit(s6e63m0_exit);
917 MODULE_AUTHOR("InKi Dae <inki.dae@samsung.com>");
918 MODULE_DESCRIPTION("S6E63M0 LCD Driver");
919 MODULE_LICENSE("GPL");