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>
34 #include "ld9040_gamma.h"
36 #define SLEEPMSEC 0x1000
38 #define DEFMASK 0xFF00
39 #define COMMAND_ONLY 0xFE
40 #define DATA_ONLY 0xFF
42 #define MIN_BRIGHTNESS 0
43 #define MAX_BRIGHTNESS 24
44 #define power_is_on(pwr) ((pwr) <= FB_BLANK_NORMAL)
48 struct spi_device
*spi
;
50 unsigned int current_brightness
;
52 struct lcd_device
*ld
;
53 struct backlight_device
*bd
;
54 struct lcd_platform_data
*lcd_pd
;
57 static const unsigned short seq_swreset
[] = {
62 static const unsigned short seq_user_setting
[] = {
69 static const unsigned short seq_elvss_on
[] = {
77 static const unsigned short seq_gtcon
[] = {
85 static const unsigned short seq_panel_condition
[] = {
113 static const unsigned short seq_gamma_set1
[] = {
139 static const unsigned short seq_gamma_ctrl
[] = {
146 static const unsigned short seq_gamma_start
[] = {
152 static const unsigned short seq_apon
[] = {
162 static const unsigned short seq_display_ctrl
[] = {
172 static const unsigned short seq_manual_pwr
[] = {
177 static const unsigned short seq_pwr_ctrl
[] = {
189 static const unsigned short seq_sleep_out
[] = {
194 static const unsigned short seq_sleep_in
[] = {
199 static const unsigned short seq_display_on
[] = {
204 static const unsigned short seq_display_off
[] = {
209 static const unsigned short seq_vci1_1st_en
[] = {
219 static const unsigned short seq_vl1_en
[] = {
229 static const unsigned short seq_vl2_en
[] = {
239 static const unsigned short seq_vci1_2nd_en
[] = {
249 static const unsigned short seq_vl3_en
[] = {
259 static const unsigned short seq_vreg1_amp_en
[] = {
269 static const unsigned short seq_vgh_amp_en
[] = {
279 static const unsigned short seq_vgl_amp_en
[] = {
289 static const unsigned short seq_vmos_amp_en
[] = {
299 static const unsigned short seq_vint_amp_en
[] = {
303 /* DATA_ONLY, 0x71, VMOS/VBL/VBH not used */
307 /* DATA_ONLY, 0x02, VMOS/VBL/VBH not used */
311 static const unsigned short seq_vbh_amp_en
[] = {
321 static const unsigned short seq_vbl_amp_en
[] = {
331 static const unsigned short seq_gam_amp_en
[] = {
335 /* DATA_ONLY, 0x73, VMOS/VBL/VBH not used */
339 /* DATA_ONLY, 0x02, VMOS/VBL/VBH not used */
343 static const unsigned short seq_sd_amp_en
[] = {
347 /* DATA_ONLY, 0x73, VMOS/VBL/VBH not used */
351 /* DATA_ONLY, 0x02, VMOS/VBL/VBH not used */
355 static const unsigned short seq_gls_en
[] = {
359 /* DATA_ONLY, 0x73, VMOS/VBL/VBH not used */
363 /* DATA_ONLY, 0x02, VMOS/VBL/VBH not used */
367 static const unsigned short seq_els_en
[] = {
371 /* DATA_ONLY, 0x73, VMOS/VBL/VBH not used */
375 /* DATA_ONLY, 0x02, VMOS/VBL/VBH not used */
379 static const unsigned short seq_el_on
[] = {
383 /* DATA_ONLY, 0x73, VMOS/VBL/VBH not used */
387 /* DATA_ONLY, 0x02, VMOS/VBL/VBH not used */
391 static int ld9040_spi_write_byte(struct ld9040
*lcd
, int addr
, int data
)
394 struct spi_message msg
;
396 struct spi_transfer xfer
= {
401 buf
[0] = (addr
<< 8) | data
;
403 spi_message_init(&msg
);
404 spi_message_add_tail(&xfer
, &msg
);
406 return spi_sync(lcd
->spi
, &msg
);
409 static int ld9040_spi_write(struct ld9040
*lcd
, unsigned char address
,
410 unsigned char command
)
414 if (address
!= DATA_ONLY
)
415 ret
= ld9040_spi_write_byte(lcd
, 0x0, address
);
416 if (command
!= COMMAND_ONLY
)
417 ret
= ld9040_spi_write_byte(lcd
, 0x1, command
);
422 static int ld9040_panel_send_sequence(struct ld9040
*lcd
,
423 const unsigned short *wbuf
)
427 while ((wbuf
[i
] & DEFMASK
) != ENDDEF
) {
428 if ((wbuf
[i
] & DEFMASK
) != SLEEPMSEC
) {
429 ret
= ld9040_spi_write(lcd
, wbuf
[i
], wbuf
[i
+1]);
433 udelay(wbuf
[i
+1]*1000);
440 static int _ld9040_gamma_ctl(struct ld9040
*lcd
, const unsigned int *gamma
)
445 /* start gamma table updating. */
446 ret
= ld9040_panel_send_sequence(lcd
, seq_gamma_start
);
448 dev_err(lcd
->dev
, "failed to disable gamma table updating.\n");
452 for (i
= 0 ; i
< GAMMA_TABLE_COUNT
; i
++) {
453 ret
= ld9040_spi_write(lcd
, DATA_ONLY
, gamma
[i
]);
455 dev_err(lcd
->dev
, "failed to set gamma table.\n");
460 /* update gamma table. */
461 ret
= ld9040_panel_send_sequence(lcd
, seq_gamma_ctrl
);
463 dev_err(lcd
->dev
, "failed to update gamma table.\n");
469 static int ld9040_gamma_ctl(struct ld9040
*lcd
, int gamma
)
473 ret
= _ld9040_gamma_ctl(lcd
, gamma_table
.gamma_22_table
[gamma
]);
479 static int ld9040_ldi_init(struct ld9040
*lcd
)
482 static const unsigned short *init_seq
[] = {
494 for (i
= 0; i
< ARRAY_SIZE(init_seq
); i
++) {
495 ret
= ld9040_panel_send_sequence(lcd
, init_seq
[i
]);
496 /* workaround: minimum delay time for transferring CMD */
505 static int ld9040_ldi_enable(struct ld9040
*lcd
)
509 ret
= ld9040_panel_send_sequence(lcd
, seq_display_on
);
514 static int ld9040_ldi_disable(struct ld9040
*lcd
)
518 ret
= ld9040_panel_send_sequence(lcd
, seq_display_off
);
519 ret
= ld9040_panel_send_sequence(lcd
, seq_sleep_in
);
524 static int ld9040_power_on(struct ld9040
*lcd
)
527 struct lcd_platform_data
*pd
= NULL
;
530 dev_err(lcd
->dev
, "platform data is NULL.\n");
535 dev_err(lcd
->dev
, "power_on is NULL.\n");
538 pd
->power_on(lcd
->ld
, 1);
539 mdelay(pd
->power_on_delay
);
543 dev_err(lcd
->dev
, "reset is NULL.\n");
547 mdelay(pd
->reset_delay
);
550 ret
= ld9040_ldi_init(lcd
);
552 dev_err(lcd
->dev
, "failed to initialize ldi.\n");
556 ret
= ld9040_ldi_enable(lcd
);
558 dev_err(lcd
->dev
, "failed to enable ldi.\n");
565 static int ld9040_power_off(struct ld9040
*lcd
)
568 struct lcd_platform_data
*pd
= NULL
;
572 dev_err(lcd
->dev
, "platform data is NULL.\n");
576 ret
= ld9040_ldi_disable(lcd
);
578 dev_err(lcd
->dev
, "lcd setting failed.\n");
582 mdelay(pd
->power_off_delay
);
585 dev_err(lcd
->dev
, "power_on is NULL.\n");
588 pd
->power_on(lcd
->ld
, 0);
593 static int ld9040_power(struct ld9040
*lcd
, int power
)
597 if (power_is_on(power
) && !power_is_on(lcd
->power
))
598 ret
= ld9040_power_on(lcd
);
599 else if (!power_is_on(power
) && power_is_on(lcd
->power
))
600 ret
= ld9040_power_off(lcd
);
608 static int ld9040_set_power(struct lcd_device
*ld
, int power
)
610 struct ld9040
*lcd
= lcd_get_data(ld
);
612 if (power
!= FB_BLANK_UNBLANK
&& power
!= FB_BLANK_POWERDOWN
&&
613 power
!= FB_BLANK_NORMAL
) {
614 dev_err(lcd
->dev
, "power value should be 0, 1 or 4.\n");
618 return ld9040_power(lcd
, power
);
621 static int ld9040_get_power(struct lcd_device
*ld
)
623 struct ld9040
*lcd
= lcd_get_data(ld
);
628 static int ld9040_get_brightness(struct backlight_device
*bd
)
630 return bd
->props
.brightness
;
633 static int ld9040_set_brightness(struct backlight_device
*bd
)
635 int ret
= 0, brightness
= bd
->props
.brightness
;
636 struct ld9040
*lcd
= bl_get_data(bd
);
638 if (brightness
< MIN_BRIGHTNESS
||
639 brightness
> bd
->props
.max_brightness
) {
640 dev_err(&bd
->dev
, "lcd brightness should be %d to %d.\n",
641 MIN_BRIGHTNESS
, MAX_BRIGHTNESS
);
645 ret
= ld9040_gamma_ctl(lcd
, bd
->props
.brightness
);
647 dev_err(&bd
->dev
, "lcd brightness setting failed.\n");
654 static struct lcd_ops ld9040_lcd_ops
= {
655 .set_power
= ld9040_set_power
,
656 .get_power
= ld9040_get_power
,
659 static const struct backlight_ops ld9040_backlight_ops
= {
660 .get_brightness
= ld9040_get_brightness
,
661 .update_status
= ld9040_set_brightness
,
665 static int ld9040_probe(struct spi_device
*spi
)
668 struct ld9040
*lcd
= NULL
;
669 struct lcd_device
*ld
= NULL
;
670 struct backlight_device
*bd
= NULL
;
671 struct backlight_properties props
;
673 lcd
= kzalloc(sizeof(struct ld9040
), GFP_KERNEL
);
677 /* ld9040 lcd panel uses 3-wire 9bits SPI Mode. */
678 spi
->bits_per_word
= 9;
680 ret
= spi_setup(spi
);
682 dev_err(&spi
->dev
, "spi setup failed.\n");
687 lcd
->dev
= &spi
->dev
;
689 lcd
->lcd_pd
= spi
->dev
.platform_data
;
691 dev_err(&spi
->dev
, "platform data is NULL.\n");
695 ld
= lcd_device_register("ld9040", &spi
->dev
, lcd
, &ld9040_lcd_ops
);
703 memset(&props
, 0, sizeof(struct backlight_properties
));
704 props
.type
= BACKLIGHT_RAW
;
705 props
.max_brightness
= MAX_BRIGHTNESS
;
707 bd
= backlight_device_register("ld9040-bl", &spi
->dev
,
708 lcd
, &ld9040_backlight_ops
, &props
);
711 goto out_unregister_lcd
;
714 bd
->props
.brightness
= MAX_BRIGHTNESS
;
718 * if lcd panel was on from bootloader like u-boot then
721 if (!lcd
->lcd_pd
->lcd_enabled
) {
723 * if lcd panel was off from bootloader then
724 * current lcd status is powerdown and then
725 * it enables lcd panel.
727 lcd
->power
= FB_BLANK_POWERDOWN
;
729 ld9040_power(lcd
, FB_BLANK_UNBLANK
);
731 lcd
->power
= FB_BLANK_UNBLANK
;
733 dev_set_drvdata(&spi
->dev
, lcd
);
735 dev_info(&spi
->dev
, "ld9040 panel driver has been probed.\n");
739 lcd_device_unregister(lcd
->ld
);
745 static int __devexit
ld9040_remove(struct spi_device
*spi
)
747 struct ld9040
*lcd
= dev_get_drvdata(&spi
->dev
);
749 ld9040_power(lcd
, FB_BLANK_POWERDOWN
);
750 backlight_device_unregister(lcd
->bd
);
751 lcd_device_unregister(lcd
->ld
);
757 #if defined(CONFIG_PM)
758 static int ld9040_suspend(struct spi_device
*spi
, pm_message_t mesg
)
761 struct ld9040
*lcd
= dev_get_drvdata(&spi
->dev
);
763 dev_dbg(&spi
->dev
, "lcd->power = %d\n", lcd
->power
);
766 * when lcd panel is suspend, lcd panel becomes off
767 * regardless of status.
769 ret
= ld9040_power(lcd
, FB_BLANK_POWERDOWN
);
774 static int ld9040_resume(struct spi_device
*spi
)
777 struct ld9040
*lcd
= dev_get_drvdata(&spi
->dev
);
779 lcd
->power
= FB_BLANK_POWERDOWN
;
781 ret
= ld9040_power(lcd
, FB_BLANK_UNBLANK
);
786 #define ld9040_suspend NULL
787 #define ld9040_resume NULL
790 /* Power down all displays on reboot, poweroff or halt. */
791 static void ld9040_shutdown(struct spi_device
*spi
)
793 struct ld9040
*lcd
= dev_get_drvdata(&spi
->dev
);
795 ld9040_power(lcd
, FB_BLANK_POWERDOWN
);
798 static struct spi_driver ld9040_driver
= {
801 .bus
= &spi_bus_type
,
802 .owner
= THIS_MODULE
,
804 .probe
= ld9040_probe
,
805 .remove
= __devexit_p(ld9040_remove
),
806 .shutdown
= ld9040_shutdown
,
807 .suspend
= ld9040_suspend
,
808 .resume
= ld9040_resume
,
811 static int __init
ld9040_init(void)
813 return spi_register_driver(&ld9040_driver
);
816 static void __exit
ld9040_exit(void)
818 spi_unregister_driver(&ld9040_driver
);
821 module_init(ld9040_init
);
822 module_exit(ld9040_exit
);
824 MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>");
825 MODULE_DESCRIPTION("ld9040 LCD Driver");
826 MODULE_LICENSE("GPL");