diff options
author | codeworkx <codeworkx@cyanogenmod.com> | 2012-09-22 09:48:20 +0200 |
---|---|---|
committer | codeworkx <codeworkx@cyanogenmod.com> | 2012-09-22 14:02:16 +0200 |
commit | 2489007e7d740ccbc3e0a202914e243ad5178787 (patch) | |
tree | b8e6380ea7b1da63474ad68a5dba997e01146043 /drivers/video/backlight | |
parent | 5f67568eb31e3a813c7c52461dcf66ade15fc2e7 (diff) | |
download | kernel_samsung_smdk4412-2489007e7d740ccbc3e0a202914e243ad5178787.zip kernel_samsung_smdk4412-2489007e7d740ccbc3e0a202914e243ad5178787.tar.gz kernel_samsung_smdk4412-2489007e7d740ccbc3e0a202914e243ad5178787.tar.bz2 |
merge opensource jb u5
Change-Id: I1aaec157aa196f3448eff8636134fce89a814cf2
Diffstat (limited to 'drivers/video/backlight')
-rw-r--r-- | drivers/video/backlight/Kconfig | 25 | ||||
-rw-r--r-- | drivers/video/backlight/Makefile | 2 | ||||
-rw-r--r-- | drivers/video/backlight/backlight.c | 2 | ||||
-rw-r--r-- | drivers/video/backlight/ea8061.c | 899 | ||||
-rw-r--r-- | drivers/video/backlight/ea8061.h | 21 | ||||
-rw-r--r-- | drivers/video/backlight/ea8061_gamma.h | 250 | ||||
-rw-r--r-- | drivers/video/backlight/s6d6aa1.c | 976 | ||||
-rw-r--r-- | drivers/video/backlight/s6d6aa1.h | 21 | ||||
-rw-r--r-- | drivers/video/backlight/s6e8aa0.c | 339 | ||||
-rw-r--r-- | drivers/video/backlight/s6e8aa0.h | 9 | ||||
-rw-r--r-- | drivers/video/backlight/smart_dimming.c | 11 | ||||
-rw-r--r-- | drivers/video/backlight/smart_dimming.h | 2 |
12 files changed, 2462 insertions, 95 deletions
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index 80f42e6..29c581f 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -174,12 +174,33 @@ config LCD_LTE480WV 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) + (MACH_SLP_MIDAS || MACH_SLP_PQ_LTE || MACH_SLP_PQ || MACH_TRATS) default n help If you have an S6E8AA0 MIPI AMOLED LCD Panel, say Y to enable its LCD control driver. +config LCD_EA8061 + tristate "EA8061 MIPI AMOLED LCD Driver" + depends on S5P_MIPI_DSI2 && BACKLIGHT_CLASS_DEVICE && \ + (MACH_SLP_T0_LTE) + default n + help + If you have an S6E8AA0 MIPI AMOLED LCD Panel, say Y to enable its + LCD control driver. this driver supported S-Strip Portrait. + It support MIPI interface for transmitting video data to LCD. + please find quality about display. + +config LCD_S6D6AA1 + tristate "S6D6AA1 MIPI TFT-LCD Driver" + depends on S5P_MIPI_DSI2 && BACKLIGHT_CLASS_DEVICE && \ + (MACH_REDWOOD) + help + If you have an S6D6AA1 MIPI TFT-LCD Panel, say Y to enable its + LCD control driver. this driver supported white maic sony ip. + improve power consumtion and outdoor mode using white pixel. + please find advantage about this driver. + config LCD_S6E39A0X02 tristate "S6E39A0X02 MIPI AMOLED LCD Driver" depends on S5P_MIPI_DSI2 && BACKLIGHT_CLASS_DEVICE @@ -399,7 +420,7 @@ config BACKLIGHT_PCF50633 config BACKLIGHT_SMART_DIMMING bool "SLP Backlight driver feature for smart dimming" - depends on (MACH_SLP_MIDAS || MACH_SLP_PQ_LTE || MACH_SLP_PQ) + depends on (MACH_SLP_MIDAS || MACH_SLP_T0_LTE || MACH_SLP_PQ_LTE || MACH_SLP_PQ || MACH_TRATS) help Say Y to enable the Smart Dimming Feature. diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index 16737d6..9d8d82f 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -18,6 +18,8 @@ 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_EA8061) += ea8061.o +obj-$(CONFIG_LCD_S6D6AA1) += s6d6aa1.o obj-$(CONFIG_LCD_S6E39A0X02) += s6e39a0x02.o obj-$(CONFIG_LCD_LMS501KF03) += lms501kf03.o diff --git a/drivers/video/backlight/backlight.c b/drivers/video/backlight/backlight.c index 1a6fe06..2086dd1 100644 --- a/drivers/video/backlight/backlight.c +++ b/drivers/video/backlight/backlight.c @@ -211,7 +211,7 @@ static ssize_t backlight_store_brightness(struct device *dev, } mutex_unlock(&bd->ops_lock); - backlight_generate_event(bd, BACKLIGHT_UPDATE_SYSFS); + /* backlight_generate_event(bd, BACKLIGHT_UPDATE_SYSFS); */ return rc; } diff --git a/drivers/video/backlight/ea8061.c b/drivers/video/backlight/ea8061.c new file mode 100644 index 0000000..b8da952 --- /dev/null +++ b/drivers/video/backlight/ea8061.c @@ -0,0 +1,899 @@ +/* linux/drivers/video/backlight/ea8061.c + * + * MIPI-DSI based ea8061 AMOLED lcd 5.55 inch panel driver. + * + * Joongmock Shin <jmock.shin@samsung.com> + * Eunchul Kim <chulspro.kim@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/lcd-property.h> +#include <linux/fb.h> +#include <linux/backlight.h> +#include <linux/regulator/consumer.h> +#include <linux/firmware.h> +#include <video/mipi_display.h> + +#ifdef CONFIG_ARM_EXYNOS4_DISPLAY_DEVFREQ +#include <linux/devfreq/exynos4_display.h> +#endif + +#include <plat/mipi_dsim2.h> +#include "ea8061.h" + +#include "ea8061_gamma.h" +#ifdef CONFIG_BACKLIGHT_SMART_DIMMING +#include "smart_dimming.h" +#endif + +#define VER_161 (0xA1) /* MACH_SLP_T0_LTE */ +#define LDI_FW_PATH "ea8061/reg_%s.bin" +#define MAX_STR 255 +#define LDI_MTP_LENGTH 24 +#define MAX_READ_LENGTH 64 +#define MIN_BRIGHTNESS (0) +#define MAX_BRIGHTNESS (24) + +#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 panel_model { + int ver; + char *name; +}; + +struct ea8061 { + struct device *dev; + struct lcd_device *ld; + struct backlight_device *bd; + struct mipi_dsim_lcd_device *dsim_dev; + struct lcd_platform_data *ddi_pd; + struct lcd_property *property; + + struct regulator *reg_vdd3; + struct regulator *reg_vci; + +#ifdef CONFIG_ARM_EXYNOS4_DISPLAY_DEVFREQ + struct notifier_block nb_disp; +#endif + struct mutex lock; + + unsigned int id; + unsigned int aid; + unsigned int ver; + unsigned int power; + unsigned int acl_enable; + unsigned int cur_addr; + + const struct panel_model *model; + unsigned int model_count; + +#ifdef CONFIG_BACKLIGHT_SMART_DIMMING + unsigned int support_elvss; + struct str_smart_dim smart_dim; +#endif +}; + +static void ea8061_delay(unsigned int msecs) +{ + /* refer from documentation/timers/timers-howto.txt */ + if (msecs < 20) + usleep_range(msecs*1000, (msecs+1)*1000); + else + msleep(msecs); +} + +static void ea8061_sleep_in(struct ea8061 *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 ea8061_sleep_out(struct ea8061 *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 ea8061_apply_level_1_key(struct ea8061 *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 void ea8061_apply_level_2_key(struct ea8061 *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 ea8061_acl_on(struct ea8061 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + /* FIXME: off, 33%, 40%, 50% */ + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0x55, 0x03); +} + +static void ea8061_acl_off(struct ea8061 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + /* FIXME: off, 33%, 40%, 50% */ + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0x55, 0x00); +} + +static void ea8061_enable_mtp_register(struct ea8061 *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 ea8061_disable_mtp_register(struct ea8061 *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 ea8061_read_id(struct ea8061 *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 ea8061_read_mtp(struct ea8061 *lcd, u8 *mtp_data) +{ + unsigned int ret; + unsigned int addr = 0xD3; /* MTP addr */ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + + ea8061_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); + + ea8061_disable_mtp_register(lcd); + + return ret; +} + +static void ea8061_disp_cond(struct ea8061 *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, 0x36, 0x02); +} + +static void ea8061_panel_cond(struct ea8061 *lcd, int high_freq) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + const unsigned char data_to_send[] = { + 0xc4, 0x4E, 0xBD, 0x00, 0x00, 0x58, 0xA7, 0x0B, 0x34, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x92, 0x0B, 0x92, + 0x08, 0x08, 0x07, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x04, 0x04 + }; + + /* ToDo : Low requency control */ + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + (unsigned int)data_to_send, ARRAY_SIZE(data_to_send)); +} + +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 ea8061_gamma_ctrl(struct ea8061 *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)ea8061_gamma22_table[brightness], + GAMMA_TABLE_COUNT); +#endif + + /* update gamma table. */ + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_SHORT_WRITE_PARAM, + 0xf7, 0x03); + + ea8061_acl_on(lcd); + + return 0; +} + +static void ea8061_elvss_nvm_set(struct ea8061 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + unsigned char data_to_send[] = { + 0xB2, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0C, 0x0D, + 0x0E, 0x0F, 0x10, 0x11, 0x0B, 0x0C, 0x0E, 0x10, 0x12, + 0x13, 0x15, 0x17, 0x18, 0x1A, 0x1A, 0x1B, 0x1B, 0x1B, + 0x1C, 0x1C, 0x1C, 0xB4, 0xA0, 0x00, 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 ea8061_slew_ctl(struct ea8061 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + unsigned char data_to_send[] = { + 0xB4, 0x33, 0x0D, 0x00 + }; + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + (unsigned int)data_to_send, ARRAY_SIZE(data_to_send)); +} + +static int ea8061_panel_init(struct ea8061 *lcd) +{ + struct backlight_device *bd = lcd->bd; + int brightness = bd->props.brightness; + + ea8061_delay(5); + ea8061_apply_level_1_key(lcd); + ea8061_apply_level_2_key(lcd); + + ea8061_panel_cond(lcd, 1); + ea8061_disp_cond(lcd); + ea8061_gamma_ctrl(lcd, brightness); + ea8061_elvss_nvm_set(lcd); + ea8061_acl_on(lcd); + ea8061_slew_ctl(lcd); + + ea8061_sleep_out(lcd); + + /* wait more than 120ms */ + ea8061_delay(lcd->ddi_pd->power_on_delay); + dev_info(lcd->dev, "panel init sequence done.\n"); + + return 0; +} + +static void ea8061_display_on(struct ea8061 *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 ea8061_display_off(struct ea8061 *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 int ea8061_early_set_power(struct lcd_device *ld, int power) +{ + struct ea8061 *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 ea8061_set_power(struct lcd_device *ld, int power) +{ + struct ea8061 *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 ea8061_get_power(struct lcd_device *ld) +{ + struct ea8061 *lcd = lcd_get_data(ld); + + return lcd->power; +} + +static struct lcd_ops ea8061_lcd_ops = { + .early_set_power = ea8061_early_set_power, + .set_power = ea8061_set_power, + .get_power = ea8061_get_power, +}; + +static int ea8061_get_brightness(struct backlight_device *bd) +{ + return bd->props.brightness; +} + +static int ea8061_set_brightness(struct backlight_device *bd) +{ + int ret = 0, brightness = bd->props.brightness; + struct ea8061 *lcd = bl_get_data(bd); + + if (lcd->power == FB_BLANK_POWERDOWN) { + 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 = ea8061_gamma_ctrl(lcd, brightness); + if (ret) { + dev_err(&bd->dev, "lcd brightness setting failed.\n"); + return -EIO; + } + + return ret; +} + +static const struct backlight_ops ea8061_backlight_ops = { + .get_brightness = ea8061_get_brightness, + .update_status = ea8061_set_brightness, +}; + +static ssize_t acl_control_show(struct device *dev, struct + device_attribute * attr, char *buf) +{ + struct ea8061 *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 ea8061 *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; + if (lcd->acl_enable) + ea8061_acl_on(lcd); + else + ea8061_acl_off(lcd); + } + return size; +} + +static ssize_t lcd_type_show(struct device *dev, struct + device_attribute * attr, char *buf) +{ + struct ea8061 *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 ea8061_read_reg(struct ea8061 *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 ea8061_write_reg(struct ea8061 *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 == 1) + ret = ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE, + (unsigned int)fw->data[0], 0); + else if (fw->size == 2) + ret = ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE_PARAM, + (unsigned int)fw->data[0], fw->data[1]); + 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 ea8061 *lcd = dev_get_drvdata(dev); + + if (lcd->cur_addr == 0) { + dev_err(dev, "failed to set current lcd register.\n"); + return -EINVAL; + } + + return ea8061_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 ea8061 *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 ea8061 *lcd = dev_get_drvdata(dev); + char name[32]; + int ret; + + ret = sscanf(buf, "%s", name); + if (ret < 0) + return ret; + + ret = ea8061_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 ea8061_model[] = { + { + .ver = VER_161, /* MACH_SLP_T0_LTE */ + .name = "SMD_AMS555HBxx-0", + } +}; + +#ifdef CONFIG_ARM_EXYNOS4_DISPLAY_DEVFREQ +static int ea8061_notifier_callback(struct notifier_block *this, + unsigned long event, void *_data) +{ + struct ea8061 *lcd = container_of(this, struct ea8061, nb_disp); + + if (lcd->power == FB_BLANK_POWERDOWN) + return NOTIFY_DONE; + + switch (event) { + case EXYNOS4_DISPLAY_LV_HF: + ea8061_panel_cond(lcd, 1); + break; + case EXYNOS4_DISPLAY_LV_LF: + ea8061_panel_cond(lcd, 0); + break; + default: + return NOTIFY_BAD; + } + + return NOTIFY_DONE; +} +#endif + +static void ea8061_regulator_ctl(struct ea8061 *lcd, bool enable) +{ + mutex_lock(&lcd->lock); + + if (enable) { + if (lcd->reg_vdd3) + regulator_enable(lcd->reg_vdd3); + + if (lcd->reg_vci) + regulator_enable(lcd->reg_vci); + } else { + if (lcd->reg_vci) + regulator_disable(lcd->reg_vci); + + if (lcd->reg_vdd3) + regulator_disable(lcd->reg_vdd3); + } + + mutex_unlock(&lcd->lock); +} + +static void ea8061_power_on(struct mipi_dsim_lcd_device *dsim_dev, + unsigned int enable) +{ + struct ea8061 *lcd = dev_get_drvdata(&dsim_dev->dev); + + dev_dbg(lcd->dev, "%s:enable[%d]\n", __func__, enable); + + if (enable) { + /* lcd power on */ + ea8061_regulator_ctl(lcd, true); + + ea8061_delay(lcd->ddi_pd->reset_delay); + + /* lcd reset high */ + if (lcd->ddi_pd->reset) + lcd->ddi_pd->reset(lcd->ld); + + /* wait more than 5ms */ + ea8061_delay(5); + } else { + /* lcd reset low */ + if (lcd->ddi_pd->reset) + lcd->ddi_pd->reset(lcd->ld); + + /* lcd power off */ + ea8061_regulator_ctl(lcd, false); + } +} + +static int ea8061_check_mtp(struct mipi_dsim_lcd_device *dsim_dev) +{ + /* FIXME:! read id mtp failed */ + return 0; +} + +static void ea8061_set_sequence(struct mipi_dsim_lcd_device *dsim_dev) +{ + struct ea8061 *lcd = dev_get_drvdata(&dsim_dev->dev); + + ea8061_panel_init(lcd); + ea8061_display_on(lcd); +} + +static int ea8061_probe(struct mipi_dsim_lcd_device *dsim_dev) +{ + struct ea8061 *lcd; + int ret; + int i; + + lcd = kzalloc(sizeof(struct ea8061), GFP_KERNEL); + if (!lcd) { + dev_err(&dsim_dev->dev, "failed to allocate ea8061 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("ea8061", lcd->dev, lcd, + &ea8061_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("ea8061-bl", lcd->dev, lcd, + &ea8061_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; + } + + ea8061_regulator_ctl(lcd, true); + + if (lcd->ddi_pd) + lcd->property = lcd->ddi_pd->pdata; + +#ifdef CONFIG_ARM_EXYNOS4_DISPLAY_DEVFREQ + if (lcd->property && lcd->property->dynamic_refresh) { + lcd->nb_disp.notifier_call = ea8061_notifier_callback; + ret = exynos4_display_register_client(&lcd->nb_disp); + if (ret < 0) + dev_warn(&lcd->ld->dev, "failed to register exynos-display notifier\n"); + } +#endif + + lcd->bd->props.max_brightness = MAX_BRIGHTNESS; + lcd->bd->props.brightness = MAX_BRIGHTNESS; + lcd->power = FB_BLANK_UNBLANK; + lcd->model = ea8061_model; + lcd->model_count = ARRAY_SIZE(ea8061_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); + dev_info(lcd->dev, "probed ea8061 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 ea8061_remove(struct mipi_dsim_lcd_device *dsim_dev) +{ + struct ea8061 *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); + +#ifdef CONFIG_ARM_EXYNOS4_DISPLAY_DEVFREQ + if (lcd->property && lcd->property->dynamic_refresh) + exynos4_display_unregister_client(&lcd->nb_disp); +#endif + kfree(lcd); +} + +#ifdef CONFIG_PM +static int ea8061_suspend(struct mipi_dsim_lcd_device *dsim_dev) +{ + struct ea8061 *lcd = dev_get_drvdata(&dsim_dev->dev); + + ea8061_display_off(lcd); + ea8061_sleep_in(lcd); + ea8061_delay(lcd->ddi_pd->power_off_delay); + + return 0; +} + +static int ea8061_resume(struct mipi_dsim_lcd_device *dsim_dev) +{ + struct ea8061 *lcd = dev_get_drvdata(&dsim_dev->dev); + + ea8061_sleep_out(lcd); + ea8061_delay(lcd->ddi_pd->power_on_delay); + + return 0; +} +#else +#define ea8061_suspend NULL +#define ea8061_resume NULL +#endif + +static struct mipi_dsim_lcd_driver ea8061_dsim_ddi_driver = { + .name = "ea8061", + .id = -1, + + .power_on = ea8061_power_on, + .check_mtp = ea8061_check_mtp, + .set_sequence = ea8061_set_sequence, + .probe = ea8061_probe, + .remove = ea8061_remove, + .suspend = ea8061_suspend, + .resume = ea8061_resume, +}; + +static int ea8061_init(void) +{ + s5p_mipi_dsi_register_lcd_driver(&ea8061_dsim_ddi_driver); + + return 0; +} + +static void ea8061_exit(void) +{ + return; +} + +module_init(ea8061_init); +module_exit(ea8061_exit); + + +MODULE_AUTHOR("Joongmock Shin <jmock.shin@samsung.com>"); +MODULE_AUTHOR("Eunchul Kim <chulspro.kim@samsung.com>"); +MODULE_DESCRIPTION("MIPI-DSI based ea8061 AMOLED Panel Driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/video/backlight/ea8061.h b/drivers/video/backlight/ea8061.h new file mode 100644 index 0000000..d283d06 --- /dev/null +++ b/drivers/video/backlight/ea8061.h @@ -0,0 +1,21 @@ +/* linux/drivers/video/backlight/ea8061.h + * + * MIPI-DSI based ea8061 AMOLED lcd 5.55 inch panel driver. + * + * Copyright (c) 2012 Samsung Electronics + * + * Joongmock Shin <jmock.shin@samsung.com> + * Eunchul Kim <chulspro.kim@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 _EA8061_H +#define _EA8061_H + +/* ToDo */ + +#endif + diff --git a/drivers/video/backlight/ea8061_gamma.h b/drivers/video/backlight/ea8061_gamma.h new file mode 100644 index 0000000..2a9e6a4 --- /dev/null +++ b/drivers/video/backlight/ea8061_gamma.h @@ -0,0 +1,250 @@ +/* linux/drivers/video/backlight/ea8061_gamma.h + * + * Brightness level definition. + * + * Copyright (c) 2012 Samsung Electronics + * + * Joongmock Shin <jmock.shin@samsung.com> + * Eunchul Kim <chulspro.kim@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 _EA8061_GAMMA_H +#define _EA8061_GAMMA_H + +#define MAX_GAMMA_LEVEL 25 +#define GAMMA_TABLE_COUNT 26 + +static const unsigned char ea8061_gamma22_20[] = { + 0xCA, 0x00, 0xE8, 0x00, 0xF7, 0x01, 0x03, 0xDB, 0xDB, + 0xDC, 0xD9, 0xD8, 0xDA, 0xCB, 0xC8, 0xCB, 0xD4, 0xD3, + 0xD7, 0xE6, 0xE6, 0xEA, 0xE2, 0xE4, 0xE5, 0xCE, 0xC3, + 0xCF, 0xB9, 0x9D, 0xDE, 0x11, 0x00 +}; + +static const unsigned char ea8061_gamma22_30[] = { + 0xCA, 0x00, 0xE8, 0x00, 0xF7, 0x01, 0x03, 0xDB, 0xDB, + 0xDC, 0xD9, 0xD8, 0xDA, 0xCB, 0xC8, 0xCB, 0xD4, 0xD3, + 0xD7, 0xE6, 0xE6, 0xEA, 0xE2, 0xE4, 0xE5, 0xCE, 0xC3, + 0xCF, 0xB9, 0x9D, 0xDE, 0x11, 0x00 +}; + +static const unsigned char ea8061_gamma22_40[] = { + 0xCA, 0x00, 0xE8, 0x00, 0xF7, 0x01, 0x03, 0xDB, 0xDB, + 0xDC, 0xD9, 0xD8, 0xDA, 0xCB, 0xC8, 0xCB, 0xD4, 0xD3, + 0xD7, 0xE6, 0xE6, 0xEA, 0xE2, 0xE4, 0xE5, 0xCE, 0xC3, + 0xCF, 0xB9, 0x9D, 0xDE, 0x11, 0x00 +}; + +static const unsigned char ea8061_gamma22_50[] = { + 0xCA, 0x00, 0xE8, 0x00, 0xF7, 0x01, 0x03, 0xDB, 0xDB, + 0xDC, 0xD9, 0xD8, 0xDA, 0xCB, 0xC8, 0xCB, 0xD4, 0xD3, + 0xD7, 0xE6, 0xE6, 0xEA, 0xE2, 0xE4, 0xE5, 0xCE, 0xC3, + 0xCF, 0xB9, 0x9D, 0xDE, 0x11, 0x00 +}; + +static const unsigned char ea8061_gamma22_60[] = { + 0xCA, 0x00, 0xE8, 0x00, 0xF7, 0x01, 0x03, 0xDB, 0xDB, + 0xDC, 0xD9, 0xD8, 0xDA, 0xCB, 0xC8, 0xCB, 0xD4, 0xD3, + 0xD7, 0xE6, 0xE6, 0xEA, 0xE2, 0xE4, 0xE5, 0xCE, 0xC3, + 0xCF, 0xB9, 0x9D, 0xDE, 0x11, 0x00 +}; + +static const unsigned char ea8061_gamma22_70[] = { + 0xFA, 0x01, 0x1F, 0x1F, 0x1F, 0xEA, 0xC9, 0xEA, 0xD6, + 0xD2, 0xD2, 0xDF, 0xE1, 0xE3, 0xC2, 0xC1, 0xC0, 0xD1, + 0xD0, 0xCE, 0x00, 0x84, 0x00, 0x84, 0x00, 0x96 +}; + +static const unsigned char ea8061_gamma22_80[] = { + 0xFA, 0x01, 0x1F, 0x1F, 0x1F, 0xEB, 0xCC, 0xE9, 0xD5, + 0xD4, 0xD3, 0xDE, 0xE1, 0xE2, 0xC2, 0xBF, 0xBF, 0xCF, + 0xCF, 0xCC, 0x00, 0x89, 0x00, 0x89, 0x00, 0x9C +}; + +static const unsigned char ea8061_gamma22_90[] = { + 0xCA, 0x00, 0xE8, 0x00, 0xF7, 0x01, 0x03, 0xDB, 0xDB, + 0xDC, 0xD9, 0xD8, 0xDA, 0xCB, 0xC8, 0xCB, 0xD4, 0xD3, + 0xD7, 0xE6, 0xE6, 0xEA, 0xE2, 0xE4, 0xE5, 0xCE, 0xC3, + 0xCF, 0xB9, 0x9D, 0xDE, 0x11, 0x00 +}; + +static const unsigned char ea8061_gamma22_100[] = { + 0xCA, 0x00, 0xE8, 0x00, 0xF7, 0x01, 0x03, 0xDB, 0xDB, + 0xDC, 0xD9, 0xD8, 0xDA, 0xCB, 0xC8, 0xCB, 0xD4, 0xD3, + 0xD7, 0xE6, 0xE6, 0xEA, 0xE2, 0xE4, 0xE5, 0xCE, 0xC3, + 0xCF, 0xB9, 0x9D, 0xDE, 0x11, 0x00 +}; + +static const unsigned char ea8061_gamma22_110[] = { + 0xCA, 0x00, 0xE8, 0x00, 0xF7, 0x01, 0x03, 0xDB, 0xDB, + 0xDC, 0xD9, 0xD8, 0xDA, 0xCB, 0xC8, 0xCB, 0xD4, 0xD3, + 0xD7, 0xE6, 0xE6, 0xEA, 0xE2, 0xE4, 0xE5, 0xCE, 0xC3, + 0xCF, 0xB9, 0x9D, 0xDE, 0x11, 0x00 +}; + +static const unsigned char ea8061_gamma22_120[] = { + 0xCA, 0x00, 0xE8, 0x00, 0xF7, 0x01, 0x03, 0xDB, 0xDB, + 0xDC, 0xD9, 0xD8, 0xDA, 0xCB, 0xC8, 0xCB, 0xD4, 0xD3, + 0xD7, 0xE6, 0xE6, 0xEA, 0xE2, 0xE4, 0xE5, 0xCE, 0xC3, + 0xCF, 0xB9, 0x9D, 0xDE, 0x11, 0x00 +}; + +static const unsigned char ea8061_gamma22_130[] = { + 0xCA, 0x00, 0xE8, 0x00, 0xF7, 0x01, 0x03, 0xDB, 0xDB, + 0xDC, 0xD9, 0xD8, 0xDA, 0xCB, 0xC8, 0xCB, 0xD4, 0xD3, + 0xD7, 0xE6, 0xE6, 0xEA, 0xE2, 0xE4, 0xE5, 0xCE, 0xC3, + 0xCF, 0xB9, 0x9D, 0xDE, 0x11, 0x00 +}; + +static const unsigned char ea8061_gamma22_140[] = { + 0xCA, 0x00, 0xE8, 0x00, 0xF7, 0x01, 0x03, 0xDB, 0xDB, + 0xDC, 0xD9, 0xD8, 0xDA, 0xCB, 0xC8, 0xCB, 0xD4, 0xD3, + 0xD7, 0xE6, 0xE6, 0xEA, 0xE2, 0xE4, 0xE5, 0xCE, 0xC3, + 0xCF, 0xB9, 0x9D, 0xDE, 0x11, 0x00 +}; + +static const unsigned char ea8061_gamma22_150[] = { + 0xCA, 0x00, 0xE8, 0x00, 0xF7, 0x01, 0x03, 0xDB, 0xDB, + 0xDC, 0xD9, 0xD8, 0xDA, 0xCB, 0xC8, 0xCB, 0xD4, 0xD3, + 0xD7, 0xE6, 0xE6, 0xEA, 0xE2, 0xE4, 0xE5, 0xCE, 0xC3, + 0xCF, 0xB9, 0x9D, 0xDE, 0x11, 0x00 +}; + +static const unsigned char ea8061_gamma22_160[] = { + 0xCA, 0x00, 0xE8, 0x00, 0xF7, 0x01, 0x03, 0xDB, 0xDB, + 0xDC, 0xD9, 0xD8, 0xDA, 0xCB, 0xC8, 0xCB, 0xD4, 0xD3, + 0xD7, 0xE6, 0xE6, 0xEA, 0xE2, 0xE4, 0xE5, 0xCE, 0xC3, + 0xCF, 0xB9, 0x9D, 0xDE, 0x11, 0x00 +}; + +static const unsigned char ea8061_gamma22_170[] = { + 0xCA, 0x00, 0xE8, 0x00, 0xF7, 0x01, 0x03, 0xDB, 0xDB, + 0xDC, 0xD9, 0xD8, 0xDA, 0xCB, 0xC8, 0xCB, 0xD4, 0xD3, + 0xD7, 0xE6, 0xE6, 0xEA, 0xE2, 0xE4, 0xE5, 0xCE, 0xC3, + 0xCF, 0xB9, 0x9D, 0xDE, 0x11, 0x00 +}; + +static const unsigned char ea8061_gamma22_180[] = { + 0xCA, 0x00, 0xE8, 0x00, 0xF7, 0x01, 0x03, 0xDB, 0xDB, + 0xDC, 0xD9, 0xD8, 0xDA, 0xCB, 0xC8, 0xCB, 0xD4, 0xD3, + 0xD7, 0xE6, 0xE6, 0xEA, 0xE2, 0xE4, 0xE5, 0xCE, 0xC3, + 0xCF, 0xB9, 0x9D, 0xDE, 0x11, 0x00 +}; + +static const unsigned char ea8061_gamma22_190[] = { + 0xCA, 0x00, 0xE8, 0x00, 0xF7, 0x01, 0x03, 0xDB, 0xDB, + 0xDC, 0xD9, 0xD8, 0xDA, 0xCB, 0xC8, 0xCB, 0xD4, 0xD3, + 0xD7, 0xE6, 0xE6, 0xEA, 0xE2, 0xE4, 0xE5, 0xCE, 0xC3, + 0xCF, 0xB9, 0x9D, 0xDE, 0x11, 0x00 +}; + +static const unsigned char ea8061_gamma22_200[] = { + 0xCA, 0x00, 0xE8, 0x00, 0xF7, 0x01, 0x03, 0xDB, 0xDB, + 0xDC, 0xD9, 0xD8, 0xDA, 0xCB, 0xC8, 0xCB, 0xD4, 0xD3, + 0xD7, 0xE6, 0xE6, 0xEA, 0xE2, 0xE4, 0xE5, 0xCE, 0xC3, + 0xCF, 0xB9, 0x9D, 0xDE, 0x11, 0x00 +}; + +static const unsigned char ea8061_gamma22_210[] = { + 0xCA, 0x00, 0xE8, 0x00, 0xF7, 0x01, 0x03, 0xDB, 0xDB, + 0xDC, 0xD9, 0xD8, 0xDA, 0xCB, 0xC8, 0xCB, 0xD4, 0xD3, + 0xD7, 0xE6, 0xE6, 0xEA, 0xE2, 0xE4, 0xE5, 0xCE, 0xC3, + 0xCF, 0xB9, 0x9D, 0xDE, 0x11, 0x00 +}; + +static const unsigned char ea8061_gamma22_220[] = { + 0xCA, 0x00, 0xE8, 0x00, 0xF7, 0x01, 0x03, 0xDB, 0xDB, + 0xDC, 0xD9, 0xD8, 0xDA, 0xCB, 0xC8, 0xCB, 0xD4, 0xD3, + 0xD7, 0xE6, 0xE6, 0xEA, 0xE2, 0xE4, 0xE5, 0xCE, 0xC3, + 0xCF, 0xB9, 0x9D, 0xDE, 0x11, 0x00 +}; + +static const unsigned char ea8061_gamma22_230[] = { + 0xCA, 0x00, 0xE8, 0x00, 0xF7, 0x01, 0x03, 0xDB, 0xDB, + 0xDC, 0xD9, 0xD8, 0xDA, 0xCB, 0xC8, 0xCB, 0xD4, 0xD3, + 0xD7, 0xE6, 0xE6, 0xEA, 0xE2, 0xE4, 0xE5, 0xCE, 0xC3, + 0xCF, 0xB9, 0x9D, 0xDE, 0x11, 0x00 +}; + +static const unsigned char ea8061_gamma22_240[] = { + 0xCA, 0x00, 0xE8, 0x00, 0xF7, 0x01, 0x03, 0xDB, 0xDB, + 0xDC, 0xD9, 0xD8, 0xDA, 0xCB, 0xC8, 0xCB, 0xD4, 0xD3, + 0xD7, 0xE6, 0xE6, 0xEA, 0xE2, 0xE4, 0xE5, 0xCE, 0xC3, + 0xCF, 0xB9, 0x9D, 0xDE, 0x11, 0x00 +}; + +static const unsigned char ea8061_gamma22_250[] = { + 0xCA, 0x00, 0xE8, 0x00, 0xF7, 0x01, 0x03, 0xDB, 0xDB, + 0xDC, 0xD9, 0xD8, 0xDA, 0xCB, 0xC8, 0xCB, 0xD4, 0xD3, + 0xD7, 0xE6, 0xE6, 0xEA, 0xE2, 0xE4, 0xE5, 0xCE, 0xC3, + 0xCF, 0xB9, 0x9D, 0xDE, 0x11, 0x00 +}; + +static const unsigned char ea8061_gamma22_260[] = { + 0xCA, 0x00, 0xE8, 0x00, 0xF7, 0x01, 0x03, 0xDB, 0xDB, + 0xDC, 0xD9, 0xD8, 0xDA, 0xCB, 0xC8, 0xCB, 0xD4, 0xD3, + 0xD7, 0xE6, 0xE6, 0xEA, 0xE2, 0xE4, 0xE5, 0xCE, 0xC3, + 0xCF, 0xB9, 0x9D, 0xDE, 0x11, 0x00 +}; + +static const unsigned char ea8061_gamma22_270[] = { + 0xCA, 0x00, 0xE8, 0x00, 0xF7, 0x01, 0x03, 0xDB, 0xDB, + 0xDC, 0xD9, 0xD8, 0xDA, 0xCB, 0xC8, 0xCB, 0xD4, 0xD3, + 0xD7, 0xE6, 0xE6, 0xEA, 0xE2, 0xE4, 0xE5, 0xCE, 0xC3, + 0xCF, 0xB9, 0x9D, 0xDE, 0x11, 0x00 +}; + +static const unsigned char ea8061_gamma22_280[] = { + 0xCA, 0x00, 0xE8, 0x00, 0xF7, 0x01, 0x03, 0xDB, 0xDB, + 0xDC, 0xD9, 0xD8, 0xDA, 0xCB, 0xC8, 0xCB, 0xD4, 0xD3, + 0xD7, 0xE6, 0xE6, 0xEA, 0xE2, 0xE4, 0xE5, 0xCE, 0xC3, + 0xCF, 0xB9, 0x9D, 0xDE, 0x11, 0x00 +}; + +static const unsigned char ea8061_gamma22_290[] = { + 0xCA, 0x00, 0xE8, 0x00, 0xF7, 0x01, 0x03, 0xDB, 0xDB, + 0xDC, 0xD9, 0xD8, 0xDA, 0xCB, 0xC8, 0xCB, 0xD4, 0xD3, + 0xD7, 0xE6, 0xE6, 0xEA, 0xE2, 0xE4, 0xE5, 0xCE, 0xC3, + 0xCF, 0xB9, 0x9D, 0xDE, 0x11, 0x00 +}; + +static const unsigned char ea8061_gamma22_300[] = { + 0xCA, 0x00, 0xE8, 0x00, 0xF7, 0x01, 0x03, 0xDB, 0xDB, + 0xDC, 0xD9, 0xD8, 0xDA, 0xCB, 0xC8, 0xCB, 0xD4, 0xD3, + 0xD7, 0xE6, 0xE6, 0xEA, 0xE2, 0xE4, 0xE5, 0xCE, 0xC3, + 0xCF, 0xB9, 0x9D, 0xDE, 0x11, 0x00 +}; + +static const unsigned char *ea8061_gamma22_table[MAX_GAMMA_LEVEL] = { + ea8061_gamma22_30, + ea8061_gamma22_50, + ea8061_gamma22_60, + ea8061_gamma22_70, + ea8061_gamma22_80, + ea8061_gamma22_90, + ea8061_gamma22_100, + ea8061_gamma22_110, + ea8061_gamma22_120, + ea8061_gamma22_130, + ea8061_gamma22_140, + ea8061_gamma22_150, + ea8061_gamma22_160, + ea8061_gamma22_170, + ea8061_gamma22_180, + ea8061_gamma22_190, + ea8061_gamma22_200, + ea8061_gamma22_210, + ea8061_gamma22_220, + ea8061_gamma22_230, + ea8061_gamma22_240, + ea8061_gamma22_270, + ea8061_gamma22_280, + ea8061_gamma22_290, + ea8061_gamma22_300 +}; + +#endif /* _EA8061_GAMMA_H */ diff --git a/drivers/video/backlight/s6d6aa1.c b/drivers/video/backlight/s6d6aa1.c new file mode 100644 index 0000000..15a0b63 --- /dev/null +++ b/drivers/video/backlight/s6d6aa1.c @@ -0,0 +1,976 @@ +/* linux/drivers/video/backlight/s6d6aa1.c + * + * MIPI-DSI based s6d6aa1 TFT-LCD 4.77 inch panel driver. + * + * Joongmock Shin <jmock.shin@samsung.com> + * Eunchul Kim <chulspro.kim@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/lcd-property.h> +#include <linux/fb.h> +#include <linux/backlight.h> +#include <linux/regulator/consumer.h> +#include <linux/firmware.h> +#include <video/mipi_display.h> + +#if defined(CONFIG_ARM_EXYNOS4_DISPLAY_DEVFREQ) || defined(CONFIG_DISPFREQ_OPP) +#include <linux/devfreq/exynos4_display.h> +#endif + +#include <plat/mipi_dsim2.h> +#include "s6d6aa1.h" + +#define VER_16 (0x10) /* MACH_SLP_REDWOORD */ +#define LDI_FW_PATH "s6d6aa1/reg_%s.bin" +#define MAX_STR 255 +#define MAX_READ_LENGTH 64 +#define MIN_BRIGHTNESS (0) +#define MAX_BRIGHTNESS (0xff) +#define DSCTL_VFLIP (1 << 7) +#define DSCTL_HFLIP (1 << 6) + +/* + * FIXME:!!simple init vs full init + * If lcd don't working simple sequence, + * we use full initialization sequence + */ +#define SIMPLE_INIT + +#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) + +/* white magic mode */ +enum wm_mode { + WM_MODE_MIN = 0x00, + WM_MODE_NORMAL = WM_MODE_MIN, + WM_MODE_CONSERVATIVE, + WM_MODE_MEDIUM, + WM_MODE_AGGRESSIVE, + WM_MODE_OUTDOOR, + WM_MODE_MAX = WM_MODE_OUTDOOR +}; + +struct panel_model { + int ver; + char *name; +}; + +struct s6d6aa1 { + struct device *dev; + struct lcd_device *ld; + struct backlight_device *bd; + struct mipi_dsim_lcd_device *dsim_dev; + struct lcd_platform_data *ddi_pd; + struct lcd_property *property; + + struct regulator *reg_vddi; + struct regulator *reg_vdd; + +#if defined(CONFIG_ARM_EXYNOS4_DISPLAY_DEVFREQ) || defined(CONFIG_DISPFREQ_OPP) + struct notifier_block nb_disp; +#endif + struct mutex lock; + + unsigned int ver; + unsigned int power; + enum wm_mode wm_mode; + unsigned int cur_addr; + + const struct panel_model *model; + unsigned int model_count; +}; + +static void s6d6aa1_delay(unsigned int msecs) +{ + /* refer from documentation/timers/timers-howto.txt */ + if (msecs < 20) + usleep_range(msecs*1000, (msecs+1)*1000); + else + msleep(msecs); +} + +static void s6d6aa1_sleep_in(struct s6d6aa1 *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 s6d6aa1_sleep_out(struct s6d6aa1 *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 s6d6aa1_apply_level_1_key(struct s6d6aa1 *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 void s6d6aa1_apply_level_2_key(struct s6d6aa1 *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 s6d6aa1_read_id(struct s6d6aa1 *lcd, u8 *mtp_id) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + + ops->cmd_read(lcd_to_master(lcd), + MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM, + 0xDA, 1, &mtp_id[0]); + ops->cmd_read(lcd_to_master(lcd), + MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM, + 0xDB, 1, &mtp_id[1]); + ops->cmd_read(lcd_to_master(lcd), + MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM, + 0xDC, 1, &mtp_id[2]); +} + +static void s6d6aa1_write_ddb(struct s6d6aa1 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + const unsigned char data_to_send[] = { + 0xB4, 0x59, 0x10, 0x10, 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 s6d6aa1_bcm_mode(struct s6d6aa1 *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, 0xC1, 0x03); +} + +static void s6d6aa1_wrbl_ctl(struct s6d6aa1 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + const unsigned char data_to_send[] = { + 0xC3, 0x7C, 0x00, 0x22 + }; + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + (unsigned int)data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6d6aa1_sony_ip_setting(struct s6d6aa1 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + const unsigned char data_to_send1[] = { + 0xC4, 0x72, 0xFF, 0x72, 0xFF, 0x72, 0xFF, 0x72, 0x72, + 0x05, 0x0F, 0x1F, 0x01, 0x00, 0x00 + }; + const unsigned char data_to_send2[] = { + 0xC5, 0x80, 0x80, 0x80, 0x60, 0x4E, 0x36, 0x83, 0x85, + 0x01, 0xFF, 0x20, 0x40, 0x50 + }; + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + (unsigned int)data_to_send1, ARRAY_SIZE(data_to_send1)); + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + (unsigned int)data_to_send2, ARRAY_SIZE(data_to_send2)); +} + +/* + * FIXME:!!simple init vs full init + * If lcd don't working simple sequence, + * we use full initialization sequence + */ +#ifdef SIMPLE_INIT +static void s6d6aa1_disp_ctl(struct s6d6aa1 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + struct lcd_property *property = lcd->property; + unsigned char cfg = 0; + + if (property) { + if (property->flip & LCD_PROPERTY_FLIP_VERTICAL) + cfg |= DSCTL_VFLIP; + + if (property->flip & LCD_PROPERTY_FLIP_HORIZONTAL) + cfg |= DSCTL_HFLIP; + } + + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0x36, cfg); +} +#else +static void s6d6aa1_disp_ctl(struct s6d6aa1 *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, 0xEF, 0x02); +} +#endif + +static void s6d6aa1_source_ctl(struct s6d6aa1 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + const unsigned char data_to_send[] = { + 0xF2, 0x03, 0x03, 0x91, 0x85 + }; + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + (unsigned int)data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6d6aa1_pwr_ctl(struct s6d6aa1 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + const unsigned char data_to_send[] = { + 0xF4, 0x04, 0x0B, 0x07, 0x07, 0x10, 0x14, 0x0D, 0x0C, + 0xAD, 0x00, 0x33, 0x33 + }; + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + (unsigned int)data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6d6aa1_panel_ctl(struct s6d6aa1 *lcd, int high_freq) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + const unsigned char data_to_send[] = { + 0xF6, 0x0B, 0x11, 0x0F, 0x25, 0x0A, 0x00, 0x13, 0x22, + 0x1B, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x03, + 0x12, 0x32, 0x51 + }; + + /* ToDo : Low requency control */ + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + (unsigned int)data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6d6aa1_mount_ctl(struct s6d6aa1 *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, 0xF7, 0x00); +} + +static int s6d6aa1_gamma_ctrl(struct s6d6aa1 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + const unsigned char data_to_send1[] = { + 0xFA, 0x1C, 0x3F, 0x20, 0xDF, 0xEB, 0xEB, 0xE3, 0x61, + 0x61, 0xDB, 0x94, 0x15, 0xD9, 0xD8, 0xDB, 0xE1, 0xE4, + 0xE6, 0xE3, 0xA2, 0x49, 0xDC, 0x7F, 0x5E, 0xDD, 0x6B, + 0x6A, 0xA3, 0xE1, 0xE2, 0x9B, 0x55, 0xD6, 0x99, 0x59, + 0x9C, 0xA1, 0xA4, 0x64, 0xA0, 0x9F, 0x06, 0x80, 0xBF, + 0xB0, 0xB1, 0xFB, 0xFA, 0xF4, 0x31, 0x72, 0xEC, 0x25, + 0xA5, 0xA7, 0xE9, 0x6B, 0xAE, 0xB1, 0xB4, 0x35, 0xF8, + 0x9F + }; + + const unsigned char data_to_send2[] = { + 0xFB, 0x1C, 0x3F, 0x20, 0xDF, 0xEB, 0xEB, 0xE3, 0x61, + 0x61, 0xDB, 0x94, 0x15, 0xD9, 0xD8, 0xDB, 0xE1, 0xE4, + 0xE6, 0xE3, 0xA2, 0x49, 0xDC, 0x7F, 0x5E, 0xDD, 0x6B, + 0x6A, 0xA3, 0xE1, 0xE2, 0x9B, 0x55, 0xD6, 0x99, 0x59, + 0x9C, 0xA1, 0xA4, 0x64, 0xA0, 0x9F, 0x06, 0x80, 0xBF, + 0xB0, 0xB1, 0xFB, 0xFA, 0xF4, 0x31, 0x72, 0xEC, 0x25, + 0xA5, 0xA7, 0xE9, 0x6B, 0xAE, 0xB1, 0xB4, 0x35, 0xF8, + 0x9F + }; + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + (unsigned int)data_to_send1, ARRAY_SIZE(data_to_send1)); + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + (unsigned int)data_to_send2, ARRAY_SIZE(data_to_send2)); + + return 0; +} + +static int s6d6aa1_panel_init(struct s6d6aa1 *lcd) +{ + s6d6aa1_sleep_out(lcd); + s6d6aa1_delay(140); +/* + * FIXME:!!simple init vs full init + * If lcd don't working simple sequence, + * we use full initialization sequence + */ +#ifdef SIMPLE_INIT + s6d6aa1_disp_ctl(lcd); +#else + s6d6aa1_apply_level_1_key(lcd); + s6d6aa1_apply_level_2_key(lcd); + + s6d6aa1_write_ddb(lcd); + s6d6aa1_bcm_mode(lcd); + s6d6aa1_wrbl_ctl(lcd); + s6d6aa1_sony_ip_setting(lcd); + s6d6aa1_disp_ctl(lcd); + s6d6aa1_source_ctl(lcd); + s6d6aa1_pwr_ctl(lcd); + s6d6aa1_panel_ctl(lcd, 1); + s6d6aa1_mount_ctl(lcd); + + s6d6aa1_gamma_ctrl(lcd); + + /* wait more than 10ms */ + s6d6aa1_delay(lcd->ddi_pd->power_on_delay); +#endif + + return 0; +} + +static void s6d6aa1_write_disbv(struct s6d6aa1 *lcd, + unsigned int brightness) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0x51, brightness); +} + +static void s6d6aa1_write_ctrld(struct s6d6aa1 *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, 0x53, 0x2C); +} + +static void s6d6aa1_write_cabc(struct s6d6aa1 *lcd, + enum wm_mode wm_mode) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0x55, wm_mode); +} + +static void s6d6aa1_display_on(struct s6d6aa1 *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 s6d6aa1_display_off(struct s6d6aa1 *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 int s6d6aa1_early_set_power(struct lcd_device *ld, int power) +{ + struct s6d6aa1 *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 s6d6aa1_set_power(struct lcd_device *ld, int power) +{ + struct s6d6aa1 *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 s6d6aa1_get_power(struct lcd_device *ld) +{ + struct s6d6aa1 *lcd = lcd_get_data(ld); + + return lcd->power; +} + +static struct lcd_ops s6d6aa1_lcd_ops = { + .early_set_power = s6d6aa1_early_set_power, + .set_power = s6d6aa1_set_power, + .get_power = s6d6aa1_get_power, +}; + +static int s6d6aa1_get_brightness(struct backlight_device *bd) +{ + return bd->props.brightness; +} + +static int s6d6aa1_set_brightness(struct backlight_device *bd) +{ + int ret = 0, brightness = bd->props.brightness; + struct s6d6aa1 *lcd = bl_get_data(bd); + + if (lcd->power == FB_BLANK_POWERDOWN) { + 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; + } + + s6d6aa1_write_disbv(lcd, brightness); + return ret; +} + +static const struct backlight_ops s6d6aa1_backlight_ops = { + .get_brightness = s6d6aa1_get_brightness, + .update_status = s6d6aa1_set_brightness, +}; + +static ssize_t wm_mode_show(struct device *dev, struct + device_attribute * attr, char *buf) +{ + struct s6d6aa1 *lcd = dev_get_drvdata(dev); + char temp[3]; + + sprintf(temp, "%d\n", lcd->wm_mode); + strcpy(buf, temp); + + return strlen(buf); +} + +static ssize_t wm_mode_store(struct device *dev, struct + device_attribute * attr, const char *buf, size_t size) +{ + struct s6d6aa1 *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 (value < WM_MODE_MIN || value > WM_MODE_MAX) { + dev_info(dev, "failed to set white magic mode to %d\n", + value); + return -EINVAL; + } + + if (lcd->wm_mode != value) { + dev_info(dev, "wm mode changed from %d to %d\n", + lcd->wm_mode, value); + lcd->wm_mode = value; + s6d6aa1_write_cabc(lcd, lcd->wm_mode); + } + return size; +} + +static ssize_t lcd_type_show(struct device *dev, struct + device_attribute * attr, char *buf) +{ + struct s6d6aa1 *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 s6d6aa1_read_reg(struct s6d6aa1 *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 s6d6aa1_write_reg(struct s6d6aa1 *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 == 1) + ret = ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE, + (unsigned int)fw->data[0], 0); + else if (fw->size == 2) + ret = ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE_PARAM, + (unsigned int)fw->data[0], fw->data[1]); + 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 s6d6aa1 *lcd = dev_get_drvdata(dev); + + if (lcd->cur_addr == 0) { + dev_err(dev, "failed to set current lcd register.\n"); + return -EINVAL; + } + + return s6d6aa1_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 s6d6aa1 *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 s6d6aa1 *lcd = dev_get_drvdata(dev); + char name[32]; + int ret; + + ret = sscanf(buf, "%s", name); + if (ret < 0) + return ret; + + ret = s6d6aa1_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(wm_mode, S_IRUGO|S_IWUSR|S_IWGRP, + wm_mode_show, wm_mode_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 s6d6aa1_model[] = { + { + .ver = VER_16, /* MACH_SLP_REDWOORD */ + .name = "SMD_ACX445AKM", + } +}; + +#if defined(CONFIG_ARM_EXYNOS4_DISPLAY_DEVFREQ) || defined(CONFIG_DISPFREQ_OPP) +static int s6d6aa1_notifier_callback(struct notifier_block *this, + unsigned long event, void *_data) +{ + struct s6d6aa1 *lcd = container_of(this, struct s6d6aa1, nb_disp); + + if (lcd->power == FB_BLANK_POWERDOWN) + return NOTIFY_DONE; + + switch (event) { + case EXYNOS4_DISPLAY_LV_HF: + s6d6aa1_panel_ctl(lcd, 1); + break; + case EXYNOS4_DISPLAY_LV_LF: + s6d6aa1_panel_ctl(lcd, 0); + break; + default: + return NOTIFY_BAD; + } + + return NOTIFY_DONE; +} +#endif + +static void s6d6aa1_regulator_ctl(struct s6d6aa1 *lcd, bool enable) +{ + mutex_lock(&lcd->lock); + + if (enable) { + if (lcd->reg_vddi) + regulator_enable(lcd->reg_vddi); + + if (lcd->reg_vdd) + regulator_enable(lcd->reg_vdd); + } else { + if (lcd->reg_vdd) + regulator_disable(lcd->reg_vdd); + + if (lcd->reg_vddi) + regulator_disable(lcd->reg_vddi); + } + + mutex_unlock(&lcd->lock); +} + +static void s6d6aa1_power_on(struct mipi_dsim_lcd_device *dsim_dev, + unsigned int enable) +{ + struct s6d6aa1 *lcd = dev_get_drvdata(&dsim_dev->dev); + + dev_dbg(lcd->dev, "%s:enable[%d]\n", __func__, enable); + + if (enable) { + /* lcd power on */ + s6d6aa1_regulator_ctl(lcd, true); + + s6d6aa1_delay(lcd->ddi_pd->reset_delay); + + /* lcd reset high */ + if (lcd->ddi_pd->reset) + lcd->ddi_pd->reset(lcd->ld); + + /* wait more than 10ms */ + s6d6aa1_delay(10); + } else { + /* lcd reset low */ + if (lcd->ddi_pd->reset) + lcd->ddi_pd->reset(lcd->ld); + + /* lcd power off */ + s6d6aa1_regulator_ctl(lcd, false); + } +} + +static int s6d6aa1_check_mtp(struct mipi_dsim_lcd_device *dsim_dev) +{ + struct s6d6aa1 *lcd = dev_get_drvdata(&dsim_dev->dev); + u8 mtp_id[3] = {0, }; + + s6d6aa1_read_id(lcd, mtp_id); + if (mtp_id[0] == 0x00) { + dev_err(lcd->dev, "read id failed\n"); + return -EIO; + } + + lcd->ver = mtp_id[1]; + dev_info(lcd->dev, + "Read ID : %x, %x, %x\n", mtp_id[0], mtp_id[1], mtp_id[2]); + + return 0; +} + +static void s6d6aa1_set_sequence(struct mipi_dsim_lcd_device *dsim_dev) +{ + struct s6d6aa1 *lcd = dev_get_drvdata(&dsim_dev->dev); + struct backlight_device *bd = lcd->bd; + + s6d6aa1_panel_init(lcd); + + s6d6aa1_write_disbv(lcd, bd->props.brightness); + s6d6aa1_write_ctrld(lcd); + s6d6aa1_write_cabc(lcd, lcd->wm_mode); + s6d6aa1_display_on(lcd); +} + +static int s6d6aa1_probe(struct mipi_dsim_lcd_device *dsim_dev) +{ + struct s6d6aa1 *lcd; + int ret; + int i; + + lcd = kzalloc(sizeof(struct s6d6aa1), GFP_KERNEL); + if (!lcd) { + dev_err(&dsim_dev->dev, "failed to allocate s6d6aa1 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_vddi = regulator_get(lcd->dev, "VDDI"); + if (IS_ERR(lcd->reg_vddi)) { + ret = PTR_ERR(lcd->reg_vddi); + dev_err(lcd->dev, "failed to get %s regulator (%d)\n", + "VDDI", ret); + lcd->reg_vddi = NULL; + } + + lcd->reg_vdd = regulator_get(lcd->dev, "VDD"); + if (IS_ERR(lcd->reg_vdd)) { + ret = PTR_ERR(lcd->reg_vdd); + dev_err(lcd->dev, "failed to get %s regulator (%d)\n", + "VDD", ret); + lcd->reg_vdd = NULL; + } + + lcd->ld = lcd_device_register("s6d6aa1", lcd->dev, lcd, + &s6d6aa1_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("s6d6aa1-bl", lcd->dev, lcd, + &s6d6aa1_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; + } + + s6d6aa1_regulator_ctl(lcd, true); + + if (lcd->ddi_pd) + lcd->property = lcd->ddi_pd->pdata; + +#if defined(CONFIG_ARM_EXYNOS4_DISPLAY_DEVFREQ) || defined(CONFIG_DISPFREQ_OPP) + if (lcd->property && lcd->property->dynamic_refresh) { + lcd->nb_disp.notifier_call = s6d6aa1_notifier_callback; + ret = exynos4_display_register_client(&lcd->nb_disp); + if (ret < 0) + dev_warn(&lcd->ld->dev, "failed to register exynos-display notifier\n"); + } +#endif + + lcd->bd->props.max_brightness = MAX_BRIGHTNESS; + lcd->bd->props.brightness = MAX_BRIGHTNESS; + lcd->power = FB_BLANK_UNBLANK; + lcd->wm_mode = WM_MODE_CONSERVATIVE; + lcd->model = s6d6aa1_model; + lcd->model_count = ARRAY_SIZE(s6d6aa1_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); + dev_info(lcd->dev, "probed s6d6aa1 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_vdd); + regulator_put(lcd->reg_vddi); + + kfree(lcd); + + return ret; +} + +static void s6d6aa1_remove(struct mipi_dsim_lcd_device *dsim_dev) +{ + struct s6d6aa1 *lcd = dev_get_drvdata(&dsim_dev->dev); + + backlight_device_unregister(lcd->bd); + lcd_device_unregister(lcd->ld); + + regulator_put(lcd->reg_vdd); + regulator_put(lcd->reg_vddi); + +#if defined(CONFIG_ARM_EXYNOS4_DISPLAY_DEVFREQ) || defined(CONFIG_DISPFREQ_OPP) + if (lcd->property && lcd->property->dynamic_refresh) + exynos4_display_unregister_client(&lcd->nb_disp); +#endif + kfree(lcd); +} + +#ifdef CONFIG_PM +static int s6d6aa1_suspend(struct mipi_dsim_lcd_device *dsim_dev) +{ + struct s6d6aa1 *lcd = dev_get_drvdata(&dsim_dev->dev); + + s6d6aa1_display_off(lcd); + s6d6aa1_sleep_in(lcd); + s6d6aa1_delay(lcd->ddi_pd->power_off_delay); + + return 0; +} + +static int s6d6aa1_resume(struct mipi_dsim_lcd_device *dsim_dev) +{ + struct s6d6aa1 *lcd = dev_get_drvdata(&dsim_dev->dev); + + s6d6aa1_sleep_out(lcd); + s6d6aa1_delay(lcd->ddi_pd->power_on_delay); + + return 0; +} +#else +#define s6d6aa1_suspend NULL +#define s6d6aa1_resume NULL +#endif + +static struct mipi_dsim_lcd_driver s6d6aa1_dsim_ddi_driver = { + .name = "s6d6aa1", + .id = -1, + + .power_on = s6d6aa1_power_on, + .check_mtp = s6d6aa1_check_mtp, + .set_sequence = s6d6aa1_set_sequence, + .probe = s6d6aa1_probe, + .remove = s6d6aa1_remove, + .suspend = s6d6aa1_suspend, + .resume = s6d6aa1_resume, +}; + +static int s6d6aa1_init(void) +{ + s5p_mipi_dsi_register_lcd_driver(&s6d6aa1_dsim_ddi_driver); + + return 0; +} + +static void s6d6aa1_exit(void) +{ + return; +} + +module_init(s6d6aa1_init); +module_exit(s6d6aa1_exit); + + +MODULE_AUTHOR("Joongmock Shin <jmock.shin@samsung.com>"); +MODULE_AUTHOR("Eunchul Kim <chulspro.kim@samsung.com>"); +MODULE_DESCRIPTION("MIPI-DSI based s6d6aa1 TFT-LCD Panel Driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/video/backlight/s6d6aa1.h b/drivers/video/backlight/s6d6aa1.h new file mode 100644 index 0000000..aaf8446a --- /dev/null +++ b/drivers/video/backlight/s6d6aa1.h @@ -0,0 +1,21 @@ +/* linux/drivers/video/backlight/s6d6aa1.h + * + * MIPI-DSI based s6d6aa1 TFT-LCD 4.77 inch panel driver. + * + * Copyright (c) 2012 Samsung Electronics + * + * Joongmock Shin <jmock.shin@samsung.com> + * Eunchul Kim <chulspro.kim@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 _S6D6AA1_H +#define _S6D6AA1_H + +/* ToDo */ + +#endif + diff --git a/drivers/video/backlight/s6e8aa0.c b/drivers/video/backlight/s6e8aa0.c index c9b1184..2cc5466 100644 --- a/drivers/video/backlight/s6e8aa0.c +++ b/drivers/video/backlight/s6e8aa0.c @@ -4,6 +4,8 @@ * * Inki Dae, <inki.dae@samsung.com> * Donghwa Lee, <dh09.lee@samsung.com> + * Joongmock Shin <jmock.shin@samsung.com> + * Eunchul Kim <chulspro.kim@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 @@ -21,6 +23,7 @@ #include <linux/irq.h> #include <linux/interrupt.h> #include <linux/lcd.h> +#include <linux/lcd-property.h> #include <linux/fb.h> #include <linux/backlight.h> #include <linux/regulator/consumer.h> @@ -28,8 +31,13 @@ #include <video/mipi_display.h> +#include <plat/cpu.h> #include <plat/mipi_dsim2.h> +#if defined(CONFIG_ARM_EXYNOS4_DISPLAY_DEVFREQ) || defined(CONFIG_DISPFREQ_OPP) +#include <linux/devfreq/exynos4_display.h> +#endif + #include "s6e8aa0_gamma.h" #ifdef CONFIG_BACKLIGHT_SMART_DIMMING #include "smart_dimming.h" @@ -43,10 +51,62 @@ #define MIN_BRIGHTNESS (0) #define MAX_BRIGHTNESS (24) +/* 1 */ +#define PANELCTL_SS_MASK (1 << 5) +#define PANELCTL_SS_1_800 (0 << 5) +#define PANELCTL_SS_800_1 (1 << 5) +#define PANELCTL_GTCON_MASK (7 << 2) +#define PANELCTL_GTCON_110 (6 << 2) +#define PANELCTL_GTCON_111 (7 << 2) +/* LTPS */ +/* 30 */ +#define PANELCTL_CLK1_CON_MASK (7 << 3) +#define PANELCTL_CLK1_000 (0 << 3) +#define PANELCTL_CLK1_001 (1 << 3) +#define PANELCTL_CLK2_CON_MASK (7 << 0) +#define PANELCTL_CLK2_000 (0 << 0) +#define PANELCTL_CLK2_001 (1 << 0) +/* 31 */ +#define PANELCTL_INT1_CON_MASK (7 << 3) +#define PANELCTL_INT1_000 (0 << 3) +#define PANELCTL_INT1_001 (1 << 3) +#define PANELCTL_INT2_CON_MASK (7 << 0) +#define PANELCTL_INT2_000 (0 << 0) +#define PANELCTL_INT2_001 (1 << 0) +/* 32 */ +#define PANELCTL_BICTL_CON_MASK (7 << 3) +#define PANELCTL_BICTL_000 (0 << 3) +#define PANELCTL_BICTL_001 (1 << 3) +#define PANELCTL_BICTLB_CON_MASK (7 << 0) +#define PANELCTL_BICTLB_000 (0 << 0) +#define PANELCTL_BICTLB_001 (1 << 0) +/* 36 */ +#define PANELCTL_EM_CLK1_CON_MASK (7 << 3) +#define PANELCTL_EM_CLK1_110 (6 << 3) +#define PANELCTL_EM_CLK1_111 (7 << 3) +#define PANELCTL_EM_CLK1B_CON_MASK (7 << 0) +#define PANELCTL_EM_CLK1B_110 (6 << 0) +#define PANELCTL_EM_CLK1B_111 (7 << 0) +/* 37 */ +#define PANELCTL_EM_CLK2_CON_MASK (7 << 3) +#define PANELCTL_EM_CLK2_110 (6 << 3) +#define PANELCTL_EM_CLK2_111 (7 << 3) +#define PANELCTL_EM_CLK2B_CON_MASK (7 << 0) +#define PANELCTL_EM_CLK2B_110 (6 << 0) +#define PANELCTL_EM_CLK2B_111 (7 << 0) +/* 38 */ +#define PANELCTL_EM_INT1_CON_MASK (7 << 3) +#define PANELCTL_EM_INT1_000 (0 << 3) +#define PANELCTL_EM_INT1_001 (1 << 3) +#define PANELCTL_EM_INT2_CON_MASK (7 << 0) +#define PANELCTL_EM_INT2_000 (0 << 0) +#define PANELCTL_EM_INT2_001 (1 << 0) + #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_96 (0x60) /* MACH_SLP_PQ Galaxy S3 */ #define VER_210 (0xd2) /* MACH_SLP_PQ M0 A-Type */ #define AID_DISABLE (0x4) @@ -61,87 +121,64 @@ #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 lcd_device *ld; + struct backlight_device *bd; struct mipi_dsim_lcd_device *dsim_dev; struct lcd_platform_data *ddi_pd; + struct lcd_property *property; - struct mutex lock; - struct regulator *reg_vdd3; - struct regulator *reg_vci; + struct regulator *reg_vdd3; + struct regulator *reg_vci; + +#if defined(CONFIG_ARM_EXYNOS4_DISPLAY_DEVFREQ) || defined(CONFIG_DISPFREQ_OPP) + struct notifier_block nb_disp; +#endif + struct mutex lock; + + unsigned int id; + unsigned int aid; + unsigned int ver; + unsigned int power; + unsigned int acl_enable; + unsigned int cur_acl; + unsigned int cur_addr; const struct panel_model *model; - unsigned int model_count; + 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; + unsigned int support_elvss; + struct str_smart_dim smart_dim; #endif }; -struct s6e8aa0 *lcd_global; - -static void s6e8aa0_regulator_enable(struct s6e8aa0 *lcd) +static void s6e8aa0_regulator_ctl(struct s6e8aa0 *lcd, bool enable) { + dev_dbg(lcd->dev, "%s:enable[%d]\n", __func__, enable); + mutex_lock(&lcd->lock); - if (!lcd->enabled) { + + if (enable) { 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) { + } else { 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); } @@ -178,9 +215,8 @@ static unsigned char s6e8aa0_apply_aid_panel_cond(unsigned int aid) return ret; } -void s6e8aa0_panel_cond(int high_freq) +static void s6e8aa0_panel_cond(struct s6e8aa0 *lcd, 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, @@ -204,6 +240,13 @@ void s6e8aa0_panel_cond(int high_freq) 0x23, 0x6e, 0xc0, 0xc1, 0x01, 0x81, 0xc1, 0x00, 0xc3, 0xf6, 0xf6, 0xc1 }; + unsigned char data_to_send_v96[] = { + 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, 0x37, 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, @@ -213,17 +256,92 @@ void s6e8aa0_panel_cond(int high_freq) }; unsigned char *data_to_send; unsigned int size; + struct lcd_property *property = lcd->property; + unsigned char cfg; 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) { + } else if (lcd->ver == VER_142) { + data_to_send_v142[18] = s6e8aa0_apply_aid_panel_cond(lcd->aid); + data_to_send = data_to_send_v142; + if (property) { + if (property->flip & LCD_PROPERTY_FLIP_VERTICAL) { + /* GTCON */ + cfg = data_to_send[1]; + cfg &= ~(PANELCTL_GTCON_MASK); + cfg |= (PANELCTL_GTCON_110); + data_to_send[1] = cfg; + } + + if (property->flip & LCD_PROPERTY_FLIP_HORIZONTAL) { + /* SS */ + cfg = data_to_send[1]; + cfg &= ~(PANELCTL_SS_MASK); + cfg |= (PANELCTL_SS_1_800); + data_to_send[1] = cfg; + } + + if (property->flip & (LCD_PROPERTY_FLIP_VERTICAL | + LCD_PROPERTY_FLIP_HORIZONTAL)) { + /* CLK1,2_CON */ + cfg = data_to_send[30]; + cfg &= ~(PANELCTL_CLK1_CON_MASK | + PANELCTL_CLK2_CON_MASK); + cfg |= (PANELCTL_CLK1_000 | PANELCTL_CLK2_001); + data_to_send[30] = cfg; + + /* INT1,2_CON */ + cfg = data_to_send[31]; + cfg &= ~(PANELCTL_INT1_CON_MASK | + PANELCTL_INT2_CON_MASK); + cfg |= (PANELCTL_INT1_000 | PANELCTL_INT2_001); + data_to_send[31] = cfg; + + /* BICTL,B_CON */ + cfg = data_to_send[32]; + cfg &= ~(PANELCTL_BICTL_CON_MASK | + PANELCTL_BICTLB_CON_MASK); + cfg |= (PANELCTL_BICTL_000 | + PANELCTL_BICTLB_001); + data_to_send[32] = cfg; + + /* EM_CLK1,1B_CON */ + cfg = data_to_send[36]; + cfg &= ~(PANELCTL_EM_CLK1_CON_MASK | + PANELCTL_EM_CLK1B_CON_MASK); + cfg |= (PANELCTL_EM_CLK1_110 | + PANELCTL_EM_CLK1B_110); + data_to_send[36] = cfg; + + /* EM_CLK2,2B_CON */ + cfg = data_to_send[37]; + cfg &= ~(PANELCTL_EM_CLK2_CON_MASK | + PANELCTL_EM_CLK2B_CON_MASK); + cfg |= (PANELCTL_EM_CLK2_110 | + PANELCTL_EM_CLK2B_110); + data_to_send[37] = cfg; + + /* EM_INT1,2_CON */ + cfg = data_to_send[38]; + cfg &= ~(PANELCTL_EM_INT1_CON_MASK | + PANELCTL_EM_INT2_CON_MASK); + cfg |= (PANELCTL_EM_INT1_000 | + PANELCTL_EM_INT2_001); + data_to_send[38] = cfg; + } + } + size = ARRAY_SIZE(data_to_send_v142); + } else if (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_96) { + data_to_send = data_to_send_v96; + size = ARRAY_SIZE(data_to_send_v96); } else if (lcd->ver == VER_210) { data_to_send = data_to_send_v210; size = ARRAY_SIZE(data_to_send_v210); @@ -270,7 +388,7 @@ static void s6e8aa0_etc_pentile_control(struct s6e8aa0 *lcd) 0x00 }; - if (lcd->ver == VER_32) + if (lcd->ver == VER_32 || lcd->ver == VER_96) data_to_send[5] = 0xc0; ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, @@ -306,7 +424,7 @@ static void s6e8aa0_etc_power_control(struct s6e8aa0 *lcd) 0xf4, 0xcf, 0x0a, 0x12, 0x10, 0x1e, 0x33, 0x02 }; - if (lcd->ver == VER_32) { + if (lcd->ver == VER_32 || lcd->ver == VER_96) { data_to_send[3] = 0x15; data_to_send[5] = 0x19; } @@ -349,6 +467,8 @@ static void s6e8aa0_etc_elvss_control(struct s6e8aa0 *lcd) static void s6e8aa0_elvss_nvm_set(struct s6e8aa0 *lcd) { struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + struct backlight_device *bd = lcd->bd; + int brightness = bd->props.brightness; unsigned char data_to_send[] = { 0xd9, 0x14, 0x40, 0x0c, 0xcb, 0xce, 0x6e, 0xc4, 0x0f, 0x40, 0x41, 0xd9, 0x00, 0x60, 0x19 @@ -358,11 +478,15 @@ static void s6e8aa0_elvss_nvm_set(struct s6e8aa0 *lcd) if (lcd->ver == VER_32) { data_to_send[8] = 0x07; data_to_send[11] = 0xc1; + } else if (lcd->ver == VER_96) { + data_to_send[8] = 0x07; + data_to_send[9] = 0xc0; + 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) { + switch (brightness) { case 0 ... 6: /* 30cd ~ 100cd */ data_to_send[11] = 0xdf; break; @@ -428,19 +552,21 @@ 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); + MIPI_DSI_DCS_SHORT_WRITE_PARAM, 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); + MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0xc0, 0x00); } static void s6e8aa0_acl_ctrl_set(struct s6e8aa0 *lcd) { struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + struct backlight_device *bd = lcd->bd; + int brightness = bd->props.brightness; /* FIXME: !! must be review acl % value */ /* Full white 50% reducing setting */ const unsigned char cutoff_50[] = { @@ -466,14 +592,14 @@ static void s6e8aa0_acl_ctrl_set(struct s6e8aa0 *lcd) if (lcd->acl_enable) { if (lcd->cur_acl == 0) { - if (lcd->brightness == 0 || lcd->brightness == 1) { + if (brightness == 0 || 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) { + switch (brightness) { case 0 ... 1: /* 30cd */ s6e8aa0_acl_off(lcd); lcd->cur_acl = 0; @@ -570,7 +696,7 @@ unsigned int convert_brightness_to_gamma(int brightness) return gamma_table[brightness] - 1; } -static int s6e8aa0_update_gamma_ctrl(struct s6e8aa0 *lcd, int brightness) +static int s6e8aa0_gamma_ctrl(struct s6e8aa0 *lcd, int brightness) { struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); @@ -597,22 +723,16 @@ static int s6e8aa0_update_gamma_ctrl(struct s6e8aa0 *lcd, int brightness) 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) { + struct backlight_device *bd = lcd->bd; + int brightness = bd->props.brightness; + s6e8aa0_apply_level_1_key(lcd); if (system_rev == 3) s6e8aa0_apply_level_2_key(lcd); @@ -620,10 +740,10 @@ static int s6e8aa0_panel_init(struct s6e8aa0 *lcd) s6e8aa0_sleep_out(lcd); usleep_range(5000, 6000); - s6e8aa0_panel_cond(1); + s6e8aa0_panel_cond(lcd, 1); s6e8aa0_display_condition_set(lcd); - s6e8aa0_gamma_ctrl(lcd, lcd->bd->props.brightness); + s6e8aa0_gamma_ctrl(lcd, brightness); s6e8aa0_etc_source_control(lcd); s6e8aa0_etc_pentile_control(lcd); @@ -633,6 +753,7 @@ static int s6e8aa0_panel_init(struct s6e8aa0 *lcd) /* if ID3 value is not 33h, branch private elvss mode */ mdelay(lcd->ddi_pd->power_on_delay); + dev_info(lcd->dev, "panel init sequence done.\n"); return 0; } @@ -712,7 +833,7 @@ 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) { + if (lcd->power == FB_BLANK_POWERDOWN) { dev_err(lcd->dev, "lcd off: brightness set failed.\n"); return -EINVAL; @@ -802,9 +923,11 @@ static void s6e8aa0_power_on(struct mipi_dsim_lcd_device *dsim_dev, { struct s6e8aa0 *lcd = dev_get_drvdata(&dsim_dev->dev); + dev_dbg(lcd->dev, "%s:enable[%d]\n", __func__, enable); + if (enable) { /* lcd power on */ - s6e8aa0_regulator_enable(lcd); + s6e8aa0_regulator_ctl(lcd, true); mdelay(lcd->ddi_pd->reset_delay); @@ -813,8 +936,10 @@ static void s6e8aa0_power_on(struct mipi_dsim_lcd_device *dsim_dev, lcd->ddi_pd->reset(lcd->ld); mdelay(5); - } else - s6e8aa0_regulator_disable(lcd); + } else { + /* lcd power off */ + s6e8aa0_regulator_ctl(lcd, false); + } } @@ -924,10 +1049,14 @@ static int s6e8aa0_write_reg(struct s6e8aa0 *lcd, char *name) return ret; } - if (fw->size < 2) + if (fw->size == 1) ret = ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_SHORT_WRITE, - (unsigned int)fw->data, fw->size); + (unsigned int)fw->data[0], 0); + else if (fw->size == 2) + ret = ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE_PARAM, + (unsigned int)fw->data[0], fw->data[1]); else ret = ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, @@ -1017,11 +1146,38 @@ static struct panel_model s6e8aa0_model[] = { .ver = VER_32, /* MACH_SLP_PQ M0 B-Type */ .name = "SMD_AMS480GYXX-0", }, { + .ver = VER_96, /* MACH_SLP_PQ Galaxy S3 */ + .name = "SMD_AMS480GYXX-0", + }, { .ver = VER_210, /* MACH_SLP_PQ M0 A-Type */ .name = "SMD_AMS465GS0x", } }; +#if defined(CONFIG_ARM_EXYNOS4_DISPLAY_DEVFREQ) || defined(CONFIG_DISPFREQ_OPP) +static int s6e8aa0_notifier_callback(struct notifier_block *this, + unsigned long event, void *_data) +{ + struct s6e8aa0 *lcd = container_of(this, struct s6e8aa0, nb_disp); + + if (lcd->power == FB_BLANK_POWERDOWN) + return NOTIFY_DONE; + + switch (event) { + case EXYNOS4_DISPLAY_LV_HF: + s6e8aa0_panel_cond(lcd, 1); + break; + case EXYNOS4_DISPLAY_LV_LF: + s6e8aa0_panel_cond(lcd, 0); + break; + default: + return NOTIFY_BAD; + } + + return NOTIFY_DONE; +} +#endif + static int s6e8aa0_probe(struct mipi_dsim_lcd_device *dsim_dev) { struct s6e8aa0 *lcd; @@ -1072,9 +1228,20 @@ static int s6e8aa0_probe(struct mipi_dsim_lcd_device *dsim_dev) goto err_unregister_lcd; } + if (lcd->ddi_pd) + lcd->property = lcd->ddi_pd->pdata; + +#if defined(CONFIG_ARM_EXYNOS4_DISPLAY_DEVFREQ) || defined(CONFIG_DISPFREQ_OPP) + if (lcd->property && lcd->property->dynamic_refresh) { + lcd->nb_disp.notifier_call = s6e8aa0_notifier_callback; + ret = exynos4_display_register_client(&lcd->nb_disp); + if (ret < 0) + dev_warn(&lcd->ld->dev, "failed to register exynos-display notifier\n"); + } +#endif + 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; @@ -1090,11 +1257,9 @@ static int s6e8aa0_probe(struct mipi_dsim_lcd_device *dsim_dev) dev_set_drvdata(&dsim_dev->dev, lcd); - s6e8aa0_regulator_enable(lcd); + s6e8aa0_regulator_ctl(lcd, true); lcd->power = FB_BLANK_UNBLANK; - lcd_global = lcd; - dev_info(lcd->dev, "probed s6e8aa0 panel driver(%s).\n", dev_name(&lcd->ld->dev)); @@ -1121,9 +1286,13 @@ static void s6e8aa0_remove(struct mipi_dsim_lcd_device *dsim_dev) lcd_device_unregister(lcd->ld); regulator_put(lcd->reg_vci); - regulator_put(lcd->reg_vdd3); +#if defined(CONFIG_ARM_EXYNOS4_DISPLAY_DEVFREQ) || defined(CONFIG_DISPFREQ_OPP) + if (lcd->property && lcd->property->dynamic_refresh) + exynos4_display_unregister_client(&lcd->nb_disp); +#endif + kfree(lcd); } @@ -1183,5 +1352,7 @@ module_exit(s6e8aa0_exit); MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>"); MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); +MODULE_AUTHOR("Joongmock Shin <jmock.shin@samsung.com>"); +MODULE_AUTHOR("Eunchul Kim <chulspro.kim@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 index 4e19d7d..0510ec2 100644 --- a/drivers/video/backlight/s6e8aa0.h +++ b/drivers/video/backlight/s6e8aa0.h @@ -6,6 +6,8 @@ * * Inki Dae, <inki.dae@samsung.com> * Donghwa Lee <dh09.lee@samsung.com> + * Joongmock Shin <jmock.shin@samsung.com> + * Eunchul Kim <chulspro.kim@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 @@ -15,12 +17,7 @@ #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)); +/* ToDo */ #endif diff --git a/drivers/video/backlight/smart_dimming.c b/drivers/video/backlight/smart_dimming.c index 42438ad..5f8bea0 100644 --- a/drivers/video/backlight/smart_dimming.c +++ b/drivers/video/backlight/smart_dimming.c @@ -167,6 +167,14 @@ const unsigned char gamma_300cd_d2[] = { 0x00, 0xBD, 0x00, 0xAC, 0x00, 0xDE, }; +/* SM2, C1/M0 4.8" - D*/ +const unsigned char gamma_300cd_60[] = { + 0x3F, 0x12, 0x41, 0xB4, 0xCB, 0xB0, + 0xB2, 0xC4, 0xB2, 0xC4, 0xCF, 0xC2, + 0x9A, 0xA7, 0x97, 0xB2, 0xBA, 0xB0, + 0x00, 0xA0, 0x00, 0x98, 0x00, 0xB7, +}; + const unsigned char *gamma_300cd_list[GAMMA_300CD_MAX] = { gamma_300cd_23, gamma_300cd_33, @@ -182,11 +190,12 @@ const unsigned char *gamma_300cd_list[GAMMA_300CD_MAX] = { gamma_300cd_8e, gamma_300cd_ae, gamma_300cd_d2, + gamma_300cd_60, }; const unsigned char gamma_id_list[GAMMA_300CD_MAX] = { 0x23, 0x33, 0x43, 0x53, 0x63, 0x73, 0x83, 0x20, - 0x2a, 0x29, 0x4e, 0x8e, 0xae, 0xd2 + 0x2a, 0x29, 0x4e, 0x8e, 0xae, 0xd2, 0x60 }; static s16 s9_to_s16(s16 v) diff --git a/drivers/video/backlight/smart_dimming.h b/drivers/video/backlight/smart_dimming.h index ae21182..3531213 100644 --- a/drivers/video/backlight/smart_dimming.h +++ b/drivers/video/backlight/smart_dimming.h @@ -23,7 +23,7 @@ #define MAX_GRADATION 300 #define PANEL_ID_MAX 3 -#define GAMMA_300CD_MAX 14 +#define GAMMA_300CD_MAX 15 enum { |