sh_eth: fix EESIPR values for SH77{34|63}
[linux/fpc-iii.git] / drivers / video / backlight / ld9040.c
blob677f8abba27c9765bd10a2d1e7816d890369a03f
1 /*
2 * ld9040 AMOLED LCD panel driver.
4 * Copyright (c) 2011 Samsung Electronics
5 * Author: Donghwa Lee <dh09.lee@samsung.com>
6 * Derived from drivers/video/backlight/s6e63m0.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.
14 #include <linux/backlight.h>
15 #include <linux/delay.h>
16 #include <linux/fb.h>
17 #include <linux/gpio.h>
18 #include <linux/interrupt.h>
19 #include <linux/irq.h>
20 #include <linux/kernel.h>
21 #include <linux/lcd.h>
22 #include <linux/module.h>
23 #include <linux/regulator/consumer.h>
24 #include <linux/spi/spi.h>
25 #include <linux/wait.h>
27 #include "ld9040_gamma.h"
29 #define SLEEPMSEC 0x1000
30 #define ENDDEF 0x2000
31 #define DEFMASK 0xFF00
32 #define COMMAND_ONLY 0xFE
33 #define DATA_ONLY 0xFF
35 #define MIN_BRIGHTNESS 0
36 #define MAX_BRIGHTNESS 24
38 struct ld9040 {
39 struct device *dev;
40 struct spi_device *spi;
41 unsigned int power;
42 unsigned int current_brightness;
44 struct lcd_device *ld;
45 struct backlight_device *bd;
46 struct lcd_platform_data *lcd_pd;
48 struct mutex lock;
49 bool enabled;
52 static struct regulator_bulk_data supplies[] = {
53 { .supply = "vdd3", },
54 { .supply = "vci", },
57 static void ld9040_regulator_enable(struct ld9040 *lcd)
59 int ret = 0;
60 struct lcd_platform_data *pd = NULL;
62 pd = lcd->lcd_pd;
63 mutex_lock(&lcd->lock);
64 if (!lcd->enabled) {
65 ret = regulator_bulk_enable(ARRAY_SIZE(supplies), supplies);
66 if (ret)
67 goto out;
69 lcd->enabled = true;
71 msleep(pd->power_on_delay);
72 out:
73 mutex_unlock(&lcd->lock);
76 static void ld9040_regulator_disable(struct ld9040 *lcd)
78 int ret = 0;
80 mutex_lock(&lcd->lock);
81 if (lcd->enabled) {
82 ret = regulator_bulk_disable(ARRAY_SIZE(supplies), supplies);
83 if (ret)
84 goto out;
86 lcd->enabled = false;
88 out:
89 mutex_unlock(&lcd->lock);
92 static const unsigned short seq_swreset[] = {
93 0x01, COMMAND_ONLY,
94 ENDDEF, 0x00
97 static const unsigned short seq_user_setting[] = {
98 0xF0, 0x5A,
100 DATA_ONLY, 0x5A,
101 ENDDEF, 0x00
104 static const unsigned short seq_elvss_on[] = {
105 0xB1, 0x0D,
107 DATA_ONLY, 0x00,
108 DATA_ONLY, 0x16,
109 ENDDEF, 0x00
112 static const unsigned short seq_gtcon[] = {
113 0xF7, 0x09,
115 DATA_ONLY, 0x00,
116 DATA_ONLY, 0x00,
117 ENDDEF, 0x00
120 static const unsigned short seq_panel_condition[] = {
121 0xF8, 0x05,
123 DATA_ONLY, 0x65,
124 DATA_ONLY, 0x96,
125 DATA_ONLY, 0x71,
126 DATA_ONLY, 0x7D,
127 DATA_ONLY, 0x19,
128 DATA_ONLY, 0x3B,
129 DATA_ONLY, 0x0D,
130 DATA_ONLY, 0x19,
131 DATA_ONLY, 0x7E,
132 DATA_ONLY, 0x0D,
133 DATA_ONLY, 0xE2,
134 DATA_ONLY, 0x00,
135 DATA_ONLY, 0x00,
136 DATA_ONLY, 0x7E,
137 DATA_ONLY, 0x7D,
138 DATA_ONLY, 0x07,
139 DATA_ONLY, 0x07,
140 DATA_ONLY, 0x20,
141 DATA_ONLY, 0x20,
142 DATA_ONLY, 0x20,
143 DATA_ONLY, 0x02,
144 DATA_ONLY, 0x02,
145 ENDDEF, 0x00
148 static const unsigned short seq_gamma_set1[] = {
149 0xF9, 0x00,
151 DATA_ONLY, 0xA7,
152 DATA_ONLY, 0xB4,
153 DATA_ONLY, 0xAE,
154 DATA_ONLY, 0xBF,
155 DATA_ONLY, 0x00,
156 DATA_ONLY, 0x91,
157 DATA_ONLY, 0x00,
158 DATA_ONLY, 0xB2,
159 DATA_ONLY, 0xB4,
160 DATA_ONLY, 0xAA,
161 DATA_ONLY, 0xBB,
162 DATA_ONLY, 0x00,
163 DATA_ONLY, 0xAC,
164 DATA_ONLY, 0x00,
165 DATA_ONLY, 0xB3,
166 DATA_ONLY, 0xB1,
167 DATA_ONLY, 0xAA,
168 DATA_ONLY, 0xBC,
169 DATA_ONLY, 0x00,
170 DATA_ONLY, 0xB3,
171 ENDDEF, 0x00
174 static const unsigned short seq_gamma_ctrl[] = {
175 0xFB, 0x02,
177 DATA_ONLY, 0x5A,
178 ENDDEF, 0x00
181 static const unsigned short seq_gamma_start[] = {
182 0xF9, COMMAND_ONLY,
184 ENDDEF, 0x00
187 static const unsigned short seq_apon[] = {
188 0xF3, 0x00,
190 DATA_ONLY, 0x00,
191 DATA_ONLY, 0x00,
192 DATA_ONLY, 0x0A,
193 DATA_ONLY, 0x02,
194 ENDDEF, 0x00
197 static const unsigned short seq_display_ctrl[] = {
198 0xF2, 0x02,
200 DATA_ONLY, 0x08,
201 DATA_ONLY, 0x08,
202 DATA_ONLY, 0x10,
203 DATA_ONLY, 0x10,
204 ENDDEF, 0x00
207 static const unsigned short seq_manual_pwr[] = {
208 0xB0, 0x04,
209 ENDDEF, 0x00
212 static const unsigned short seq_pwr_ctrl[] = {
213 0xF4, 0x0A,
215 DATA_ONLY, 0x87,
216 DATA_ONLY, 0x25,
217 DATA_ONLY, 0x6A,
218 DATA_ONLY, 0x44,
219 DATA_ONLY, 0x02,
220 DATA_ONLY, 0x88,
221 ENDDEF, 0x00
224 static const unsigned short seq_sleep_out[] = {
225 0x11, COMMAND_ONLY,
226 ENDDEF, 0x00
229 static const unsigned short seq_sleep_in[] = {
230 0x10, COMMAND_ONLY,
231 ENDDEF, 0x00
234 static const unsigned short seq_display_on[] = {
235 0x29, COMMAND_ONLY,
236 ENDDEF, 0x00
239 static const unsigned short seq_display_off[] = {
240 0x28, COMMAND_ONLY,
241 ENDDEF, 0x00
244 static const unsigned short seq_vci1_1st_en[] = {
245 0xF3, 0x10,
247 DATA_ONLY, 0x00,
248 DATA_ONLY, 0x00,
249 DATA_ONLY, 0x00,
250 DATA_ONLY, 0x02,
251 ENDDEF, 0x00
254 static const unsigned short seq_vl1_en[] = {
255 0xF3, 0x11,
257 DATA_ONLY, 0x00,
258 DATA_ONLY, 0x00,
259 DATA_ONLY, 0x00,
260 DATA_ONLY, 0x02,
261 ENDDEF, 0x00
264 static const unsigned short seq_vl2_en[] = {
265 0xF3, 0x13,
267 DATA_ONLY, 0x00,
268 DATA_ONLY, 0x00,
269 DATA_ONLY, 0x00,
270 DATA_ONLY, 0x02,
271 ENDDEF, 0x00
274 static const unsigned short seq_vci1_2nd_en[] = {
275 0xF3, 0x33,
277 DATA_ONLY, 0x00,
278 DATA_ONLY, 0x00,
279 DATA_ONLY, 0x00,
280 DATA_ONLY, 0x02,
281 ENDDEF, 0x00
284 static const unsigned short seq_vl3_en[] = {
285 0xF3, 0x37,
287 DATA_ONLY, 0x00,
288 DATA_ONLY, 0x00,
289 DATA_ONLY, 0x00,
290 DATA_ONLY, 0x02,
291 ENDDEF, 0x00
294 static const unsigned short seq_vreg1_amp_en[] = {
295 0xF3, 0x37,
297 DATA_ONLY, 0x01,
298 DATA_ONLY, 0x00,
299 DATA_ONLY, 0x00,
300 DATA_ONLY, 0x02,
301 ENDDEF, 0x00
304 static const unsigned short seq_vgh_amp_en[] = {
305 0xF3, 0x37,
307 DATA_ONLY, 0x11,
308 DATA_ONLY, 0x00,
309 DATA_ONLY, 0x00,
310 DATA_ONLY, 0x02,
311 ENDDEF, 0x00
314 static const unsigned short seq_vgl_amp_en[] = {
315 0xF3, 0x37,
317 DATA_ONLY, 0x31,
318 DATA_ONLY, 0x00,
319 DATA_ONLY, 0x00,
320 DATA_ONLY, 0x02,
321 ENDDEF, 0x00
324 static const unsigned short seq_vmos_amp_en[] = {
325 0xF3, 0x37,
327 DATA_ONLY, 0xB1,
328 DATA_ONLY, 0x00,
329 DATA_ONLY, 0x00,
330 DATA_ONLY, 0x03,
331 ENDDEF, 0x00
334 static const unsigned short seq_vint_amp_en[] = {
335 0xF3, 0x37,
337 DATA_ONLY, 0xF1,
338 /* DATA_ONLY, 0x71, VMOS/VBL/VBH not used */
339 DATA_ONLY, 0x00,
340 DATA_ONLY, 0x00,
341 DATA_ONLY, 0x03,
342 /* DATA_ONLY, 0x02, VMOS/VBL/VBH not used */
343 ENDDEF, 0x00
346 static const unsigned short seq_vbh_amp_en[] = {
347 0xF3, 0x37,
349 DATA_ONLY, 0xF9,
350 DATA_ONLY, 0x00,
351 DATA_ONLY, 0x00,
352 DATA_ONLY, 0x03,
353 ENDDEF, 0x00
356 static const unsigned short seq_vbl_amp_en[] = {
357 0xF3, 0x37,
359 DATA_ONLY, 0xFD,
360 DATA_ONLY, 0x00,
361 DATA_ONLY, 0x00,
362 DATA_ONLY, 0x03,
363 ENDDEF, 0x00
366 static const unsigned short seq_gam_amp_en[] = {
367 0xF3, 0x37,
369 DATA_ONLY, 0xFF,
370 /* DATA_ONLY, 0x73, VMOS/VBL/VBH not used */
371 DATA_ONLY, 0x00,
372 DATA_ONLY, 0x00,
373 DATA_ONLY, 0x03,
374 /* DATA_ONLY, 0x02, VMOS/VBL/VBH not used */
375 ENDDEF, 0x00
378 static const unsigned short seq_sd_amp_en[] = {
379 0xF3, 0x37,
381 DATA_ONLY, 0xFF,
382 /* DATA_ONLY, 0x73, VMOS/VBL/VBH not used */
383 DATA_ONLY, 0x80,
384 DATA_ONLY, 0x00,
385 DATA_ONLY, 0x03,
386 /* DATA_ONLY, 0x02, VMOS/VBL/VBH not used */
387 ENDDEF, 0x00
390 static const unsigned short seq_gls_en[] = {
391 0xF3, 0x37,
393 DATA_ONLY, 0xFF,
394 /* DATA_ONLY, 0x73, VMOS/VBL/VBH not used */
395 DATA_ONLY, 0x81,
396 DATA_ONLY, 0x00,
397 DATA_ONLY, 0x03,
398 /* DATA_ONLY, 0x02, VMOS/VBL/VBH not used */
399 ENDDEF, 0x00
402 static const unsigned short seq_els_en[] = {
403 0xF3, 0x37,
405 DATA_ONLY, 0xFF,
406 /* DATA_ONLY, 0x73, VMOS/VBL/VBH not used */
407 DATA_ONLY, 0x83,
408 DATA_ONLY, 0x00,
409 DATA_ONLY, 0x03,
410 /* DATA_ONLY, 0x02, VMOS/VBL/VBH not used */
411 ENDDEF, 0x00
414 static const unsigned short seq_el_on[] = {
415 0xF3, 0x37,
417 DATA_ONLY, 0xFF,
418 /* DATA_ONLY, 0x73, VMOS/VBL/VBH not used */
419 DATA_ONLY, 0x87,
420 DATA_ONLY, 0x00,
421 DATA_ONLY, 0x03,
422 /* DATA_ONLY, 0x02, VMOS/VBL/VBH not used */
423 ENDDEF, 0x00
426 static int ld9040_spi_write_byte(struct ld9040 *lcd, int addr, int data)
428 u16 buf[1];
429 struct spi_message msg;
431 struct spi_transfer xfer = {
432 .len = 2,
433 .tx_buf = buf,
436 buf[0] = (addr << 8) | data;
438 spi_message_init(&msg);
439 spi_message_add_tail(&xfer, &msg);
441 return spi_sync(lcd->spi, &msg);
444 static int ld9040_spi_write(struct ld9040 *lcd, unsigned char address,
445 unsigned char command)
447 int ret = 0;
449 if (address != DATA_ONLY)
450 ret = ld9040_spi_write_byte(lcd, 0x0, address);
451 if (command != COMMAND_ONLY)
452 ret = ld9040_spi_write_byte(lcd, 0x1, command);
454 return ret;
457 static int ld9040_panel_send_sequence(struct ld9040 *lcd,
458 const unsigned short *wbuf)
460 int ret = 0, i = 0;
462 while ((wbuf[i] & DEFMASK) != ENDDEF) {
463 if ((wbuf[i] & DEFMASK) != SLEEPMSEC) {
464 ret = ld9040_spi_write(lcd, wbuf[i], wbuf[i+1]);
465 if (ret)
466 break;
467 } else {
468 msleep(wbuf[i+1]);
470 i += 2;
473 return ret;
476 static int _ld9040_gamma_ctl(struct ld9040 *lcd, const unsigned int *gamma)
478 unsigned int i = 0;
479 int ret = 0;
481 /* start gamma table updating. */
482 ret = ld9040_panel_send_sequence(lcd, seq_gamma_start);
483 if (ret) {
484 dev_err(lcd->dev, "failed to disable gamma table updating.\n");
485 goto gamma_err;
488 for (i = 0 ; i < GAMMA_TABLE_COUNT; i++) {
489 ret = ld9040_spi_write(lcd, DATA_ONLY, gamma[i]);
490 if (ret) {
491 dev_err(lcd->dev, "failed to set gamma table.\n");
492 goto gamma_err;
496 /* update gamma table. */
497 ret = ld9040_panel_send_sequence(lcd, seq_gamma_ctrl);
498 if (ret)
499 dev_err(lcd->dev, "failed to update gamma table.\n");
501 gamma_err:
502 return ret;
505 static int ld9040_gamma_ctl(struct ld9040 *lcd, int gamma)
507 return _ld9040_gamma_ctl(lcd, gamma_table.gamma_22_table[gamma]);
510 static int ld9040_ldi_init(struct ld9040 *lcd)
512 int ret, i;
513 static const unsigned short *init_seq[] = {
514 seq_user_setting,
515 seq_panel_condition,
516 seq_display_ctrl,
517 seq_manual_pwr,
518 seq_elvss_on,
519 seq_gtcon,
520 seq_gamma_set1,
521 seq_gamma_ctrl,
522 seq_sleep_out,
525 for (i = 0; i < ARRAY_SIZE(init_seq); i++) {
526 ret = ld9040_panel_send_sequence(lcd, init_seq[i]);
527 /* workaround: minimum delay time for transferring CMD */
528 usleep_range(300, 310);
529 if (ret)
530 break;
533 return ret;
536 static int ld9040_ldi_enable(struct ld9040 *lcd)
538 return ld9040_panel_send_sequence(lcd, seq_display_on);
541 static int ld9040_ldi_disable(struct ld9040 *lcd)
543 int ret;
545 ret = ld9040_panel_send_sequence(lcd, seq_display_off);
546 ret = ld9040_panel_send_sequence(lcd, seq_sleep_in);
548 return ret;
551 static int ld9040_power_is_on(int power)
553 return power <= FB_BLANK_NORMAL;
556 static int ld9040_power_on(struct ld9040 *lcd)
558 int ret = 0;
559 struct lcd_platform_data *pd;
561 pd = lcd->lcd_pd;
563 /* lcd power on */
564 ld9040_regulator_enable(lcd);
566 if (!pd->reset) {
567 dev_err(lcd->dev, "reset is NULL.\n");
568 return -EINVAL;
571 pd->reset(lcd->ld);
572 msleep(pd->reset_delay);
574 ret = ld9040_ldi_init(lcd);
575 if (ret) {
576 dev_err(lcd->dev, "failed to initialize ldi.\n");
577 return ret;
580 ret = ld9040_ldi_enable(lcd);
581 if (ret) {
582 dev_err(lcd->dev, "failed to enable ldi.\n");
583 return ret;
586 return 0;
589 static int ld9040_power_off(struct ld9040 *lcd)
591 int ret;
592 struct lcd_platform_data *pd;
594 pd = lcd->lcd_pd;
596 ret = ld9040_ldi_disable(lcd);
597 if (ret) {
598 dev_err(lcd->dev, "lcd setting failed.\n");
599 return -EIO;
602 msleep(pd->power_off_delay);
604 /* lcd power off */
605 ld9040_regulator_disable(lcd);
607 return 0;
610 static int ld9040_power(struct ld9040 *lcd, int power)
612 int ret = 0;
614 if (ld9040_power_is_on(power) && !ld9040_power_is_on(lcd->power))
615 ret = ld9040_power_on(lcd);
616 else if (!ld9040_power_is_on(power) && ld9040_power_is_on(lcd->power))
617 ret = ld9040_power_off(lcd);
619 if (!ret)
620 lcd->power = power;
622 return ret;
625 static int ld9040_set_power(struct lcd_device *ld, int power)
627 struct ld9040 *lcd = lcd_get_data(ld);
629 if (power != FB_BLANK_UNBLANK && power != FB_BLANK_POWERDOWN &&
630 power != FB_BLANK_NORMAL) {
631 dev_err(lcd->dev, "power value should be 0, 1 or 4.\n");
632 return -EINVAL;
635 return ld9040_power(lcd, power);
638 static int ld9040_get_power(struct lcd_device *ld)
640 struct ld9040 *lcd = lcd_get_data(ld);
642 return lcd->power;
645 static int ld9040_set_brightness(struct backlight_device *bd)
647 int ret = 0, brightness = bd->props.brightness;
648 struct ld9040 *lcd = bl_get_data(bd);
650 if (brightness < MIN_BRIGHTNESS ||
651 brightness > bd->props.max_brightness) {
652 dev_err(&bd->dev, "lcd brightness should be %d to %d.\n",
653 MIN_BRIGHTNESS, MAX_BRIGHTNESS);
654 return -EINVAL;
657 ret = ld9040_gamma_ctl(lcd, bd->props.brightness);
658 if (ret) {
659 dev_err(&bd->dev, "lcd brightness setting failed.\n");
660 return -EIO;
663 return ret;
666 static struct lcd_ops ld9040_lcd_ops = {
667 .set_power = ld9040_set_power,
668 .get_power = ld9040_get_power,
671 static const struct backlight_ops ld9040_backlight_ops = {
672 .update_status = ld9040_set_brightness,
675 static int ld9040_probe(struct spi_device *spi)
677 int ret = 0;
678 struct ld9040 *lcd = NULL;
679 struct lcd_device *ld = NULL;
680 struct backlight_device *bd = NULL;
681 struct backlight_properties props;
683 lcd = devm_kzalloc(&spi->dev, sizeof(struct ld9040), GFP_KERNEL);
684 if (!lcd)
685 return -ENOMEM;
687 /* ld9040 lcd panel uses 3-wire 9bits SPI Mode. */
688 spi->bits_per_word = 9;
690 ret = spi_setup(spi);
691 if (ret < 0) {
692 dev_err(&spi->dev, "spi setup failed.\n");
693 return ret;
696 lcd->spi = spi;
697 lcd->dev = &spi->dev;
699 lcd->lcd_pd = dev_get_platdata(&spi->dev);
700 if (!lcd->lcd_pd) {
701 dev_err(&spi->dev, "platform data is NULL.\n");
702 return -EINVAL;
705 mutex_init(&lcd->lock);
707 ret = devm_regulator_bulk_get(lcd->dev, ARRAY_SIZE(supplies), supplies);
708 if (ret) {
709 dev_err(lcd->dev, "Failed to get regulators: %d\n", ret);
710 return ret;
713 ld = devm_lcd_device_register(&spi->dev, "ld9040", &spi->dev, lcd,
714 &ld9040_lcd_ops);
715 if (IS_ERR(ld))
716 return PTR_ERR(ld);
718 lcd->ld = ld;
720 memset(&props, 0, sizeof(struct backlight_properties));
721 props.type = BACKLIGHT_RAW;
722 props.max_brightness = MAX_BRIGHTNESS;
724 bd = devm_backlight_device_register(&spi->dev, "ld9040-bl", &spi->dev,
725 lcd, &ld9040_backlight_ops, &props);
726 if (IS_ERR(bd))
727 return PTR_ERR(bd);
729 bd->props.brightness = MAX_BRIGHTNESS;
730 lcd->bd = bd;
733 * if lcd panel was on from bootloader like u-boot then
734 * do not lcd on.
736 if (!lcd->lcd_pd->lcd_enabled) {
738 * if lcd panel was off from bootloader then
739 * current lcd status is powerdown and then
740 * it enables lcd panel.
742 lcd->power = FB_BLANK_POWERDOWN;
744 ld9040_power(lcd, FB_BLANK_UNBLANK);
745 } else {
746 lcd->power = FB_BLANK_UNBLANK;
749 spi_set_drvdata(spi, lcd);
751 dev_info(&spi->dev, "ld9040 panel driver has been probed.\n");
752 return 0;
755 static int ld9040_remove(struct spi_device *spi)
757 struct ld9040 *lcd = spi_get_drvdata(spi);
759 ld9040_power(lcd, FB_BLANK_POWERDOWN);
760 return 0;
763 #ifdef CONFIG_PM_SLEEP
764 static int ld9040_suspend(struct device *dev)
766 struct ld9040 *lcd = dev_get_drvdata(dev);
768 dev_dbg(dev, "lcd->power = %d\n", lcd->power);
771 * when lcd panel is suspend, lcd panel becomes off
772 * regardless of status.
774 return ld9040_power(lcd, FB_BLANK_POWERDOWN);
777 static int ld9040_resume(struct device *dev)
779 struct ld9040 *lcd = dev_get_drvdata(dev);
781 lcd->power = FB_BLANK_POWERDOWN;
783 return ld9040_power(lcd, FB_BLANK_UNBLANK);
785 #endif
787 static SIMPLE_DEV_PM_OPS(ld9040_pm_ops, ld9040_suspend, ld9040_resume);
789 /* Power down all displays on reboot, poweroff or halt. */
790 static void ld9040_shutdown(struct spi_device *spi)
792 struct ld9040 *lcd = spi_get_drvdata(spi);
794 ld9040_power(lcd, FB_BLANK_POWERDOWN);
797 static struct spi_driver ld9040_driver = {
798 .driver = {
799 .name = "ld9040",
800 .pm = &ld9040_pm_ops,
802 .probe = ld9040_probe,
803 .remove = ld9040_remove,
804 .shutdown = ld9040_shutdown,
807 module_spi_driver(ld9040_driver);
809 MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>");
810 MODULE_DESCRIPTION("ld9040 LCD Driver");
811 MODULE_LICENSE("GPL");