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>
35 #include "ld9040_gamma.h"
37 #define SLEEPMSEC 0x1000
39 #define DEFMASK 0xFF00
40 #define COMMAND_ONLY 0xFE
41 #define DATA_ONLY 0xFF
43 #define MIN_BRIGHTNESS 0
44 #define MAX_BRIGHTNESS 24
45 #define power_is_on(pwr) ((pwr) <= FB_BLANK_NORMAL)
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
;
58 static const unsigned short seq_swreset
[] = {
63 static const unsigned short seq_user_setting
[] = {
70 static const unsigned short seq_elvss_on
[] = {
78 static const unsigned short seq_gtcon
[] = {
86 static const unsigned short seq_panel_condition
[] = {
114 static const unsigned short seq_gamma_set1
[] = {
140 static const unsigned short seq_gamma_ctrl
[] = {
147 static const unsigned short seq_gamma_start
[] = {
153 static const unsigned short seq_apon
[] = {
163 static const unsigned short seq_display_ctrl
[] = {
173 static const unsigned short seq_manual_pwr
[] = {
178 static const unsigned short seq_pwr_ctrl
[] = {
190 static const unsigned short seq_sleep_out
[] = {
195 static const unsigned short seq_sleep_in
[] = {
200 static const unsigned short seq_display_on
[] = {
205 static const unsigned short seq_display_off
[] = {
210 static const unsigned short seq_vci1_1st_en
[] = {
220 static const unsigned short seq_vl1_en
[] = {
230 static const unsigned short seq_vl2_en
[] = {
240 static const unsigned short seq_vci1_2nd_en
[] = {
250 static const unsigned short seq_vl3_en
[] = {
260 static const unsigned short seq_vreg1_amp_en
[] = {
270 static const unsigned short seq_vgh_amp_en
[] = {
280 static const unsigned short seq_vgl_amp_en
[] = {
290 static const unsigned short seq_vmos_amp_en
[] = {
300 static const unsigned short seq_vint_amp_en
[] = {
304 /* DATA_ONLY, 0x71, VMOS/VBL/VBH not used */
308 /* DATA_ONLY, 0x02, VMOS/VBL/VBH not used */
312 static const unsigned short seq_vbh_amp_en
[] = {
322 static const unsigned short seq_vbl_amp_en
[] = {
332 static const unsigned short seq_gam_amp_en
[] = {
336 /* DATA_ONLY, 0x73, VMOS/VBL/VBH not used */
340 /* DATA_ONLY, 0x02, VMOS/VBL/VBH not used */
344 static const unsigned short seq_sd_amp_en
[] = {
348 /* DATA_ONLY, 0x73, VMOS/VBL/VBH not used */
352 /* DATA_ONLY, 0x02, VMOS/VBL/VBH not used */
356 static const unsigned short seq_gls_en
[] = {
360 /* DATA_ONLY, 0x73, VMOS/VBL/VBH not used */
364 /* DATA_ONLY, 0x02, VMOS/VBL/VBH not used */
368 static const unsigned short seq_els_en
[] = {
372 /* DATA_ONLY, 0x73, VMOS/VBL/VBH not used */
376 /* DATA_ONLY, 0x02, VMOS/VBL/VBH not used */
380 static const unsigned short seq_el_on
[] = {
384 /* DATA_ONLY, 0x73, VMOS/VBL/VBH not used */
388 /* DATA_ONLY, 0x02, VMOS/VBL/VBH not used */
392 static int ld9040_spi_write_byte(struct ld9040
*lcd
, int addr
, int data
)
395 struct spi_message msg
;
397 struct spi_transfer xfer
= {
402 buf
[0] = (addr
<< 8) | data
;
404 spi_message_init(&msg
);
405 spi_message_add_tail(&xfer
, &msg
);
407 return spi_sync(lcd
->spi
, &msg
);
410 static int ld9040_spi_write(struct ld9040
*lcd
, unsigned char address
,
411 unsigned char command
)
415 if (address
!= DATA_ONLY
)
416 ret
= ld9040_spi_write_byte(lcd
, 0x0, address
);
417 if (command
!= COMMAND_ONLY
)
418 ret
= ld9040_spi_write_byte(lcd
, 0x1, command
);
423 static int ld9040_panel_send_sequence(struct ld9040
*lcd
,
424 const unsigned short *wbuf
)
428 while ((wbuf
[i
] & DEFMASK
) != ENDDEF
) {
429 if ((wbuf
[i
] & DEFMASK
) != SLEEPMSEC
) {
430 ret
= ld9040_spi_write(lcd
, wbuf
[i
], wbuf
[i
+1]);
434 udelay(wbuf
[i
+1]*1000);
441 static int _ld9040_gamma_ctl(struct ld9040
*lcd
, const unsigned int *gamma
)
446 /* start gamma table updating. */
447 ret
= ld9040_panel_send_sequence(lcd
, seq_gamma_start
);
449 dev_err(lcd
->dev
, "failed to disable gamma table updating.\n");
453 for (i
= 0 ; i
< GAMMA_TABLE_COUNT
; i
++) {
454 ret
= ld9040_spi_write(lcd
, DATA_ONLY
, gamma
[i
]);
456 dev_err(lcd
->dev
, "failed to set gamma table.\n");
461 /* update gamma table. */
462 ret
= ld9040_panel_send_sequence(lcd
, seq_gamma_ctrl
);
464 dev_err(lcd
->dev
, "failed to update gamma table.\n");
470 static int ld9040_gamma_ctl(struct ld9040
*lcd
, int gamma
)
474 ret
= _ld9040_gamma_ctl(lcd
, gamma_table
.gamma_22_table
[gamma
]);
480 static int ld9040_ldi_init(struct ld9040
*lcd
)
483 static const unsigned short *init_seq
[] = {
495 for (i
= 0; i
< ARRAY_SIZE(init_seq
); i
++) {
496 ret
= ld9040_panel_send_sequence(lcd
, init_seq
[i
]);
497 /* workaround: minimum delay time for transferring CMD */
506 static int ld9040_ldi_enable(struct ld9040
*lcd
)
510 ret
= ld9040_panel_send_sequence(lcd
, seq_display_on
);
515 static int ld9040_ldi_disable(struct ld9040
*lcd
)
519 ret
= ld9040_panel_send_sequence(lcd
, seq_display_off
);
520 ret
= ld9040_panel_send_sequence(lcd
, seq_sleep_in
);
525 static int ld9040_power_on(struct ld9040
*lcd
)
528 struct lcd_platform_data
*pd
= NULL
;
531 dev_err(lcd
->dev
, "platform data is NULL.\n");
536 dev_err(lcd
->dev
, "power_on is NULL.\n");
539 pd
->power_on(lcd
->ld
, 1);
540 mdelay(pd
->power_on_delay
);
544 dev_err(lcd
->dev
, "reset is NULL.\n");
548 mdelay(pd
->reset_delay
);
551 ret
= ld9040_ldi_init(lcd
);
553 dev_err(lcd
->dev
, "failed to initialize ldi.\n");
557 ret
= ld9040_ldi_enable(lcd
);
559 dev_err(lcd
->dev
, "failed to enable ldi.\n");
566 static int ld9040_power_off(struct ld9040
*lcd
)
569 struct lcd_platform_data
*pd
= NULL
;
573 dev_err(lcd
->dev
, "platform data is NULL.\n");
577 ret
= ld9040_ldi_disable(lcd
);
579 dev_err(lcd
->dev
, "lcd setting failed.\n");
583 mdelay(pd
->power_off_delay
);
586 dev_err(lcd
->dev
, "power_on is NULL.\n");
589 pd
->power_on(lcd
->ld
, 0);
594 static int ld9040_power(struct ld9040
*lcd
, int power
)
598 if (power_is_on(power
) && !power_is_on(lcd
->power
))
599 ret
= ld9040_power_on(lcd
);
600 else if (!power_is_on(power
) && power_is_on(lcd
->power
))
601 ret
= ld9040_power_off(lcd
);
609 static int ld9040_set_power(struct lcd_device
*ld
, int power
)
611 struct ld9040
*lcd
= lcd_get_data(ld
);
613 if (power
!= FB_BLANK_UNBLANK
&& power
!= FB_BLANK_POWERDOWN
&&
614 power
!= FB_BLANK_NORMAL
) {
615 dev_err(lcd
->dev
, "power value should be 0, 1 or 4.\n");
619 return ld9040_power(lcd
, power
);
622 static int ld9040_get_power(struct lcd_device
*ld
)
624 struct ld9040
*lcd
= lcd_get_data(ld
);
629 static int ld9040_get_brightness(struct backlight_device
*bd
)
631 return bd
->props
.brightness
;
634 static int ld9040_set_brightness(struct backlight_device
*bd
)
636 int ret
= 0, brightness
= bd
->props
.brightness
;
637 struct ld9040
*lcd
= bl_get_data(bd
);
639 if (brightness
< MIN_BRIGHTNESS
||
640 brightness
> bd
->props
.max_brightness
) {
641 dev_err(&bd
->dev
, "lcd brightness should be %d to %d.\n",
642 MIN_BRIGHTNESS
, MAX_BRIGHTNESS
);
646 ret
= ld9040_gamma_ctl(lcd
, bd
->props
.brightness
);
648 dev_err(&bd
->dev
, "lcd brightness setting failed.\n");
655 static struct lcd_ops ld9040_lcd_ops
= {
656 .set_power
= ld9040_set_power
,
657 .get_power
= ld9040_get_power
,
660 static const struct backlight_ops ld9040_backlight_ops
= {
661 .get_brightness
= ld9040_get_brightness
,
662 .update_status
= ld9040_set_brightness
,
666 static int ld9040_probe(struct spi_device
*spi
)
669 struct ld9040
*lcd
= NULL
;
670 struct lcd_device
*ld
= NULL
;
671 struct backlight_device
*bd
= NULL
;
672 struct backlight_properties props
;
674 lcd
= kzalloc(sizeof(struct ld9040
), GFP_KERNEL
);
678 /* ld9040 lcd panel uses 3-wire 9bits SPI Mode. */
679 spi
->bits_per_word
= 9;
681 ret
= spi_setup(spi
);
683 dev_err(&spi
->dev
, "spi setup failed.\n");
688 lcd
->dev
= &spi
->dev
;
690 lcd
->lcd_pd
= spi
->dev
.platform_data
;
692 dev_err(&spi
->dev
, "platform data is NULL.\n");
696 ld
= lcd_device_register("ld9040", &spi
->dev
, lcd
, &ld9040_lcd_ops
);
704 memset(&props
, 0, sizeof(struct backlight_properties
));
705 props
.type
= BACKLIGHT_RAW
;
706 props
.max_brightness
= MAX_BRIGHTNESS
;
708 bd
= backlight_device_register("ld9040-bl", &spi
->dev
,
709 lcd
, &ld9040_backlight_ops
, &props
);
712 goto out_unregister_lcd
;
715 bd
->props
.brightness
= MAX_BRIGHTNESS
;
719 * if lcd panel was on from bootloader like u-boot then
722 if (!lcd
->lcd_pd
->lcd_enabled
) {
724 * if lcd panel was off from bootloader then
725 * current lcd status is powerdown and then
726 * it enables lcd panel.
728 lcd
->power
= FB_BLANK_POWERDOWN
;
730 ld9040_power(lcd
, FB_BLANK_UNBLANK
);
732 lcd
->power
= FB_BLANK_UNBLANK
;
734 dev_set_drvdata(&spi
->dev
, lcd
);
736 dev_info(&spi
->dev
, "ld9040 panel driver has been probed.\n");
740 lcd_device_unregister(lcd
->ld
);
746 static int __devexit
ld9040_remove(struct spi_device
*spi
)
748 struct ld9040
*lcd
= dev_get_drvdata(&spi
->dev
);
750 ld9040_power(lcd
, FB_BLANK_POWERDOWN
);
751 backlight_device_unregister(lcd
->bd
);
752 lcd_device_unregister(lcd
->ld
);
758 #if defined(CONFIG_PM)
759 static int ld9040_suspend(struct spi_device
*spi
, pm_message_t mesg
)
762 struct ld9040
*lcd
= dev_get_drvdata(&spi
->dev
);
764 dev_dbg(&spi
->dev
, "lcd->power = %d\n", lcd
->power
);
767 * when lcd panel is suspend, lcd panel becomes off
768 * regardless of status.
770 ret
= ld9040_power(lcd
, FB_BLANK_POWERDOWN
);
775 static int ld9040_resume(struct spi_device
*spi
)
778 struct ld9040
*lcd
= dev_get_drvdata(&spi
->dev
);
780 lcd
->power
= FB_BLANK_POWERDOWN
;
782 ret
= ld9040_power(lcd
, FB_BLANK_UNBLANK
);
787 #define ld9040_suspend NULL
788 #define ld9040_resume NULL
791 /* Power down all displays on reboot, poweroff or halt. */
792 static void ld9040_shutdown(struct spi_device
*spi
)
794 struct ld9040
*lcd
= dev_get_drvdata(&spi
->dev
);
796 ld9040_power(lcd
, FB_BLANK_POWERDOWN
);
799 static struct spi_driver ld9040_driver
= {
802 .bus
= &spi_bus_type
,
803 .owner
= THIS_MODULE
,
805 .probe
= ld9040_probe
,
806 .remove
= __devexit_p(ld9040_remove
),
807 .shutdown
= ld9040_shutdown
,
808 .suspend
= ld9040_suspend
,
809 .resume
= ld9040_resume
,
812 static int __init
ld9040_init(void)
814 return spi_register_driver(&ld9040_driver
);
817 static void __exit
ld9040_exit(void)
819 spi_unregister_driver(&ld9040_driver
);
822 module_init(ld9040_init
);
823 module_exit(ld9040_exit
);
825 MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>");
826 MODULE_DESCRIPTION("ld9040 LCD Driver");
827 MODULE_LICENSE("GPL");