2 * S6E63M0 AMOLED LCD panel driver.
4 * Author: InKi Dae <inki.dae@samsung.com>
6 * Derived from drivers/video/omap/lcd-apollon.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 "s6e63m0_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 10
48 struct spi_device *spi;
50 unsigned int current_brightness;
51 unsigned int gamma_mode;
52 unsigned int gamma_table_count;
53 struct lcd_device *ld;
54 struct backlight_device *bd;
55 struct lcd_platform_data *lcd_pd;
58 static const unsigned short seq_panel_condition_set[] = {
77 static const unsigned short seq_display_condition_set[] = {
91 static const unsigned short seq_gamma_setting[] = {
120 static const unsigned short seq_etc_condition_set[] = {
319 static const unsigned short seq_acl_on[] = {
326 static const unsigned short seq_acl_off[] = {
333 static const unsigned short seq_elvss_on[] = {
340 static const unsigned short seq_elvss_off[] = {
347 static const unsigned short seq_stand_by_off[] = {
353 static const unsigned short seq_stand_by_on[] = {
359 static const unsigned short seq_display_on[] = {
366 static int s6e63m0_spi_write_byte(struct s6e63m0 *lcd, int addr, int data)
369 struct spi_message msg;
371 struct spi_transfer xfer = {
376 buf[0] = (addr << 8) | data;
378 spi_message_init(&msg);
379 spi_message_add_tail(&xfer, &msg);
381 return spi_sync(lcd->spi, &msg);
384 static int s6e63m0_spi_write(struct s6e63m0 *lcd, unsigned char address,
385 unsigned char command)
389 if (address != DATA_ONLY)
390 ret = s6e63m0_spi_write_byte(lcd, 0x0, address);
391 if (command != COMMAND_ONLY)
392 ret = s6e63m0_spi_write_byte(lcd, 0x1, command);
397 static int s6e63m0_panel_send_sequence(struct s6e63m0 *lcd,
398 const unsigned short *wbuf)
402 while ((wbuf[i] & DEFMASK) != ENDDEF) {
403 if ((wbuf[i] & DEFMASK) != SLEEPMSEC) {
404 ret = s6e63m0_spi_write(lcd, wbuf[i], wbuf[i+1]);
416 static int _s6e63m0_gamma_ctl(struct s6e63m0 *lcd, const unsigned int *gamma)
421 /* disable gamma table updating. */
422 ret = s6e63m0_spi_write(lcd, 0xfa, 0x00);
424 dev_err(lcd->dev, "failed to disable gamma table updating.\n");
428 for (i = 0 ; i < GAMMA_TABLE_COUNT; i++) {
429 ret = s6e63m0_spi_write(lcd, DATA_ONLY, gamma[i]);
431 dev_err(lcd->dev, "failed to set gamma table.\n");
436 /* update gamma table. */
437 ret = s6e63m0_spi_write(lcd, 0xfa, 0x01);
439 dev_err(lcd->dev, "failed to update gamma table.\n");
445 static int s6e63m0_gamma_ctl(struct s6e63m0 *lcd, int gamma)
449 ret = _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_22_table[gamma]);
455 static int s6e63m0_ldi_init(struct s6e63m0 *lcd)
458 const unsigned short *init_seq[] = {
459 seq_panel_condition_set,
460 seq_display_condition_set,
462 seq_etc_condition_set,
467 for (i = 0; i < ARRAY_SIZE(init_seq); i++) {
468 ret = s6e63m0_panel_send_sequence(lcd, init_seq[i]);
476 static int s6e63m0_ldi_enable(struct s6e63m0 *lcd)
479 const unsigned short *enable_seq[] = {
484 for (i = 0; i < ARRAY_SIZE(enable_seq); i++) {
485 ret = s6e63m0_panel_send_sequence(lcd, enable_seq[i]);
493 static int s6e63m0_ldi_disable(struct s6e63m0 *lcd)
497 ret = s6e63m0_panel_send_sequence(lcd, seq_stand_by_on);
502 static int s6e63m0_power_is_on(int power)
504 return power <= FB_BLANK_NORMAL;
507 static int s6e63m0_power_on(struct s6e63m0 *lcd)
510 struct lcd_platform_data *pd;
511 struct backlight_device *bd;
517 dev_err(lcd->dev, "power_on is NULL.\n");
520 pd->power_on(lcd->ld, 1);
521 msleep(pd->power_on_delay);
525 dev_err(lcd->dev, "reset is NULL.\n");
529 msleep(pd->reset_delay);
532 ret = s6e63m0_ldi_init(lcd);
534 dev_err(lcd->dev, "failed to initialize ldi.\n");
538 ret = s6e63m0_ldi_enable(lcd);
540 dev_err(lcd->dev, "failed to enable ldi.\n");
544 /* set brightness to current value after power on or resume. */
545 ret = s6e63m0_gamma_ctl(lcd, bd->props.brightness);
547 dev_err(lcd->dev, "lcd gamma setting failed.\n");
554 static int s6e63m0_power_off(struct s6e63m0 *lcd)
557 struct lcd_platform_data *pd;
561 ret = s6e63m0_ldi_disable(lcd);
563 dev_err(lcd->dev, "lcd setting failed.\n");
567 msleep(pd->power_off_delay);
569 pd->power_on(lcd->ld, 0);
574 static int s6e63m0_power(struct s6e63m0 *lcd, int power)
578 if (s6e63m0_power_is_on(power) && !s6e63m0_power_is_on(lcd->power))
579 ret = s6e63m0_power_on(lcd);
580 else if (!s6e63m0_power_is_on(power) && s6e63m0_power_is_on(lcd->power))
581 ret = s6e63m0_power_off(lcd);
589 static int s6e63m0_set_power(struct lcd_device *ld, int power)
591 struct s6e63m0 *lcd = lcd_get_data(ld);
593 if (power != FB_BLANK_UNBLANK && power != FB_BLANK_POWERDOWN &&
594 power != FB_BLANK_NORMAL) {
595 dev_err(lcd->dev, "power value should be 0, 1 or 4.\n");
599 return s6e63m0_power(lcd, power);
602 static int s6e63m0_get_power(struct lcd_device *ld)
604 struct s6e63m0 *lcd = lcd_get_data(ld);
609 static int s6e63m0_get_brightness(struct backlight_device *bd)
611 return bd->props.brightness;
614 static int s6e63m0_set_brightness(struct backlight_device *bd)
616 int ret = 0, brightness = bd->props.brightness;
617 struct s6e63m0 *lcd = bl_get_data(bd);
619 if (brightness < MIN_BRIGHTNESS ||
620 brightness > bd->props.max_brightness) {
621 dev_err(&bd->dev, "lcd brightness should be %d to %d.\n",
622 MIN_BRIGHTNESS, MAX_BRIGHTNESS);
626 ret = s6e63m0_gamma_ctl(lcd, bd->props.brightness);
628 dev_err(&bd->dev, "lcd brightness setting failed.\n");
635 static struct lcd_ops s6e63m0_lcd_ops = {
636 .set_power = s6e63m0_set_power,
637 .get_power = s6e63m0_get_power,
640 static const struct backlight_ops s6e63m0_backlight_ops = {
641 .get_brightness = s6e63m0_get_brightness,
642 .update_status = s6e63m0_set_brightness,
645 static ssize_t s6e63m0_sysfs_show_gamma_mode(struct device *dev,
646 struct device_attribute *attr, char *buf)
648 struct s6e63m0 *lcd = dev_get_drvdata(dev);
651 switch (lcd->gamma_mode) {
653 sprintf(temp, "2.2 mode\n");
657 sprintf(temp, "1.9 mode\n");
661 sprintf(temp, "1.7 mode\n");
665 dev_info(dev, "gamma mode could be 0:2.2, 1:1.9 or 2:1.7)n");
672 static ssize_t s6e63m0_sysfs_store_gamma_mode(struct device *dev,
673 struct device_attribute *attr,
674 const char *buf, size_t len)
676 struct s6e63m0 *lcd = dev_get_drvdata(dev);
677 struct backlight_device *bd = NULL;
680 rc = kstrtouint(buf, 0, &lcd->gamma_mode);
686 brightness = bd->props.brightness;
688 switch (lcd->gamma_mode) {
690 _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_22_table[brightness]);
693 _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_19_table[brightness]);
696 _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_17_table[brightness]);
699 dev_info(dev, "gamma mode could be 0:2.2, 1:1.9 or 2:1.7\n");
700 _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_22_table[brightness]);
706 static DEVICE_ATTR(gamma_mode, 0644,
707 s6e63m0_sysfs_show_gamma_mode, s6e63m0_sysfs_store_gamma_mode);
709 static ssize_t s6e63m0_sysfs_show_gamma_table(struct device *dev,
710 struct device_attribute *attr, char *buf)
712 struct s6e63m0 *lcd = dev_get_drvdata(dev);
715 sprintf(temp, "%d\n", lcd->gamma_table_count);
720 static DEVICE_ATTR(gamma_table, 0444,
721 s6e63m0_sysfs_show_gamma_table, NULL);
723 static int s6e63m0_probe(struct spi_device *spi)
726 struct s6e63m0 *lcd = NULL;
727 struct lcd_device *ld = NULL;
728 struct backlight_device *bd = NULL;
729 struct backlight_properties props;
731 lcd = devm_kzalloc(&spi->dev, sizeof(struct s6e63m0), GFP_KERNEL);
735 /* s6e63m0 lcd panel uses 3-wire 9bits SPI Mode. */
736 spi->bits_per_word = 9;
738 ret = spi_setup(spi);
740 dev_err(&spi->dev, "spi setup failed.\n");
745 lcd->dev = &spi->dev;
747 lcd->lcd_pd = spi->dev.platform_data;
749 dev_err(&spi->dev, "platform data is NULL.\n");
753 ld = lcd_device_register("s6e63m0", &spi->dev, lcd, &s6e63m0_lcd_ops);
759 memset(&props, 0, sizeof(struct backlight_properties));
760 props.type = BACKLIGHT_RAW;
761 props.max_brightness = MAX_BRIGHTNESS;
763 bd = backlight_device_register("s6e63m0bl-bl", &spi->dev, lcd,
764 &s6e63m0_backlight_ops, &props);
767 goto out_lcd_unregister;
770 bd->props.brightness = MAX_BRIGHTNESS;
774 * it gets gamma table count available so it gets user
777 lcd->gamma_table_count =
778 sizeof(gamma_table) / (MAX_GAMMA_LEVEL * sizeof(int));
780 ret = device_create_file(&(spi->dev), &dev_attr_gamma_mode);
782 dev_err(&(spi->dev), "failed to add sysfs entries\n");
784 ret = device_create_file(&(spi->dev), &dev_attr_gamma_table);
786 dev_err(&(spi->dev), "failed to add sysfs entries\n");
789 * if lcd panel was on from bootloader like u-boot then
792 if (!lcd->lcd_pd->lcd_enabled) {
794 * if lcd panel was off from bootloader then
795 * current lcd status is powerdown and then
796 * it enables lcd panel.
798 lcd->power = FB_BLANK_POWERDOWN;
800 s6e63m0_power(lcd, FB_BLANK_UNBLANK);
802 lcd->power = FB_BLANK_UNBLANK;
805 dev_set_drvdata(&spi->dev, lcd);
807 dev_info(&spi->dev, "s6e63m0 panel driver has been probed.\n");
812 lcd_device_unregister(ld);
816 static int s6e63m0_remove(struct spi_device *spi)
818 struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
820 s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
821 device_remove_file(&spi->dev, &dev_attr_gamma_table);
822 device_remove_file(&spi->dev, &dev_attr_gamma_mode);
823 backlight_device_unregister(lcd->bd);
824 lcd_device_unregister(lcd->ld);
829 #if defined(CONFIG_PM)
830 static unsigned int before_power;
832 static int s6e63m0_suspend(struct spi_device *spi, pm_message_t mesg)
835 struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
837 dev_dbg(&spi->dev, "lcd->power = %d\n", lcd->power);
839 before_power = lcd->power;
842 * when lcd panel is suspend, lcd panel becomes off
843 * regardless of status.
845 ret = s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
850 static int s6e63m0_resume(struct spi_device *spi)
853 struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
856 * after suspended, if lcd panel status is FB_BLANK_UNBLANK
857 * (at that time, before_power is FB_BLANK_UNBLANK) then
858 * it changes that status to FB_BLANK_POWERDOWN to get lcd on.
860 if (before_power == FB_BLANK_UNBLANK)
861 lcd->power = FB_BLANK_POWERDOWN;
863 dev_dbg(&spi->dev, "before_power = %d\n", before_power);
865 ret = s6e63m0_power(lcd, before_power);
870 #define s6e63m0_suspend NULL
871 #define s6e63m0_resume NULL
874 /* Power down all displays on reboot, poweroff or halt. */
875 static void s6e63m0_shutdown(struct spi_device *spi)
877 struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
879 s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
882 static struct spi_driver s6e63m0_driver = {
885 .owner = THIS_MODULE,
887 .probe = s6e63m0_probe,
888 .remove = s6e63m0_remove,
889 .shutdown = s6e63m0_shutdown,
890 .suspend = s6e63m0_suspend,
891 .resume = s6e63m0_resume,
894 module_spi_driver(s6e63m0_driver);
896 MODULE_AUTHOR("InKi Dae <inki.dae@samsung.com>");
897 MODULE_DESCRIPTION("S6E63M0 LCD Driver");
898 MODULE_LICENSE("GPL");