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.
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>
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>
36 #include "ld9040_gamma.h"
38 #define SLEEPMSEC 0x1000
40 #define DEFMASK 0xFF00
41 #define COMMAND_ONLY 0xFE
42 #define DATA_ONLY 0xFF
44 #define MIN_BRIGHTNESS 0
45 #define MAX_BRIGHTNESS 24
49 struct spi_device
*spi
;
51 unsigned int current_brightness
;
53 struct lcd_device
*ld
;
54 struct backlight_device
*bd
;
55 struct lcd_platform_data
*lcd_pd
;
61 static struct regulator_bulk_data supplies
[] = {
62 { .supply
= "vdd3", },
66 static void ld9040_regulator_enable(struct ld9040
*lcd
)
69 struct lcd_platform_data
*pd
= NULL
;
72 mutex_lock(&lcd
->lock
);
74 ret
= regulator_bulk_enable(ARRAY_SIZE(supplies
), supplies
);
80 msleep(pd
->power_on_delay
);
82 mutex_unlock(&lcd
->lock
);
85 static void ld9040_regulator_disable(struct ld9040
*lcd
)
89 mutex_lock(&lcd
->lock
);
91 ret
= regulator_bulk_disable(ARRAY_SIZE(supplies
), supplies
);
98 mutex_unlock(&lcd
->lock
);
101 static const unsigned short seq_swreset
[] = {
106 static const unsigned short seq_user_setting
[] = {
113 static const unsigned short seq_elvss_on
[] = {
121 static const unsigned short seq_gtcon
[] = {
129 static const unsigned short seq_panel_condition
[] = {
157 static const unsigned short seq_gamma_set1
[] = {
183 static const unsigned short seq_gamma_ctrl
[] = {
190 static const unsigned short seq_gamma_start
[] = {
196 static const unsigned short seq_apon
[] = {
206 static const unsigned short seq_display_ctrl
[] = {
216 static const unsigned short seq_manual_pwr
[] = {
221 static const unsigned short seq_pwr_ctrl
[] = {
233 static const unsigned short seq_sleep_out
[] = {
238 static const unsigned short seq_sleep_in
[] = {
243 static const unsigned short seq_display_on
[] = {
248 static const unsigned short seq_display_off
[] = {
253 static const unsigned short seq_vci1_1st_en
[] = {
263 static const unsigned short seq_vl1_en
[] = {
273 static const unsigned short seq_vl2_en
[] = {
283 static const unsigned short seq_vci1_2nd_en
[] = {
293 static const unsigned short seq_vl3_en
[] = {
303 static const unsigned short seq_vreg1_amp_en
[] = {
313 static const unsigned short seq_vgh_amp_en
[] = {
323 static const unsigned short seq_vgl_amp_en
[] = {
333 static const unsigned short seq_vmos_amp_en
[] = {
343 static const unsigned short seq_vint_amp_en
[] = {
347 /* DATA_ONLY, 0x71, VMOS/VBL/VBH not used */
351 /* DATA_ONLY, 0x02, VMOS/VBL/VBH not used */
355 static const unsigned short seq_vbh_amp_en
[] = {
365 static const unsigned short seq_vbl_amp_en
[] = {
375 static const unsigned short seq_gam_amp_en
[] = {
379 /* DATA_ONLY, 0x73, VMOS/VBL/VBH not used */
383 /* DATA_ONLY, 0x02, VMOS/VBL/VBH not used */
387 static const unsigned short seq_sd_amp_en
[] = {
391 /* DATA_ONLY, 0x73, VMOS/VBL/VBH not used */
395 /* DATA_ONLY, 0x02, VMOS/VBL/VBH not used */
399 static const unsigned short seq_gls_en
[] = {
403 /* DATA_ONLY, 0x73, VMOS/VBL/VBH not used */
407 /* DATA_ONLY, 0x02, VMOS/VBL/VBH not used */
411 static const unsigned short seq_els_en
[] = {
415 /* DATA_ONLY, 0x73, VMOS/VBL/VBH not used */
419 /* DATA_ONLY, 0x02, VMOS/VBL/VBH not used */
423 static const unsigned short seq_el_on
[] = {
427 /* DATA_ONLY, 0x73, VMOS/VBL/VBH not used */
431 /* DATA_ONLY, 0x02, VMOS/VBL/VBH not used */
435 static int ld9040_spi_write_byte(struct ld9040
*lcd
, int addr
, int data
)
438 struct spi_message msg
;
440 struct spi_transfer xfer
= {
445 buf
[0] = (addr
<< 8) | data
;
447 spi_message_init(&msg
);
448 spi_message_add_tail(&xfer
, &msg
);
450 return spi_sync(lcd
->spi
, &msg
);
453 static int ld9040_spi_write(struct ld9040
*lcd
, unsigned char address
,
454 unsigned char command
)
458 if (address
!= DATA_ONLY
)
459 ret
= ld9040_spi_write_byte(lcd
, 0x0, address
);
460 if (command
!= COMMAND_ONLY
)
461 ret
= ld9040_spi_write_byte(lcd
, 0x1, command
);
466 static int ld9040_panel_send_sequence(struct ld9040
*lcd
,
467 const unsigned short *wbuf
)
471 while ((wbuf
[i
] & DEFMASK
) != ENDDEF
) {
472 if ((wbuf
[i
] & DEFMASK
) != SLEEPMSEC
) {
473 ret
= ld9040_spi_write(lcd
, wbuf
[i
], wbuf
[i
+1]);
485 static int _ld9040_gamma_ctl(struct ld9040
*lcd
, const unsigned int *gamma
)
490 /* start gamma table updating. */
491 ret
= ld9040_panel_send_sequence(lcd
, seq_gamma_start
);
493 dev_err(lcd
->dev
, "failed to disable gamma table updating.\n");
497 for (i
= 0 ; i
< GAMMA_TABLE_COUNT
; i
++) {
498 ret
= ld9040_spi_write(lcd
, DATA_ONLY
, gamma
[i
]);
500 dev_err(lcd
->dev
, "failed to set gamma table.\n");
505 /* update gamma table. */
506 ret
= ld9040_panel_send_sequence(lcd
, seq_gamma_ctrl
);
508 dev_err(lcd
->dev
, "failed to update gamma table.\n");
514 static int ld9040_gamma_ctl(struct ld9040
*lcd
, int gamma
)
518 ret
= _ld9040_gamma_ctl(lcd
, gamma_table
.gamma_22_table
[gamma
]);
524 static int ld9040_ldi_init(struct ld9040
*lcd
)
527 static const unsigned short *init_seq
[] = {
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 usleep_range(300, 310);
550 static int ld9040_ldi_enable(struct ld9040
*lcd
)
554 ret
= ld9040_panel_send_sequence(lcd
, seq_display_on
);
559 static int ld9040_ldi_disable(struct ld9040
*lcd
)
563 ret
= ld9040_panel_send_sequence(lcd
, seq_display_off
);
564 ret
= ld9040_panel_send_sequence(lcd
, seq_sleep_in
);
569 static int ld9040_power_is_on(int power
)
571 return power
<= FB_BLANK_NORMAL
;
574 static int ld9040_power_on(struct ld9040
*lcd
)
577 struct lcd_platform_data
*pd
;
582 ld9040_regulator_enable(lcd
);
585 dev_err(lcd
->dev
, "reset is NULL.\n");
589 msleep(pd
->reset_delay
);
592 ret
= ld9040_ldi_init(lcd
);
594 dev_err(lcd
->dev
, "failed to initialize ldi.\n");
598 ret
= ld9040_ldi_enable(lcd
);
600 dev_err(lcd
->dev
, "failed to enable ldi.\n");
607 static int ld9040_power_off(struct ld9040
*lcd
)
610 struct lcd_platform_data
*pd
;
614 ret
= ld9040_ldi_disable(lcd
);
616 dev_err(lcd
->dev
, "lcd setting failed.\n");
620 msleep(pd
->power_off_delay
);
623 ld9040_regulator_disable(lcd
);
628 static int ld9040_power(struct ld9040
*lcd
, int power
)
632 if (ld9040_power_is_on(power
) && !ld9040_power_is_on(lcd
->power
))
633 ret
= ld9040_power_on(lcd
);
634 else if (!ld9040_power_is_on(power
) && ld9040_power_is_on(lcd
->power
))
635 ret
= ld9040_power_off(lcd
);
643 static int ld9040_set_power(struct lcd_device
*ld
, int power
)
645 struct ld9040
*lcd
= lcd_get_data(ld
);
647 if (power
!= FB_BLANK_UNBLANK
&& power
!= FB_BLANK_POWERDOWN
&&
648 power
!= FB_BLANK_NORMAL
) {
649 dev_err(lcd
->dev
, "power value should be 0, 1 or 4.\n");
653 return ld9040_power(lcd
, power
);
656 static int ld9040_get_power(struct lcd_device
*ld
)
658 struct ld9040
*lcd
= lcd_get_data(ld
);
663 static int ld9040_get_brightness(struct backlight_device
*bd
)
665 return bd
->props
.brightness
;
668 static int ld9040_set_brightness(struct backlight_device
*bd
)
670 int ret
= 0, brightness
= bd
->props
.brightness
;
671 struct ld9040
*lcd
= bl_get_data(bd
);
673 if (brightness
< MIN_BRIGHTNESS
||
674 brightness
> bd
->props
.max_brightness
) {
675 dev_err(&bd
->dev
, "lcd brightness should be %d to %d.\n",
676 MIN_BRIGHTNESS
, MAX_BRIGHTNESS
);
680 ret
= ld9040_gamma_ctl(lcd
, bd
->props
.brightness
);
682 dev_err(&bd
->dev
, "lcd brightness setting failed.\n");
689 static struct lcd_ops ld9040_lcd_ops
= {
690 .set_power
= ld9040_set_power
,
691 .get_power
= ld9040_get_power
,
694 static const struct backlight_ops ld9040_backlight_ops
= {
695 .get_brightness
= ld9040_get_brightness
,
696 .update_status
= ld9040_set_brightness
,
700 static int ld9040_probe(struct spi_device
*spi
)
703 struct ld9040
*lcd
= NULL
;
704 struct lcd_device
*ld
= NULL
;
705 struct backlight_device
*bd
= NULL
;
706 struct backlight_properties props
;
708 lcd
= devm_kzalloc(&spi
->dev
, sizeof(struct ld9040
), GFP_KERNEL
);
712 /* ld9040 lcd panel uses 3-wire 9bits SPI Mode. */
713 spi
->bits_per_word
= 9;
715 ret
= spi_setup(spi
);
717 dev_err(&spi
->dev
, "spi setup failed.\n");
722 lcd
->dev
= &spi
->dev
;
724 lcd
->lcd_pd
= spi
->dev
.platform_data
;
726 dev_err(&spi
->dev
, "platform data is NULL.\n");
730 mutex_init(&lcd
->lock
);
732 ret
= regulator_bulk_get(lcd
->dev
, ARRAY_SIZE(supplies
), supplies
);
734 dev_err(lcd
->dev
, "Failed to get regulators: %d\n", ret
);
738 ld
= lcd_device_register("ld9040", &spi
->dev
, lcd
, &ld9040_lcd_ops
);
741 goto out_free_regulator
;
746 memset(&props
, 0, sizeof(struct backlight_properties
));
747 props
.type
= BACKLIGHT_RAW
;
748 props
.max_brightness
= MAX_BRIGHTNESS
;
750 bd
= backlight_device_register("ld9040-bl", &spi
->dev
,
751 lcd
, &ld9040_backlight_ops
, &props
);
754 goto out_unregister_lcd
;
757 bd
->props
.brightness
= MAX_BRIGHTNESS
;
761 * if lcd panel was on from bootloader like u-boot then
764 if (!lcd
->lcd_pd
->lcd_enabled
) {
766 * if lcd panel was off from bootloader then
767 * current lcd status is powerdown and then
768 * it enables lcd panel.
770 lcd
->power
= FB_BLANK_POWERDOWN
;
772 ld9040_power(lcd
, FB_BLANK_UNBLANK
);
774 lcd
->power
= FB_BLANK_UNBLANK
;
777 dev_set_drvdata(&spi
->dev
, lcd
);
779 dev_info(&spi
->dev
, "ld9040 panel driver has been probed.\n");
783 lcd_device_unregister(lcd
->ld
);
785 regulator_bulk_free(ARRAY_SIZE(supplies
), supplies
);
790 static int ld9040_remove(struct spi_device
*spi
)
792 struct ld9040
*lcd
= dev_get_drvdata(&spi
->dev
);
794 ld9040_power(lcd
, FB_BLANK_POWERDOWN
);
795 backlight_device_unregister(lcd
->bd
);
796 lcd_device_unregister(lcd
->ld
);
797 regulator_bulk_free(ARRAY_SIZE(supplies
), supplies
);
802 #if defined(CONFIG_PM)
803 static int ld9040_suspend(struct spi_device
*spi
, pm_message_t mesg
)
806 struct ld9040
*lcd
= dev_get_drvdata(&spi
->dev
);
808 dev_dbg(&spi
->dev
, "lcd->power = %d\n", lcd
->power
);
811 * when lcd panel is suspend, lcd panel becomes off
812 * regardless of status.
814 ret
= ld9040_power(lcd
, FB_BLANK_POWERDOWN
);
819 static int ld9040_resume(struct spi_device
*spi
)
822 struct ld9040
*lcd
= dev_get_drvdata(&spi
->dev
);
824 lcd
->power
= FB_BLANK_POWERDOWN
;
826 ret
= ld9040_power(lcd
, FB_BLANK_UNBLANK
);
831 #define ld9040_suspend NULL
832 #define ld9040_resume NULL
835 /* Power down all displays on reboot, poweroff or halt. */
836 static void ld9040_shutdown(struct spi_device
*spi
)
838 struct ld9040
*lcd
= dev_get_drvdata(&spi
->dev
);
840 ld9040_power(lcd
, FB_BLANK_POWERDOWN
);
843 static struct spi_driver ld9040_driver
= {
846 .owner
= THIS_MODULE
,
848 .probe
= ld9040_probe
,
849 .remove
= ld9040_remove
,
850 .shutdown
= ld9040_shutdown
,
851 .suspend
= ld9040_suspend
,
852 .resume
= ld9040_resume
,
855 module_spi_driver(ld9040_driver
);
857 MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>");
858 MODULE_DESCRIPTION("ld9040 LCD Driver");
859 MODULE_LICENSE("GPL");