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/ea8061.c | |
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/ea8061.c')
-rw-r--r-- | drivers/video/backlight/ea8061.c | 899 |
1 files changed, 899 insertions, 0 deletions
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"); + |