diff options
Diffstat (limited to 'drivers/video/backlight')
-rw-r--r-- | drivers/video/backlight/Kconfig | 76 | ||||
-rw-r--r-- | drivers/video/backlight/Makefile | 8 | ||||
-rw-r--r-- | drivers/video/backlight/ams369fg06.c | 636 | ||||
-rw-r--r-- | drivers/video/backlight/ams369fg06_gamma.h | 61 | ||||
-rw-r--r-- | drivers/video/backlight/backlight.c | 46 | ||||
-rw-r--r-- | drivers/video/backlight/lcd.c | 22 | ||||
-rw-r--r-- | drivers/video/backlight/lms501kf03.c | 598 | ||||
-rw-r--r-- | drivers/video/backlight/pwm_bl.c | 8 | ||||
-rw-r--r-- | drivers/video/backlight/s6e39a0x02.c | 799 | ||||
-rw-r--r-- | drivers/video/backlight/s6e39a0x02_gamma.h | 180 | ||||
-rw-r--r-- | drivers/video/backlight/s6e63m0_mipi_lcd.c | 166 | ||||
-rw-r--r-- | drivers/video/backlight/s6e8aa0.c | 1187 | ||||
-rw-r--r-- | drivers/video/backlight/s6e8aa0.h | 26 | ||||
-rw-r--r-- | drivers/video/backlight/s6e8aa0_gamma.h | 220 | ||||
-rw-r--r-- | drivers/video/backlight/s6e8aa0_volt_tbl.h | 967 | ||||
-rw-r--r-- | drivers/video/backlight/s6e8ab0_mipi_lcd.c | 77 | ||||
-rw-r--r-- | drivers/video/backlight/smart_dimming.c | 771 | ||||
-rw-r--r-- | drivers/video/backlight/smart_dimming.h | 104 | ||||
-rw-r--r-- | drivers/video/backlight/tc358764_mipi_lcd.c | 110 |
19 files changed, 6062 insertions, 0 deletions
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index 2d93c8d..80f42e6 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -109,6 +109,30 @@ config LCD_S6E63M0 If you have an S6E63M0 LCD Panel, say Y to enable its LCD control driver. +config LCD_MIPI_S6E63M0 + tristate "S6E63M0 AMOLED MIPI LCD Driver" + depends on BACKLIGHT_CLASS_DEVICE + default n + help + If you have an S6E63M0 MIPI LCD Panel, say Y to enable its + LCD control driver. + +config LCD_MIPI_S6E8AB0 + tristate "1280 X 800 S6E8AB0 AMOLED MIPI LCD Driver" + depends on BACKLIGHT_CLASS_DEVICE + default n + help + If you have an S6E8AB0 MIPI LCD Panel, say Y to enable its + LCD control driver. + +config LCD_MIPI_TC358764 + tristate "1280 X 800 TC358764 AMOLED MIPI LCD Driver" + depends on BACKLIGHT_CLASS_DEVICE + default n + help + If you have an TC358764 MIPI LCD Panel, say Y to enable its + LCD control driver. + config LCD_LD9040 tristate "LD9040 AMOLED LCD Driver" depends on SPI && BACKLIGHT_CLASS_DEVICE @@ -117,6 +141,52 @@ config LCD_LD9040 If you have an LD9040 Panel, say Y to enable its control driver. +config LCD_AMS369FG06 + tristate "AMS369FG06 AMOLED LCD Driver" + depends on SPI_GPIO && BACKLIGHT_CLASS_DEVICE && FB_S3C + default y + help + If you have an AMS369FG06 AMOLED Panel, say Y to enable its + LCD control driver. + +config LCD_LMS501KF03 + tristate "LMS501KF03 TFT LCD Driver" + depends on SPI_GPIO && BACKLIGHT_CLASS_DEVICE && FB_S3C + default y + help + If you have an LMS501KF03 AMOLED Panel, say Y to enable its + LCD control driver. + +config LCD_WA101S + tristate "LCD_WA101S LCD Driver" + default n + help + If you have a WA101S Panel(WXGA), say Y to enable its + LCD control driver. + +config LCD_LTE480WV + tristate "LCD_LTE480WV LCD Driver" + default n + help + If you have an LTE480 Panel(WVGA), say Y to enable its + LCD control drvier. + +config LCD_S6E8AA0 + tristate "S6E8AA0 MIPI AMOLED LCD Driver" + depends on S5P_MIPI_DSI2 && BACKLIGHT_CLASS_DEVICE && \ + (MACH_SLP_MIDAS || MACH_SLP_PQ_LTE || MACH_SLP_PQ) + default n + help + If you have an S6E8AA0 MIPI AMOLED LCD Panel, say Y to enable its + LCD control driver. + +config LCD_S6E39A0X02 + tristate "S6E39A0X02 MIPI AMOLED LCD Driver" + depends on S5P_MIPI_DSI2 && BACKLIGHT_CLASS_DEVICE + default n + help + If you have an S6E39A0X02 MIPI AMOLED LCD Panel, say Y to enable its + LCD control driver. endif # LCD_CLASS_DEVICE # @@ -327,6 +397,12 @@ config BACKLIGHT_PCF50633 If you have a backlight driven by a NXP PCF50633 MFD, say Y here to enable its driver. +config BACKLIGHT_SMART_DIMMING + bool "SLP Backlight driver feature for smart dimming" + depends on (MACH_SLP_MIDAS || MACH_SLP_PQ_LTE || MACH_SLP_PQ) + help + Say Y to enable the Smart Dimming Feature. + endif # BACKLIGHT_CLASS_DEVICE endif # BACKLIGHT_LCD_SUPPORT diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index ee72adb..16737d6 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -12,7 +12,14 @@ obj-$(CONFIG_LCD_VGG2432A4) += vgg2432a4.o obj-$(CONFIG_LCD_TDO24M) += tdo24m.o obj-$(CONFIG_LCD_TOSA) += tosa_lcd.o obj-$(CONFIG_LCD_S6E63M0) += s6e63m0.o +obj-$(CONFIG_LCD_MIPI_S6E63M0) += s6e63m0_mipi_lcd.o +obj-$(CONFIG_LCD_MIPI_S6E8AB0) += s6e8ab0_mipi_lcd.o +obj-$(CONFIG_LCD_MIPI_TC358764) += tc358764_mipi_lcd.o obj-$(CONFIG_LCD_LD9040) += ld9040.o +obj-$(CONFIG_LCD_AMS369FG06) += ams369fg06.o +obj-$(CONFIG_LCD_S6E8AA0) += s6e8aa0.o +obj-$(CONFIG_LCD_S6E39A0X02) += s6e39a0x02.o +obj-$(CONFIG_LCD_LMS501KF03) += lms501kf03.o obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o obj-$(CONFIG_BACKLIGHT_ATMEL_PWM) += atmel-pwm-bl.o @@ -37,4 +44,5 @@ obj-$(CONFIG_BACKLIGHT_ADP8860) += adp8860_bl.o obj-$(CONFIG_BACKLIGHT_ADP8870) += adp8870_bl.o obj-$(CONFIG_BACKLIGHT_88PM860X) += 88pm860x_bl.o obj-$(CONFIG_BACKLIGHT_PCF50633) += pcf50633-backlight.o +obj-$(CONFIG_BACKLIGHT_SMART_DIMMING) += smart_dimming.o diff --git a/drivers/video/backlight/ams369fg06.c b/drivers/video/backlight/ams369fg06.c new file mode 100644 index 0000000..66643a1 --- /dev/null +++ b/drivers/video/backlight/ams369fg06.c @@ -0,0 +1,636 @@ +/* + * ams369fg06 AMOLED LCD panel driver. + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * Author: Jingoo Han <jg1.han@samsung.com> + * + * Derived from drivers/video/s6e63m0.c + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <linux/wait.h> +#include <linux/fb.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/spi/spi.h> +#include <linux/lcd.h> +#include <linux/backlight.h> +#ifdef CONFIG_HAS_EARLYSUSPEND +#include <linux/earlysuspend.h> +#endif + +#include "ams369fg06_gamma.h" + +#define SLEEPMSEC 0x1000 +#define ENDDEF 0x2000 +#define DEFMASK 0xFF00 +#define COMMAND_ONLY 0xFE +#define DATA_ONLY 0xFF + +#define MIN_BRIGHTNESS 0 +#define MAX_BRIGHTNESS 255 +#define DEFAULT_BRIGHTNESS 150 + +#define POWER_IS_ON(pwr) ((pwr) <= FB_BLANK_NORMAL) + +struct ams369fg06 { + struct device *dev; + struct spi_device *spi; + unsigned int power; + struct lcd_device *ld; + struct backlight_device *bd; + struct lcd_platform_data *lcd_pd; +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend early_suspend; +#endif +}; + +const unsigned short SEQ_DISPLAY_ON[] = { + 0x14, 0x03, + ENDDEF, 0x0000 +}; + +const unsigned short SEQ_DISPLAY_OFF[] = { + 0x14, 0x00, + ENDDEF, 0x0000 +}; + +const unsigned short SEQ_STAND_BY_ON[] = { + 0x1D, 0xA1, + SLEEPMSEC, 200, + ENDDEF, 0x0000 +}; + +const unsigned short SEQ_STAND_BY_OFF[] = { + 0x1D, 0xA0, + SLEEPMSEC, 250, + ENDDEF, 0x0000 +}; + +const unsigned short SEQ_SETTING[] = { + 0x31, 0x08, + 0x32, 0x14, + 0x30, 0x02, + 0x27, 0x01, + 0x12, 0x08, + 0x13, 0x08, + 0x15, 0x00, + 0x16, 0x00, + + 0xef, 0xd0, + DATA_ONLY, 0xe8, + + 0x39, 0x44, + 0x40, 0x00, + 0x41, 0x3f, + 0x42, 0x2a, + 0x43, 0x27, + 0x44, 0x27, + 0x45, 0x1f, + 0x46, 0x44, + 0x50, 0x00, + 0x51, 0x00, + 0x52, 0x17, + 0x53, 0x24, + 0x54, 0x26, + 0x55, 0x1f, + 0x56, 0x43, + 0x60, 0x00, + 0x61, 0x3f, + 0x62, 0x2a, + 0x63, 0x25, + 0x64, 0x24, + 0x65, 0x1b, + 0x66, 0x5c, + + 0x17, 0x22, + 0x18, 0x33, + 0x19, 0x03, + 0x1a, 0x01, + 0x22, 0xa4, + 0x23, 0x00, + 0x26, 0xa0, + + 0x1d, 0xa0, + SLEEPMSEC, 300, + + 0x14, 0x03, + + ENDDEF, 0x0000 +}; + +static int ams369fg06_spi_write_byte(struct ams369fg06 *lcd, int addr, int data) +{ + u16 buf[1]; + struct spi_message msg; + + struct spi_transfer xfer = { + .len = 2, + .tx_buf = buf, + }; + + buf[0] = (addr << 8) | data; + + spi_message_init(&msg); + spi_message_add_tail(&xfer, &msg); + + return spi_sync(lcd->spi, &msg); +} + +static int ams369fg06_spi_write(struct ams369fg06 *lcd, unsigned char address, + unsigned char command) +{ + int ret = 0; + + if (address != DATA_ONLY) + ret = ams369fg06_spi_write_byte(lcd, 0x70, address); + if (command != COMMAND_ONLY) + ret = ams369fg06_spi_write_byte(lcd, 0x72, command); + + return ret; +} + +static int ams369fg06_panel_send_sequence(struct ams369fg06 *lcd, + const unsigned short *wbuf) +{ + int ret = 0, i = 0; + + while ((wbuf[i] & DEFMASK) != ENDDEF) { + if ((wbuf[i] & DEFMASK) != SLEEPMSEC) { + ret = ams369fg06_spi_write(lcd, wbuf[i], wbuf[i+1]); + if (ret) + break; + } else + udelay(wbuf[i+1]*1000); + i += 2; + } + + return ret; +} + +static int _ams369fg06_gamma_ctl(struct ams369fg06 *lcd, + const unsigned int *gamma) +{ + unsigned int i = 0; + int ret = 0; + + for (i = 0 ; i < GAMMA_TABLE_COUNT / 3; i++) { + ret = ams369fg06_spi_write(lcd, 0x40 + i, gamma[i]); + ret = ams369fg06_spi_write(lcd, 0x50 + i, gamma[i+7*1]); + ret = ams369fg06_spi_write(lcd, 0x60 + i, gamma[i+7*2]); + if (ret) { + dev_err(lcd->dev, "failed to set gamma table.\n"); + goto gamma_err; + } + } + +gamma_err: + return ret; +} + +static int ams369fg06_gamma_ctl(struct ams369fg06 *lcd, int brightness) +{ + int ret = 0; + int gamma = 0; + + if ((brightness >= 0) && (brightness <= 50)) + gamma = 0; + else if ((brightness > 50) && (brightness <= 100)) + gamma = 1; + else if ((brightness > 100) && (brightness <= 150)) + gamma = 2; + else if ((brightness > 150) && (brightness <= 200)) + gamma = 3; + else if ((brightness > 200) && (brightness <= 255)) + gamma = 4; + + ret = _ams369fg06_gamma_ctl(lcd, gamma_table.gamma_22_table[gamma]); + + return ret; +} + +static int ams369fg06_ldi_init(struct ams369fg06 *lcd) +{ + int ret, i; + const unsigned short *init_seq[] = { + SEQ_SETTING, + SEQ_STAND_BY_OFF, + }; + + for (i = 0; i < ARRAY_SIZE(init_seq); i++) { + ret = ams369fg06_panel_send_sequence(lcd, init_seq[i]); + if (ret) + break; + } + + return ret; +} + +static int ams369fg06_ldi_enable(struct ams369fg06 *lcd) +{ + int ret, i; + const unsigned short *init_seq[] = { + SEQ_STAND_BY_OFF, + SEQ_DISPLAY_ON, + }; + + for (i = 0; i < ARRAY_SIZE(init_seq); i++) { + ret = ams369fg06_panel_send_sequence(lcd, init_seq[i]); + if (ret) + break; + } + + return ret; +} + +static int ams369fg06_ldi_disable(struct ams369fg06 *lcd) +{ + int ret, i; + + const unsigned short *init_seq[] = { + SEQ_DISPLAY_OFF, + SEQ_STAND_BY_ON, + }; + + for (i = 0; i < ARRAY_SIZE(init_seq); i++) { + ret = ams369fg06_panel_send_sequence(lcd, init_seq[i]); + if (ret) + break; + } + + return ret; +} + +static int ams369fg06_power_on(struct ams369fg06 *lcd) +{ + int ret = 0; + struct lcd_platform_data *pd = NULL; + struct backlight_device *bd = NULL; + + pd = lcd->lcd_pd; + if (!pd) { + dev_err(lcd->dev, "platform data is NULL.\n"); + return -EFAULT; + } + + bd = lcd->bd; + if (!bd) { + dev_err(lcd->dev, "backlight device is NULL.\n"); + return -EFAULT; + } + + if (!pd->power_on) { + dev_err(lcd->dev, "power_on is NULL.\n"); + return -EFAULT; + } else { + pd->power_on(lcd->ld, 1); + mdelay(pd->power_on_delay); + } + + if (!pd->reset) { + dev_err(lcd->dev, "reset is NULL.\n"); + return -EFAULT; + } else { + pd->reset(lcd->ld); + mdelay(pd->reset_delay); + } + + ret = ams369fg06_ldi_init(lcd); + if (ret) { + dev_err(lcd->dev, "failed to initialize ldi.\n"); + return ret; + } + + ret = ams369fg06_ldi_enable(lcd); + if (ret) { + dev_err(lcd->dev, "failed to enable ldi.\n"); + return ret; + } + + /* set brightness to current value after power on or resume. */ + ret = ams369fg06_gamma_ctl(lcd, bd->props.brightness); + if (ret) { + dev_err(lcd->dev, "lcd gamma setting failed.\n"); + return ret; + } + + return 0; +} + +static int ams369fg06_power_off(struct ams369fg06 *lcd) +{ + int ret = 0; + struct lcd_platform_data *pd = NULL; + + pd = lcd->lcd_pd; + if (!pd) { + dev_err(lcd->dev, "platform data is NULL\n"); + return -EFAULT; + } + + ret = ams369fg06_ldi_disable(lcd); + if (ret) { + dev_err(lcd->dev, "lcd setting failed.\n"); + return -EIO; + } + + mdelay(pd->power_off_delay); + + if (!pd->power_on) { + dev_err(lcd->dev, "power_on is NULL.\n"); + return -EFAULT; + } else + pd->power_on(lcd->ld, 0); + + return 0; +} + +static int ams369fg06_power(struct ams369fg06 *lcd, int power) +{ + int ret = 0; + + if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power)) + ret = ams369fg06_power_on(lcd); + else if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->power)) + ret = ams369fg06_power_off(lcd); + + if (!ret) + lcd->power = power; + + return ret; +} + +static int ams369fg06_get_power(struct lcd_device *ld) +{ + struct ams369fg06 *lcd = lcd_get_data(ld); + + return lcd->power; +} + +static int ams369fg06_set_power(struct lcd_device *ld, int power) +{ + struct ams369fg06 *lcd = lcd_get_data(ld); + + if (power != FB_BLANK_UNBLANK && power != FB_BLANK_POWERDOWN && + power != FB_BLANK_NORMAL) { + dev_err(lcd->dev, "power value should be 0, 1 or 4.\n"); + return -EINVAL; + } + + return ams369fg06_power(lcd, power); +} + +static int ams369fg06_get_brightness(struct backlight_device *bd) +{ + return bd->props.brightness; +} + +static int ams369fg06_set_brightness(struct backlight_device *bd) +{ + int ret = 0; + int brightness = bd->props.brightness; + struct ams369fg06 *lcd = dev_get_drvdata(&bd->dev); + + if (brightness < MIN_BRIGHTNESS || + brightness > bd->props.max_brightness) { + dev_err(&bd->dev, "lcd brightness should be %d to %d.\n", + MIN_BRIGHTNESS, MAX_BRIGHTNESS); + return -EINVAL; + } + + ret = ams369fg06_gamma_ctl(lcd, bd->props.brightness); + if (ret) { + dev_err(&bd->dev, "lcd brightness setting failed.\n"); + return -EIO; + } + + return ret; +} + +static struct lcd_ops ams369fg06_lcd_ops = { + .get_power = ams369fg06_get_power, + .set_power = ams369fg06_set_power, +}; + +static const struct backlight_ops ams369fg06_backlight_ops = { + .get_brightness = ams369fg06_get_brightness, + .update_status = ams369fg06_set_brightness, +}; + +#ifdef CONFIG_HAS_EARLYSUSPEND +unsigned int before_power; + +static void ams369fg06_early_suspend(struct early_suspend *handler) +{ + struct ams369fg06 *lcd = NULL; + + lcd = container_of(handler, struct ams369fg06, early_suspend); + + before_power = lcd->power; + + ams369fg06_power(lcd, FB_BLANK_POWERDOWN); +} + +static void ams369fg06_late_resume(struct early_suspend *handler) +{ + struct ams369fg06 *lcd = NULL; + + lcd = container_of(handler, struct ams369fg06, early_suspend); + + if (before_power == FB_BLANK_UNBLANK) + lcd->power = FB_BLANK_POWERDOWN; + + ams369fg06_power(lcd, before_power); +} +#endif + +static int __init ams369fg06_probe(struct spi_device *spi) +{ + int ret = 0; + struct ams369fg06 *lcd = NULL; + struct lcd_device *ld = NULL; + struct backlight_device *bd = NULL; + + lcd = kzalloc(sizeof(struct ams369fg06), GFP_KERNEL); + if (!lcd) + return -ENOMEM; + + /* ams369fg06 lcd panel uses 3-wire 16bits SPI Mode. */ + spi->bits_per_word = 16; + + ret = spi_setup(spi); + if (ret < 0) { + dev_err(&spi->dev, "spi setup failed.\n"); + goto out_free_lcd; + } + + lcd->spi = spi; + lcd->dev = &spi->dev; + + lcd->lcd_pd = (struct lcd_platform_data *)spi->dev.platform_data; + if (!lcd->lcd_pd) { + dev_err(&spi->dev, "platform data is NULL\n"); + goto out_free_lcd; + } + + ld = lcd_device_register("ams369fg06", &spi->dev, lcd, + &ams369fg06_lcd_ops); + if (IS_ERR(ld)) { + ret = PTR_ERR(ld); + goto out_free_lcd; + } + + lcd->ld = ld; + + bd = backlight_device_register("ams369fg06-bl", &spi->dev, lcd, + &ams369fg06_backlight_ops, NULL); + if (IS_ERR(bd)) { + ret = PTR_ERR(bd); + goto out_lcd_unregister; + } + + bd->props.max_brightness = MAX_BRIGHTNESS; + bd->props.brightness = DEFAULT_BRIGHTNESS; + bd->props.type = BACKLIGHT_RAW; + lcd->bd = bd; + + if (!lcd->lcd_pd->lcd_enabled) { + /* + * if lcd panel was off from bootloader then + * current lcd status is powerdown and then + * it enables lcd panel. + */ + lcd->power = FB_BLANK_POWERDOWN; + + ams369fg06_power(lcd, FB_BLANK_UNBLANK); + } else + lcd->power = FB_BLANK_UNBLANK; + + dev_set_drvdata(&spi->dev, lcd); + +#ifdef CONFIG_HAS_EARLYSUSPEND + lcd->early_suspend.suspend = ams369fg06_early_suspend; + lcd->early_suspend.resume = ams369fg06_late_resume; + lcd->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB - 1; + register_early_suspend(&lcd->early_suspend); +#endif + dev_info(&spi->dev, "ams369fg06 panel driver has been probed.\n"); + + return 0; + +out_lcd_unregister: + lcd_device_unregister(ld); +out_free_lcd: + kfree(lcd); + return ret; +} + +static int __devexit ams369fg06_remove(struct spi_device *spi) +{ + struct ams369fg06 *lcd = dev_get_drvdata(&spi->dev); + + ams369fg06_power(lcd, FB_BLANK_POWERDOWN); + lcd_device_unregister(lcd->ld); + kfree(lcd); + + return 0; +} + +#if defined(CONFIG_PM) +#ifndef CONFIG_HAS_EARLYSUSPEND +unsigned int before_power; + +static int ams369fg06_suspend(struct spi_device *spi, pm_message_t mesg) +{ + int ret = 0; + struct ams369fg06 *lcd = dev_get_drvdata(&spi->dev); + + dev_dbg(&spi->dev, "lcd->power = %d\n", lcd->power); + + before_power = lcd->power; + + /* + * when lcd panel is suspend, lcd panel becomes off + * regardless of status. + */ + ret = ams369fg06_power(lcd, FB_BLANK_POWERDOWN); + + return ret; +} + +static int ams369fg06_resume(struct spi_device *spi) +{ + int ret = 0; + struct ams369fg06 *lcd = dev_get_drvdata(&spi->dev); + + /* + * after suspended, if lcd panel status is FB_BLANK_UNBLANK + * (at that time, before_power is FB_BLANK_UNBLANK) then + * it changes that status to FB_BLANK_POWERDOWN to get lcd on. + */ + if (before_power == FB_BLANK_UNBLANK) + lcd->power = FB_BLANK_POWERDOWN; + + dev_dbg(&spi->dev, "before_power = %d\n", before_power); + + ret = ams369fg06_power(lcd, before_power); + + return ret; +} +#endif +#else +#define ams369fg06_suspend NULL +#define ams369fg06_resume NULL +#endif + +void ams369fg06_shutdown(struct spi_device *spi) +{ + struct ams369fg06 *lcd = dev_get_drvdata(&spi->dev); + + ams369fg06_power(lcd, FB_BLANK_POWERDOWN); +} + +static struct spi_driver ams369fg06_driver = { + .driver = { + .name = "ams369fg06", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = ams369fg06_probe, + .remove = __devexit_p(ams369fg06_remove), + .shutdown = ams369fg06_shutdown, +#ifndef CONFIG_HAS_EARLYSUSPEND + .suspend = ams369fg06_suspend, + .resume = ams369fg06_resume, +#endif +}; + +static int __init ams369fg06_init(void) +{ + return spi_register_driver(&ams369fg06_driver); +} + +static void __exit ams369fg06_exit(void) +{ + spi_unregister_driver(&ams369fg06_driver); +} + +module_init(ams369fg06_init); +module_exit(ams369fg06_exit); + +MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>"); +MODULE_DESCRIPTION("ams369fg06 LCD Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/backlight/ams369fg06_gamma.h b/drivers/video/backlight/ams369fg06_gamma.h new file mode 100644 index 0000000..6ecea88 --- /dev/null +++ b/drivers/video/backlight/ams369fg06_gamma.h @@ -0,0 +1,61 @@ +/* + * Gamma level definitions. + * + * Copyright (c) 2011 Samsung Electronics + * Jingoo Han <jg1.han@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#ifndef _AMS369FG06_BRIGHTNESS_H +#define _AMS369FG06_BRIGHTNESS_H + +#define MAX_GAMMA_LEVEL 5 +#define GAMMA_TABLE_COUNT 21 + +/* gamma value: 2.2 */ +static const unsigned int ams369fg06_22_250[] = { + 0x00, 0x3f, 0x2a, 0x27, 0x27, 0x1f, 0x44, + 0x00, 0x00, 0x17, 0x24, 0x26, 0x1f, 0x43, + 0x00, 0x3f, 0x2a, 0x25, 0x24, 0x1b, 0x5c, +}; + +static const unsigned int ams369fg06_22_200[] = { + 0x00, 0x3f, 0x28, 0x29, 0x27, 0x21, 0x3e, + 0x00, 0x00, 0x10, 0x25, 0x27, 0x20, 0x3d, + 0x00, 0x3f, 0x28, 0x27, 0x25, 0x1d, 0x53, +}; + +static const unsigned int ams369fg06_22_150[] = { + 0x00, 0x3f, 0x2d, 0x29, 0x28, 0x23, 0x37, + 0x00, 0x00, 0x0b, 0x25, 0x28, 0x22, 0x36, + 0x00, 0x3f, 0x2b, 0x28, 0x26, 0x1f, 0x4a, +}; + +static const unsigned int ams369fg06_22_100[] = { + 0x00, 0x3f, 0x30, 0x2a, 0x2b, 0x24, 0x2f, + 0x00, 0x00, 0x00, 0x25, 0x29, 0x24, 0x2e, + 0x00, 0x3f, 0x2f, 0x29, 0x29, 0x21, 0x3f, +}; + +static const unsigned int ams369fg06_22_50[] = { + 0x00, 0x3f, 0x3c, 0x2c, 0x2d, 0x27, 0x24, + 0x00, 0x00, 0x00, 0x22, 0x2a, 0x27, 0x23, + 0x00, 0x3f, 0x3b, 0x2c, 0x2b, 0x24, 0x31, +}; + +struct ams369fg06_gamma { + unsigned int *gamma_22_table[MAX_GAMMA_LEVEL]; +}; + +struct ams369fg06_gamma gamma_table = { + .gamma_22_table[0] = (unsigned int *)&ams369fg06_22_50, + .gamma_22_table[1] = (unsigned int *)&ams369fg06_22_100, + .gamma_22_table[2] = (unsigned int *)&ams369fg06_22_150, + .gamma_22_table[3] = (unsigned int *)&ams369fg06_22_200, + .gamma_22_table[4] = (unsigned int *)&ams369fg06_22_250, +}; + +#endif diff --git a/drivers/video/backlight/backlight.c b/drivers/video/backlight/backlight.c index 80d292f..1a6fe06 100644 --- a/drivers/video/backlight/backlight.c +++ b/drivers/video/backlight/backlight.c @@ -19,6 +19,10 @@ #include <asm/backlight.h> #endif +#ifdef CONFIG_DRM +#include <drm/drm_backlight.h> +#endif + static const char const *backlight_types[] = { [BACKLIGHT_RAW] = "raw", [BACKLIGHT_PLATFORM] = "platform", @@ -101,6 +105,43 @@ static void backlight_generate_event(struct backlight_device *bd, sysfs_notify(&bd->dev.kobj, NULL, "actual_brightness"); } +static ssize_t backlight_store_dimming(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int rc; + struct backlight_device *bd = to_backlight_device(dev); + unsigned long dimming; + + rc = strict_strtoul(buf, 0, &dimming); + if (rc) + return rc; + + if (dimming < 0) + rc = -EINVAL; + else { + pr_debug("set dimming mode\n"); + + if (dimming) + bd->props.dimming = true; + else + bd->props.dimming = false; + + backlight_set_dimming(bd); + + rc = count; + } + + return rc; +} + +static ssize_t backlight_show_dimming(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct backlight_device *bd = to_backlight_device(dev); + + return sprintf(buf, "%d\n", bd->props.dimming); +} + static ssize_t backlight_show_power(struct device *dev, struct device_attribute *attr,char *buf) { @@ -242,6 +283,7 @@ static void bl_device_release(struct device *dev) } static struct device_attribute bl_device_attributes[] = { + __ATTR(dimming, 0644, backlight_show_dimming, backlight_store_dimming), __ATTR(bl_power, 0644, backlight_show_power, backlight_store_power), __ATTR(brightness, 0644, backlight_show_brightness, backlight_store_brightness), @@ -354,6 +396,10 @@ void backlight_device_unregister(struct backlight_device *bd) if (!bd) return; +#ifdef CONFIG_DRM + drm_bl_unregister(&bd->dev); +#endif + #ifdef CONFIG_PMAC_BACKLIGHT mutex_lock(&pmac_backlight_mutex); if (pmac_backlight == bd) diff --git a/drivers/video/backlight/lcd.c b/drivers/video/backlight/lcd.c index 71a11ca..cc2c538 100644 --- a/drivers/video/backlight/lcd.c +++ b/drivers/video/backlight/lcd.c @@ -15,6 +15,10 @@ #include <linux/fb.h> #include <linux/slab.h> +#ifdef CONFIG_DRM +#include <drm/drm_backlight.h> +#endif + #if defined(CONFIG_FB) || (defined(CONFIG_FB_MODULE) && \ defined(CONFIG_LCD_CLASS_DEVICE_MODULE)) /* This callback gets called when something important happens inside a @@ -32,6 +36,8 @@ static int fb_notifier_callback(struct notifier_block *self, case FB_EVENT_BLANK: case FB_EVENT_MODE_CHANGE: case FB_EVENT_MODE_CHANGE_ALL: + case FB_EARLY_EVENT_BLANK: + case FB_R_EARLY_EVENT_BLANK: break; default: return 0; @@ -46,6 +52,14 @@ static int fb_notifier_callback(struct notifier_block *self, if (event == FB_EVENT_BLANK) { if (ld->ops->set_power) ld->ops->set_power(ld, *(int *)evdata->data); + } else if (event == FB_EARLY_EVENT_BLANK) { + if (ld->ops->early_set_power) + ld->ops->early_set_power(ld, + *(int *)evdata->data); + } else if (event == FB_R_EARLY_EVENT_BLANK) { + if (ld->ops->r_early_set_power) + ld->ops->r_early_set_power(ld, + *(int *)evdata->data); } else { if (ld->ops->set_mode) ld->ops->set_mode(ld, evdata->data); @@ -226,6 +240,10 @@ struct lcd_device *lcd_device_register(const char *name, struct device *parent, new_ld->ops = ops; +#ifdef CONFIG_DRM + drm_bl_register(&new_ld->dev, BL_LCD_CLASS); +#endif + return new_ld; } EXPORT_SYMBOL(lcd_device_register); @@ -241,6 +259,10 @@ void lcd_device_unregister(struct lcd_device *ld) if (!ld) return; +#ifdef CONFIG_DRM + drm_bl_unregister(&ld->dev); +#endif + mutex_lock(&ld->ops_lock); ld->ops = NULL; mutex_unlock(&ld->ops_lock); diff --git a/drivers/video/backlight/lms501kf03.c b/drivers/video/backlight/lms501kf03.c new file mode 100644 index 0000000..6876be8 --- /dev/null +++ b/drivers/video/backlight/lms501kf03.c @@ -0,0 +1,598 @@ +/* linux/drivers/video/backlight/lms501kf03.c + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * LMS501KF03 5.0" WVGA Landscape LCD module driver for the SMDK + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include <linux/wait.h> +#include <linux/fb.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/spi/spi.h> +#include <linux/lcd.h> +#include <linux/backlight.h> +#ifdef CONFIG_HAS_EARLYSUSPEND +#include <linux/earlysuspend.h> +#endif + +#define ENDDEF 0xFF00 +#define COMMAND_ONLY 0x00 +#define DATA_ONLY 0x01 + +#define MIN_BRIGHTNESS 0 +#define MAX_BRIGHTNESS 255 +#define DEFAULT_BRIGHTNESS 150 + +#define POWER_IS_ON(pwr) ((pwr) <= FB_BLANK_NORMAL) + +struct lms501kf03 { + struct device *dev; + struct spi_device *spi; + unsigned int power; + struct lcd_device *ld; + struct backlight_device *bd; + struct lcd_platform_data *lcd_pd; +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend early_suspend; +#endif +}; + +const unsigned short SEQ_PASSWORD[] = { + 0xb9, 0xff, 0x83, 0x69, + ENDDEF +}; + +const unsigned short SEQ_POWER[] = { + 0xb1, 0x01, 0x00, 0x34, 0x06, 0x00, 0x14, 0x14, 0x20, 0x28, + 0x12, 0x12, 0x17, 0x0a, 0x01, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, + ENDDEF +}; + +const unsigned short SEQ_DISPLAY[] = { + 0xb2, 0x00, 0x2b, 0x03, 0x03, 0x70, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x03, 0x00, 0x01, + ENDDEF +}; + +const unsigned short SEQ_RGB_IF[] = { + 0xb3, 0x09, + ENDDEF +}; + +const unsigned short SEQ_DISPLAY_INV[] = { + 0xb4, 0x01, 0x08, 0x77, 0x0e, 0x06, + ENDDEF +}; + +const unsigned short SEQ_VCOM[] = { + 0xb6, 0x4c, 0x2e, + ENDDEF +}; + +const unsigned short SEQ_GATE[] = { + 0xd5, 0x00, 0x05, 0x03, 0x29, 0x01, 0x07, 0x17, 0x68, 0x13, + 0x37, 0x20, 0x31, 0x8a, 0x46, 0x9b, 0x57, 0x13, 0x02, 0x75, + 0xb9, 0x64, 0xa8, 0x07, 0x0f, 0x04, 0x07, + ENDDEF +}; + +const unsigned short SEQ_PANEL[] = { + 0xcc, 0x02, + ENDDEF +}; + +const unsigned short SEQ_COL_MOD[] = { + 0x3a, 0x77, + ENDDEF +}; + +const unsigned short SEQ_W_GAMMA[] = { + 0xe0, 0x00, 0x04, 0x09, 0x0f, 0x1f, 0x3f, 0x1f, 0x2f, 0x0a, + 0x0f, 0x10, 0x16, 0x18, 0x16, 0x17, 0x0d, 0x15, 0x00, 0x04, + 0x09, 0x0f, 0x38, 0x3f, 0x20, 0x39, 0x0a, 0x0f, 0x10, 0x16, + 0x18, 0x16, 0x17, 0x0d, 0x15, + ENDDEF +}; + +const unsigned short SEQ_RGB_GAMMA[] = { + 0xc1, 0x01, 0x03, 0x07, 0x0f, 0x1a, 0x22, 0x2c, 0x33, 0x3c, + 0x46, 0x4f, 0x58, 0x60, 0x69, 0x71, 0x79, 0x82, 0x89, 0x92, + 0x9a, 0xa1, 0xa9, 0xb1, 0xb9, 0xc1, 0xc9, 0xcf, 0xd6, 0xde, + 0xe5, 0xec, 0xf3, 0xf9, 0xff, 0xdd, 0x39, 0x07, 0x1c, 0xcb, + 0xab, 0x5f, 0x49, 0x80, 0x03, 0x07, 0x0f, 0x19, 0x20, 0x2a, + 0x31, 0x39, 0x42, 0x4b, 0x53, 0x5b, 0x63, 0x6b, 0x73, 0x7b, + 0x83, 0x8a, 0x92, 0x9b, 0xa2, 0xaa, 0xb2, 0xba, 0xc2, 0xca, + 0xd0, 0xd8, 0xe1, 0xe8, 0xf0, 0xf8, 0xff, 0xf7, 0xd8, 0xbe, + 0xa7, 0x39, 0x40, 0x85, 0x8c, 0xc0, 0x04, 0x07, 0x0c, 0x17, + 0x1c, 0x23, 0x2b, 0x34, 0x3b, 0x43, 0x4c, 0x54, 0x5b, 0x63, + 0x6a, 0x73, 0x7a, 0x82, 0x8a, 0x91, 0x98, 0xa1, 0xa8, 0xb0, + 0xb7, 0xc1, 0xc9, 0xcf, 0xd9, 0xe3, 0xea, 0xf4, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ENDDEF +}; + +const unsigned short SEQ_UP_DN[] = { + 0x36, 0x10, + ENDDEF +}; + +const unsigned short SEQ_SLEEP_IN[] = { + 0x10, + ENDDEF +}; + +const unsigned short SEQ_SLEEP_OUT[] = { + 0x11, + ENDDEF +}; + +const unsigned short SEQ_DISPLAY_ON[] = { + 0x29, + ENDDEF +}; + +const unsigned short SEQ_DISPLAY_OFF[] = { + 0x10, + ENDDEF +}; + +static int lms501kf03_spi_write_byte(struct lms501kf03 *lcd, int addr, int data) +{ + u16 buf[1]; + struct spi_message msg; + + struct spi_transfer xfer = { + .len = 2, + .tx_buf = buf, + }; + + buf[0] = (addr << 8) | data; + + spi_message_init(&msg); + spi_message_add_tail(&xfer, &msg); + + return spi_sync(lcd->spi, &msg); +} + +static int lms501kf03_spi_write(struct lms501kf03 *lcd, unsigned char address, + unsigned char command) +{ + int ret = 0; + + ret = lms501kf03_spi_write_byte(lcd, address, command); + + return ret; +} + +static int lms501kf03_panel_send_sequence(struct lms501kf03 *lcd, + const unsigned short *wbuf) +{ + int ret = 0, i = 0; + + while (wbuf[i] != ENDDEF) { + if (i == 0) + ret = lms501kf03_spi_write(lcd, COMMAND_ONLY, wbuf[i]); + else + ret = lms501kf03_spi_write(lcd, DATA_ONLY, wbuf[i]); + if (ret) + break; + + udelay(100); + i += 1; + } + return ret; +} + +static int lms501kf03_ldi_init(struct lms501kf03 *lcd) +{ + int ret, i; + const unsigned short *init_seq[] = { + SEQ_PASSWORD, + SEQ_POWER, + SEQ_DISPLAY, + SEQ_RGB_IF, + SEQ_DISPLAY_INV, + SEQ_VCOM, + SEQ_GATE, + SEQ_PANEL, + SEQ_COL_MOD, + SEQ_W_GAMMA, + SEQ_RGB_GAMMA, + SEQ_SLEEP_OUT, + }; + + for (i = 0; i < ARRAY_SIZE(init_seq); i++) { + ret = lms501kf03_panel_send_sequence(lcd, init_seq[i]); + if (ret) + break; + } + mdelay(120); + + return ret; +} + +static int lms501kf03_ldi_enable(struct lms501kf03 *lcd) +{ + int ret, i; + const unsigned short *init_seq[] = { + SEQ_DISPLAY_ON, + }; + + for (i = 0; i < ARRAY_SIZE(init_seq); i++) { + ret = lms501kf03_panel_send_sequence(lcd, init_seq[i]); + if (ret) + break; + } + + return ret; +} + +static int lms501kf03_ldi_disable(struct lms501kf03 *lcd) +{ + int ret, i; + + const unsigned short *init_seq[] = { + SEQ_DISPLAY_OFF, + }; + + for (i = 0; i < ARRAY_SIZE(init_seq); i++) { + ret = lms501kf03_panel_send_sequence(lcd, init_seq[i]); + if (ret) + break; + } + + return ret; +} + +static int lms501kf03_power_on(struct lms501kf03 *lcd) +{ + int ret = 0; + struct lcd_platform_data *pd = NULL; + struct backlight_device *bd = NULL; + + pd = lcd->lcd_pd; + if (!pd) { + dev_err(lcd->dev, "platform data is NULL.\n"); + return -EFAULT; + } + + bd = lcd->bd; + if (!bd) { + dev_err(lcd->dev, "backlight device is NULL.\n"); + return -EFAULT; + } + + if (!pd->power_on) { + dev_err(lcd->dev, "power_on is NULL.\n"); + return -EFAULT; + } else { + pd->power_on(lcd->ld, 1); + mdelay(pd->power_on_delay); + } + + if (!pd->reset) { + dev_err(lcd->dev, "reset is NULL.\n"); + return -EFAULT; + } else { + pd->reset(lcd->ld); + mdelay(pd->reset_delay); + } + + ret = lms501kf03_ldi_init(lcd); + if (ret) { + dev_err(lcd->dev, "failed to initialize ldi.\n"); + return ret; + } + + ret = lms501kf03_ldi_enable(lcd); + if (ret) { + dev_err(lcd->dev, "failed to enable ldi.\n"); + return ret; + } + + return 0; +} + +static int lms501kf03_power_off(struct lms501kf03 *lcd) +{ + int ret = 0; + struct lcd_platform_data *pd = NULL; + + pd = lcd->lcd_pd; + if (!pd) { + dev_err(lcd->dev, "platform data is NULL\n"); + return -EFAULT; + } + + ret = lms501kf03_ldi_disable(lcd); + if (ret) { + dev_err(lcd->dev, "lcd setting failed.\n"); + return -EIO; + } + + mdelay(pd->power_off_delay); + + if (!pd->power_on) { + dev_err(lcd->dev, "power_on is NULL.\n"); + return -EFAULT; + } else + pd->power_on(lcd->ld, 0); + + return 0; +} + +static int lms501kf03_power(struct lms501kf03 *lcd, int power) +{ + int ret = 0; + + if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power)) + ret = lms501kf03_power_on(lcd); + else if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->power)) + ret = lms501kf03_power_off(lcd); + + if (!ret) + lcd->power = power; + + return ret; +} + +static int lms501kf03_get_power(struct lcd_device *ld) +{ + struct lms501kf03 *lcd = lcd_get_data(ld); + + return lcd->power; +} + +static int lms501kf03_set_power(struct lcd_device *ld, int power) +{ + struct lms501kf03 *lcd = lcd_get_data(ld); + + if (power != FB_BLANK_UNBLANK && power != FB_BLANK_POWERDOWN && + power != FB_BLANK_NORMAL) { + dev_err(lcd->dev, "power value should be 0, 1 or 4.\n"); + return -EINVAL; + } + + return lms501kf03_power(lcd, power); +} + +static int lms501kf03_get_brightness(struct backlight_device *bd) +{ + return bd->props.brightness; +} + +static int lms501kf03_set_brightness(struct backlight_device *bd) +{ + int ret = 0; + int brightness = bd->props.brightness; + + if (brightness < MIN_BRIGHTNESS || + brightness > bd->props.max_brightness) { + dev_err(&bd->dev, "lcd brightness should be %d to %d.\n", + MIN_BRIGHTNESS, MAX_BRIGHTNESS); + return -EINVAL; + } + + return ret; +} + +static struct lcd_ops lms501kf03_lcd_ops = { + .get_power = lms501kf03_get_power, + .set_power = lms501kf03_set_power, +}; + +static const struct backlight_ops lms501kf03_backlight_ops = { + .get_brightness = lms501kf03_get_brightness, + .update_status = lms501kf03_set_brightness, +}; + +#ifdef CONFIG_HAS_EARLYSUSPEND +unsigned int before_power; + +static void lms501kf03_early_suspend(struct early_suspend *handler) +{ + struct lms501kf03 *lcd = NULL; + + lcd = container_of(handler, struct lms501kf03, early_suspend); + + before_power = lcd->power; + + lms501kf03_power(lcd, FB_BLANK_POWERDOWN); +} + +static void lms501kf03_late_resume(struct early_suspend *handler) +{ + struct lms501kf03 *lcd = NULL; + + lcd = container_of(handler, struct lms501kf03, early_suspend); + + if (before_power == FB_BLANK_UNBLANK) + lcd->power = FB_BLANK_POWERDOWN; + + lms501kf03_power(lcd, before_power); +} +#endif + +static int __init lms501kf03_probe(struct spi_device *spi) +{ + int ret = 0; + struct lms501kf03 *lcd = NULL; + struct lcd_device *ld = NULL; + struct backlight_device *bd = NULL; + + lcd = kzalloc(sizeof(struct lms501kf03), GFP_KERNEL); + if (!lcd) + return -ENOMEM; + + /* lms501kf03 lcd panel uses 3-wire 9-bit SPI Mode. */ + spi->bits_per_word = 9; + + ret = spi_setup(spi); + if (ret < 0) { + dev_err(&spi->dev, "spi setup failed.\n"); + goto out_free_lcd; + } + + lcd->spi = spi; + lcd->dev = &spi->dev; + + lcd->lcd_pd = (struct lcd_platform_data *)spi->dev.platform_data; + if (!lcd->lcd_pd) { + dev_err(&spi->dev, "platform data is NULL\n"); + goto out_free_lcd; + } + + ld = lcd_device_register("lms501kf03", &spi->dev, lcd, + &lms501kf03_lcd_ops); + if (IS_ERR(ld)) { + ret = PTR_ERR(ld); + goto out_free_lcd; + } + + lcd->ld = ld; + + bd = backlight_device_register("lms501kf03-bl", &spi->dev, lcd, + &lms501kf03_backlight_ops, NULL); + if (IS_ERR(bd)) { + ret = PTR_ERR(bd); + goto out_lcd_unregister; + } + + bd->props.max_brightness = MAX_BRIGHTNESS; + bd->props.brightness = DEFAULT_BRIGHTNESS; + bd->props.type = BACKLIGHT_RAW; + lcd->bd = bd; + + if (!lcd->lcd_pd->lcd_enabled) { + /* + * if lcd panel was off from bootloader then + * current lcd status is powerdown and then + * it enables lcd panel. + */ + lcd->power = FB_BLANK_POWERDOWN; + + lms501kf03_power(lcd, FB_BLANK_UNBLANK); + } else + lcd->power = FB_BLANK_UNBLANK; + + dev_set_drvdata(&spi->dev, lcd); + +#ifdef CONFIG_HAS_EARLYSUSPEND + lcd->early_suspend.suspend = lms501kf03_early_suspend; + lcd->early_suspend.resume = lms501kf03_late_resume; + lcd->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB - 1; + register_early_suspend(&lcd->early_suspend); +#endif + dev_info(&spi->dev, "lms501kf03 panel driver has been probed.\n"); + + return 0; + +out_lcd_unregister: + lcd_device_unregister(ld); +out_free_lcd: + kfree(lcd); + return ret; +} + +static int __devexit lms501kf03_remove(struct spi_device *spi) +{ + struct lms501kf03 *lcd = dev_get_drvdata(&spi->dev); + + lms501kf03_power(lcd, FB_BLANK_POWERDOWN); + lcd_device_unregister(lcd->ld); + kfree(lcd); + + return 0; +} + +#if defined(CONFIG_PM) +#ifndef CONFIG_HAS_EARLYSUSPEND +unsigned int before_power; + +static int lms501kf03_suspend(struct spi_device *spi, pm_message_t mesg) +{ + int ret = 0; + struct lms501kf03 *lcd = dev_get_drvdata(&spi->dev); + + dev_dbg(&spi->dev, "lcd->power = %d\n", lcd->power); + + before_power = lcd->power; + + /* + * when lcd panel is suspend, lcd panel becomes off + * regardless of status. + */ + ret = lms501kf03_power(lcd, FB_BLANK_POWERDOWN); + + return ret; +} + +static int lms501kf03_resume(struct spi_device *spi) +{ + int ret = 0; + struct lms501kf03 *lcd = dev_get_drvdata(&spi->dev); + + /* + * after suspended, if lcd panel status is FB_BLANK_UNBLANK + * (at that time, before_power is FB_BLANK_UNBLANK) then + * it changes that status to FB_BLANK_POWERDOWN to get lcd on. + */ + if (before_power == FB_BLANK_UNBLANK) + lcd->power = FB_BLANK_POWERDOWN; + + dev_dbg(&spi->dev, "before_power = %d\n", before_power); + + ret = lms501kf03_power(lcd, before_power); + + return ret; +} +#endif +#else +#define lms501kf03_suspend NULL +#define lms501kf03_resume NULL +#endif + +void lms501kf03_shutdown(struct spi_device *spi) +{ + struct lms501kf03 *lcd = dev_get_drvdata(&spi->dev); + + lms501kf03_power(lcd, FB_BLANK_POWERDOWN); +} + +static struct spi_driver lms501kf03_driver = { + .driver = { + .name = "lms501kf03", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = lms501kf03_probe, + .remove = __devexit_p(lms501kf03_remove), + .shutdown = lms501kf03_shutdown, +#ifndef CONFIG_HAS_EARLYSUSPEND + .suspend = lms501kf03_suspend, + .resume = lms501kf03_resume, +#endif +}; + +static int __init lms501kf03_init(void) +{ + return spi_register_driver(&lms501kf03_driver); +} + +static void __exit lms501kf03_exit(void) +{ + spi_unregister_driver(&lms501kf03_driver); +} + +module_init(lms501kf03_init); +module_exit(lms501kf03_exit); + +MODULE_AUTHOR("Ilho Lee <Ilho215.lee@samsung.com>"); +MODULE_DESCRIPTION("lms501kf03 LCD Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c index b8f38ec..37837f5 100644 --- a/drivers/video/backlight/pwm_bl.c +++ b/drivers/video/backlight/pwm_bl.c @@ -31,11 +31,19 @@ struct pwm_bl_data { int (*check_fb)(struct device *, struct fb_info *); }; +/*H/W team requirement for MWC Demo*/ +/* lcd max brightnessis restrictd to PWM 80% MAX PWM Duty : 80 (204)%*/ +#define CONFIG_MWC_DEMO + static int pwm_backlight_update_status(struct backlight_device *bl) { struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev); int brightness = bl->props.brightness; int max = bl->props.max_brightness; +#ifdef CONFIG_MWC_DEMO + if (brightness >= 204) + brightness = 204; +#endif if (bl->props.power != FB_BLANK_UNBLANK) brightness = 0; diff --git a/drivers/video/backlight/s6e39a0x02.c b/drivers/video/backlight/s6e39a0x02.c new file mode 100644 index 0000000..c054ba4 --- /dev/null +++ b/drivers/video/backlight/s6e39a0x02.c @@ -0,0 +1,799 @@ +/* linux/drivers/video/s6e39a0x.c + * + * MIPI-DSI based s6e39a0x AMOLED lcd 5.3 inch panel driver. + * + * Inki Dae, <inki.dae@samsung.com> + * Donghwa Lee, <dh09.lee@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/mutex.h> +#include <linux/wait.h> +#include <linux/ctype.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/lcd.h> +#include <linux/fb.h> +#include <linux/backlight.h> +#include <linux/regulator/consumer.h> +#include <linux/gpio.h> + +#include <video/mipi_display.h> + +#include <plat/mipi_dsim2.h> +#include <plat/regs-dsim2.h> +#include <plat/gpio-cfg.h> + +#include "s6e39a0x02_gamma.h" + +#define LDI_MTP_LENGTH 24 +#define DSIM_PM_STABLE_TIME (10) +#define MIN_BRIGHTNESS (0) +#define MAX_BRIGHTNESS (10) + +#define VER_142 (0x8e) /* MACH_SLP_PQ */ +#define VER_42 (0x2a) /* MACH_SLP_PQ_LTE */ + +#define POWER_IS_ON(pwr) ((pwr) == FB_BLANK_UNBLANK) +#define POWER_IS_OFF(pwr) ((pwr) == FB_BLANK_POWERDOWN) +#define POWER_IS_NRM(pwr) ((pwr) == FB_BLANK_NORMAL) + +#define lcd_to_master(a) (a->dsim_dev->master) +#define lcd_to_master_ops(a) ((lcd_to_master(a))->master_ops) + +enum { + DSIM_NONE_STATE = 0, + DSIM_RESUME_COMPLETE = 1, + DSIM_FRAME_DONE = 2, +}; + +struct s6e39a0x02 { + struct device *dev; + unsigned int id; + unsigned int ver; + unsigned int power; + unsigned int current_brightness; + unsigned int updated; + unsigned int gamma; + unsigned int resume_complete; + unsigned int acl_enable; + unsigned int cur_acl; + + struct lcd_device *ld; + struct backlight_device *bd; + + struct mipi_dsim_lcd_device *dsim_dev; + struct lcd_platform_data *ddi_pd; + + struct mutex lock; + struct regulator *reg_vci; + bool enabled; +}; + +static struct mipi_dsim_device *dsim; + +static void s6e39a0x02_regulator_enable(struct s6e39a0x02 *lcd) +{ + mutex_lock(&lcd->lock); + if (!lcd->enabled) { + if (lcd->reg_vci) + regulator_enable(lcd->reg_vci); + + lcd->enabled = true; + } + mutex_unlock(&lcd->lock); +} + +static void s6e39a0x02_regulator_disable(struct s6e39a0x02 *lcd) +{ + mutex_lock(&lcd->lock); + if (lcd->enabled) { + if (lcd->reg_vci) + regulator_disable(lcd->reg_vci); + + lcd->enabled = false; + } + mutex_unlock(&lcd->lock); +} + +static void s6e39a0x02_sleep_in(struct s6e39a0x02 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE, 0x10, 0x00); +} + +static void s6e39a0x02_sleep_out(struct s6e39a0x02 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE, 0x11, 0x00); +} + +static void s6e39a0x02_display_on(struct s6e39a0x02 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE, 0x29, 0x00); +} + +static void s6e39a0x02_acl_on(struct s6e39a0x02 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE, 0xc0, 0x01); +} + +static void s6e39a0x02_acl_off(struct s6e39a0x02 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE, 0xc0, 0x00); +} + +static void s6e39a0x02_acl_ctrl_set(struct s6e39a0x02 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + /* Full white 50% reducing setting */ + const unsigned char cutoff_50[] = { + 0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf, + 0x00, 0x00, 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x08, 0x0f, 0x16, 0x1d, 0x24, 0x2a, 0x31, 0x38, + 0x3f, 0x46 + }; + /* Full white 45% reducing setting */ + const unsigned char cutoff_45[] = { + 0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf, + 0x00, 0x00, 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x07, 0x0d, 0x13, 0x19, 0x1f, 0x25, 0x2b, 0x31, + 0x37, 0x3d + }; + /* Full white 40% reducing setting */ + const unsigned char cutoff_40[] = { + 0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf, + 0x00, 0x00, 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x06, 0x0c, 0x11, 0x16, 0x1c, 0x21, 0x26, 0x2b, + 0x31, 0x36 + }; + + if (lcd->acl_enable) { + if (lcd->cur_acl == 0) { + if (lcd->gamma == 0 || lcd->gamma == 1) { + s6e39a0x02_acl_off(lcd); + dev_dbg(&lcd->ld->dev, + "cur_acl=%d\n", lcd->cur_acl); + } else + s6e39a0x02_acl_on(lcd); + } + switch (lcd->gamma) { + case 0: /* 30cd */ + s6e39a0x02_acl_off(lcd); + lcd->cur_acl = 0; + break; + case 1 ... 3: /* 50cd ~ 90cd */ + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_LONG_WRITE, + (unsigned int)cutoff_40, + ARRAY_SIZE(cutoff_40)); + lcd->cur_acl = 40; + break; + case 4 ... 7: /* 120cd ~ 210cd */ + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_LONG_WRITE, + (unsigned int)cutoff_45, + ARRAY_SIZE(cutoff_45)); + lcd->cur_acl = 45; + break; + case 8 ... 10: /* 220cd ~ 300cd */ + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_LONG_WRITE, + (unsigned int)cutoff_50, + ARRAY_SIZE(cutoff_50)); + lcd->cur_acl = 50; + break; + default: + break; + } + } else { + s6e39a0x02_acl_off(lcd); + lcd->cur_acl = 0; + dev_dbg(&lcd->ld->dev, "cur_acl = %d\n", lcd->cur_acl); + } +} + +static void s6e39a0x02_etc_cond1(struct s6e39a0x02 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + + unsigned char data_to_send_1[3] = { + 0xf0, 0x5a, 0x5a + }; + + unsigned char data_to_send_2[3] = { + 0xf1, 0x5a, 0x5a + }; + + unsigned char data_to_send_3[3] = { + 0xfc, 0x5a, 0x5a + }; + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + (unsigned int) data_to_send_1, sizeof(data_to_send_1)); + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + (unsigned int) data_to_send_2, sizeof(data_to_send_2)); + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + (unsigned int) data_to_send_3, sizeof(data_to_send_3)); +} + +static void s6e39a0x02_gamma_cond(struct s6e39a0x02 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + + unsigned char data_to_send[26] = { + 0xfa, 0x02, 0x10, 0x10, 0x10, 0xf0, 0x8c, 0xed, 0xd5, + 0xca, 0xd8, 0xdc, 0xdb, 0xdc, 0xbb, 0xbd, 0xb7, 0xcb, + 0xcd, 0xc5, 0x00, 0x9c, 0x00, 0x7a, 0x00, 0xb2 + }; + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + (unsigned int) data_to_send, sizeof(data_to_send)); + + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0xfa, 0x03); +} + +static void s6e39a0x02_panel_cond(struct s6e39a0x02 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + + unsigned char data_to_send_1[14] = { + 0xf8, 0x27, 0x27, 0x08, 0x08, 0x4e, 0xaa, + 0x5e, 0x8a, 0x10, 0x3f, 0x10, 0x10, 0x00 + }; + + unsigned char data_to_send_2[6] = { + 0xb3, 0x63, 0x02, 0xc3, 0x32, 0xff + }; + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + (unsigned int) data_to_send_1, sizeof(data_to_send_1)); + + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0xf7, 0x03); + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + (unsigned int) data_to_send_2, sizeof(data_to_send_2)); +} + +static void s6e39a0x02_etc_cond2(struct s6e39a0x02 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + + unsigned char data_to_send_1[4] = { + 0xf6, 0x00, 0x84, 0x09 + }; + + unsigned char data_to_send_2[4] = { + 0xd5, 0xa4, 0x7e, 0x20 + }; + + unsigned char data_to_send_3[4] = { + 0xb1, 0x01, 0x00, 0x16 + }; + + unsigned char data_to_send_4[5] = { + 0xb2, 0x15, 0x15, 0x15, 0x15 + }; + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + (unsigned int) data_to_send_1, sizeof(data_to_send_1)); + + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0xb0, 0x09); + + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0xd5, 0x64); + + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0xb0, 0x0b); + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + (unsigned int) data_to_send_2, sizeof(data_to_send_2)); + + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0xb0, 0x08); + + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0xfd, 0xf8); + + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0xb0, 0x01); + + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0xf2, 0x07); + + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0xb0, 0x04); + + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0xf2, 0x4d); + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + (unsigned int) data_to_send_3, sizeof(data_to_send_3)); + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + (unsigned int) data_to_send_4, sizeof(data_to_send_4)); + + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE, 0x11, 0x00); +} + +static void s6e39a0x02_memory_window_1(struct s6e39a0x02 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + + unsigned char data_to_send_1[5] = { + 0x2a, 0x00, 0x00, 0x02, 0x57 + }; + + unsigned char data_to_send_2[5] = { + 0x2b, 0x00, 0x00, 0x03, 0xff + }; + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + (unsigned int) data_to_send_1, sizeof(data_to_send_1)); + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + (unsigned int) data_to_send_2, sizeof(data_to_send_2)); + + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE, 0x2C, 0x00); +} + +static void s6e39a0x02_memory_window_2(struct s6e39a0x02 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + + unsigned char data_to_send_1[5] = { + 0x2a, 0x00, 0x1e, 0x02, 0x39 + }; + + unsigned char data_to_send_2[5] = { + 0x2b, 0x00, 0x00, 0x03, 0xbf + }; + + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE, 0x35, 0x00); + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + (unsigned int) data_to_send_1, sizeof(data_to_send_1)); + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + (unsigned int) data_to_send_2, sizeof(data_to_send_2)); + + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0xd1, 0x8a); +} + +static int s6e39a0x02_panel_init(struct s6e39a0x02 *lcd) +{ + mdelay(10); + + s6e39a0x02_etc_cond1(lcd); + s6e39a0x02_gamma_cond(lcd); + s6e39a0x02_panel_cond(lcd); + s6e39a0x02_etc_cond2(lcd); + + mdelay(120); + + s6e39a0x02_memory_window_1(lcd); + + mdelay(20); + + s6e39a0x02_memory_window_2(lcd); + + return 0; +} + +static int s6e39a0x02_update_gamma_ctrl(struct s6e39a0x02 *lcd, int gamma) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + (unsigned int)s6e39a0x02_22_gamma_table[gamma], + GAMMA_TABLE_COUNT); + + /* update gamma table. */ + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_SHORT_WRITE_PARAM, + 0xfA, 0x03); + + lcd->gamma = gamma; + + return 0; +} + +static int s6e39a0x02_gamma_ctrl(struct s6e39a0x02 *lcd, int gamma) +{ + s6e39a0x02_update_gamma_ctrl(lcd, gamma); + + return 0; +} + +static int s6e39a0x02_early_set_power(struct lcd_device *ld, int power) +{ + struct s6e39a0x02 *lcd = lcd_get_data(ld); + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + int ret = 0; + + if (power != FB_BLANK_UNBLANK && power != FB_BLANK_POWERDOWN && + power != FB_BLANK_NORMAL) { + dev_err(lcd->dev, "power value should be 0, 1 or 4.\n"); + return -EINVAL; + } + + if (lcd->power == power) { + dev_err(lcd->dev, "power mode is same as previous one.\n"); + return -EINVAL; + } + + if (ops->set_early_blank_mode) { + /* LCD power off */ + if ((POWER_IS_OFF(power) && POWER_IS_ON(lcd->power)) + || (POWER_IS_ON(lcd->power) && POWER_IS_NRM(power))) { + ret = ops->set_early_blank_mode(lcd_to_master(lcd), + power); + if (!ret && lcd->power != power) + lcd->power = power; + } + } + + return ret; +} + +static int s6e39a0x02_set_power(struct lcd_device *ld, int power) +{ + struct s6e39a0x02 *lcd = lcd_get_data(ld); + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + int ret = 0; + + if (power != FB_BLANK_UNBLANK && power != FB_BLANK_POWERDOWN && + power != FB_BLANK_NORMAL) { + dev_err(lcd->dev, "power value should be 0, 1 or 4.\n"); + return -EINVAL; + } + + if (lcd->power == power) { + dev_err(lcd->dev, "power mode is same as previous one.\n"); + return -EINVAL; + } + + if (ops->set_blank_mode) { + ret = ops->set_blank_mode(lcd_to_master(lcd), power); + if (!ret && lcd->power != power) + lcd->power = power; + } + + return ret; +} + +static int s6e39a0x02_get_power(struct lcd_device *ld) +{ + struct s6e39a0x02 *lcd = lcd_get_data(ld); + + return lcd->power; +} + +static int s6e39a0x02_get_brightness(struct backlight_device *bd) +{ + return bd->props.brightness; +} + +static int s6e39a0x02_set_brightness(struct backlight_device *bd) +{ + int ret = 0, brightness = bd->props.brightness; + struct s6e39a0x02 *lcd = bl_get_data(bd); + + if (brightness < MIN_BRIGHTNESS || + brightness > bd->props.max_brightness) { + dev_err(lcd->dev, "lcd brightness should be %d to %d.\n", + MIN_BRIGHTNESS, MAX_BRIGHTNESS); + return -EINVAL; + } + + ret = s6e39a0x02_gamma_ctrl(lcd, brightness); + if (ret) { + dev_err(&bd->dev, "lcd brightness setting failed.\n"); + return -EIO; + } + + return ret; +} + +static struct lcd_ops s6e39a0x02_lcd_ops = { + .early_set_power = s6e39a0x02_early_set_power, + .set_power = s6e39a0x02_set_power, + .get_power = s6e39a0x02_get_power, +}; + +static const struct backlight_ops s6e39a0x02_backlight_ops = { + .get_brightness = s6e39a0x02_get_brightness, + .update_status = s6e39a0x02_set_brightness, +}; + +static void s6e39a0x02_power_on(struct mipi_dsim_lcd_device *dsim_dev, + unsigned int enable) +{ + struct s6e39a0x02 *lcd = dev_get_drvdata(&dsim_dev->dev); + + if (enable) { + mdelay(lcd->ddi_pd->power_on_delay); + + /* lcd power on */ + s6e39a0x02_regulator_enable(lcd); + + mdelay(lcd->ddi_pd->reset_delay); + + /* lcd reset */ + if (lcd->ddi_pd->reset) + lcd->ddi_pd->reset(lcd->ld); + + mdelay(5); + } else + s6e39a0x02_regulator_disable(lcd); + +} + +static void s6e39a0x02_set_sequence(struct mipi_dsim_lcd_device *dsim_dev) +{ + struct s6e39a0x02 *lcd = dev_get_drvdata(&dsim_dev->dev); + + s6e39a0x02_panel_init(lcd); + s6e39a0x02_display_on(lcd); + + mdelay(50); +} + +static ssize_t acl_control_show(struct device *dev, struct + device_attribute * attr, char *buf) +{ + struct s6e39a0x02 *lcd = dev_get_drvdata(dev); + char temp[3]; + + sprintf(temp, "%d\n", lcd->acl_enable); + strcpy(buf, temp); + + return strlen(buf); +} + +static ssize_t acl_control_store(struct device *dev, struct + device_attribute * attr, const char *buf, size_t size) +{ + struct s6e39a0x02 *lcd = dev_get_drvdata(dev); + unsigned int value; + int rc; + + rc = strict_strtoul(buf, (unsigned int)0, (unsigned long *)&value); + if (rc < 0) + return rc; + + if (lcd->acl_enable != value) { + dev_info(dev, "acl control changed from %d to %d\n", + lcd->acl_enable, value); + lcd->acl_enable = value; + s6e39a0x02_acl_ctrl_set(lcd); + } + return size; +} + +static irqreturn_t te_interrupt(int irq, void *dev_id) +{ + struct device *dev = NULL; + u32 intsrc = 0; + static bool suspended; + + dev = (struct device *)dev_id; + + intsrc = readl_relaxed(dsim->reg_base + S5P_DSIM_INTSRC); + + if (intsrc & (1 << 24)) { + int val; + + val = __raw_readl(dsim->reg_base + S5P_DSIM_INTSRC); + val |= (1 << 24); + __raw_writel(val, dsim->reg_base + S5P_DSIM_INTSRC); + dsim->master_ops->trigger(NULL); + } + + if (suspended == true && dsim->suspended == false) { + dev_info(dev, "te_interrupt: dsim->suspended is false.\n"); + dsim->master_ops->trigger(NULL); + } + + suspended = dsim->suspended; + + return IRQ_HANDLED; +} + +static DEVICE_ATTR(acl_control, 0664, acl_control_show, acl_control_store); + +static int s6e39a0x02_probe(struct mipi_dsim_lcd_device *dsim_dev) +{ + struct s6e39a0x02 *lcd; + int ret; + + dsim = dsim_dev->master; + + lcd = kzalloc(sizeof(struct s6e39a0x02), GFP_KERNEL); + if (!lcd) { + dev_err(&dsim_dev->dev, "failed to allocate s6e39a0x02 structure.\n"); + return -ENOMEM; + } + + lcd->dsim_dev = dsim_dev; + lcd->ddi_pd = (struct lcd_platform_data *)dsim_dev->platform_data; + lcd->dev = &dsim_dev->dev; + + mutex_init(&lcd->lock); + + lcd->reg_vci = regulator_get(lcd->dev, "VCI"); + if (IS_ERR(lcd->reg_vci)) { + ret = PTR_ERR(lcd->reg_vci); + dev_err(lcd->dev, "failed to get %s regulator (%d)\n", + "VCI", ret); + lcd->reg_vci = NULL; + } + + lcd->ld = lcd_device_register("s6e39a0x02", lcd->dev, lcd, + &s6e39a0x02_lcd_ops); + if (IS_ERR(lcd->ld)) { + dev_err(lcd->dev, "failed to register lcd ops.\n"); + ret = PTR_ERR(lcd->ld); + goto err_regulator; + } + + lcd->bd = backlight_device_register("s6e39a0x02-bl", lcd->dev, lcd, + &s6e39a0x02_backlight_ops, NULL); + if (IS_ERR(lcd->bd)) { + dev_err(lcd->dev, "failed to register backlight ops.\n"); + ret = PTR_ERR(lcd->bd); + goto err_unregister_lcd; + } + + lcd->bd->props.max_brightness = MAX_BRIGHTNESS; + lcd->bd->props.brightness = MAX_BRIGHTNESS; + + lcd->acl_enable = 1; + lcd->cur_acl = 0; + + ret = device_create_file(&lcd->ld->dev, &dev_attr_acl_control); + if (ret < 0) + dev_err(&lcd->ld->dev, "failed to add sysfs entries\n"); + + dev_set_drvdata(&dsim_dev->dev, lcd); + + gpio_request(EXYNOS4_GPF0(2), "TE_INT"); + s5p_register_gpio_interrupt(EXYNOS4_GPF0(2)); + + s3c_gpio_cfgpin(EXYNOS4_GPF0(2), S3C_GPIO_SFN(0xF)); + s3c_gpio_setpull(EXYNOS4_GPF0(2), S3C_GPIO_PULL_NONE); + s5p_gpio_set_drvstr(EXYNOS4_GPF0(2), S5P_GPIO_DRVSTR_LV1); + irq_set_irq_type(gpio_to_irq(EXYNOS4_GPF0(2)), IRQ_TYPE_EDGE_RISING); + + ret = request_irq(gpio_to_irq(EXYNOS4_GPF0(2)), te_interrupt, + IRQF_TRIGGER_RISING | IRQF_SHARED, + "te_signal", lcd->dev); + + if (ret < 0) { + dev_err(lcd->dev, "%s: request_irq failed. %d\n", + __func__, ret); + goto err_te_irq; + } + + mdelay(100); + + s6e39a0x02_regulator_enable(lcd); + + lcd->power = FB_BLANK_UNBLANK; + + if (!lcd->ddi_pd->lcd_enabled) { + s6e39a0x02_panel_init(lcd); + s6e39a0x02_display_on(lcd); + } + + dev_info(lcd->dev, "probed s6e39a0x02 panel driver(%s).\n", + dev_name(&lcd->ld->dev)); + + return 0; + +err_unregister_lcd: +err_te_irq: + lcd_device_unregister(lcd->ld); + +err_regulator: + regulator_put(lcd->reg_vci); + + kfree(lcd); + + return ret; +} + +static void s6e39a0x02_remove(struct mipi_dsim_lcd_device *dsim_dev) +{ + struct s6e39a0x02 *lcd = dev_get_drvdata(&dsim_dev->dev); + + backlight_device_unregister(lcd->bd); + + lcd_device_unregister(lcd->ld); + + regulator_put(lcd->reg_vci); + + kfree(lcd); +} + +#ifdef CONFIG_PM +static int s6e39a0x02_suspend(struct mipi_dsim_lcd_device *dsim_dev) +{ + struct s6e39a0x02 *lcd = dev_get_drvdata(&dsim_dev->dev); + + s6e39a0x02_sleep_in(lcd); + mdelay(lcd->ddi_pd->power_off_delay); + + return 0; +} + +static int s6e39a0x02_resume(struct mipi_dsim_lcd_device *dsim_dev) +{ + struct s6e39a0x02 *lcd = dev_get_drvdata(&dsim_dev->dev); + + s6e39a0x02_sleep_out(lcd); + mdelay(lcd->ddi_pd->power_on_delay); + + return 0; +} +#else +#define s6e39a0x02_suspend NULL +#define s6e39a0x02_resume NULL +#endif + +static struct mipi_dsim_lcd_driver s6e39a0x02_dsim_ddi_driver = { + .name = "s6e39a0x02", + .id = -1, + + .power_on = s6e39a0x02_power_on, + .set_sequence = s6e39a0x02_set_sequence, + .probe = s6e39a0x02_probe, + .remove = s6e39a0x02_remove, + .suspend = s6e39a0x02_suspend, + .resume = s6e39a0x02_resume, +}; + +static int s6e39a0x02_init(void) +{ + s5p_mipi_dsi_register_lcd_driver(&s6e39a0x02_dsim_ddi_driver); + + return 0; +} + +static void s6e39a0x02_exit(void) +{ + return; +} + +module_init(s6e39a0x02_init); +module_exit(s6e39a0x02_exit); + +MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>"); +MODULE_DESCRIPTION("MIPI-DSI based s6e39a0x02 AMOLED LCD Panel Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/backlight/s6e39a0x02_gamma.h b/drivers/video/backlight/s6e39a0x02_gamma.h new file mode 100644 index 0000000..63412e3 --- /dev/null +++ b/drivers/video/backlight/s6e39a0x02_gamma.h @@ -0,0 +1,180 @@ +/* linux/drivers/video/backlight/s6e8aa0_brightness.h + * + * Brightness level definition. + * + * Copyright (c) 2011 Samsung Electronics + * + * Inki Dae, <inki.dae@samsung.com> + * Donghwa Lee <dh09.lee@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#ifndef _S6E39A0X02_BRIGHTNESS_H +#define _S6E39A0X02_BRIGHTNESS_H + +#define MAX_GAMMA_LEVEL 11 +#define GAMMA_TABLE_COUNT 26 + +/* gamma value: 2.2 */ +static const unsigned char s6e39a0x02_22_gamma_70[] = { + 0xFA, 0x02, 0x10, 0x10, 0x10, 0xD1, 0x34, 0xD0, 0xD6, 0xBA, + 0xDC, 0xE0, 0xD9, 0xE2, 0xC2, 0xC0, 0xBF, 0xD4, 0xD5, 0xD0, + 0x00, 0x73, 0x00, 0x59, 0x00, 0x82 +}; + +static const unsigned char s6e39a0x02_22_gamma_80[] = { + 0xFA, 0x02, 0x10, 0x10, 0x10, 0xD7, 0x39, 0xD6, 0xD6, 0xBF, + 0xDD, 0xE1, 0xDA, 0xE2, 0xC0, 0xBF, 0xBD, 0xD3, 0xD5, 0xCF, + 0x00, 0x78, 0x00, 0x5D, 0x00, 0x88 +}; + +static const unsigned char s6e39a0x02_22_gamma_90[] = { + 0xFA, 0x02, 0x10, 0x10, 0x10, 0xD7, 0x39, 0xD5, 0xD5, 0xBF, + 0xDC, 0xDF, 0xDA, 0xE0, 0xC1, 0xC0, 0xBD, 0xD2, 0xD4, 0xCF, + 0x00, 0x7C, 0x00, 0x60, 0x00, 0x8C +}; + +static const unsigned char s6e39a0x02_22_gamma_100[] = { + 0xFA, 0x02, 0x10, 0x10, 0x10, 0xDD, 0x3A, 0xE3, 0xD7, 0xC5, + 0xDD, 0xDF, 0xDA, 0xDF, 0xC0, 0xBF, 0xBC, 0xD0, 0xD3, 0xCD, + 0x00, 0x81, 0x00, 0x64, 0x00, 0x92 +}; + +static const unsigned char s6e39a0x02_22_gamma_110[] = { + 0xFA, 0x02, 0x10, 0x10, 0x10, 0xE1, 0x43, 0xE2, 0xD6, 0xC5, + 0xDC, 0xDE, 0xDA, 0xDF, 0xBF, 0xBF, 0xBB, 0xD0, 0xD3, 0xCD, + 0x00, 0x85, 0x00, 0x67, 0x00, 0x96 +}; + +static const unsigned char s6e39a0x02_22_gamma_120[] = { + 0xFA, 0x02, 0x10, 0x10, 0x10, 0xE5, 0x48, 0xE4, 0xD5, 0xC5, + 0xDB, 0xDE, 0xDA, 0xDD, 0xBE, 0xBF, 0xBB, 0xD0, 0xD2, 0xCC, + 0x00, 0x88, 0x00, 0x6A, 0x00, 0x9A +}; + +static const unsigned char s6e39a0x02_22_gamma_130[] = { + 0xFA, 0x02, 0x10, 0x10, 0x10, 0xE6, 0x4D, 0xE3, 0xD5, 0xC5, + 0xDA, 0xDD, 0xDA, 0xDD, 0xBE, 0xBE, 0xBA, 0xCE, 0xD1, 0xCA, + 0x00, 0x8C, 0x00, 0x6D, 0x00, 0x9F +}; + +static const unsigned char s6e39a0x02_22_gamma_140[] = { + 0xFA, 0x02, 0x10, 0x10, 0x10, 0xE8, 0x51, 0xE4, 0xD6, 0xC9, + 0xDB, 0xDC, 0xD9, 0xDC, 0xBE, 0xBF, 0xBA, 0xCD, 0xD0, 0xC9, + 0x00, 0x90, 0x00, 0x70, 0x00, 0xA3 +}; + +static const unsigned char s6e39a0x02_22_gamma_150[] = { + 0xFA, 0x02, 0x10, 0x10, 0x10, 0xEA, 0x57, 0xE9, 0xD6, 0xC9, + 0xDA, 0xDD, 0xDA, 0xDC, 0xBC, 0xBE, 0xB8, 0xCE, 0xD0, 0xCA, + 0x00, 0x92, 0x00, 0x72, 0x00, 0xA6 +}; + +static const unsigned char s6e39a0x02_22_gamma_160[] = { + 0xFA, 0x02, 0x10, 0x10, 0x10, 0xF0, 0x61, 0xEE, 0xD5, 0xC9, + 0xD9, 0xDD, 0xDA, 0xDB, 0xBC, 0xBE, 0xB9, 0xCC, 0xCF, 0xC8, + 0x00, 0x96, 0x00, 0x75, 0x00, 0xAA +}; + +static const unsigned char s6e39a0x02_22_gamma_170[] = { + 0xFA, 0x02, 0x10, 0x10, 0x10, 0xF1, 0x81, 0xEE, 0xD4, 0xC9, + 0xD9, 0xDD, 0xDB, 0xDB, 0xBB, 0xBD, 0xB7, 0xCC, 0xCE, 0xC8, + 0x00, 0x99, 0x00, 0x78, 0x00, 0xAE +}; + +static const unsigned char s6e39a0x02_22_gamma_180[] = { + 0xFA, 0x02, 0x10, 0x10, 0x10, 0xF0, 0x8C, 0xED, 0xD5, 0xCA, + 0xD8, 0xDC, 0xDB, 0xDC, 0xBB, 0xBD, 0xB7, 0xCB, 0xCD, 0xC5, + 0x00, 0x9C, 0x00, 0x7A, 0x00, 0xB2 +}; + +static const unsigned char s6e39a0x02_22_gamma_190[] = { + 0xFA, 0x02, 0x10, 0x10, 0x10, 0xED, 0x90, 0xEC, 0xD4, 0xC9, + 0xD7, 0xDD, 0xDB, 0xDB, 0xBA, 0xBD, 0xB7, 0xCA, 0xCD, 0xC5, + 0x00, 0x9F, 0x00, 0x7C, 0x00, 0xB5 +}; + +static const unsigned char s6e39a0x02_22_gamma_200[] = { + 0xFA, 0x02, 0x10, 0x10, 0x10, 0xED, 0x8F, 0xED, 0xD2, 0xC7, + 0xD5, 0xDC, 0xDA, 0xDA, 0xBA, 0xBB, 0xB5, 0xCA, 0xCC, 0xC4, + 0x00, 0xA0, 0x00, 0x7F, 0x00, 0xBB +}; + +static const unsigned char s6e39a0x02_22_gamma_210[] = { + 0xFA, 0x02, 0x10, 0x10, 0x10, 0xEF, 0x96, 0xEF, 0xD1, 0xC7, + 0xD3, 0xDB, 0xD9, 0xD9, 0xB9, 0xBB, 0xB4, 0xCA, 0xCC, 0xC6, + 0x00, 0xA3, 0x00, 0x81, 0x00, 0xBD +}; + +static const unsigned char s6e39a0x02_22_gamma_220[] = { + 0xFA, 0x02, 0x10, 0x10, 0x10, 0xED, 0x99, 0xEE, 0xD3, 0xC8, + 0xD3, 0xDB, 0xD9, 0xD9, 0xB8, 0xBB, 0xB4, 0xC9, 0xCC, 0xC4, + 0x00, 0xA6, 0x00, 0x83, 0x00, 0xC1 +}; + +static const unsigned char s6e39a0x02_22_gamma_230[] = { + 0xFA, 0x02, 0x10, 0x10, 0x10, 0xEE, 0xA3, 0xEF, 0xD2, 0xC9, + 0xD3, 0xDB, 0xD9, 0xD9, 0xB9, 0xBA, 0xB3, 0xC8, 0xCC, 0xC4, + 0x00, 0xA8, 0x00, 0x85, 0x00, 0xC4 +}; + +static const unsigned char s6e39a0x02_22_gamma_240[] = { + 0xFA, 0x02, 0x10, 0x10, 0x10, 0xED, 0xA4, 0xEE, 0xD2, 0xC9, + 0xD3, 0xDC, 0xD9, 0xD9, 0xB7, 0xBA, 0xB3, 0xC8, 0xCB, 0xC2, + 0x00, 0xAB, 0x00, 0x87, 0x00, 0xC8 +}; + +static const unsigned char s6e39a0x02_22_gamma_250[] = { + 0xFA, 0x02, 0x10, 0x10, 0x10, 0xED, 0xA8, 0xEF, 0xD2, 0xC8, + 0xD2, 0xDA, 0xD9, 0xD8, 0xB7, 0xB9, 0xB2, 0xC8, 0xCB, 0xC2, + 0x00, 0xAD, 0x00, 0x89, 0x00, 0xCB +}; + +static const unsigned char s6e39a0x02_22_gamma_260[] = { + 0xFA, 0x02, 0x10, 0x10, 0x10, 0xEB, 0xB2, 0xEF, 0xD2, 0xC9, + 0xD3, 0xDB, 0xDA, 0xD9, 0xB6, 0xB9, 0xBA, 0xC7, 0xCA, 0xC1, + 0x00, 0xB0, 0x00, 0x8B, 0x00, 0xCE +}; + +static const unsigned char s6e39a0x02_22_gamma_270[] = { + 0xFA, 0x02, 0x10, 0x10, 0x10, 0xEC, 0xB0, 0xEF, 0xD1, 0xC9, + 0xD2, 0xDB, 0xDA, 0xD9, 0xB7, 0xB9, 0xB1, 0xC6, 0xC9, 0xC0, + 0x00, 0xB2, 0x00, 0x8D, 0x00, 0xD1 +}; + +static const unsigned char s6e39a0x02_22_gamma_280[] = { + 0xFA, 0x02, 0x10, 0x10, 0x10, 0xED, 0xB6, 0xF0, 0xD1, 0xC9, + 0xD2, 0xDB, 0xDA, 0xD9, 0xB6, 0xB8, 0xAF, 0xC7, 0xCA, 0xC2, + 0x00, 0xB4, 0x00, 0x8F, 0x00, 0xD3 +}; + +static const unsigned char s6e39a0x02_22_gamma_290[] = { + 0xFA, 0x02, 0x10, 0x10, 0x10, 0xEC, 0xB7, 0xEE, 0xD0, 0xC8, + 0xD1, 0xDB, 0xDA, 0xD9, 0xB6, 0xB8, 0xB0, 0xC5, 0xC9, 0xC1, + 0x00, 0xB7, 0x00, 0x91, 0x00, 0xD5 +}; + +static const unsigned char s6e39a0x02_22_gamma_300[] = { + 0xFA, 0x02, 0x10, 0x10, 0x10, 0xEC, 0xB7, 0xEF, 0xD1, 0xCA, + 0xD1, 0xDB, 0xDA, 0xD8, 0xB5, 0xB8, 0xB0, 0xC5, 0xC8, 0xBF, + 0x00, 0xB9, 0x00, 0x93, 0x00, 0xD9 +}; + +static const unsigned char *s6e39a0x02_22_gamma_table[MAX_GAMMA_LEVEL] = { + s6e39a0x02_22_gamma_70, + s6e39a0x02_22_gamma_90, + s6e39a0x02_22_gamma_110, + s6e39a0x02_22_gamma_130, + s6e39a0x02_22_gamma_150, + s6e39a0x02_22_gamma_170, + s6e39a0x02_22_gamma_190, + s6e39a0x02_22_gamma_210, + s6e39a0x02_22_gamma_240, + s6e39a0x02_22_gamma_270, + s6e39a0x02_22_gamma_300, +}; + +#endif diff --git a/drivers/video/backlight/s6e63m0_mipi_lcd.c b/drivers/video/backlight/s6e63m0_mipi_lcd.c new file mode 100644 index 0000000..f0b2d00 --- /dev/null +++ b/drivers/video/backlight/s6e63m0_mipi_lcd.c @@ -0,0 +1,166 @@ +/* linux/drivers/video/backlight/s6e63m0_mipi_lcd.c + * + * + * Copyright (c) 2011 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * +*/ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/mutex.h> +#include <linux/wait.h> +#include <linux/ctype.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/gpio.h> +#include <linux/workqueue.h> +#include <linux/backlight.h> +#include <linux/lcd.h> + +#include <video/mipi_display.h> + +#include <plat/gpio-cfg.h> +#include <plat/regs-dsim.h> + +#include <plat/dsim.h> +#include <plat/mipi_dsi.h> + +void init_lcd(struct mipi_dsim_device *dsim) +{ + unsigned char buf1[4] = {0xf2, 0x02, 0x03, 0x1b}; + unsigned char buf2[15] = {0xf8, 0x01, 0x27, 0x27, 0x07, 0x07, 0x54, + 0x9f, 0x63, 0x86, 0x1a, + 0x33, 0x0d, 0x00, 0x00}; + unsigned char buf3[4] = {0xf6, 0x00, 0x8c, 0x07}; + + /* password */ + unsigned char rf[3] = {0x00, 0x5a, 0x5a}; + + rf[0] = 0xf0; + s5p_mipi_dsi_wr_data(dsim, MIPI_DSI_DCS_LONG_WRITE, + (unsigned int) rf, 3); + + rf[0] = 0xf1; + s5p_mipi_dsi_wr_data(dsim, MIPI_DSI_DCS_LONG_WRITE, + (unsigned int) rf, 3); + + rf[0] = 0xfc; + s5p_mipi_dsi_wr_data(dsim, MIPI_DSI_DCS_LONG_WRITE, + (unsigned int) rf, 3); + + /* HZ (0x16 = 60Hz) */ + s5p_mipi_dsi_wr_data(dsim, MIPI_DSI_DCS_SHORT_WRITE_PARAM, + 0xfd, 0x14); + + s5p_mipi_dsi_wr_data(dsim, + MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0xb0, 0x09); + s5p_mipi_dsi_wr_data(dsim, + MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0xd5, 0x64); + s5p_mipi_dsi_wr_data(dsim, + MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0xb0, 0x09); + + /* nonBurstSyncPulse */ + if (dsim->pd->dsim_config->e_burst_mode == DSIM_NON_BURST_SYNC_PULSE) + s5p_mipi_dsi_wr_data(dsim, + MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0xd5, 0x84); + else + s5p_mipi_dsi_wr_data(dsim, + MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0xd5, 0xc4); + + s5p_mipi_dsi_wr_data(dsim, + MIPI_DSI_DCS_LONG_WRITE, (unsigned int) buf1, sizeof(buf1)); + s5p_mipi_dsi_wr_data(dsim, + MIPI_DSI_DCS_LONG_WRITE, (unsigned int) buf2, sizeof(buf2)); + s5p_mipi_dsi_wr_data(dsim, + MIPI_DSI_DCS_LONG_WRITE, (unsigned int) buf3, sizeof(buf3)); + + s5p_mipi_dsi_wr_data(dsim, + MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0xfa, 0x01); + /* Exit sleep */ + s5p_mipi_dsi_wr_data(dsim, MIPI_DSI_DCS_SHORT_WRITE, + 0x11, 0); + mdelay(600); + /* Set Display ON */ + s5p_mipi_dsi_wr_data(dsim, MIPI_DSI_DCS_SHORT_WRITE, + 0x29, 0); +} + +void s6e63m0_mipi_lcd_off(struct mipi_dsim_device *dsim) +{ + /* softreset */ + s5p_mipi_dsi_wr_data(dsim, MIPI_DSI_DCS_SHORT_WRITE, + 0x1, 0); + /* Enter sleep */ + s5p_mipi_dsi_wr_data(dsim, MIPI_DSI_DCS_SHORT_WRITE, + 0x10, 0); + mdelay(60); + + /* Set Display off */ + s5p_mipi_dsi_wr_data(dsim, MIPI_DSI_DCS_SHORT_WRITE, + 0x28, 0); +} + +static int s6e63m0_mipi_lcd_bl_update_status(struct backlight_device *bd) +{ + return 0; +} + +static const struct backlight_ops s6e63m0_mipi_lcd_bl_ops = { + .update_status = s6e63m0_mipi_lcd_bl_update_status, +}; + +static int s6e63m0_mipi_lcd_probe(struct mipi_dsim_device *dsim) +{ + struct mipi_dsim_device *dsim_drv; + struct backlight_device *bd = NULL; + struct backlight_properties props; + + dsim_drv = kzalloc(sizeof(struct mipi_dsim_device), GFP_KERNEL); + if (!dsim_drv) + return -ENOMEM; + + dsim_drv = (struct mipi_dsim_device *) dsim; + + props.max_brightness = 1; + props.type = BACKLIGHT_PLATFORM; + + bd = backlight_device_register("pwm-backlight", + dsim_drv->dev, dsim_drv, &s6e63m0_mipi_lcd_bl_ops, &props); + + return 0; +} + +static int s6e63m0_mipi_lcd_suspend(struct mipi_dsim_device *dsim) +{ + s6e63m0_mipi_lcd_off(dsim); + return 0; +} + +static int s6e63m0_mipi_lcd_displayon(struct mipi_dsim_device *dsim) +{ + init_lcd(dsim); + + return 0; +} + +static int s6e63m0_mipi_lcd_resume(struct mipi_dsim_device *dsim) +{ + init_lcd(dsim); + return 0; +} + +struct mipi_dsim_lcd_driver s6e63m0_mipi_lcd_driver = { + .probe = s6e63m0_mipi_lcd_probe, + .suspend = s6e63m0_mipi_lcd_suspend, + .displayon = s6e63m0_mipi_lcd_displayon, + .resume = s6e63m0_mipi_lcd_resume, +}; + diff --git a/drivers/video/backlight/s6e8aa0.c b/drivers/video/backlight/s6e8aa0.c new file mode 100644 index 0000000..c9b1184 --- /dev/null +++ b/drivers/video/backlight/s6e8aa0.c @@ -0,0 +1,1187 @@ +/* linux/drivers/video/s6e8aa0.c + * + * MIPI-DSI based s6e8aa0 AMOLED lcd 5.3 inch panel driver. + * + * Inki Dae, <inki.dae@samsung.com> + * Donghwa Lee, <dh09.lee@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/mutex.h> +#include <linux/wait.h> +#include <linux/ctype.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/lcd.h> +#include <linux/fb.h> +#include <linux/backlight.h> +#include <linux/regulator/consumer.h> +#include <linux/firmware.h> + +#include <video/mipi_display.h> + +#include <plat/mipi_dsim2.h> + +#include "s6e8aa0_gamma.h" +#ifdef CONFIG_BACKLIGHT_SMART_DIMMING +#include "smart_dimming.h" +#endif + +#define LDI_FW_PATH "s6e8aa0/reg_%s.bin" +#define MAX_STR 255 +#define LDI_MTP_LENGTH 24 +#define MAX_READ_LENGTH 64 +#define DSIM_PM_STABLE_TIME (10) +#define MIN_BRIGHTNESS (0) +#define MAX_BRIGHTNESS (24) + +#define VER_142 (0x8e) /* MACH_SLP_PQ */ +#define VER_174 (0xae) /* MACH_SLP_PQ Dali */ +#define VER_42 (0x2a) /* MACH_SLP_PQ_LTE */ +#define VER_32 (0x20) /* MACH_SLP_PQ M0 B-Type */ +#define VER_210 (0xd2) /* MACH_SLP_PQ M0 A-Type */ + +#define AID_DISABLE (0x4) +#define AID_1 (0x5) +#define AID_2 (0x6) +#define AID_3 (0x7) + +#define POWER_IS_ON(pwr) ((pwr) == FB_BLANK_UNBLANK) +#define POWER_IS_OFF(pwr) ((pwr) == FB_BLANK_POWERDOWN) +#define POWER_IS_NRM(pwr) ((pwr) == FB_BLANK_NORMAL) + +#define lcd_to_master(a) (a->dsim_dev->master) +#define lcd_to_master_ops(a) ((lcd_to_master(a))->master_ops) + +struct str_elvss { + u8 reference; + u8 limit; +}; + +struct panel_model { + int ver; + char *name; +}; + +enum { + DSIM_NONE_STATE = 0, + DSIM_RESUME_COMPLETE = 1, + DSIM_FRAME_DONE = 2, +}; + +struct s6e8aa0 { + struct device *dev; + unsigned int id; + unsigned int aid; + unsigned int ver; + unsigned int power; + unsigned int current_brightness; + unsigned int updated; + unsigned int brightness; + unsigned int resume_complete; + unsigned int acl_enable; + unsigned int cur_acl; + unsigned int cur_addr; + + struct lcd_device *ld; + struct backlight_device *bd; + + struct mipi_dsim_lcd_device *dsim_dev; + struct lcd_platform_data *ddi_pd; + + struct mutex lock; + struct regulator *reg_vdd3; + struct regulator *reg_vci; + + const struct panel_model *model; + unsigned int model_count; + + bool enabled; +#ifdef CONFIG_BACKLIGHT_SMART_DIMMING + unsigned int support_elvss; + struct str_smart_dim smart_dim; + struct str_elvss elvss; + struct mutex bl_lock; +#endif +}; + +struct s6e8aa0 *lcd_global; + +static void s6e8aa0_regulator_enable(struct s6e8aa0 *lcd) +{ + mutex_lock(&lcd->lock); + if (!lcd->enabled) { + if (lcd->reg_vdd3) + regulator_enable(lcd->reg_vdd3); + + if (lcd->reg_vci) + regulator_enable(lcd->reg_vci); + + lcd->enabled = true; + } + mutex_unlock(&lcd->lock); +} + +static void s6e8aa0_regulator_disable(struct s6e8aa0 *lcd) +{ + mutex_lock(&lcd->lock); + if (lcd->enabled) { + if (lcd->reg_vci) + regulator_disable(lcd->reg_vci); + + if (lcd->reg_vdd3) + regulator_disable(lcd->reg_vdd3); + + lcd->enabled = false; + } + mutex_unlock(&lcd->lock); +} + +static void s6e8aa0_apply_level_1_key(struct s6e8aa0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + const unsigned char data_to_send[] = { + 0xf0, 0x5a, 0x5a + }; + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + (unsigned int)data_to_send, ARRAY_SIZE(data_to_send)); +} + +static unsigned char s6e8aa0_apply_aid_panel_cond(unsigned int aid) +{ + unsigned char ret; + + switch (aid) { + case AID_1: + ret = 0x60; + break; + case AID_2: + ret = 0x80; + break; + case AID_3: + ret = 0xA0; + break; + default: + ret = 0x04; + break; + } + + return ret; +} + +void s6e8aa0_panel_cond(int high_freq) +{ + struct s6e8aa0 *lcd = lcd_global; + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + unsigned char data_to_send_v42[] = { + 0xf8, 0x01, 0x34, 0x00, 0x00, 0x00, 0x95, 0x00, 0x3c, + 0x7d, 0x08, 0x27, 0x00, 0x00, 0x10, 0x00, 0x00, 0x20, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x08, + 0x23, 0x63, 0xc0, 0xc1, 0x01, 0x81, 0xc1, 0x00, 0xc8, + 0xc1, 0xd3, 0x01 + }; + /* same with v174(0xae) panel */ + unsigned char data_to_send_v142[] = { + 0xf8, 0x3d, 0x35, 0x00, 0x00, 0x00, 0x93, 0x00, 0x3c, + 0x7d, 0x08, 0x27, 0x7d, 0x3f, 0x00, 0x00, 0x00, 0x20, + 0x04, 0x08, 0x6e, 0x00, 0x00, 0x00, 0x02, 0x08, 0x08, + 0x23, 0x23, 0xc0, 0xc8, 0x08, 0x48, 0xc1, 0x00, 0xc1, + 0xff, 0xff, 0xc8 + }; + unsigned char data_to_send_v32[] = { + 0xf8, 0x19, 0x35, 0x00, 0x00, 0x00, 0x94, 0x00, 0x3c, + 0x7d, 0x10, 0x27, 0x08, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x08, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, + 0x23, 0x6e, 0xc0, 0xc1, 0x01, 0x81, 0xc1, 0x00, 0xc3, + 0xf6, 0xf6, 0xc1 + }; + unsigned char data_to_send_v210[] = { + 0xf8, 0x3d, 0x32, 0x00, 0x00, 0x00, 0x8d, 0x00, 0x39, + 0x78, 0x08, 0x26, 0x78, 0x3c, 0x00, 0x00, 0x00, 0x20, + 0x04, 0x08, 0x69, 0x00, 0x00, 0x00, 0x02, 0x07, 0x07, + 0x21, 0x21, 0xc0, 0xc8, 0x08, 0x48, 0xc1, 0x00, 0xc1, + 0xff, 0xff, 0xc8 + }; + unsigned char *data_to_send; + unsigned int size; + + if (lcd->ver == VER_42) { + data_to_send = data_to_send_v42; + size = ARRAY_SIZE(data_to_send_v42); + } else if (lcd->ver == VER_142 || lcd->ver == VER_174) { + data_to_send_v142[18] = s6e8aa0_apply_aid_panel_cond(lcd->aid); + data_to_send = data_to_send_v142; + size = ARRAY_SIZE(data_to_send_v142); + } else if (lcd->ver == VER_32) { + data_to_send = data_to_send_v32; + size = ARRAY_SIZE(data_to_send_v32); + } else if (lcd->ver == VER_210) { + data_to_send = data_to_send_v210; + size = ARRAY_SIZE(data_to_send_v210); + } else + return; + + if (!high_freq) { + data_to_send[9] = 0x7e; + data_to_send[25] = 0x0a; + data_to_send[26] = 0x0a; + } + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + (unsigned int)data_to_send, size); +} + +static void s6e8aa0_display_condition_set(struct s6e8aa0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + const unsigned char data_to_send[] = { + 0xf2, 0x80, 0x03, 0x0d + }; + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + (unsigned int)data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8aa0_etc_source_control(struct s6e8aa0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + const unsigned char data_to_send[] = { + 0xf6, 0x00, 0x02, 0x00 + }; + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + (unsigned int)data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8aa0_etc_pentile_control(struct s6e8aa0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + unsigned char data_to_send[] = { + 0xb6, 0x0c, 0x02, 0x03, 0x32, 0xff, 0x44, 0x44, 0xc0, + 0x00 + }; + + if (lcd->ver == VER_32) + data_to_send[5] = 0xc0; + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + (unsigned int)data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8aa0_etc_mipi_control1(struct s6e8aa0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + const unsigned char data_to_send[] = { + 0xe1, 0x10, 0x1c, 0x17, 0x08, 0x1d + }; + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + (unsigned int)data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8aa0_etc_mipi_control2(struct s6e8aa0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + const unsigned char data_to_send[] = { + 0xe2, 0xed, 0x07, 0xc3, 0x13, 0x0d, 0x03 + }; + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + (unsigned int)data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8aa0_etc_power_control(struct s6e8aa0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + unsigned char data_to_send[] = { + 0xf4, 0xcf, 0x0a, 0x12, 0x10, 0x1e, 0x33, 0x02 + }; + + if (lcd->ver == VER_32) { + data_to_send[3] = 0x15; + data_to_send[5] = 0x19; + } + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + (unsigned int)data_to_send, ARRAY_SIZE(data_to_send)); +} +static void s6e8aa0_etc_mipi_control3(struct s6e8aa0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0xe3, 0x40); +} + +static void s6e8aa0_etc_mipi_control4(struct s6e8aa0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + const unsigned char data_to_send[] = { + 0xe4, 0x00, 0x00, 0x14, 0x80, 0x00, 0x00, 0x00 + }; + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + (unsigned int)data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8aa0_etc_elvss_control(struct s6e8aa0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + unsigned char data_to_send[] = { + 0xb1, 0x04, 0x00 + }; + + if (lcd->id == 0x00) + data_to_send[2] = 0x95; + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + (unsigned int)data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8aa0_elvss_nvm_set(struct s6e8aa0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + unsigned char data_to_send[] = { + 0xd9, 0x14, 0x40, 0x0c, 0xcb, 0xce, 0x6e, 0xc4, 0x0f, + 0x40, 0x41, 0xd9, 0x00, 0x60, 0x19 + }; + + /* FIXME: !! need to change brightness and elvss */ + if (lcd->ver == VER_32) { + data_to_send[8] = 0x07; + data_to_send[11] = 0xc1; + } else if (lcd->ver == VER_210 || lcd->ver == VER_174) { + data_to_send[8] = 0x07; + data_to_send[11] = 0xd0; + } else { + switch (lcd->brightness) { + case 0 ... 6: /* 30cd ~ 100cd */ + data_to_send[11] = 0xdf; + break; + case 7 ... 11: /* 120cd ~ 150cd */ + data_to_send[11] = 0xdd; + break; + case 12 ... 15: /* 180cd ~ 210cd */ + data_to_send[11] = 0xd9; + break; + case 16 ... 24: /* 240cd ~ 300cd */ + data_to_send[11] = 0xd0; + break; + default: + break; + } + } + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + (unsigned int)data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8aa0_sleep_in(struct s6e8aa0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE, 0x10, 0x00); +} + +static void s6e8aa0_sleep_out(struct s6e8aa0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE, 0x11, 0x00); +} + +static void s6e8aa0_display_on(struct s6e8aa0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE, 0x29, 0x00); +} + +static void s6e8aa0_display_off(struct s6e8aa0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE, 0x28, 0x00); +} + +static void s6e8aa0_apply_level_2_key(struct s6e8aa0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + const unsigned char data_to_send[] = { + 0xfc, 0x5a, 0x5a + }; + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + (unsigned int)data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8aa0_acl_on(struct s6e8aa0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE, 0xc0, 0x01); +} + +static void s6e8aa0_acl_off(struct s6e8aa0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE, 0xc0, 0x00); +} + +static void s6e8aa0_acl_ctrl_set(struct s6e8aa0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + /* FIXME: !! must be review acl % value */ + /* Full white 50% reducing setting */ + const unsigned char cutoff_50[] = { + 0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf, + 0x00, 0x00, 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x08, 0x0f, 0x16, 0x1d, 0x24, 0x2a, 0x31, 0x38, + 0x3f, 0x46 + }; + /* Full white 45% reducing setting */ + const unsigned char cutoff_45[] = { + 0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf, + 0x00, 0x00, 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x07, 0x0d, 0x13, 0x19, 0x1f, 0x25, 0x2b, 0x31, + 0x37, 0x3d + }; + /* Full white 40% reducing setting */ + const unsigned char cutoff_40[] = { + 0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf, + 0x00, 0x00, 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x06, 0x0c, 0x11, 0x16, 0x1c, 0x21, 0x26, 0x2b, + 0x31, 0x36 + }; + + if (lcd->acl_enable) { + if (lcd->cur_acl == 0) { + if (lcd->brightness == 0 || lcd->brightness == 1) { + s6e8aa0_acl_off(lcd); + dev_dbg(&lcd->ld->dev, + "cur_acl=%d\n", lcd->cur_acl); + } else + s6e8aa0_acl_on(lcd); + } + switch (lcd->brightness) { + case 0 ... 1: /* 30cd */ + s6e8aa0_acl_off(lcd); + lcd->cur_acl = 0; + break; + case 2 ... 6: /* 50cd ~ 100cd */ + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_LONG_WRITE, + (unsigned int)cutoff_40, + ARRAY_SIZE(cutoff_40)); + lcd->cur_acl = 40; + break; + case 7 ... 16: /* 120cd ~ 210cd */ + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_LONG_WRITE, + (unsigned int)cutoff_45, + ARRAY_SIZE(cutoff_45)); + lcd->cur_acl = 45; + break; + case 17 ... 24: /* 220cd ~ 300cd */ + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_LONG_WRITE, + (unsigned int)cutoff_50, + ARRAY_SIZE(cutoff_50)); + lcd->cur_acl = 50; + break; + default: + break; + } + } else { + s6e8aa0_acl_off(lcd); + lcd->cur_acl = 0; + dev_dbg(&lcd->ld->dev, "cur_acl = %d\n", lcd->cur_acl); + } +} + +static void s6e8aa0_enable_mtp_register(struct s6e8aa0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + const unsigned char data_to_send[] = { + 0xf1, 0x5a, 0x5a + }; + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + (unsigned int)data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8aa0_disable_mtp_register(struct s6e8aa0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + const unsigned char data_to_send[] = { + 0xf1, 0xa5, 0xa5 + }; + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + (unsigned int)data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8aa0_read_id(struct s6e8aa0 *lcd, u8 *mtp_id) +{ + unsigned int ret; + unsigned int addr = 0xd1; /* MTP ID */ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + + ret = ops->cmd_read(lcd_to_master(lcd), + MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM, + addr, 3, mtp_id); +} + +static unsigned int s6e8aa0_read_mtp(struct s6e8aa0 *lcd, u8 *mtp_data) +{ + unsigned int ret; + unsigned int addr = 0xd3; /* MTP addr */ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + + s6e8aa0_enable_mtp_register(lcd); + + ret = ops->cmd_read(lcd_to_master(lcd), + MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM, + addr, LDI_MTP_LENGTH, mtp_data); + + s6e8aa0_disable_mtp_register(lcd); + + return ret; +} + +unsigned int convert_brightness_to_gamma(int brightness) +{ + const unsigned int gamma_table[] = { + 30, 30, 50, 70, 80, 90, 100, 120, 130, 140, + 150, 160, 170, 180, 190, 200, 210, 220, 230, + 240, 250, 260, 270, 280, 300 + }; + + return gamma_table[brightness] - 1; +} + +static int s6e8aa0_update_gamma_ctrl(struct s6e8aa0 *lcd, int brightness) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + +#ifdef CONFIG_BACKLIGHT_SMART_DIMMING + unsigned int gamma; + unsigned char gamma_set[GAMMA_TABLE_COUNT] = {0,}; + gamma = convert_brightness_to_gamma(brightness); + + gamma_set[0] = 0xfa; + gamma_set[1] = 0x01; + + calc_gamma_table(&lcd->smart_dim, gamma, gamma_set + 2); + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + (unsigned int)gamma_set, + GAMMA_TABLE_COUNT); +#else + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + (unsigned int)s6e8aa0_22_gamma_table[brightness], + GAMMA_TABLE_COUNT); +#endif + + /* update gamma table. */ + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_SHORT_WRITE_PARAM, + 0xf7, 0x03); + + lcd->brightness = brightness; + + s6e8aa0_acl_ctrl_set(lcd); + + return 0; +} + +static int s6e8aa0_gamma_ctrl(struct s6e8aa0 *lcd, int brightness) +{ + s6e8aa0_update_gamma_ctrl(lcd, brightness); + + return 0; +} + +static int s6e8aa0_panel_init(struct s6e8aa0 *lcd) +{ + s6e8aa0_apply_level_1_key(lcd); + if (system_rev == 3) + s6e8aa0_apply_level_2_key(lcd); + + s6e8aa0_sleep_out(lcd); + usleep_range(5000, 6000); + + s6e8aa0_panel_cond(1); + s6e8aa0_display_condition_set(lcd); + + s6e8aa0_gamma_ctrl(lcd, lcd->bd->props.brightness); + + s6e8aa0_etc_source_control(lcd); + s6e8aa0_etc_pentile_control(lcd); + s6e8aa0_elvss_nvm_set(lcd); + s6e8aa0_etc_power_control(lcd); + s6e8aa0_etc_elvss_control(lcd); + + /* if ID3 value is not 33h, branch private elvss mode */ + mdelay(lcd->ddi_pd->power_on_delay); + + return 0; +} + +static int s6e8aa0_early_set_power(struct lcd_device *ld, int power) +{ + struct s6e8aa0 *lcd = lcd_get_data(ld); + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + int ret = 0; + + if (power != FB_BLANK_UNBLANK && power != FB_BLANK_POWERDOWN && + power != FB_BLANK_NORMAL) { + dev_err(lcd->dev, "power value should be 0, 1 or 4.\n"); + return -EINVAL; + } + + if (lcd->power == power) { + dev_err(lcd->dev, "power mode is same as previous one.\n"); + return -EINVAL; + } + + if (ops->set_early_blank_mode) { + /* LCD power off */ + if ((POWER_IS_OFF(power) && POWER_IS_ON(lcd->power)) + || (POWER_IS_ON(lcd->power) && POWER_IS_NRM(power))) { + ret = ops->set_early_blank_mode(lcd_to_master(lcd), + power); + if (!ret && lcd->power != power) + lcd->power = power; + } + } + + return ret; +} + +static int s6e8aa0_set_power(struct lcd_device *ld, int power) +{ + struct s6e8aa0 *lcd = lcd_get_data(ld); + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + int ret = 0; + + if (power != FB_BLANK_UNBLANK && power != FB_BLANK_POWERDOWN && + power != FB_BLANK_NORMAL) { + dev_err(lcd->dev, "power value should be 0, 1 or 4.\n"); + return -EINVAL; + } + + if (lcd->power == power) { + dev_err(lcd->dev, "power mode is same as previous one.\n"); + return -EINVAL; + } + + if (ops->set_blank_mode) { + ret = ops->set_blank_mode(lcd_to_master(lcd), power); + if (!ret && lcd->power != power) + lcd->power = power; + } + + return ret; +} + + +static int s6e8aa0_get_power(struct lcd_device *ld) +{ + struct s6e8aa0 *lcd = lcd_get_data(ld); + + return lcd->power; +} + +static int s6e8aa0_get_brightness(struct backlight_device *bd) +{ + return bd->props.brightness; +} + +static int s6e8aa0_set_brightness(struct backlight_device *bd) +{ + int ret = 0, brightness = bd->props.brightness; + struct s6e8aa0 *lcd = bl_get_data(bd); + + if (!lcd->enabled) { + dev_err(lcd->dev, + "lcd off: brightness set failed.\n"); + return -EINVAL; + } + + if (brightness < MIN_BRIGHTNESS || + brightness > bd->props.max_brightness) { + dev_err(lcd->dev, "lcd brightness should be %d to %d.\n", + MIN_BRIGHTNESS, MAX_BRIGHTNESS); + return -EINVAL; + } + + ret = s6e8aa0_gamma_ctrl(lcd, brightness); + if (ret) { + dev_err(&bd->dev, "lcd brightness setting failed.\n"); + return -EIO; + } + + return ret; +} + +static struct lcd_ops s6e8aa0_lcd_ops = { + .early_set_power = s6e8aa0_early_set_power, + .set_power = s6e8aa0_set_power, + .get_power = s6e8aa0_get_power, +}; + +const static struct backlight_ops s6e8aa0_backlight_ops = { + .get_brightness = s6e8aa0_get_brightness, + .update_status = s6e8aa0_set_brightness, +}; + +static int s6e8aa0_check_mtp(struct mipi_dsim_lcd_device *dsim_dev) +{ + struct s6e8aa0 *lcd = dev_get_drvdata(&dsim_dev->dev); + int ret; +#ifdef CONFIG_BACKLIGHT_SMART_DIMMING + int i; +#endif + u8 mtp_data[LDI_MTP_LENGTH] = {0, }; + u8 mtp_id[3] = {0, }; + + s6e8aa0_read_id(lcd, mtp_id); + if (mtp_id[0] == 0x00) { + dev_err(lcd->dev, "read id failed\n"); + return -EIO; + } + + dev_info(lcd->dev, + "Read ID : %x, %x, %x\n", mtp_id[0], mtp_id[1], mtp_id[2]); + + if (mtp_id[2] == 0x33) + dev_info(lcd->dev, + "ID-3 is 0xff does not support dynamic elvss\n"); + else { + dev_info(lcd->dev, + "ID-3 is 0x%x support dynamic elvss\n", mtp_id[2]); + dev_info(lcd->dev, "Dynamic ELVSS Information\n"); + } + + lcd->ver = mtp_id[1]; + lcd->id = mtp_id[2]; + lcd->aid = (mtp_id[2] >> 5); + +#ifdef CONFIG_BACKLIGHT_SMART_DIMMING + for (i = 0; i < PANEL_ID_MAX; i++) + lcd->smart_dim.panelid[i] = mtp_id[i]; + + init_table_info(&lcd->smart_dim); +#endif + + ret = s6e8aa0_read_mtp(lcd, mtp_data); + if (!ret) { + dev_err(lcd->dev, "read mtp failed\n"); + return -EIO; + } + +#ifdef CONFIG_BACKLIGHT_SMART_DIMMING + calc_voltage_table(&lcd->smart_dim, mtp_data); +#endif + + return 0; +} + +static void s6e8aa0_power_on(struct mipi_dsim_lcd_device *dsim_dev, + unsigned int enable) +{ + struct s6e8aa0 *lcd = dev_get_drvdata(&dsim_dev->dev); + + if (enable) { + /* lcd power on */ + s6e8aa0_regulator_enable(lcd); + + mdelay(lcd->ddi_pd->reset_delay); + + /* lcd reset */ + if (lcd->ddi_pd->reset) + lcd->ddi_pd->reset(lcd->ld); + + mdelay(5); + } else + s6e8aa0_regulator_disable(lcd); + +} + +static void s6e8aa0_set_sequence(struct mipi_dsim_lcd_device *dsim_dev) +{ + struct s6e8aa0 *lcd = dev_get_drvdata(&dsim_dev->dev); + + s6e8aa0_panel_init(lcd); + s6e8aa0_display_on(lcd); +} + +static ssize_t acl_control_show(struct device *dev, struct + device_attribute * attr, char *buf) +{ + struct s6e8aa0 *lcd = dev_get_drvdata(dev); + char temp[3]; + + sprintf(temp, "%d\n", lcd->acl_enable); + strcpy(buf, temp); + + return strlen(buf); +} + +static ssize_t acl_control_store(struct device *dev, struct + device_attribute * attr, const char *buf, size_t size) +{ + struct s6e8aa0 *lcd = dev_get_drvdata(dev); + unsigned int value; + int rc; + + rc = strict_strtoul(buf, (unsigned int)0, (unsigned long *)&value); + if (rc < 0) + return rc; + + if (lcd->acl_enable != value) { + dev_info(dev, "acl control changed from %d to %d\n", + lcd->acl_enable, value); + lcd->acl_enable = value; + s6e8aa0_acl_ctrl_set(lcd); + } + return size; +} + +static ssize_t lcd_type_show(struct device *dev, struct + device_attribute * attr, char *buf) +{ + struct s6e8aa0 *lcd = dev_get_drvdata(dev); + char temp[32]; + int i; + + for (i = 0; i < lcd->model_count; i++) { + if (lcd->ver == lcd->model[i].ver) + break; + } + + if (i == lcd->model_count) + return -EINVAL; + + sprintf(temp, "%s\n", lcd->model[i].name); + strcpy(buf, temp); + + return strlen(buf); +} + +static int s6e8aa0_read_reg(struct s6e8aa0 *lcd, unsigned int addr, char *buf) +{ + unsigned char data[MAX_READ_LENGTH]; + unsigned int size; + int i; + int pos = 0; + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + + memset(data, 0x0, ARRAY_SIZE(data)); + size = ops->cmd_read(lcd_to_master(lcd), + MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM, + addr, MAX_READ_LENGTH, data); + if (!size) { + dev_err(lcd->dev, "failed to read 0x%.2x register.\n", addr); + return size; + } + + pos += sprintf(buf, "0x%.2x, ", addr); + for (i = 1; i < size+1; i++) { + if (i % 9 == 0) + pos += sprintf(buf+pos, "\n"); + pos += sprintf(buf+pos, "0x%.2x, ", data[i-1]); + } + pos += sprintf(buf+pos, "\n"); + + return pos; +} + +static int s6e8aa0_write_reg(struct s6e8aa0 *lcd, char *name) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + const struct firmware *fw; + char fw_path[MAX_STR+1]; + int ret = 0; + + mutex_lock(&lcd->lock); + snprintf(fw_path, MAX_STR, LDI_FW_PATH, name); + + ret = request_firmware(&fw, fw_path, lcd->dev); + if (ret) { + dev_err(lcd->dev, "failed to request firmware.\n"); + mutex_unlock(&lcd->lock); + return ret; + } + + if (fw->size < 2) + ret = ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE, + (unsigned int)fw->data, fw->size); + else + ret = ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_LONG_WRITE, + (unsigned int)fw->data, fw->size); + if (ret) + dev_err(lcd->dev, "failed to write 0x%.2x register and %d error.\n", + fw->data[0], ret); + + release_firmware(fw); + mutex_unlock(&lcd->lock); + + return ret; +} + +static ssize_t read_reg_show(struct device *dev, struct + device_attribute * attr, char *buf) +{ + struct s6e8aa0 *lcd = dev_get_drvdata(dev); + + if (lcd->cur_addr == 0) { + dev_err(dev, "failed to set current lcd register.\n"); + return -EINVAL; + } + + return s6e8aa0_read_reg(lcd, lcd->cur_addr, buf); +} + +static ssize_t read_reg_store(struct device *dev, struct + device_attribute * attr, const char *buf, size_t size) +{ + struct s6e8aa0 *lcd = dev_get_drvdata(dev); + unsigned int value; + int ret; + + ret = sscanf(buf, "0x%x", &value); + if (ret < 0) + return ret; + + dev_info(dev, "success to set 0x%x address.\n", value); + lcd->cur_addr = value; + + return size; +} + +static ssize_t write_reg_store(struct device *dev, struct + device_attribute * attr, const char *buf, size_t size) +{ + struct s6e8aa0 *lcd = dev_get_drvdata(dev); + char name[32]; + int ret; + + ret = sscanf(buf, "%s", name); + if (ret < 0) + return ret; + + ret = s6e8aa0_write_reg(lcd, name); + if (ret < 0) + return ret; + + dev_info(dev, "success to set %s address.\n", name); + + return size; +} + +static struct device_attribute device_attrs[] = { + __ATTR(acl_control, S_IRUGO|S_IWUSR|S_IWGRP, + acl_control_show, acl_control_store), + __ATTR(lcd_type, S_IRUGO, + lcd_type_show, NULL), + __ATTR(read_reg, S_IRUGO|S_IWUSR|S_IWGRP, + read_reg_show, read_reg_store), + __ATTR(write_reg, S_IWUSR|S_IWGRP, + NULL, write_reg_store), +}; + +static struct panel_model s6e8aa0_model[] = { + { + .ver = VER_142, /* MACH_SLP_PQ */ + .name = "SMD_AMS465GS0x", + }, { + .ver = VER_174, /* MACH_SLP_PQ Dali */ + .name = "SMD_AMS465GS0x", + }, { + .ver = VER_42, /* MACH_SLP_PQ_LTE */ + .name = "SMD_AMS465GS0x", + }, { + .ver = VER_32, /* MACH_SLP_PQ M0 B-Type */ + .name = "SMD_AMS480GYXX-0", + }, { + .ver = VER_210, /* MACH_SLP_PQ M0 A-Type */ + .name = "SMD_AMS465GS0x", + } +}; + +static int s6e8aa0_probe(struct mipi_dsim_lcd_device *dsim_dev) +{ + struct s6e8aa0 *lcd; + int ret; + int i; + + lcd = kzalloc(sizeof(struct s6e8aa0), GFP_KERNEL); + if (!lcd) { + dev_err(&dsim_dev->dev, "failed to allocate s6e8aa0 structure.\n"); + return -ENOMEM; + } + + lcd->dsim_dev = dsim_dev; + lcd->ddi_pd = (struct lcd_platform_data *)dsim_dev->platform_data; + lcd->dev = &dsim_dev->dev; + + mutex_init(&lcd->lock); + + lcd->reg_vdd3 = regulator_get(lcd->dev, "VDD3"); + if (IS_ERR(lcd->reg_vdd3)) { + ret = PTR_ERR(lcd->reg_vdd3); + dev_err(lcd->dev, "failed to get %s regulator (%d)\n", + "VDD3", ret); + lcd->reg_vdd3 = NULL; + } + + lcd->reg_vci = regulator_get(lcd->dev, "VCI"); + if (IS_ERR(lcd->reg_vci)) { + ret = PTR_ERR(lcd->reg_vci); + dev_err(lcd->dev, "failed to get %s regulator (%d)\n", + "VCI", ret); + lcd->reg_vci = NULL; + } + + lcd->ld = lcd_device_register("s6e8aa0", lcd->dev, lcd, + &s6e8aa0_lcd_ops); + if (IS_ERR(lcd->ld)) { + dev_err(lcd->dev, "failed to register lcd ops.\n"); + ret = PTR_ERR(lcd->ld); + goto err_regulator; + } + + lcd->bd = backlight_device_register("s6e8aa0-bl", lcd->dev, lcd, + &s6e8aa0_backlight_ops, NULL); + if (IS_ERR(lcd->bd)) { + dev_err(lcd->dev, "failed to register backlight ops.\n"); + ret = PTR_ERR(lcd->bd); + goto err_unregister_lcd; + } + + lcd->bd->props.max_brightness = MAX_BRIGHTNESS; + lcd->bd->props.brightness = MAX_BRIGHTNESS; + + lcd->acl_enable = 1; + lcd->cur_acl = 0; + lcd->model = s6e8aa0_model; + lcd->model_count = ARRAY_SIZE(s6e8aa0_model); + for (i = 0; i < ARRAY_SIZE(device_attrs); i++) { + ret = device_create_file(&lcd->ld->dev, + &device_attrs[i]); + if (ret < 0) { + dev_err(&lcd->ld->dev, "failed to add sysfs entries\n"); + break; + } + } + + dev_set_drvdata(&dsim_dev->dev, lcd); + + s6e8aa0_regulator_enable(lcd); + lcd->power = FB_BLANK_UNBLANK; + + lcd_global = lcd; + + dev_info(lcd->dev, "probed s6e8aa0 panel driver(%s).\n", + dev_name(&lcd->ld->dev)); + + return 0; + +err_unregister_lcd: + lcd_device_unregister(lcd->ld); + +err_regulator: + regulator_put(lcd->reg_vci); + regulator_put(lcd->reg_vdd3); + + kfree(lcd); + + return ret; +} + +static void s6e8aa0_remove(struct mipi_dsim_lcd_device *dsim_dev) +{ + struct s6e8aa0 *lcd = dev_get_drvdata(&dsim_dev->dev); + + backlight_device_unregister(lcd->bd); + + lcd_device_unregister(lcd->ld); + + regulator_put(lcd->reg_vci); + + regulator_put(lcd->reg_vdd3); + + kfree(lcd); +} + +#ifdef CONFIG_PM +static int s6e8aa0_suspend(struct mipi_dsim_lcd_device *dsim_dev) +{ + struct s6e8aa0 *lcd = dev_get_drvdata(&dsim_dev->dev); + + s6e8aa0_display_off(lcd); + s6e8aa0_sleep_in(lcd); + mdelay(lcd->ddi_pd->power_off_delay); + + return 0; +} + +static int s6e8aa0_resume(struct mipi_dsim_lcd_device *dsim_dev) +{ + struct s6e8aa0 *lcd = dev_get_drvdata(&dsim_dev->dev); + + s6e8aa0_sleep_out(lcd); + mdelay(lcd->ddi_pd->power_on_delay); + + return 0; +} +#else +#define s6e8aa0_suspend NULL +#define s6e8aa0_resume NULL +#endif + +static struct mipi_dsim_lcd_driver s6e8aa0_dsim_ddi_driver = { + .name = "s6e8aa0", + .id = -1, + + .power_on = s6e8aa0_power_on, + .check_mtp = s6e8aa0_check_mtp, + .set_sequence = s6e8aa0_set_sequence, + .probe = s6e8aa0_probe, + .remove = s6e8aa0_remove, + .suspend = s6e8aa0_suspend, + .resume = s6e8aa0_resume, +}; + +static int s6e8aa0_init(void) +{ + s5p_mipi_dsi_register_lcd_driver(&s6e8aa0_dsim_ddi_driver); + + return 0; +} + +static void s6e8aa0_exit(void) +{ + return; +} + +module_init(s6e8aa0_init); +module_exit(s6e8aa0_exit); + +MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>"); +MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); +MODULE_DESCRIPTION("MIPI-DSI based s6e8aa0 AMOLED LCD Panel Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/backlight/s6e8aa0.h b/drivers/video/backlight/s6e8aa0.h new file mode 100644 index 0000000..4e19d7d --- /dev/null +++ b/drivers/video/backlight/s6e8aa0.h @@ -0,0 +1,26 @@ +/* linux/drivers/video/samsung/s6e8aa0.h + * + * MIPI-DSI based s6e8aa0 AMOLED LCD Panel definitions. + * + * Copyright (c) 2011 Samsung Electronics + * + * Inki Dae, <inki.dae@samsung.com> + * Donghwa Lee <dh09.lee@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#ifndef _S6E8AA0_H +#define _S6E8AA0_H + +extern void s6e8aa0_init(void); +extern void s6e8aa0_set_link(void *pd, unsigned int dsim_base, + unsigned char (*cmd_write) (unsigned int dsim_base, unsigned int data0, + unsigned int data1, unsigned int data2), + unsigned char (*cmd_read) (unsigned int dsim_base, unsigned int data0, + unsigned int data1, unsigned int data2)); + +#endif + diff --git a/drivers/video/backlight/s6e8aa0_gamma.h b/drivers/video/backlight/s6e8aa0_gamma.h new file mode 100644 index 0000000..f76f7d2 --- /dev/null +++ b/drivers/video/backlight/s6e8aa0_gamma.h @@ -0,0 +1,220 @@ +/* linux/drivers/video/backlight/s6e8aa0_brightness.h + * + * Brightness level definition. + * + * Copyright (c) 2011 Samsung Electronics + * + * Inki Dae, <inki.dae@samsung.com> + * Donghwa Lee <dh09.lee@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#ifndef _S6E8AA0_BRIGHTNESS_H +#define _S6E8AA0_BRIGHTNESS_H + +#define MAX_GAMMA_LEVEL 25 +#define GAMMA_TABLE_COUNT 26 + +/* gamma value: 2.2 */ +static const unsigned char s6e8aa0_22_gamma_30[] = { + 0xFA, 0x01, 0x1F, 0x1F, 0x1F, 0xFF, 0x00, 0xFF, 0xDF, 0x1F, + 0xD7, 0xDC, 0xB7, 0xE1, 0xC0, 0xAF, 0xC4, 0xD2, 0xD0, 0xCF, + 0x00, 0x4D, 0x00, 0x40, 0x00, 0x5F, +}; + +static const unsigned char s6e8aa0_22_gamma_40[] = { + 0xFA, 0x01, 0x1F, 0x1F, 0x1F, 0xFF, 0x00, 0xFF, 0xD5, 0x35, + 0xCF, 0xDC, 0xC1, 0xE1, 0xBF, 0xB3, 0xC1, 0xD2, 0xD1, 0xCE, + 0x00, 0x53, 0x00, 0x46, 0x00, 0x67, + +}; + +static const unsigned char s6e8aa0_22_gamma_50[] = { + 0xFA, 0x01, 0x1F, 0x1F, 0x1F, 0xFF, 0x00, 0xFF, 0xD2, 0x64, + 0xCF, 0xDB, 0xC6, 0xE1, 0xBD, 0xB3, 0xBD, 0xD2, 0xD2, 0xCE, + 0x00, 0x59, 0x00, 0x4B, 0x00, 0x6E, +}; + +static const unsigned char s6e8aa0_22_gamma_60[] = { + 0xFA, 0x01, 0x1F, 0x1F, 0x1F, 0xFF, 0x00, 0xFF, 0xD0, 0x7C, + 0xCF, 0xDB, 0xC9, 0xE0, 0xBC, 0xB4, 0xBB, 0xCF, 0xD1, 0xCC, + 0x00, 0x5F, 0x00, 0x50, 0x00, 0x75, +}; + +static const unsigned char s6e8aa0_22_gamma_70[] = { + 0xFA, 0x01, 0x1F, 0x1F, 0x1F, 0xFF, 0x00, 0xFF, 0xD0, 0x8E, + 0xD1, 0xDB, 0xCC, 0xDF, 0xBB, 0xB6, 0xB9, 0xD0, 0xD1, 0xCD, + 0x00, 0x63, 0x00, 0x54, 0x00, 0x7A, +}; + +static const unsigned char s6e8aa0_22_gamma_80[] = { + 0xFA, 0x01, 0x1F, 0x1F, 0x1F, 0xFF, 0x00, 0xFF, 0xD1, 0x9E, + 0xD5, 0xDA, 0xCD, 0xDD, 0xBB, 0xB7, 0xB9, 0xCE, 0xCE, 0xC9, + 0x00, 0x68, 0x00, 0x59, 0x00, 0x81, +}; + +static const unsigned char s6e8aa0_22_gamma_90[] = { + 0xFA, 0x01, 0x1F, 0x1F, 0x1F, 0xFF, 0x00, 0xFF, 0xD0, 0xA5, + 0xD6, 0xDA, 0xCF, 0xDD, 0xBB, 0xB7, 0xB8, 0xCC, 0xCD, 0xC7, + 0x00, 0x6C, 0x00, 0x5C, 0x00, 0x86, +}; + +static const unsigned char s6e8aa0_22_gamma_100[] = { + 0xFA, 0x01, 0x1F, 0x1F, 0x1F, 0xFF, 0x00, 0xFF, 0xCF, 0xA7, + 0xD6, 0xDA, 0xD0, 0xDC, 0xB8, 0xB6, 0xB5, 0xCB, 0xCC, 0xC6, + 0x00, 0x71, 0x00, 0x60, 0x00, 0x8C, +}; + +static const unsigned char s6e8aa0_22_gamma_110[] = { + 0xFA, 0x01, 0x1F, 0x1F, 0x1F, 0xFF, 0x1F, 0xFE, 0xD0, 0xAE, + 0xD7, 0xD9, 0xD0, 0xDB, 0xB9, 0xB6, 0xB5, 0xCA, 0xCC, 0xC5, + 0x00, 0x74, 0x00, 0x63, 0x00, 0x90, +}; + +static const unsigned char s6e8aa0_22_gamma_120[] = { + 0xFA, 0x01, 0x1F, 0x1F, 0x1F, 0xFF, 0x1F, 0xF9, 0xCF, 0xB0, + 0xD6, 0xD9, 0xD1, 0xDB, 0xB9, 0xB6, 0xB4, 0xCA, 0xCB, 0xC5, + 0x00, 0x77, 0x00, 0x66, 0x00, 0x94, +}; + +static const unsigned char s6e8aa0_22_gamma_130[] = { + 0xFA, 0x01, 0x1F, 0x1F, 0x1F, 0xFF, 0x1F, 0xF7, 0xCF, 0xB3, + 0xD7, 0xD8, 0xD1, 0xD9, 0xB7, 0xB6, 0xB3, 0xC9, 0xCA, 0xC3, + 0x00, 0x7B, 0x00, 0x69, 0x00, 0x99, +}; + +static const unsigned char s6e8aa0_22_gamma_140[] = { + 0xFA, 0x01, 0x1F, 0x1F, 0x1F, 0xFD, 0x2F, 0xF7, 0xDF, 0xB5, + 0xD6, 0xD8, 0xD1, 0xD8, 0xB6, 0xB5, 0xB2, 0xCA, 0xCB, 0xC4, + 0x00, 0x7E, 0x00, 0x6C, 0x00, 0x9D, +}; + +static const unsigned char s6e8aa0_22_gamma_150[] = { + 0xFA, 0x01, 0x1F, 0x1F, 0x1F, 0xFB, 0x2F, 0xF5, 0xD0, 0xB7, + 0xD7, 0xD7, 0xD1, 0xD8, 0xB6, 0xB5, 0xB1, 0xC8, 0xCA, 0xC3, + 0x00, 0x81, 0x00, 0x6E, 0x00, 0xA0, +}; + +static const unsigned char s6e8aa0_22_gamma_160[] = { + 0xFA, 0x01, 0x1F, 0x1F, 0x1F, 0xFA, 0x2F, 0xF5, 0xCE, 0xB6, + 0xD5, 0xD7, 0xD2, 0xD8, 0xB6, 0xB4, 0xB0, 0xC7, 0xC9, 0xC1, + 0x00, 0x84, 0x00, 0x71, 0x00, 0xA5, +}; + +static const unsigned char s6e8aa0_22_gamma_170[] = { + 0xFA, 0x01, 0x1F, 0x1F, 0x1F, 0xF7, 0x2F, 0xF2, 0xCE, 0xB9, + 0xD5, 0xD8, 0xD2, 0xD8, 0xB4, 0xB4, 0xAF, 0xC7, 0xC9, 0xC1, + 0x00, 0x87, 0x00, 0x73, 0x00, 0xA8, +}; + +static const unsigned char s6e8aa0_22_gamma_180[] = { + 0xFA, 0x01, 0x1F, 0x1F, 0x1F, 0xF5, 0x2F, 0xF0, 0xDF, 0xBA, + 0xD5, 0xD7, 0xD2, 0xD7, 0xB4, 0xB4, 0xAF, 0xC5, 0xC7, 0xBF, + 0x00, 0x8A, 0x00, 0x76, 0x00, 0xAC, +}; + +static const unsigned char s6e8aa0_22_gamma_190[] = { + 0xFA, 0x01, 0x1F, 0x1F, 0x1F, 0xF2, 0x2F, 0xED, 0xCE, 0xBB, + 0xD4, 0xD6, 0xD2, 0xD6, 0xB5, 0xB4, 0xAF, 0xC5, 0xC7, 0xBF, + 0x00, 0x8c, 0x00, 0x78, 0x00, 0xAF, +}; + +static const unsigned char s6e8aa0_22_gamma_200[] = { + 0xFA, 0x01, 0x1F, 0x1F, 0x1F, 0xF2, 0x2F, 0xED, 0xCE, 0xBB, + 0xD4, 0xD7, 0xD3, 0xD6, 0xB3, 0xB3, 0xAE, 0xC6, 0xC7, 0xBF, + 0x00, 0x8E, 0x00, 0x7A, 0x00, 0xB2, +}; + +static const unsigned char s6e8aa0_22_gamma_210[] = { + 0xFA, 0x01, 0x1F, 0x1F, 0x1F, 0xEF, 0x2F, 0xEB, 0xCD, 0xBB, + 0xD2, 0xD7, 0xD3, 0xD6, 0xB3, 0xB4, 0xAE, 0xC5, 0xC6, 0xBE, + 0x00, 0x91, 0x00, 0x7D, 0x00, 0xB6, +}; + +static const unsigned char s6e8aa0_22_gamma_220[] = { + 0xFA, 0x01, 0x1F, 0x1F, 0x1F, 0xEE, 0x2F, 0xEA, 0xCE, 0xBD, + 0xD4, 0xD6, 0xD2, 0xD5, 0xB2, 0xB3, 0xAD, 0xC3, 0xC4, 0xBB, + 0x00, 0x94, 0x00, 0x7F, 0x00, 0xBA, +}; + +static const unsigned char s6e8aa0_22_gamma_230[] = { + 0xFA, 0x01, 0x1F, 0x1F, 0x1F, 0xEC, 0x2F, 0xE8, 0xCE, 0xBE, + 0xD3, 0xD6, 0xD3, 0xD5, 0xB2, 0xB2, 0xAC, 0xC3, 0xC5, 0xBC, + 0x00, 0x96, 0x00, 0x81, 0x00, 0xBD, +}; + +static const unsigned char s6e8aa0_22_gamma_240[] = { + 0xFA, 0x01, 0x1F, 0x1F, 0x1F, 0xEB, 0x2F, 0xE7, 0xCE, 0xBF, + 0xD3, 0xD6, 0xD2, 0xD5, 0xB1, 0xB2, 0xAB, 0xC2, 0xC4, 0xBB, + 0x00, 0x99, 0x00, 0x83, 0x00, 0xC0, +}; + +static const unsigned char s6e8aa0_22_gamma_250[] = { + 0xFA, 0x01, 0x1F, 0x1F, 0x1F, 0xEF, 0x5F, 0xE9, 0xCA, 0xBF, + 0xD3, 0xD5, 0xD2, 0xD4, 0xB2, 0xB2, 0xAB, 0xC1, 0xC4, 0xBA, + 0x00, 0x9B, 0x00, 0x85, 0x00, 0xC3, +}; + +static const unsigned char s6e8aa0_22_gamma_260[] = { + 0xFA, 0x01, 0x1F, 0x1F, 0x1F, 0xEA, 0x5F, 0xE8, 0xCE, 0xBF, + 0xD2, 0xD5, 0xD2, 0xD4, 0xB1, 0xB2, 0xAB, 0xC1, 0xC2, 0xB9, + 0x00, 0x9D, 0x00, 0x87, 0x00, 0xC6, +}; + +static const unsigned char s6e8aa0_22_gamma_270[] = { + 0xFA, 0x01, 0x1F, 0x1F, 0x1F, 0xE9, 0x5F, 0xE7, 0xCD, 0xBF, + 0xD2, 0xD6, 0xD2, 0xD4, 0xB1, 0xB2, 0xAB, 0xBE, 0xC0, 0xB7, + 0x00, 0xA1, 0x00, 0x8A, 0x00, 0xCA, +}; + +static const unsigned char s6e8aa0_22_gamma_280[] = { + 0xFA, 0x01, 0x1F, 0x1F, 0x1F, 0xE8, 0x61, 0xE6, 0xCD, 0xBF, + 0xD1, 0xD6, 0xD3, 0xD4, 0xAF, 0xB0, 0xA9, 0xBE, 0xC1, 0xB7, + 0x00, 0xA3, 0x00, 0x8B, 0x00, 0xCE, +}; + +static const unsigned char s6e8aa0_22_gamma_290[] = { + 0xFA, 0x01, 0x1F, 0x1F, 0x1F, 0xE8, 0x62, 0xE5, 0xCC, 0xC0, + 0xD0, 0xD6, 0xD2, 0xD4, 0xAF, 0xB1, 0xA9, 0xBD, 0xC0, 0xB6, + 0x00, 0xA5, 0x00, 0x8D, 0x00, 0xD0, + +}; + +static const unsigned char s6e8aa0_22_gamma_300[] = { + 0xFA, 0x01, 0x1F, 0x1F, 0x1F, 0xE7, 0x7F, 0xE3, 0xCC, 0xC1, + 0xD0, 0xD5, 0xD3, 0xD3, 0xAE, 0xAF, 0xA8, 0xBE, 0xC0, 0xB7, + 0x00, 0xA8, 0x00, 0x90, 0x00, 0xD3, +}; + +static const unsigned char *s6e8aa0_22_gamma_table[MAX_GAMMA_LEVEL] = { + s6e8aa0_22_gamma_30, + s6e8aa0_22_gamma_50, + s6e8aa0_22_gamma_60, + s6e8aa0_22_gamma_70, + s6e8aa0_22_gamma_80, + s6e8aa0_22_gamma_90, + s6e8aa0_22_gamma_100, + s6e8aa0_22_gamma_110, + s6e8aa0_22_gamma_120, + s6e8aa0_22_gamma_130, + s6e8aa0_22_gamma_140, + s6e8aa0_22_gamma_150, + s6e8aa0_22_gamma_160, + s6e8aa0_22_gamma_170, + s6e8aa0_22_gamma_180, + s6e8aa0_22_gamma_190, + s6e8aa0_22_gamma_200, + s6e8aa0_22_gamma_210, + s6e8aa0_22_gamma_220, + s6e8aa0_22_gamma_230, + s6e8aa0_22_gamma_240, + s6e8aa0_22_gamma_270, + s6e8aa0_22_gamma_280, + s6e8aa0_22_gamma_290, + s6e8aa0_22_gamma_300, +}; + +#endif diff --git a/drivers/video/backlight/s6e8aa0_volt_tbl.h b/drivers/video/backlight/s6e8aa0_volt_tbl.h new file mode 100644 index 0000000..3f187b9 --- /dev/null +++ b/drivers/video/backlight/s6e8aa0_volt_tbl.h @@ -0,0 +1,967 @@ +#ifndef __REF_VOLT_TABLE_H__ +#define __REF_VOLT_TABLE_H__ + +u32 volt_table_v1[256] = { +4671488, 4663296, 4655104, 4647936, +4639744, 4631552, 4624384, 4616192, +4608000, 4600832, 4592640, 4584448, +4577280, 4569088, 4560896, 4553728, +4545536, 4537344, 4530176, 4521984, +4513792, 4506624, 4498432, 4490240, +4483072, 4474880, 4466688, 4459520, +4451328, 4443136, 4435968, 4427776, +4419584, 4412416, 4404224, 4396032, +4388864, 4380672, 4372480, 4365312, +4357120, 4348928, 4341760, 4333568, +4325376, 4318208, 4310016, 4301824, +4294656, 4286464, 4278272, 4271104, +4262912, 4254720, 4247552, 4239360, +4231168, 4224000, 4215808, 4207616, +4200448, 4192256, 4184064, 4176896, +4168704, 4160512, 4153344, 4145152, +4136960, 4129792, 4121600, 4113408, +4106240, 4098048, 4089856, 4082688, +4074496, 4066304, 4059136, 4050944, +4042752, 4035584, 4027392, 4019200, +4012032, 4003840, 3995648, 3988480, +3980288, 3972096, 3964928, 3956736, +3948544, 3941376, 3933184, 3924992, +3917824, 3909632, 3901440, 3894272, +3886080, 3877888, 3870720, 3862528, +3854336, 3847168, 3838976, 3830784, +3823616, 3815424, 3807232, 3800064, +3791872, 3783680, 3776512, 3768320, +3760128, 3752960, 3744768, 3736576, +3729408, 3721216, 3713024, 3705856, +3697664, 3689472, 3682304, 3674112, +3665920, 3658752, 3650560, 3642368, +3635200, 3627008, 3618816, 3611648, +3603456, 3595264, 3588096, 3579904, +3571712, 3564544, +}; + + +u32 volt_table_v255[432] = { +3924992, 3917824, 3909632, 3901440, +3894272, 3886080, 3877888, 3870720, +3862528, 3854336, 3847168, 3838976, +3830784, 3823616, 3815424, 3807232, +3800064, 3791872, 3783680, 3776512, +3768320, 3760128, 3752960, 3744768, +3736576, 3729408, 3721216, 3713024, +3705856, 3697664, 3689472, 3682304, +3674112, 3665920, 3658752, 3650560, +3642368, 3635200, 3627008, 3618816, +3611648, 3603456, 3595264, 3588096, +3579904, 3571712, 3564544, 3556352, +3548160, 3540992, 3532800, 3524608, +3517440, 3509248, 3501056, 3493888, +3485696, 3477504, 3470336, 3462144, +3453952, 3446784, 3438592, 3430400, +3423232, 3415040, 3406848, 3399680, +3391488, 3383296, 3376128, 3367936, +3359744, 3352576, 3344384, 3336192, +3329024, 3320832, 3312640, 3305472, +3297280, 3289088, 3281920, 3273728, +3265536, 3258368, 3250176, 3241984, +3234816, 3226624, 3218432, 3211264, +3203072, 3194880, 3187712, 3179520, +3171328, 3164160, 3155968, 3147776, +3140608, 3132416, 3124224, 3117056, +3108864, 3100672, 3093504, 3085312, +3077120, 3069952, 3061760, 3053568, +3046400, 3038208, 3030016, 3022848, +3014656, 3006464, 2999296, 2991104, +2982912, 2975744, 2967552, 2959360, +2952192, 2944000, 2935808, 2928640, +2920448, 2912256, 2905088, 2896896, +2888704, 2881536, 2873344, 2865152, +2857984, 2849792, 2841600, 2834432, +2826240, 2818048, 2810880, 2802688, +2794496, 2787328, 2779136, 2770944, +2763776, 2755584, 2747392, 2740224, +2732032, 2723840, 2716672, 2708480, +2700288, 2693120, 2684928, 2676736, +2669568, 2661376, 2653184, 2646016, +2637824, 2629632, 2622464, 2614272, +2606080, 2598912, 2590720, 2582528, +2575360, 2567168, 2558976, 2551808, +2543616, 2535424, 2528256, 2520064, +2511872, 2504704, 2496512, 2488320, +2481152, 2472960, 2464768, 2457600, +2449408, 2441216, 2434048, 2425856, +2417664, 2410496, 2402304, 2394112, +2386944, 2378752, 2370560, 2363392, +2355200, 2347008, 2339840, 2331648, +2323456, 2316288, 2308096, 2299904, +2292736, 2284544, 2276352, 2269184, +2260992, 2252800, 2245632, 2237440, +2229248, 2222080, 2213888, 2205696, +2198528, 2190336, 2182144, 2174976, +2166784, 2158592, 2151424, 2143232, +2135040, 2127872, 2119680, 2111488, +2104320, 2096128, 2087936, 2080768, +2072576, 2064384, 2057216, 2049024, +2040832, 2033664, 2025472, 2017280, +2010112, 2001920, 1993728, 1986560, +1978368, 1970176, 1963008, 1954816, +1946624, 1939456, 1931264, 1923072, +1915904, 1907712, 1899520, 1892352, +1884160, 1875968, 1868800, 1860608, +1852416, 1845248, 1837056, 1828864, +1821696, 1813504, 1805312, 1798144, +1789952, 1781760, 1774592, 1766400, +1758208, 1751040, 1742848, 1734656, +1727488, 1719296, 1711104, 1703936, +1695744, 1687552, 1680384, 1672192, +1664000, 1656832, 1648640, 1640448, +1633280, 1625088, 1616896, 1609728, +1601536, 1593344, 1586176, 1577984, +1569792, 1562624, 1554432, 1546240, +1539072, 1530880, 1522688, 1515520, +1507328, 1499136, 1491968, 1483776, +1475584, 1468416, 1460224, 1452032, +1444864, 1436672, 1428480, 1421312, +1413120, 1404928, 1397760, 1389568, +1381376, 1374208, 1366016, 1357824, +1350656, 1342464, 1334272, 1327104, +1318912, 1310720, 1303552, 1295360, +1287168, 1280000, 1271808, 1263616, +1256448, 1248256, 1240064, 1232896, +1224704, 1216512, 1209344, 1201152, +1192960, 1185792, 1177600, 1169408, +1162240, 1154048, 1145856, 1138688, +1130496, 1122304, 1115136, 1106944, +1098752, 1091584, 1083392, 1075200, +1068032, 1059840, 1051648, 1044480, +1036288, 1028096, 1020928, 1012736, +1004544, 997376, 989184, 980992, +973824, 965632, 957440, 950272, +942080, 933888, 926720, 918528, +910336, 903168, 894976, 886784, +879616, 871424, 863232, 856064, +847872, 839680, 832512, 824320, +816128, 808960, 800768, 792576, +785408, 777216, 769024, 761856, +753664, 745472, 738304, 730112, +721920, 714752, 706560, 698368, +691200, 683008, 674816, 667648, +659456, 651264, 644096, 635904, +627712, 620544, 612352, 604160, +596992, 588800, 580608, 573440, +565248, 557056, 549888, 541696, + +}; + +u32 volt_table_cv_20_dv_320[256] = { +64, +67, +70, +74, +77, +80, +83, +86, +90, +93, +96, +99, +102, +106, +109, +112, +115, +118, +122, +125, +128, +131, +134, +138, +141, +144, +147, +150, +154, +157, +160, +163, +166, +170, +173, +176, +179, +182, +186, +189, +192, +195, +198, +202, +205, +208, +211, +214, +218, +221, +224, +227, +230, +234, +237, +240, +243, +246, +250, +253, +256, +259, +262, +266, +269, +272, +275, +278, +282, +285, +288, +291, +294, +298, +301, +304, +307, +310, +314, +317, +320, +323, +326, +330, +333, +336, +339, +342, +346, +349, +352, +355, +358, +362, +365, +368, +371, +374, +378, +381, +384, +387, +390, +394, +397, +400, +403, +406, +410, +413, +416, +419, +422, +426, +429, +432, +435, +438, +442, +445, +448, +451, +454, +458, +461, +464, +467, +470, +474, +477, +480, +483, +486, +490, +493, +496, +499, +502, +506, +509, +512, +515, +518, +522, +525, +528, +531, +534, +538, +541, +544, +547, +550, +554, +557, +560, +563, +566, +570, +573, +576, +579, +582, +586, +589, +592, +595, +598, +602, +605, +608, +611, +614, +618, +621, +624, +627, +630, +634, +637, +640, +643, +646, +650, +653, +656, +659, +662, +666, +669, +672, +675, +678, +682, +685, +688, +691, +694, +698, +701, +704, +707, +710, +714, +717, +720, +723, +726, +730, +733, +736, +739, +742, +746, +749, +752, +755, +758, +762, +765, +768, +771, +774, +778, +781, +784, +787, +790, +794, +797, +800, +803, +806, +810, +813, +816, +819, +822, +826, +829, +832, +835, +838, +842, +845, +848, +851, +854, +858, +861, +864, +867, +870, +874, +877, +880 +}; + +u32 volt_table_cv_65_dv_320[256] = { +208, +211, +214, +218, +221, +224, +227, +230, +234, +237, +240, +243, +246, +250, +253, +256, +259, +262, +266, +269, +272, +275, +278, +282, +285, +288, +291, +294, +298, +301, +304, +307, +310, +314, +317, +320, +323, +326, +330, +333, +336, +339, +342, +346, +349, +352, +355, +358, +362, +365, +368, +371, +374, +378, +381, +384, +387, +390, +394, +397, +400, +403, +406, +410, +413, +416, +419, +422, +426, +429, +432, +435, +438, +442, +445, +448, +451, +454, +458, +461, +464, +467, +470, +474, +477, +480, +483, +486, +490, +493, +496, +499, +502, +506, +509, +512, +515, +518, +522, +525, +528, +531, +534, +538, +541, +544, +547, +550, +554, +557, +560, +563, +566, +570, +573, +576, +579, +582, +586, +589, +592, +595, +598, +602, +605, +608, +611, +614, +618, +621, +624, +627, +630, +634, +637, +640, +643, +646, +650, +653, +656, +659, +662, +666, +669, +672, +675, +678, +682, +685, +688, +691, +694, +698, +701, +704, +707, +710, +714, +717, +720, +723, +726, +730, +733, +736, +739, +742, +746, +749, +752, +755, +758, +762, +765, +768, +771, +774, +778, +781, +784, +787, +790, +794, +797, +800, +803, +806, +810, +813, +816, +819, +822, +826, +829, +832, +835, +838, +842, +845, +848, +851, +854, +858, +861, +864, +867, +870, +874, +877, +880, +883, +886, +890, +893, +896, +899, +902, +906, +909, +912, +915, +918, +922, +925, +928, +931, +934, +938, +941, +944, +947, +950, +954, +957, +960, +963, +966, +970, +973, +976, +979, +982, +986, +989, +992, +995, +998, +1002, +1005, +1008, +1011, +1014, +1018, +1021, +1024, +}; + +const u32 gamma_300_gra_table[256] = { +0, 2, 7, 17, +32, 53, 78, 110, +148, 191, 241, 298, +361, 430, 506, 589, +679, 776, 880, 991, +1109, 1235, 1368, 1508, +1657, 1812, 1975, 2147, +2325, 2512, 2706, 2909, +3119, 3338, 3564, 3799, +4042, 4293, 4553, 4820, +5096, 5381, 5674, 5975, +6285, 6604, 6931, 7267, +7611, 7965, 8327, 8697, +9077, 9465, 9863, 10269, +10684, 11109, 11542, 11984, +12436, 12896, 13366, 13845, +14333, 14830, 15337, 15852, +16378, 16912, 17456, 18009, +18572, 19144, 19726, 20317, +20918, 21528, 22148, 22778, +23417, 24066, 24724, 25392, +26070, 26758, 27456, 28163, +28880, 29607, 30344, 31090, +31847, 32613, 33390, 34176, +34973, 35779, 36596, 37422, +38259, 39106, 39963, 40830, +41707, 42594, 43492, 44399, +45317, 46246, 47184, 48133, +49092, 50062, 51042, 52032, +53032, 54043, 55065, 56097, +57139, 58192, 59255, 60329, +61413, 62508, 63613, 64729, +65856, 66993, 68141, 69299, +70469, 71648, 72839, 74040, +75252, 76475, 77708, 78952, +80207, 81473, 82750, 84037, +85336, 86645, 87965, 89296, +90638, 91990, 93354, 94729, +96114, 97511, 98919, 100337, +101767, 103208, 104659, 106122, +107596, 109081, 110577, 112085, +113603, 115132, 116673, 118225, +119788, 121362, 122948, 124544, +126152, 127772, 129402, 131044, +132697, 134361, 136037, 137724, +139422, 141132, 142853, 144586, +146330, 148085, 149852, 151630, +153419, 155220, 157033, 158857, +160692, 162540, 164398, 166268, +168150, 170043, 171948, 173864, +175792, 177731, 179683, 181645, +183620, 185606, 187603, 189613, +191634, 193667, 195711, 197767, +199835, 201915, 204006, 206109, +208224, 210351, 212489, 214640, +216802, 218976, 221161, 223359, +225569, 227790, 230023, 232268, +234525, 236794, 239075, 241368, +243672, 245989, 248318, 250658, +253011, 255375, 257752, 260141, +262541, 264954, 267379, 269815, +272264, 274725, 277198, 279683, +282180, 284689, 287211, 289744, +292290, 294848, 297418, 300000, +}; + + +const u32 gamma_22_table[256] = { +0, 0, 0, 0, +0, 0, 0, 0, +0, 1, 1, 1, +1, 1, 2, 2, +2, 3, 3, 3, +4, 4, 5, 5, +6, 6, 7, 7, +8, 8, 9, 10, +10, 11, 12, 13, +13, 14, 15, 16, +17, 18, 19, 20, +21, 22, 23, 24, +25, 27, 28, 29, +30, 32, 33, 34, +36, 37, 38, 40, +41, 43, 45, 46, +48, 49, 51, 53, +55, 56, 58, 60, +62, 64, 66, 68, +70, 72, 74, 76, +78, 80, 82, 85, +87, 89, 92, 94, +96, 99, 101, 104, +106, 109, 111, 114, +117, 119, 122, 125, +128, 130, 133, 136, +139, 142, 145, 148, +151, 154, 157, 160, +164, 167, 170, 173, +177, 180, 184, 187, +190, 194, 198, 201, +205, 208, 212, 216, +220, 223, 227, 231, +235, 239, 243, 247, +251, 255, 259, 263, +267, 272, 276, 280, +284, 289, 293, 298, +302, 307, 311, 316, +320, 325, 330, 334, +339, 344, 349, 354, +359, 364, 369, 374, +379, 384, 389, 394, +399, 405, 410, 415, +421, 426, 431, 437, +442, 448, 453, 459, +465, 470, 476, 482, +488, 494, 500, 505, +511, 517, 523, 530, +536, 542, 548, 554, +560, 567, 573, 580, +586, 592, 599, 605, +612, 619, 625, 632, +639, 646, 652, 659, +666, 673, 680, 687, +694, 701, 708, 715, +723, 730, 737, 745, +752, 759, 767, 774, +782, 789, 797, 805, +812, 820, 828, 836, +843, 851, 859, 867, +875, 883, 891, 899, +908, 916, 924, 932, +941, 949, 957, 966, +974, 983, 991, 1000, +}; + + +const struct str_flookup_table flookup_table[302] = { +{ 0, 0}, { 1, 20}, +{ 20, 7}, { 27, 5}, +{ 32, 4}, { 36, 4}, +{ 40, 4}, { 44, 3}, +{ 47, 3}, { 50, 2}, +{ 52, 3}, { 55, 2}, +{ 57, 3}, { 60, 2}, +{ 62, 2}, { 64, 2}, +{ 66, 2}, { 68, 2}, +{ 70, 1}, { 71, 2}, +{ 73, 2}, { 75, 2}, +{ 77, 1}, { 78, 2}, +{ 80, 1}, { 81, 2}, +{ 83, 1}, { 84, 2}, +{ 86, 1}, { 87, 2}, +{ 89, 1}, { 90, 1}, +{ 91, 2}, { 93, 1}, +{ 94, 1}, { 95, 2}, +{ 97, 1}, { 98, 1}, +{ 99, 1}, {100, 1}, +{101, 2}, {103, 1}, +{104, 1}, {105, 1}, +{106, 1}, {107, 1}, +{108, 1}, {109, 1}, +{110, 1}, {111, 1}, +{112, 1}, {113, 1}, +{114, 1}, {115, 1}, +{116, 1}, {117, 1}, +{118, 1}, {119, 1}, +{120, 1}, {121, 1}, +{122, 1}, {123, 1}, +{124, 1}, {125, 1}, +{126, 1}, {127, 1}, +{128, 1}, {129, 1}, +{ 0, 0}, {130, 1}, +{131, 1}, {132, 1}, +{133, 1}, {134, 1}, +{ 0, 0}, {135, 1}, +{136, 1}, {137, 1}, +{138, 1}, {139, 1}, +{ 0, 0}, {140, 1}, +{141, 1}, {142, 1}, +{ 0, 0}, {143, 1}, +{144, 1}, {145, 1}, +{146, 1}, { 0, 0}, +{147, 1}, {148, 1}, +{149, 1}, { 0, 0}, +{150, 1}, {151, 1}, +{ 0, 0}, {152, 1}, +{153, 1}, {154, 1}, +{ 0, 0}, {155, 1}, +{156, 1}, { 0, 0}, +{157, 1}, {158, 1}, +{ 0, 0}, {159, 1}, +{160, 1}, { 0, 0}, +{161, 1}, {162, 1}, +{ 0, 0}, {163, 1}, +{164, 1}, { 0, 0}, +{165, 1}, {166, 1}, +{ 0, 0}, {167, 1}, +{168, 1}, { 0, 0}, +{169, 1}, {170, 1}, +{ 0, 0}, {171, 1}, +{ 0, 0}, {172, 1}, +{173, 1}, { 0, 0}, +{174, 1}, { 0, 0}, +{175, 1}, {176, 1}, +{ 0, 0}, {177, 1}, +{ 0, 0}, {178, 1}, +{179, 1}, { 0, 0}, +{180, 1}, { 0, 0}, +{181, 1}, {182, 1}, +{ 0, 0}, {183, 1}, +{ 0, 0}, {184, 1}, +{ 0, 0}, {185, 1}, +{186, 1}, { 0, 0}, +{187, 1}, { 0, 0}, +{188, 1}, { 0, 0}, +{189, 1}, { 0, 0}, +{190, 1}, {191, 1}, +{ 0, 0}, {192, 1}, +{ 0, 0}, {193, 1}, +{ 0, 0}, {194, 1}, +{ 0, 0}, {195, 1}, +{ 0, 0}, {196, 1}, +{ 0, 0}, {197, 1}, +{198, 1}, { 0, 0}, +{199, 1}, { 0, 0}, +{200, 1}, { 0, 0}, +{201, 1}, { 0, 0}, +{202, 1}, { 0, 0}, +{203, 1}, { 0, 0}, +{204, 1}, { 0, 0}, +{205, 1}, { 0, 0}, +{206, 1}, { 0, 0}, +{207, 1}, { 0, 0}, +{208, 1}, { 0, 0}, +{209, 1}, { 0, 0}, +{210, 1}, { 0, 0}, +{211, 1}, { 0, 0}, +{212, 1}, { 0, 0}, +{213, 1}, { 0, 0}, +{ 0, 0}, {214, 1}, +{ 0, 0}, {215, 1}, +{ 0, 0}, {216, 1}, +{ 0, 0}, {217, 1}, +{ 0, 0}, {218, 1}, +{ 0, 0}, {219, 1}, +{ 0, 0}, {220, 1}, +{ 0, 0}, {221, 1}, +{ 0, 0}, { 0, 0}, +{222, 1}, { 0, 0}, +{223, 1}, { 0, 0}, +{224, 1}, { 0, 0}, +{225, 1}, { 0, 0}, +{ 0, 0}, {226, 1}, +{ 0, 0}, {227, 1}, +{ 0, 0}, {228, 1}, +{ 0, 0}, {229, 1}, +{ 0, 0}, { 0, 0}, +{230, 1}, { 0, 0}, +{231, 1}, { 0, 0}, +{232, 1}, { 0, 0}, +{233, 1}, { 0, 0}, +{ 0, 0}, {234, 1}, +{ 0, 0}, {235, 1}, +{ 0, 0}, { 0, 0}, +{236, 1}, { 0, 0}, +{237, 1}, { 0, 0}, +{238, 1}, { 0, 0}, +{ 0, 0}, {239, 1}, +{ 0, 0}, {240, 1}, +{ 0, 0}, {241, 1}, +{ 0, 0}, { 0, 0}, +{242, 1}, { 0, 0}, +{243, 1}, { 0, 0}, +{ 0, 0}, {244, 1}, +{ 0, 0}, {245, 1}, +{ 0, 0}, { 0, 0}, +{246, 1}, { 0, 0}, +{247, 1}, { 0, 0}, +{ 0, 0}, {248, 1}, +{ 0, 0}, {249, 1}, +{ 0, 0}, { 0, 0}, +{250, 1}, { 0, 0}, +{251, 1}, { 0, 0}, +{ 0, 0}, {252, 1}, +{ 0, 0}, {253, 1}, +{ 0, 0}, { 0, 0}, +{254, 1}, { 0, 0}, +{ 0, 0}, {255, 1}, + +}; + + + +#endif diff --git a/drivers/video/backlight/s6e8ab0_mipi_lcd.c b/drivers/video/backlight/s6e8ab0_mipi_lcd.c new file mode 100644 index 0000000..ef5c41f --- /dev/null +++ b/drivers/video/backlight/s6e8ab0_mipi_lcd.c @@ -0,0 +1,77 @@ +/* linux/drivers/video/backlight/s6e8ab0_mipi_lcd.c + * + * + * Copyright (c) 2011 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * +*/ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/mutex.h> +#include <linux/wait.h> +#include <linux/ctype.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/gpio.h> +#include <linux/workqueue.h> +#include <linux/backlight.h> +#include <linux/lcd.h> + +#include <video/mipi_display.h> + +#include <plat/gpio-cfg.h> +#include <plat/regs-dsim.h> + +#include <plat/dsim.h> +#include <plat/mipi_dsi.h> + +void init_lcd(struct mipi_dsim_device *dsim) +{ + s5p_mipi_dsi_wr_data(dsim, MIPI_DSI_DCS_SHORT_WRITE, + 0, 0); + msleep(60); + /* Exit sleep */ + s5p_mipi_dsi_wr_data(dsim, MIPI_DSI_DCS_SHORT_WRITE, + 0x11, 0); + msleep(600); + s5p_mipi_dsi_wr_data(dsim, MIPI_DSI_TURN_ON_PERIPHERAL, + 0, 0); +} + +void s6e8ab0_mipi_lcd_off(struct mipi_dsim_device *dsim) +{ + usleep_range(1000, 1200); +} + +static int s6e8ab0_mipi_lcd_suspend(struct mipi_dsim_device *dsim) +{ + s6e8ab0_mipi_lcd_off(dsim); + return 0; +} + +static int s6e8ab0_mipi_lcd_displayon(struct mipi_dsim_device *dsim) +{ + init_lcd(dsim); + + return 0; +} + +static int s6e8ab0_mipi_lcd_resume(struct mipi_dsim_device *dsim) +{ + init_lcd(dsim); + return 0; +} + +struct mipi_dsim_lcd_driver s6e8ab0_mipi_lcd_driver = { + .suspend = s6e8ab0_mipi_lcd_suspend, + .displayon = s6e8ab0_mipi_lcd_displayon, + .resume = s6e8ab0_mipi_lcd_resume, +}; diff --git a/drivers/video/backlight/smart_dimming.c b/drivers/video/backlight/smart_dimming.c new file mode 100644 index 0000000..42438ad --- /dev/null +++ b/drivers/video/backlight/smart_dimming.c @@ -0,0 +1,771 @@ +/* linux/drivers/video/samsung/smartdimming.c + * + * Copyright (c) 2010 Samsung Electronics Co., Ltd. + * http://www.samsung.com + + * Samsung Smart Dimming for OCTA + * + * Minwoo Kim, <minwoo7945.kim@samsung.com> + * +*/ + + +#include "smart_dimming.h" +#include "s6e8aa0_volt_tbl.h" + +#define MTP_REVERSE 1 +#define VALUE_DIM_1000 1000 + + +const u8 v1_offset_table[14] = { + 47, 42, 37, 32, + 27, 23, 19, 15, + 12, 9, 6, 4, + 2, 0 +}; + + +const u8 v15_offset_table[20] = { + 66, 62, 58, 54, + 50, 46, 43, 38, + 34, 30, 27, 24, + 21, 18, 15, 12, + 9, 6, 3, 0, +}; + + +const u8 range_table_count[IV_TABLE_MAX] = { + 1, 14, 20, 24, 28, 84, 84, 1 +}; + + +const u32 table_radio[IV_TABLE_MAX] = { + 0, 630, 468, 1365, 1170, 390, 390, 0 +}; + + +const u32 dv_value[IV_MAX] = { + 0, 15, 35, 59, 87, 171, 255 +}; + + +const char color_name[3] = {'R', 'G', 'B'}; + + +const u8 *offset_table[IV_TABLE_MAX] = { + NULL, + v1_offset_table, + v15_offset_table, + NULL, + NULL, + NULL, + NULL, + NULL +}; + +const unsigned char gamma_300cd_23[] = { + 0x0f, 0x0f, 0x0f, 0xee, 0xb4, 0xee, + 0xcb, 0xc2, 0xc4, 0xda, 0xd7, 0xd5, + 0xae, 0xaf, 0xa7, 0xc0, 0xc1, 0xbb, + 0x00, 0x9f, 0x00, 0x95, 0x00, 0xd4, +}; + +const unsigned char gamma_300cd_33[] = { + 0x0f, 0x00, 0x0f, 0xda, 0xc0, 0xe4, + 0xc8, 0xc8, 0xc6, 0xd3, 0xd6, 0xd0, + 0xab, 0xb2, 0xa6, 0xbf, 0xc2, 0xb9, + 0x00, 0x93, 0x00, 0x86, 0x00, 0xd1, +}; + +const unsigned char gamma_300cd_43[] = { + 0x1f, 0x1f, 0x1f, 0xe3, 0x69, 0xe0, + 0xcc, 0xc1, 0xce, 0xd4, 0xd2, 0xd2, + 0xae, 0xae, 0xa6, 0xbf, 0xc0, 0xb6, + 0x00, 0xa3, 0x00, 0x90, 0x00, 0xd7, +}; + +const unsigned char gamma_300cd_53[] = { + 0x1f, 0x1f, 0x1f, 0xe7, 0x7f, 0xe3, + 0xcc, 0xc1, 0xd0, 0xd5, 0xd3, 0xd3, + 0xae, 0xaf, 0xa8, 0xbe, 0xc0, 0xb7, + 0x00, 0xa8, 0x00, 0x90, 0x00, 0xd3, +}; + +const unsigned char gamma_300cd_63[] = { + 0x60, 0x10, 0x60, 0xb5, 0xd3, 0xbd, + 0xb1, 0xd2, 0xb0, 0xc0, 0xdc, 0xc0, + 0x94, 0xba, 0x91, 0xac, 0xc5, 0xa9, + 0x00, 0xc2, 0x00, 0xb7, 0x00, 0xed, +}; + +const unsigned char gamma_300cd_73[] = { + 0x1f, 0x1f, 0x1f, 0xed, 0xe6, 0xe7, + 0xd1, 0xd3, 0xd4, 0xda, 0xd8, 0xd7, + 0xb1, 0xaf, 0xab, 0xbd, 0xbb, 0xb8, + 0x00, 0xd6, 0x00, 0xda, 0x00, 0xfa, +}; + +const unsigned char gamma_300cd_83[] = { + 0x69, 0x5A, 0x6C, 0xA1, 0xB7, 0x9D, + 0xAB, 0xB6, 0xAF, 0xB8, 0xC1, 0xB9, + 0x8E, 0x96, 0x8B, 0xA6, 0xAC, 0xA4, + 0x00, 0xD2, 0x00, 0xD3, 0x00, 0xF1 +}; + +/* SM2 , DALI PANEL - Midas Universal board */ +const unsigned char gamma_300cd_4e[] = { + 0x58, 0x1F, 0x63, 0xAC, 0xB4, 0x99, + 0xAD, 0xBA, 0xA3, 0xC0, 0xCB, 0xBB, + 0x93, 0x9F, 0x8B, 0xAD, 0xB4, 0xA7, + 0x00, 0xBE, 0x00, 0xAB, 0x00, 0xE7, +}; + +/* SM2 C1/M0 4.8" */ +const unsigned char gamma_300cd_20[] = { + 0x43, 0x14, 0x45, 0xAD, 0xBE, 0xA9, + 0xB0, 0xC3, 0xAF, 0xC1, 0xCD, 0xC0, + 0x95, 0xA2, 0x91, 0xAC, 0xB5, 0xAA, + 0x00, 0xB0, 0x00, 0xA0, 0x00, 0xCC, +}; + +/* M3, DALI PANEL - Midas Universal board */ +const unsigned char gamma_300cd_2a[] = { + 0x4A, 0x01, 0x4D, 0xBC, 0xF1, 0xC6, + 0xB1, 0xD6, 0xB3, 0xC2, 0xDD, 0xC2, + 0x96, 0xBD, 0x96, 0xAF, 0xC9, 0xAE, + 0x00, 0xA5, 0x00, 0x91, 0x00, 0xC8, +}; + +const unsigned char gamma_300cd_29[] = { + 0x4A, 0x01, 0x4D, 0xBC, 0xF1, 0xC6, + 0xB1, 0xD6, 0xB3, 0xC2, 0xDD, 0xC2, + 0x96, 0xBD, 0x96, 0xAF, 0xC9, 0xAE, + 0x00, 0xA5, 0x00, 0x91, 0x00, 0xC8, +}; + +/* SM2, C1/M0 DALI Panel */ +const unsigned char gamma_300cd_8e[] = { + 0x71, 0x31, 0x7B, 0xA4, 0xB6, 0x95, + 0xA9, 0xBC, 0xA2, 0xBB, 0xC9, 0xB6, + 0x91, 0xA3, 0x8B, 0xAD, 0xB6, 0xA9, + 0x00, 0xD6, 0x00, 0xBE, 0x00, 0xFC, +}; + +/* SM2, C1/M0 Cellox Panel */ +const unsigned char gamma_300cd_ae[] = { + 0x5F, 0x2E, 0x67, 0xAA, 0xC6, 0xAC, + 0xB0, 0xC8, 0xBB, 0xBE, 0xCB, 0xBD, + 0x97, 0xA5, 0x91, 0xAF, 0xB8, 0xAB, + 0x00, 0xC2, 0x00, 0xBA, 0x00, 0xE2, +}; + +/* M0 A-Type Panel */ +const unsigned char gamma_300cd_d2[] = { + 0x41, 0x0A, 0x47, 0xAB, 0xBE, 0xA8, + 0xAF, 0xC5, 0xB7, 0xC3, 0xCC, 0xC3, + 0x9A, 0xA3, 0x96, 0xB1, 0xB7, 0xAF, + 0x00, 0xBD, 0x00, 0xAC, 0x00, 0xDE, +}; + +const unsigned char *gamma_300cd_list[GAMMA_300CD_MAX] = { + gamma_300cd_23, + gamma_300cd_33, + gamma_300cd_43, + gamma_300cd_53, + gamma_300cd_63, + gamma_300cd_73, + gamma_300cd_83, + gamma_300cd_20, + gamma_300cd_2a, + gamma_300cd_29, + gamma_300cd_4e, + gamma_300cd_8e, + gamma_300cd_ae, + gamma_300cd_d2, +}; + +const unsigned char gamma_id_list[GAMMA_300CD_MAX] = { + 0x23, 0x33, 0x43, 0x53, 0x63, 0x73, 0x83, 0x20, + 0x2a, 0x29, 0x4e, 0x8e, 0xae, 0xd2 +}; + +static s16 s9_to_s16(s16 v) +{ + return (s16)(v << 7) >> 7; +} + + +u32 calc_v1_volt(s16 gamma, int rgb_index, u32 adjust_volt[CI_MAX][AD_IVMAX]) +{ + u32 ret = 0; + + ret = volt_table_v1[gamma] >> 10; + + return ret; +} + + +u32 calc_v15_volt(s16 gamma, int rgb_index, u32 adjust_volt[CI_MAX][AD_IVMAX]) +{ + /* for CV : 20, DV :320 */ + int ret = 0; + u32 v1, v35; + u32 ratio = 0; + + v1 = adjust_volt[rgb_index][AD_IV1]; + v35 = adjust_volt[rgb_index][AD_IV35]; + ratio = volt_table_cv_20_dv_320[gamma]; + + ret = (v1 << 10) - ((v1-v35)*ratio); + ret = ret >> 10; + + return ret; +} + + +u32 calc_v35_volt(s16 gamma, int rgb_index, u32 adjust_volt[CI_MAX][AD_IVMAX]) +{ + /* for CV : 65, DV :320 */ + int ret = 0; + u32 v1, v59; + u32 ratio = 0; + + v1 = adjust_volt[rgb_index][AD_IV1]; + v59 = adjust_volt[rgb_index][AD_IV59]; + ratio = volt_table_cv_65_dv_320[gamma]; + + ret = (v1 << 10) - ((v1-v59)*ratio); + ret = ret >> 10; + + return ret; +} + + +u32 calc_v50_volt(s16 gamma, int rgb_index, u32 adjust_volt[CI_MAX][AD_IVMAX]) +{ + /* for CV : 65, DV :320 */ + int ret = 0; + u32 v1, v87; + u32 ratio = 0; + + v1 = adjust_volt[rgb_index][AD_IV1]; + v87 = adjust_volt[rgb_index][AD_IV87]; + ratio = volt_table_cv_65_dv_320[gamma]; + + ret = (v1 << 10) - ((v1-v87)*ratio); + ret = ret >> 10; + + return ret; +} + + +u32 calc_v87_volt(s16 gamma, int rgb_index, u32 adjust_volt[CI_MAX][AD_IVMAX]) +{ + /* for CV : 65, DV :320 */ + int ret = 0; + u32 v1, v171; + u32 ratio = 0; + + v1 = adjust_volt[rgb_index][AD_IV1]; + v171 = adjust_volt[rgb_index][AD_IV171]; + ratio = volt_table_cv_65_dv_320[gamma]; + + ret = (v1 << 10) - ((v1-v171)*ratio); + ret = ret >> 10; + + return ret; +} + + +u32 calc_v171_volt(s16 gamma, int rgb_index, u32 adjust_volt[CI_MAX][AD_IVMAX]) +{ + /* for CV : 65, DV :320 */ + int ret = 0; + u32 v1, v255; + u32 ratio = 0; + + v1 = adjust_volt[rgb_index][AD_IV1]; + v255 = adjust_volt[rgb_index][AD_IV255]; + ratio = volt_table_cv_65_dv_320[gamma]; + + ret = (v1 << 10) - ((v1-v255)*ratio); + ret = ret >> 10; + + return ret; +} + + +u32 calc_v255_volt(s16 gamma, int rgb_index, u32 adjust_volt[CI_MAX][AD_IVMAX]) +{ + u32 ret = 0; + + ret = volt_table_v255[gamma] >> 10; + + return ret; +} + + +u8 calc_voltage_table(struct str_smart_dim *smart, const u8 *mtp) +{ + int c, i, j; +#if defined(MTP_REVERSE) + int offset1 = 0; +#endif + int offset = 0; + s16 t1, t2; + s16 adjust_mtp[CI_MAX][IV_MAX]; + /* u32 adjust_volt[CI_MAX][AD_IVMAX] = {0, }; */ + u8 index; + u8 table_index = 0; + + u32 v1, v2; + u32 ratio; + + u32(*calc_volt[IV_MAX])(s16 gamma, int rgb_index, + u32 adjust_volt[CI_MAX][AD_IVMAX]) = { + calc_v1_volt, + calc_v15_volt, + calc_v35_volt, + calc_v50_volt, + calc_v87_volt, + calc_v171_volt, + calc_v255_volt, + }; + + u8 calc_seq[6] = {IV_1, IV_171, IV_87, IV_59, IV_35, IV_15}; + u8 ad_seq[6] = {AD_IV1, AD_IV171, AD_IV87, AD_IV59, AD_IV35, AD_IV15}; + + memset(adjust_mtp, 0, sizeof(adjust_mtp)); + + for (c = CI_RED; c < CI_MAX; c++) { + offset = IV_255*CI_MAX+c*2; +#if defined(MTP_REVERSE) + offset1 = IV_255*(c+1)+(c*2); + t1 = s9_to_s16(mtp[offset1]<<8|mtp[offset1+1]); +#else + t1 = s9_to_s16(mtp[offset]<<8|mtp[offset+1]); +#endif + t2 = s9_to_s16(smart->default_gamma[offset]<<8| + smart->default_gamma[offset+1]) + t1; + smart->mtp[c][IV_255] = t1; + adjust_mtp[c][IV_255] = t2; + smart->adjust_volt[c][AD_IV255] = + calc_volt[IV_255](t2, c, smart->adjust_volt); + + /* for V0 All RGB Voltage Value is Reference Voltage */ + smart->adjust_volt[c][AD_IV0] = 4600; + } + + for (c = CI_RED; c < CI_MAX; c++) { + for (i = IV_1; i < IV_255; i++) { +#if defined(MTP_REVERSE) + t1 = (s8)mtp[(calc_seq[i])+(c*8)]; +#else + t1 = (s8)mtp[CI_MAX*calc_seq[i]+c]; +#endif + t2 = smart->default_gamma[CI_MAX*calc_seq[i]+c] + t1; + + smart->mtp[c][calc_seq[i]] = t1; + adjust_mtp[c][calc_seq[i]] = t2; + smart->adjust_volt[c][ad_seq[i]] = + calc_volt[calc_seq[i]](t2, c, + smart->adjust_volt); + } + } + + for (i = 0; i < AD_IVMAX; i++) { + for (c = CI_RED; c < CI_MAX; c++) + smart->ve[table_index].v[c] = smart->adjust_volt[c][i]; + + index = 0; + for (j = table_index + 1; + j < table_index + range_table_count[i]; j++) { + for (c = CI_RED; c < CI_MAX; c++) { + if (smart->t_info[i].offset != NULL) + ratio = smart->t_info[i].offset[index] + * smart->t_info[i].rv; + else + ratio = (range_table_count[i] - + (index + 1)) * + smart->t_info[i].rv; + + v1 = smart->adjust_volt[c][i+1] << 15; + v2 = (smart->adjust_volt[c][i] - + smart->adjust_volt[c][i+1]) * ratio; + smart->ve[j].v[c] = ((v1+v2) >> 15); + } + index++; + } + table_index = j; + } + +#if 0 + for (i = IV_1; i < IV_MAX; i++) { + printk(KERN_INFO "V Level : %d - ", i); + for (c = CI_RED; c < CI_MAX; c++) + printk(" %c : 0x%08x(%04d)", + color_name[c], smart->mtp[c][i], smart->mtp[c][i]); + printk("\n"); + } + + for (i = IV_1; i < IV_MAX; i++) { + printk(KERN_INFO "V Level : %d - ", i); + for (c = CI_RED; c < CI_MAX; c++) + printk(" %c : 0x%08x(%04d)", color_name[c], + adjust_mtp[c][i], adjust_mtp[c][i]); + printk("\n"); + } + + for (i = AD_IV0; i < AD_IVMAX; i++) { + printk(KERN_INFO "V Level : %d - ", i); + for (c = CI_RED; c < CI_MAX; c++) + printk(" %c : %04dV", + color_name[c], smart->adjust_volt[c][i]); + printk("\n"); + } + + for (i = 0; i < 256; i++) { + printk(KERN_INFO "Gray Level : %03d - ", i); + for (c = CI_RED; c < CI_MAX; c++) + printk(" %c : %04dV", + color_name[c], smart->ve[i].v[c]); + printk("\n"); + } +#endif + return 0; +} + + +int init_table_info(struct str_smart_dim *smart) +{ + int i; + int offset = 0; + + for (i = 0; i < IV_TABLE_MAX; i++) { + smart->t_info[i].count = (u8)range_table_count[i]; + smart->t_info[i].offset = offset_table[i]; + smart->t_info[i].rv = table_radio[i]; + offset += range_table_count[i]; + } + smart->flooktbl = flookup_table; + smart->g300_gra_tbl = gamma_300_gra_table; + smart->g22_tbl = gamma_22_table; + + for (i = 0; i < GAMMA_300CD_MAX; i++) { + if (smart->panelid[1] == gamma_id_list[i]) + break; + } + + if (i >= GAMMA_300CD_MAX) { + printk(KERN_ERR "Can't found default gamma table\n"); + smart->default_gamma = gamma_300cd_list[GAMMA_300CD_MAX-1]; + } else + smart->default_gamma = gamma_300cd_list[i]; + + return 0; +} + + +u32 lookup_vtbl_idx(struct str_smart_dim *smart, u32 gamma) +{ + u32 lookup_index; + u16 table_count, table_index; + u32 gap, i; + u32 minimum = smart->g300_gra_tbl[255]; + u32 candidate = 0; + u32 offset = 0; + + /* printk("Input Gamma Value : %d\n", gamma); */ + + lookup_index = (gamma/VALUE_DIM_1000)+1; + if (lookup_index > MAX_GRADATION) { + printk(KERN_ERR "ERROR Wrong input value LOOKUP INDEX : %d\n", + lookup_index); + return 0; + } + + /* printk("lookup index : %d\n",lookup_index); */ + + if (smart->flooktbl[lookup_index].count) { + if (smart->flooktbl[lookup_index-1].count) { + table_index = smart->flooktbl[lookup_index-1].entry; + table_count = smart->flooktbl[lookup_index].count + + smart->flooktbl[lookup_index-1].count; + } else { + table_index = smart->flooktbl[lookup_index].entry; + table_count = smart->flooktbl[lookup_index].count; + } + } else { + offset += 1; + while (!(smart->flooktbl[lookup_index + offset].count || + smart->flooktbl[lookup_index - offset].count)) + offset++; + + if (smart->flooktbl[lookup_index-offset].count) + table_index = + smart->flooktbl[lookup_index - offset].entry; + else + table_index = + smart->flooktbl[lookup_index + offset].entry; + table_count = smart->flooktbl[lookup_index + offset].count + + smart->flooktbl[lookup_index - offset].count; + } + + + for (i = 0; i < table_count; i++) { + if (gamma > smart->g300_gra_tbl[table_index]) + gap = gamma - smart->g300_gra_tbl[table_index]; + else + gap = smart->g300_gra_tbl[table_index] - gamma; + + if (gap == 0) { + candidate = table_index; + break; + } + + if (gap < minimum) { + minimum = gap; + candidate = table_index; + } + table_index++; + } +#if 0 + printk(KERN_INFO "cal : found index : %d\n", candidate); + printk(KERN_INFO "gamma : %d, found index : %d found gamma : %d\n", + gamma, candidate, smart->g300_gra_tbl[candidate]); +#endif + return candidate; +} + + +u32 calc_v1_reg(int ci, u32 dv[CI_MAX][IV_MAX]) +{ + u32 ret; + u32 v1; + + v1 = dv[ci][IV_1]; + ret = (595 * 1000) - (130 * v1); + ret = ret/1000; + + return ret; +} + + +u32 calc_v15_reg(int ci, u32 dv[CI_MAX][IV_MAX]) +{ + u32 t1, t2; + u32 v1, v15, v35; + u32 ret; + + v1 = dv[ci][IV_1]; + v15 = dv[ci][IV_15]; + v35 = dv[ci][IV_35]; + +#if 0 + t1 = (v1 - v15) * 1000; + t2 = v1 - v35; + + ret = 320*(t1/t2)-(20*1000); + + ret = ret/1000; +#else + t1 = (v1 - v15) << 10; + t2 = (v1 - v35) ? (v1 - v35) : (v1) ? v1 : 1; + ret = (320 * (t1/t2)) - (20 << 10); + ret >>= 10; + +#endif + return ret; +} + + +u32 calc_v35_reg(int ci, u32 dv[CI_MAX][IV_MAX]) +{ + u32 t1, t2; + u32 v1, v35, v57; + u32 ret; + + v1 = dv[ci][IV_1]; + v35 = dv[ci][IV_35]; + v57 = dv[ci][IV_59]; + +#if 0 + t1 = (v1 - v35) * 1000; + t2 = v1 - v57; + ret = 320*(t1/t2) - (65 * 1000); + + ret = ret/1000; +#else + t1 = (v1 - v35) << 10; + t2 = (v1 - v57) ? (v1 - v57) : (v1) ? v1 : 1; + ret = (320 * (t1/t2)) - (65 << 10); + + ret >>= 10; +#endif + + return ret; +} + + +u32 calc_v50_reg(int ci, u32 dv[CI_MAX][IV_MAX]) +{ + u32 t1, t2; + u32 v1, v57, v87; + u32 ret; + + v1 = dv[ci][IV_1]; + v57 = dv[ci][IV_59]; + v87 = dv[ci][IV_87]; + +#if 0 + t1 = (v1 - v57) * 1000; + t2 = v1 - v87; + ret = 320*(t1/t2) - (65 * 1000); + ret = ret/1000; +#else + t1 = (v1 - v57) << 10; + t2 = (v1 - v87) ? (v1 - v87) : (v1) ? v1 : 1; + ret = (320 * (t1/t2)) - (65 << 10); + ret >>= 10; +#endif + return ret; +} + + +u32 calc_v87_reg(int ci, u32 dv[CI_MAX][IV_MAX]) +{ + u32 t1, t2; + u32 v1, v87, v171; + u32 ret; + + v1 = dv[ci][IV_1]; + v87 = dv[ci][IV_87]; + v171 = dv[ci][IV_171]; + +#if 0 + t1 = (v1 - v87) * 1000; + t2 = v1 - v171; + ret = 320*(t1/t2) - (65 * 1000); + ret = ret/1000; +#else + t1 = (v1 - v87) << 10; + t2 = (v1 - v171) ? (v1 - v171) : (v1) ? v1 : 1; + ret = (320 * (t1/t2)) - (65 << 10); + ret >>= 10; +#endif + + return ret; +} + + +u32 calc_v171_reg(int ci, u32 dv[CI_MAX][IV_MAX]) +{ + u32 t1, t2; + u32 v1, v171, v255; + u32 ret; + + v1 = dv[ci][IV_1]; + v171 = dv[ci][IV_171]; + v255 = dv[ci][IV_255]; + +#if 0 + t1 = (v1 - v171) * 1000; + t2 = v1 - v255; + ret = 320*(t1/t2) - (65 * 1000); + ret = ret/1000; +#else + t1 = (v1 - v171) << 10; + t2 = (v1 - v255) ? (v1 - v255) : (v1) ? v1 : 1; + ret = (320 * (t1/t2)) - (65 << 10); + ret >>= 10; +#endif + + return ret; +} + + +u32 calc_v255_reg(int ci, u32 dv[CI_MAX][IV_MAX]) +{ + u32 ret; + u32 v255; + + v255 = dv[ci][IV_255]; + + ret = (500 * 1000) - (130 * v255); + ret = ret / 1000; + + return ret; +} + + +u32 calc_gamma_table(struct str_smart_dim *smart, u32 gv, u8 result[]) +{ + u32 i, c; + u32 temp; + u32 lidx; + u32 dv[CI_MAX][IV_MAX]; + s16 gamma[CI_MAX][IV_MAX]; + u16 offset; + u32(*calc_reg[IV_MAX])(int ci, u32 dv[CI_MAX][IV_MAX]) = { + calc_v1_reg, + calc_v15_reg, + calc_v35_reg, + calc_v50_reg, + calc_v87_reg, + calc_v171_reg, + calc_v255_reg, + }; + + memset(gamma, 0, sizeof(gamma)); + + for (c = CI_RED; c < CI_MAX; c++) + dv[c][0] = smart->adjust_volt[c][AD_IV1]; + + + for (i = IV_15; i < IV_MAX; i++) { + temp = smart->g22_tbl[dv_value[i]] * gv; + lidx = lookup_vtbl_idx(smart, temp); + for (c = CI_RED; c < CI_MAX; c++) + dv[c][i] = smart->ve[lidx].v[c]; + } + + /* for IV1 does not calculate value */ + /* just use default gamma value (IV1) */ + for (c = CI_RED; c < CI_MAX; c++) + gamma[c][IV_1] = smart->default_gamma[c]; + + for (i = IV_15; i < IV_MAX; i++) { + for (c = CI_RED; c < CI_MAX; c++) + gamma[c][i] = + (s16)calc_reg[i](c, dv) - smart->mtp[c][i]; + } + + for (c = CI_RED; c < CI_MAX; c++) { + offset = IV_255*CI_MAX+c*2; + result[offset+1] = gamma[c][IV_255]; + } + + for (c = CI_RED; c < CI_MAX; c++) { + for (i = IV_1; i < IV_255; i++) + result[(CI_MAX*i)+c] = gamma[c][i]; + } + +#if 0 + for (i = IV_1; i < IV_MAX; i++) { + printk(KERN_INFO "V Level : %d - ", i); + for (c = CI_RED; c < CI_MAX; c++) + printk("%c : %04dV", color_name[c], dv[c][i]); + printk("\n"); + } + + for (i = IV_1; i < IV_MAX; i++) { + printk(KERN_INFO "V Level : %d - ", i); + for (c = CI_RED; c < CI_MAX; c++) + printk("%c : %3d, 0x%2x", + color_name[c], gamma[c][i], gamma[c][i]); + printk("\n"); + } +#endif + return 0; +} + diff --git a/drivers/video/backlight/smart_dimming.h b/drivers/video/backlight/smart_dimming.h new file mode 100644 index 0000000..ae21182 --- /dev/null +++ b/drivers/video/backlight/smart_dimming.h @@ -0,0 +1,104 @@ +/* linux/drivers/video/samsung/smartdimming.h + * + * Copyright (c) 2010 Samsung Electronics Co., Ltd. + * http://www.samsung.com + + * Samsung Smart Dimming for OCTA + * + * Minwoo Kim, <minwoo7945.kim@samsung.com> + * +*/ + + +#ifndef __SMART_DIMMING_H__ +#define __SMART_DIMMING_H__ + + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/mutex.h> +#include <linux/wait.h> +#include <linux/ctype.h> + +#define MAX_GRADATION 300 +#define PANEL_ID_MAX 3 +#define GAMMA_300CD_MAX 14 + + +enum { + CI_RED, + CI_GREEN, + CI_BLUE, + CI_MAX, +}; + + +enum { + IV_1, + IV_15, + IV_35, + IV_59, + IV_87, + IV_171, + IV_255, + IV_MAX, + IV_TABLE_MAX, +}; + + +enum { + AD_IV0, + AD_IV1, + AD_IV15, + AD_IV35, + AD_IV59, + AD_IV87, + AD_IV171, + AD_IV255, + AD_IVMAX, +}; + + +struct str_voltage_entry { + u32 v[CI_MAX]; +}; + + +struct str_table_info { + /* et : start gray value */ + u8 st; + /* end gray value, st + count */ + u8 et; + u8 count; + const u8 *offset; + /* rv : ratio value */ + u32 rv; +}; + + +struct str_flookup_table { + u16 entry; + u16 count; +}; + + +struct str_smart_dim { + u8 panelid[PANEL_ID_MAX]; + s16 mtp[CI_MAX][IV_MAX]; + struct str_voltage_entry ve[256]; + const u8 *default_gamma; + struct str_table_info t_info[IV_TABLE_MAX]; + const struct str_flookup_table *flooktbl; + const u32 *g22_tbl; + const u32 *g300_gra_tbl; + u32 adjust_volt[CI_MAX][AD_IVMAX]; +}; + + +int init_table_info(struct str_smart_dim *smart); +u8 calc_voltage_table(struct str_smart_dim *smart, const u8 *mtp); +u32 calc_gamma_table(struct str_smart_dim *smart, u32 gv, u8 result[]); + + +#endif diff --git a/drivers/video/backlight/tc358764_mipi_lcd.c b/drivers/video/backlight/tc358764_mipi_lcd.c new file mode 100644 index 0000000..0563c85 --- /dev/null +++ b/drivers/video/backlight/tc358764_mipi_lcd.c @@ -0,0 +1,110 @@ +/* linux/drivers/video/backlight/tc358764_mipi_lcd.c + * + * + * Copyright (c) 2011 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * +*/ + +#include <linux/delay.h> + +#include <video/mipi_display.h> + +#include <plat/dsim.h> +#include <plat/mipi_dsi.h> +#include <plat/cpu.h> + +unsigned char initcode_013c[6] = {0x3c, 0x01, 0x03, 0x00, 0x02, 0x00}; +unsigned char initcode_0114[6] = {0x14, 0x01, 0x02, 0x00, 0x00, 0x00}; +unsigned char initcode_0164[6] = {0x64, 0x01, 0x05, 0x00, 0x00, 0x00}; +unsigned char initcode_0168[6] = {0x68, 0x01, 0x05, 0x00, 0x00, 0x00}; +unsigned char initcode_016c[6] = {0x6c, 0x01, 0x05, 0x00, 0x00, 0x00}; +unsigned char initcode_0170[6] = {0x70, 0x01, 0x05, 0x00, 0x00, 0x00}; +unsigned char initcode_0134[6] = {0x34, 0x01, 0x1f, 0x00, 0x00, 0x00}; +unsigned char initcode_0210[6] = {0x10, 0x02, 0x1f, 0x00, 0x00, 0x00}; +unsigned char initcode_0104[6] = {0x04, 0x01, 0x01, 0x00, 0x00, 0x00}; +unsigned char initcode_0204[6] = {0x04, 0x02, 0x01, 0x00, 0x00, 0x00}; +unsigned char initcode_0450[6] = {0x50, 0x04, 0x20, 0x01, 0xfa, 0x00}; +unsigned char initcode_0454[6] = {0x54, 0x04, 0x20, 0x00, 0x50, 0x00}; +unsigned char initcode_0458[6] = {0x58, 0x04, 0x00, 0x05, 0x30, 0x00}; +unsigned char initcode_045c[6] = {0x5c, 0x04, 0x05, 0x00, 0x0a, 0x00}; +unsigned char initcode_0460[6] = {0x60, 0x04, 0x20, 0x03, 0x0a, 0x00}; +unsigned char initcode_0464[6] = {0x64, 0x04, 0x01, 0x00, 0x00, 0x00}; +unsigned char initcode_04a0_1[6] = {0xa0, 0x04, 0x06, 0x80, 0x44, 0x00}; +unsigned char initcode_04a0_2[6] = {0xa0, 0x04, 0x06, 0x80, 0x04, 0x00}; +unsigned char initcode_0504[6] = {0x04, 0x05, 0x04, 0x00, 0x00, 0x00}; +unsigned char initcode_049c[6] = {0x9c, 0x04, 0x0d, 0x00, 0x00, 0x00}; + +unsigned int *initcode[20] = { + (unsigned int *)initcode_013c, + (unsigned int *)initcode_0114, + (unsigned int *)initcode_0164, + (unsigned int *)initcode_0168, + (unsigned int *)initcode_016c, + (unsigned int *)initcode_0170, + (unsigned int *)initcode_0134, + (unsigned int *)initcode_0210, + (unsigned int *)initcode_0104, + (unsigned int *)initcode_0204, + (unsigned int *)initcode_0450, + (unsigned int *)initcode_0454, + (unsigned int *)initcode_0458, + (unsigned int *)initcode_045c, + (unsigned int *)initcode_0460, + (unsigned int *)initcode_0464, + (unsigned int *)initcode_04a0_1, + (unsigned int *)initcode_04a0_2, + (unsigned int *)initcode_0504, + (unsigned int *)initcode_049c +}; + +static int init_lcd(struct mipi_dsim_device *dsim) +{ + int i; + + if (soc_is_exynos5250() && samsung_rev() >= EXYNOS5250_REV_1_0) { + for (i = 0; i <= 19; i++) { + s5p_mipi_dsi_wr_data(dsim, + MIPI_DSI_GENERIC_LONG_WRITE, + (unsigned int)initcode[i], 6); + usleep_range(6000, 7000); + } + } else { + for (i = 0; i <= 19; i++) { + if (s5p_mipi_dsi_wr_data(dsim, + MIPI_DSI_GENERIC_LONG_WRITE, + (unsigned int)initcode[i], 6) == -1) + return 0; + usleep_range(6000, 7000); + } + } + + msleep(800); + + return 1; +} + +static int tc358764_mipi_lcd_suspend(struct mipi_dsim_device *dsim) +{ + return 0; +} + +static int tc358764_mipi_lcd_displayon(struct mipi_dsim_device *dsim) +{ + return init_lcd(dsim); +} + +static int tc358764_mipi_lcd_resume(struct mipi_dsim_device *dsim) +{ + return init_lcd(dsim); +} + +struct mipi_dsim_lcd_driver tc358764_mipi_lcd_driver = { + .suspend = tc358764_mipi_lcd_suspend, + .displayon = tc358764_mipi_lcd_displayon, + .resume = tc358764_mipi_lcd_resume, +}; |