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>
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
31 #define DEFMASK 0xFF00
32 #define COMMAND_ONLY 0xFE
33 #define DATA_ONLY 0xFF
35 #define MIN_BRIGHTNESS 0
36 #define MAX_BRIGHTNESS 24
40 struct spi_device
*spi
;
42 unsigned int current_brightness
;
44 struct lcd_device
*ld
;
45 struct backlight_device
*bd
;
46 struct lcd_platform_data
*lcd_pd
;
52 static struct regulator_bulk_data supplies
[] = {
53 { .supply
= "vdd3", },
57 static void ld9040_regulator_enable(struct ld9040
*lcd
)
60 struct lcd_platform_data
*pd
= NULL
;
63 mutex_lock(&lcd
->lock
);
65 ret
= regulator_bulk_enable(ARRAY_SIZE(supplies
), supplies
);
71 msleep(pd
->power_on_delay
);
73 mutex_unlock(&lcd
->lock
);
76 static void ld9040_regulator_disable(struct ld9040
*lcd
)
80 mutex_lock(&lcd
->lock
);
82 ret
= regulator_bulk_disable(ARRAY_SIZE(supplies
), supplies
);
89 mutex_unlock(&lcd
->lock
);
92 static const unsigned short seq_swreset
[] = {
97 static const unsigned short seq_user_setting
[] = {
104 static const unsigned short seq_elvss_on
[] = {
112 static const unsigned short seq_gtcon
[] = {
120 static const unsigned short seq_panel_condition
[] = {
148 static const unsigned short seq_gamma_set1
[] = {
174 static const unsigned short seq_gamma_ctrl
[] = {
181 static const unsigned short seq_gamma_start
[] = {
187 static const unsigned short seq_apon
[] = {
197 static const unsigned short seq_display_ctrl
[] = {
207 static const unsigned short seq_manual_pwr
[] = {
212 static const unsigned short seq_pwr_ctrl
[] = {
224 static const unsigned short seq_sleep_out
[] = {
229 static const unsigned short seq_sleep_in
[] = {
234 static const unsigned short seq_display_on
[] = {
239 static const unsigned short seq_display_off
[] = {
244 static const unsigned short seq_vci1_1st_en
[] = {
254 static const unsigned short seq_vl1_en
[] = {
264 static const unsigned short seq_vl2_en
[] = {
274 static const unsigned short seq_vci1_2nd_en
[] = {
284 static const unsigned short seq_vl3_en
[] = {
294 static const unsigned short seq_vreg1_amp_en
[] = {
304 static const unsigned short seq_vgh_amp_en
[] = {
314 static const unsigned short seq_vgl_amp_en
[] = {
324 static const unsigned short seq_vmos_amp_en
[] = {
334 static const unsigned short seq_vint_amp_en
[] = {
338 /* DATA_ONLY, 0x71, VMOS/VBL/VBH not used */
342 /* DATA_ONLY, 0x02, VMOS/VBL/VBH not used */
346 static const unsigned short seq_vbh_amp_en
[] = {
356 static const unsigned short seq_vbl_amp_en
[] = {
366 static const unsigned short seq_gam_amp_en
[] = {
370 /* DATA_ONLY, 0x73, VMOS/VBL/VBH not used */
374 /* DATA_ONLY, 0x02, VMOS/VBL/VBH not used */
378 static const unsigned short seq_sd_amp_en
[] = {
382 /* DATA_ONLY, 0x73, VMOS/VBL/VBH not used */
386 /* DATA_ONLY, 0x02, VMOS/VBL/VBH not used */
390 static const unsigned short seq_gls_en
[] = {
394 /* DATA_ONLY, 0x73, VMOS/VBL/VBH not used */
398 /* DATA_ONLY, 0x02, VMOS/VBL/VBH not used */
402 static const unsigned short seq_els_en
[] = {
406 /* DATA_ONLY, 0x73, VMOS/VBL/VBH not used */
410 /* DATA_ONLY, 0x02, VMOS/VBL/VBH not used */
414 static const unsigned short seq_el_on
[] = {
418 /* DATA_ONLY, 0x73, VMOS/VBL/VBH not used */
422 /* DATA_ONLY, 0x02, VMOS/VBL/VBH not used */
426 static int ld9040_spi_write_byte(struct ld9040
*lcd
, int addr
, int data
)
429 struct spi_message msg
;
431 struct spi_transfer xfer
= {
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
)
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
);
457 static int ld9040_panel_send_sequence(struct ld9040
*lcd
,
458 const unsigned short *wbuf
)
462 while ((wbuf
[i
] & DEFMASK
) != ENDDEF
) {
463 if ((wbuf
[i
] & DEFMASK
) != SLEEPMSEC
) {
464 ret
= ld9040_spi_write(lcd
, wbuf
[i
], wbuf
[i
+1]);
476 static int _ld9040_gamma_ctl(struct ld9040
*lcd
, const unsigned int *gamma
)
481 /* start gamma table updating. */
482 ret
= ld9040_panel_send_sequence(lcd
, seq_gamma_start
);
484 dev_err(lcd
->dev
, "failed to disable gamma table updating.\n");
488 for (i
= 0 ; i
< GAMMA_TABLE_COUNT
; i
++) {
489 ret
= ld9040_spi_write(lcd
, DATA_ONLY
, gamma
[i
]);
491 dev_err(lcd
->dev
, "failed to set gamma table.\n");
496 /* update gamma table. */
497 ret
= ld9040_panel_send_sequence(lcd
, seq_gamma_ctrl
);
499 dev_err(lcd
->dev
, "failed to update gamma table.\n");
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
)
513 static const unsigned short *init_seq
[] = {
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);
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
)
545 ret
= ld9040_panel_send_sequence(lcd
, seq_display_off
);
546 ret
= ld9040_panel_send_sequence(lcd
, seq_sleep_in
);
551 static int ld9040_power_is_on(int power
)
553 return power
<= FB_BLANK_NORMAL
;
556 static int ld9040_power_on(struct ld9040
*lcd
)
559 struct lcd_platform_data
*pd
;
564 ld9040_regulator_enable(lcd
);
567 dev_err(lcd
->dev
, "reset is NULL.\n");
572 msleep(pd
->reset_delay
);
574 ret
= ld9040_ldi_init(lcd
);
576 dev_err(lcd
->dev
, "failed to initialize ldi.\n");
580 ret
= ld9040_ldi_enable(lcd
);
582 dev_err(lcd
->dev
, "failed to enable ldi.\n");
589 static int ld9040_power_off(struct ld9040
*lcd
)
592 struct lcd_platform_data
*pd
;
596 ret
= ld9040_ldi_disable(lcd
);
598 dev_err(lcd
->dev
, "lcd setting failed.\n");
602 msleep(pd
->power_off_delay
);
605 ld9040_regulator_disable(lcd
);
610 static int ld9040_power(struct ld9040
*lcd
, int power
)
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
);
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");
635 return ld9040_power(lcd
, power
);
638 static int ld9040_get_power(struct lcd_device
*ld
)
640 struct ld9040
*lcd
= lcd_get_data(ld
);
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
);
657 ret
= ld9040_gamma_ctl(lcd
, bd
->props
.brightness
);
659 dev_err(&bd
->dev
, "lcd brightness setting failed.\n");
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
)
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
);
687 /* ld9040 lcd panel uses 3-wire 9bits SPI Mode. */
688 spi
->bits_per_word
= 9;
690 ret
= spi_setup(spi
);
692 dev_err(&spi
->dev
, "spi setup failed.\n");
697 lcd
->dev
= &spi
->dev
;
699 lcd
->lcd_pd
= dev_get_platdata(&spi
->dev
);
701 dev_err(&spi
->dev
, "platform data is NULL.\n");
705 mutex_init(&lcd
->lock
);
707 ret
= devm_regulator_bulk_get(lcd
->dev
, ARRAY_SIZE(supplies
), supplies
);
709 dev_err(lcd
->dev
, "Failed to get regulators: %d\n", ret
);
713 ld
= devm_lcd_device_register(&spi
->dev
, "ld9040", &spi
->dev
, lcd
,
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
);
729 bd
->props
.brightness
= MAX_BRIGHTNESS
;
733 * if lcd panel was on from bootloader like u-boot then
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
);
746 lcd
->power
= FB_BLANK_UNBLANK
;
749 spi_set_drvdata(spi
, lcd
);
751 dev_info(&spi
->dev
, "ld9040 panel driver has been probed.\n");
755 static int ld9040_remove(struct spi_device
*spi
)
757 struct ld9040
*lcd
= spi_get_drvdata(spi
);
759 ld9040_power(lcd
, FB_BLANK_POWERDOWN
);
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
);
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
= {
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");