aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video/samsung/mdnie_kona.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/video/samsung/mdnie_kona.c')
-rw-r--r--drivers/video/samsung/mdnie_kona.c1171
1 files changed, 1171 insertions, 0 deletions
diff --git a/drivers/video/samsung/mdnie_kona.c b/drivers/video/samsung/mdnie_kona.c
new file mode 100644
index 0000000..d44aa0e
--- /dev/null
+++ b/drivers/video/samsung/mdnie_kona.c
@@ -0,0 +1,1171 @@
+/* linux/drivers/video/samsung/mdnie.c
+ *
+ * Register interface file for Samsung mDNIe driver
+ *
+ * Copyright (c) 2009 Samsung Electronics
+ * http://www.samsungsemi.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/mm.h>
+#include <linux/device.h>
+#include <linux/backlight.h>
+#include <linux/platform_device.h>
+#include <linux/mdnie.h>
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+#include <linux/delay.h>
+#include <linux/lcd.h>
+
+#include "s3cfb.h"
+#include "s3cfb_mdnie_kona.h"
+
+#if defined(CONFIG_FB_S5P_NT71391)
+#include "mdnie_table_kona.h"
+#endif
+#include "mdnie_color_tone_4412.h"
+
+#if defined(CONFIG_FB_EBOOK_PANEL_SCENARIO)
+#include "mdnie_table_ebook.h"
+#endif
+
+#if defined(CONFIG_FB_MDNIE_PWM)
+#define MIN_BRIGHTNESS 0
+#define DEFAULT_BRIGHTNESS 150
+#if defined(CONFIG_FB_S5P_S6F1202A)
+#define CABC_CUTOFF_BACKLIGHT_VALUE 40 /* 2.5% */
+#elif defined(CONFIG_FB_S5P_S6C1372)
+#define CABC_CUTOFF_BACKLIGHT_VALUE 34
+#elif defined(CONFIG_FB_S5P_NT71391)
+#define CABC_CUTOFF_BACKLIGHT_VALUE 31
+#endif
+#define MAX_BRIGHTNESS_LEVEL 255
+#define MID_BRIGHTNESS_LEVEL 150
+#define LOW_BRIGHTNESS_LEVEL 30
+#define DIM_BRIGHTNESS_LEVEL 20
+#endif
+
+#define MDNIE_SYSFS_PREFIX "/sdcard/mdnie/"
+#define PANEL_COLOR_OFFSET_PATH "/sys/class/lcd/panel/color_offset"
+
+#if defined(CONFIG_TDMB) || defined(CONFIG_TARGET_LOCALE_NTT)
+#define SCENARIO_IS_DMB(scenario) ((scenario >= DMB_NORMAL_MODE) && (scenario < DMB_MODE_MAX))
+#else
+#define SCENARIO_IS_DMB(scenario) NULL
+#endif
+
+#define SCENARIO_IS_COLOR(scenario) ((scenario >= COLOR_TONE_1) && (scenario < COLOR_TONE_MAX))
+#define SCENARIO_IS_VIDEO(scenario) ((scenario >= VIDEO_MODE) && (scenario <= VIDEO_COLD_MODE))
+#define SCENARIO_IS_VALID(scenario) (SCENARIO_IS_COLOR(scenario) || SCENARIO_IS_DMB(scenario) || scenario < SCENARIO_MAX)
+
+#define ACCESSIBILITY_IS_VALID(accessibility) (accessibility && (accessibility < ACCESSIBILITY_MAX))
+
+#define ADDRESS_IS_SCR_BLACK(address) (address >= MDNIE_REG_BLACK_R && address <= MDNIE_REG_BLACK_B)
+#define ADDRESS_IS_SCR_RGB(address) (address >= MDNIE_REG_RED_R && address <= MDNIE_REG_GREEN_B)
+
+#define SCR_BLACK_MASK(value) (value % MDNIE_REG_BLACK_R)
+#define SCR_RGB_MASK(value) (value % MDNIE_REG_RED_R)
+
+struct class *mdnie_class;
+struct mdnie_info *g_mdnie;
+
+static int mdnie_send_sequence(struct mdnie_info *mdnie, const unsigned short *seq)
+{
+ int ret = 0, i = 0;
+ const unsigned short *wbuf = NULL;
+
+ if (IS_ERR_OR_NULL(seq)) {
+ dev_err(mdnie->dev, "mdnie sequence is null\n");
+ return -EPERM;
+ }
+
+ mutex_lock(&mdnie->dev_lock);
+
+ wbuf = seq;
+
+ mdnie_mask();
+
+ while (wbuf[i] != END_SEQ) {
+ ret += mdnie_write(wbuf[i], wbuf[i+1]);
+ i += 2;
+ }
+
+ mdnie_unmask();
+
+ mutex_unlock(&mdnie->dev_lock);
+
+ return ret;
+}
+
+static struct mdnie_tuning_info *mdnie_request_table(struct mdnie_info *mdnie)
+{
+ struct mdnie_tuning_info *table = NULL;
+
+ mutex_lock(&mdnie->lock);
+
+#if defined(CONFIG_FB_EBOOK_PANEL_SCENARIO)
+ if (mdnie->ebook == EBOOK_ON) {
+ table = &ebook_table[mdnie->cabc];
+ goto exit;
+ }
+#endif
+
+ /* it will be removed next year */
+ if (mdnie->negative == NEGATIVE_ON) {
+ table = &negative_table[mdnie->cabc];
+ goto exit;
+ }
+
+ if (ACCESSIBILITY_IS_VALID(mdnie->accessibility)) {
+ table = &accessibility_table[mdnie->cabc][mdnie->accessibility];
+ goto exit;
+ } else if (SCENARIO_IS_DMB(mdnie->scenario)) {
+#if defined(CONFIG_TDMB) || defined(CONFIG_TARGET_LOCALE_NTT)
+ table = &tune_dmb[mdnie->mode];
+#endif
+ goto exit;
+ } else if (SCENARIO_IS_COLOR(mdnie->scenario)) {
+ table = &color_tone_table[mdnie->scenario % COLOR_TONE_1];
+ goto exit;
+ } else if (mdnie->scenario == CAMERA_MODE) {
+ table = &camera_table[mdnie->outdoor];
+ goto exit;
+ } else if (mdnie->scenario < SCENARIO_MAX) {
+ table = &tuning_table[mdnie->cabc][mdnie->mode][mdnie->scenario];
+ goto exit;
+ }
+
+exit:
+ mutex_unlock(&mdnie->lock);
+
+ return table;
+}
+
+static struct mdnie_tuning_info *mdnie_request_etc_table(struct mdnie_info *mdnie)
+{
+ struct mdnie_tuning_info *table = NULL;
+
+ mutex_lock(&mdnie->lock);
+
+ if (SCENARIO_IS_VIDEO(mdnie->scenario))
+ mdnie->tone = mdnie->scenario - VIDEO_MODE;
+ else if (SCENARIO_IS_DMB(mdnie->scenario))
+ mdnie->tone = mdnie->scenario % DMB_NORMAL_MODE;
+
+ table = &etc_table[mdnie->cabc][mdnie->outdoor][mdnie->tone];
+
+ mutex_unlock(&mdnie->lock);
+
+ return table;
+}
+
+static void mdnie_update_sequence(struct mdnie_info *mdnie, struct mdnie_tuning_info *table)
+{
+ unsigned short *wbuf = NULL;
+ int ret;
+
+ if (unlikely(mdnie->tuning)) {
+ ret = mdnie_request_firmware(mdnie->path, &wbuf, table->name);
+ if (ret < 0 && IS_ERR_OR_NULL(wbuf))
+ goto exit;
+ mdnie_send_sequence(mdnie, wbuf);
+ kfree(wbuf);
+ } else
+ mdnie_send_sequence(mdnie, table->sequence);
+
+exit:
+ return;
+}
+
+static void mdnie_update(struct mdnie_info *mdnie)
+{
+ struct mdnie_tuning_info *table = NULL;
+
+ if (!mdnie->enable) {
+ dev_err(mdnie->dev, "mdnie state is off\n");
+ return;
+ }
+
+ table = mdnie_request_table(mdnie);
+ if (!IS_ERR_OR_NULL(table) && !IS_ERR_OR_NULL(table->sequence)) {
+ mdnie_update_sequence(mdnie, table);
+ dev_info(mdnie->dev, "%s\n", table->name);
+ }
+
+ if (!(SCENARIO_IS_VIDEO(mdnie->scenario) || SCENARIO_IS_DMB(mdnie->scenario)))
+ goto exit;
+
+ table = mdnie_request_etc_table(mdnie);
+ if (!IS_ERR_OR_NULL(table) && !IS_ERR_OR_NULL(table->sequence)) {
+ mdnie_update_sequence(mdnie, table);
+ dev_info(mdnie->dev, "%s\n", table->name);
+ }
+
+exit:
+ return;
+}
+
+#if defined(CONFIG_FB_MDNIE_PWM)
+static int get_backlight_level_from_brightness(struct mdnie_info *mdnie, unsigned int brightness)
+{
+ unsigned int value;
+ struct mdnie_backlight_value *pwm = mdnie->backlight;
+
+ /* brightness tuning*/
+ if (brightness >= MID_BRIGHTNESS_LEVEL) {
+ value = (brightness - MID_BRIGHTNESS_LEVEL) *
+ (pwm->max - pwm->mid) / (MAX_BRIGHTNESS_LEVEL-MID_BRIGHTNESS_LEVEL) + pwm->mid;
+ } else if (brightness >= LOW_BRIGHTNESS_LEVEL) {
+ value = (brightness - LOW_BRIGHTNESS_LEVEL) *
+ (pwm->mid - pwm->low) / (MID_BRIGHTNESS_LEVEL-LOW_BRIGHTNESS_LEVEL) + pwm->low;
+ } else if (brightness >= DIM_BRIGHTNESS_LEVEL) {
+ value = (brightness - DIM_BRIGHTNESS_LEVEL) *
+ (pwm->low - pwm->dim) / (LOW_BRIGHTNESS_LEVEL-DIM_BRIGHTNESS_LEVEL) + pwm->dim;
+ } else if (brightness > 0)
+ value = pwm->dim;
+ else
+ return 0;
+
+ if (value > 1600)
+ value = 1600;
+
+ if (value < 16)
+ value = 1;
+ else
+ value = value >> 4;
+
+ return value;
+}
+
+static void mdnie_pwm_control(struct mdnie_info *mdnie, int value)
+{
+ mutex_lock(&mdnie->dev_lock);
+ mdnie_write(MDNIE_REG_BANK_SEL, MDNIE_PWM_BANK);
+ mdnie_write(MDNIE_REG_PWM_CONTROL, 0xC000 | value);
+ mdnie_write(MDNIE_REG_MASK, 0);
+ mutex_unlock(&mdnie->dev_lock);
+}
+
+static void mdnie_pwm_control_cabc(struct mdnie_info *mdnie, int value)
+{
+ int reg;
+ const unsigned char *p_plut;
+ u16 min_duty;
+ unsigned idx;
+
+ mutex_lock(&mdnie->dev_lock);
+
+ idx = SCENARIO_IS_VIDEO(mdnie->scenario) ? LUT_VIDEO : LUT_DEFAULT;
+ p_plut = power_lut[mdnie->power_lut_idx][idx];
+ min_duty = p_plut[7] * value / 100;
+
+ mdnie_write(MDNIE_REG_BANK_SEL, MDNIE_PWM_BANK);
+
+ if (min_duty < 4)
+ reg = 0xC000 | (max(1, (value * p_plut[3] / 100)));
+ else {
+ /*PowerLUT*/
+ mdnie_write(MDNIE_REG_POWER_LUT0, (p_plut[0] * value / 100) << 8 | (p_plut[1] * value / 100));
+ mdnie_write(MDNIE_REG_POWER_LUT2, (p_plut[2] * value / 100) << 8 | (p_plut[3] * value / 100));
+ mdnie_write(MDNIE_REG_POWER_LUT4, (p_plut[4] * value / 100) << 8 | (p_plut[5] * value / 100));
+ mdnie_write(MDNIE_REG_POWER_LUT6, (p_plut[6] * value / 100) << 8 | (p_plut[7] * value / 100));
+ mdnie_write(MDNIE_REG_POWER_LUT8, (p_plut[8] * value / 100) << 8);
+
+ reg = 0x5000 | (value << 4);
+ }
+
+ mdnie_write(MDNIE_REG_PWM_CONTROL, reg);
+ mdnie_write(MDNIE_REG_MASK, 0);
+
+ mutex_unlock(&mdnie->dev_lock);
+}
+
+void set_mdnie_pwm_value(struct mdnie_info *mdnie, int value)
+{
+ mdnie_pwm_control(mdnie, value);
+}
+
+static int update_brightness(struct mdnie_info *mdnie)
+{
+ unsigned int value;
+ unsigned int brightness = mdnie->bd->props.brightness;
+
+ value = get_backlight_level_from_brightness(mdnie, brightness);
+
+ if (!mdnie->enable) {
+ dev_err(mdnie->dev, "mdnie states is off\n");
+ return 0;
+ }
+
+ if (brightness <= CABC_CUTOFF_BACKLIGHT_VALUE) {
+ mdnie_pwm_control(mdnie, value);
+ } else {
+ if ((mdnie->cabc) && (mdnie->scenario != CAMERA_MODE) && !(mdnie->tuning))
+ mdnie_pwm_control_cabc(mdnie, value);
+ else
+ mdnie_pwm_control(mdnie, value);
+ }
+ return 0;
+}
+
+static int mdnie_set_brightness(struct backlight_device *bd)
+{
+ struct mdnie_info *mdnie = bl_get_data(bd);
+ int ret = 0;
+ unsigned int brightness = bd->props.brightness;
+
+ if (brightness < MIN_BRIGHTNESS ||
+ brightness > bd->props.max_brightness) {
+ dev_err(&bd->dev, "lcd brightness should be %d to %d. now %d\n",
+ MIN_BRIGHTNESS, bd->props.max_brightness, brightness);
+ brightness = bd->props.max_brightness;
+ }
+
+ if ((mdnie->enable) && (mdnie->bd_enable)) {
+ ret = update_brightness(mdnie);
+ dev_info(&bd->dev, "brightness=%d\n", bd->props.brightness);
+ if (ret < 0)
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int mdnie_get_brightness(struct backlight_device *bd)
+{
+ return bd->props.brightness;
+}
+
+static const struct backlight_ops mdnie_backlight_ops = {
+ .get_brightness = mdnie_get_brightness,
+ .update_status = mdnie_set_brightness,
+};
+#endif
+
+static void update_color_position(struct mdnie_info *mdnie, u16 idx)
+{
+ u8 cabc, mode, scenario, outdoor, i;
+ unsigned short *wbuf;
+
+ dev_info(mdnie->dev, "%s: idx=%d\n", __func__, idx);
+
+ mutex_lock(&mdnie->lock);
+
+ for (cabc = 0; cabc < CABC_MAX; cabc++) {
+ for (mode = 0; mode <= STANDARD; mode++) {
+ for (scenario = 0; scenario < SCENARIO_MAX; scenario++) {
+ wbuf = tuning_table[cabc][mode][scenario].sequence;
+ if (IS_ERR_OR_NULL(wbuf))
+ continue;
+ i = 0;
+ while (wbuf[i] != END_SEQ) {
+ if (ADDRESS_IS_SCR_BLACK(wbuf[i]))
+ wbuf[i+1] = tune_scr_setting[idx][SCR_BLACK_MASK(wbuf[i])];
+ i += 2;
+ }
+ }
+ }
+ }
+
+ for (outdoor = 0; outdoor < OUTDOOR_MAX; outdoor++) {
+ wbuf = camera_table[outdoor].sequence;
+ if (IS_ERR_OR_NULL(wbuf))
+ continue;
+ i = 0;
+ while (wbuf[i] != END_SEQ) {
+ if (ADDRESS_IS_SCR_BLACK(wbuf[i]))
+ wbuf[i+1] = tune_scr_setting[idx][SCR_BLACK_MASK(wbuf[i])];
+ i += 2;
+ }
+ }
+
+ mutex_unlock(&mdnie->lock);
+}
+
+static int get_panel_color_position(struct mdnie_info *mdnie, int *result)
+{
+ int ret = 0;
+ char *fp = NULL;
+ unsigned int offset[2] = {0,};
+
+ if (likely(mdnie->color_correction))
+ goto skip_color_correction;
+
+ ret = mdnie_open_file(PANEL_COLOR_OFFSET_PATH, &fp);
+ if (IS_ERR_OR_NULL(fp) || ret <= 0) {
+ dev_info(mdnie->dev, "%s: open fail: %s, %d\n", __func__, PANEL_COLOR_OFFSET_PATH, ret);
+ ret = -EINVAL;
+ goto skip_color_correction;
+ }
+
+ ret = sscanf(fp, "0x%x, 0x%x", &offset[0], &offset[1]);
+ if (!(offset[0] + offset[1]) || ret != 2) {
+ dev_info(mdnie->dev, "%s: 0x%x, 0x%x\n", __func__, offset[0], offset[1]);
+ ret = -EINVAL;
+ goto skip_color_correction;
+ }
+
+ ret = mdnie_calibration(offset[0], offset[1], result);
+ dev_info(mdnie->dev, "%s: %x, %x, idx=%d\n", __func__, offset[0], offset[1], ret - 1);
+
+skip_color_correction:
+ mdnie->color_correction = 1;
+ if (!IS_ERR_OR_NULL(fp))
+ kfree(fp);
+
+ return ret;
+}
+
+static ssize_t mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mdnie_info *mdnie = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", mdnie->mode);
+}
+
+static ssize_t mode_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct mdnie_info *mdnie = dev_get_drvdata(dev);
+ unsigned int value = 0;
+ int result[5] = {0,};
+ int ret;
+
+ ret = kstrtoul(buf, 0, (unsigned long *)&value);
+ if (ret)
+ return -EINVAL;
+
+ dev_info(dev, "%s :: value=%d\n", __func__, value);
+
+ if (value >= MODE_MAX) {
+ value = STANDARD;
+ return -EINVAL;
+ }
+
+ mutex_lock(&mdnie->lock);
+ mdnie->mode = value;
+ mutex_unlock(&mdnie->lock);
+
+ if (!mdnie->color_correction) {
+ ret = get_panel_color_position(mdnie, result);
+ if (ret > 0)
+ update_color_position(mdnie, ret - 1);
+ }
+
+ mdnie_update(mdnie);
+#if defined(CONFIG_FB_MDNIE_PWM)
+ if ((mdnie->enable) && (mdnie->bd_enable))
+ update_brightness(mdnie);
+#endif
+
+ return count;
+}
+
+
+static ssize_t scenario_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mdnie_info *mdnie = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", mdnie->scenario);
+}
+
+static ssize_t scenario_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct mdnie_info *mdnie = dev_get_drvdata(dev);
+ unsigned int value;
+ int ret;
+
+ ret = kstrtoul(buf, 0, (unsigned long *)&value);
+
+ dev_info(dev, "%s :: value=%d\n", __func__, value);
+
+ if (!SCENARIO_IS_VALID(value))
+ value = UI_MODE;
+
+#if defined(CONFIG_FB_MDNIE_PWM)
+ if (value >= SCENARIO_MAX)
+ value = UI_MODE;
+#endif
+
+ mutex_lock(&mdnie->lock);
+ mdnie->scenario = value;
+ mutex_unlock(&mdnie->lock);
+
+ mdnie_update(mdnie);
+#if defined(CONFIG_FB_MDNIE_PWM)
+ if ((mdnie->enable) && (mdnie->bd_enable))
+ update_brightness(mdnie);
+#endif
+
+ return count;
+}
+
+
+static ssize_t outdoor_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mdnie_info *mdnie = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", mdnie->outdoor);
+}
+
+static ssize_t outdoor_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct mdnie_info *mdnie = dev_get_drvdata(dev);
+ unsigned int value;
+ int ret;
+
+ ret = kstrtoul(buf, 0, (unsigned long *)&value);
+
+ dev_info(dev, "%s :: value=%d\n", __func__, value);
+
+ if (value >= OUTDOOR_MAX)
+ value = OUTDOOR_OFF;
+
+ value = (value) ? OUTDOOR_ON : OUTDOOR_OFF;
+
+ mutex_lock(&mdnie->lock);
+ mdnie->outdoor = value;
+ mutex_unlock(&mdnie->lock);
+
+ mdnie_update(mdnie);
+
+ return count;
+}
+
+
+#if defined(CONFIG_FB_MDNIE_PWM)
+static ssize_t cabc_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mdnie_info *mdnie = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", mdnie->cabc);
+}
+
+static ssize_t cabc_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct mdnie_info *mdnie = dev_get_drvdata(dev);
+ unsigned int value;
+ int ret;
+
+#if defined(CONFIG_FB_S5P_S6C1372)
+ if (mdnie->auto_brightness)
+ return -EINVAL;
+#endif
+
+ ret = strict_strtoul(buf, 0, (unsigned long *)&value);
+
+ dev_info(dev, "%s :: value=%d\n", __func__, value);
+
+ if (value >= CABC_MAX)
+ value = CABC_OFF;
+
+ value = (value) ? CABC_ON : CABC_OFF;
+
+ mutex_lock(&mdnie->lock);
+ mdnie->cabc = value;
+ mutex_unlock(&mdnie->lock);
+
+ mdnie_update(mdnie);
+ if ((mdnie->enable) && (mdnie->bd_enable))
+ update_brightness(mdnie);
+
+ return count;
+}
+
+static ssize_t auto_brightness_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mdnie_info *mdnie = dev_get_drvdata(dev);
+ char *pos = buf;
+ int i;
+
+ pos += sprintf(pos, "%d, %d, ", mdnie->auto_brightness, mdnie->power_lut_idx);
+ for (i = 0; i < 5; i++)
+ pos += sprintf(pos, "0x%02x, ", power_lut[mdnie->power_lut_idx][0][i]);
+ pos += sprintf(pos, "\n");
+
+ return pos - buf;
+}
+
+static ssize_t auto_brightness_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct mdnie_info *mdnie = dev_get_drvdata(dev);
+ int value;
+ int rc;
+
+ rc = strict_strtoul(buf, (unsigned int)0, (unsigned long *)&value);
+ if (rc < 0)
+ return rc;
+ else {
+ if (mdnie->auto_brightness != value) {
+ dev_info(dev, "%s - %d -> %d\n", __func__, mdnie->auto_brightness, value);
+ mutex_lock(&mdnie->dev_lock);
+ mdnie->auto_brightness = value;
+#if defined(CONFIG_FB_S5P_S6C1372)
+ mutex_lock(&mdnie->lock);
+ mdnie->cabc = (value) ? CABC_ON : CABC_OFF;
+ mutex_unlock(&mdnie->lock);
+#endif
+ if (mdnie->auto_brightness >= 5)
+ mdnie->power_lut_idx = LUT_LEVEL_OUTDOOR_2;
+ else if (mdnie->auto_brightness == 4)
+ mdnie->power_lut_idx = LUT_LEVEL_OUTDOOR_1;
+ else
+ mdnie->power_lut_idx = LUT_LEVEL_MANUAL_AND_INDOOR;
+ mutex_unlock(&mdnie->dev_lock);
+ mdnie_update(mdnie);
+ if (mdnie->bd_enable)
+ update_brightness(mdnie);
+ }
+ }
+ return size;
+}
+
+static DEVICE_ATTR(auto_brightness, 0644, auto_brightness_show, auto_brightness_store);
+#endif
+
+static ssize_t tuning_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mdnie_info *mdnie = dev_get_drvdata(dev);
+ char *pos = buf;
+ struct mdnie_tuning_info *table;
+ int ret, i;
+ unsigned short *wbuf;
+
+ pos += sprintf(pos, "++ %s: %s\n", __func__, mdnie->path);
+
+ if (!mdnie->tuning) {
+ pos += sprintf(pos, "tunning mode is off\n");
+ goto exit;
+ }
+
+ if (strncmp(mdnie->path, MDNIE_SYSFS_PREFIX, sizeof(MDNIE_SYSFS_PREFIX) - 1)) {
+ pos += sprintf(pos, "file path is invalid, %s\n", mdnie->path);
+ goto exit;
+ }
+
+ table = mdnie_request_table(mdnie);
+ if (!IS_ERR_OR_NULL(table)) {
+ ret = mdnie_request_firmware(mdnie->path, &wbuf, table->name);
+ i = 0;
+ while (wbuf[i] != END_SEQ) {
+ pos += sprintf(pos, "0x%04x, 0x%04x\n", wbuf[i], wbuf[i+1]);
+ i += 2;
+ }
+ if (!IS_ERR_OR_NULL(wbuf))
+ kfree(wbuf);
+ pos += sprintf(pos, "%s\n", table->name);
+ }
+
+ if (!(SCENARIO_IS_VIDEO(mdnie->scenario) || SCENARIO_IS_DMB(mdnie->scenario)))
+ goto exit;
+
+ table = mdnie_request_etc_table(mdnie);
+ if (!IS_ERR_OR_NULL(table)) {
+ ret = mdnie_request_firmware(mdnie->path, &wbuf, table->name);
+ i = 0;
+ while (wbuf[i] != END_SEQ) {
+ pos += sprintf(pos, "0x%04x, 0x%04x\n", wbuf[i], wbuf[i+1]);
+ i += 2;
+ }
+ if (!IS_ERR_OR_NULL(wbuf))
+ kfree(wbuf);
+ pos += sprintf(pos, "%s\n", table->name);
+ }
+
+exit:
+ pos += sprintf(pos, "-- %s\n", __func__);
+
+ return pos - buf;
+}
+
+static ssize_t tuning_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct mdnie_info *mdnie = dev_get_drvdata(dev);
+ int ret;
+
+ if (sysfs_streq(buf, "0") || sysfs_streq(buf, "1")) {
+ ret = kstrtoul(buf, 0, (unsigned long *)&mdnie->tuning);
+ if (!mdnie->tuning)
+ memset(mdnie->path, 0, sizeof(mdnie->path));
+ dev_info(dev, "%s :: %s\n", __func__, mdnie->tuning ? "enable" : "disable");
+ } else {
+ if (!mdnie->tuning)
+ return count;
+
+ if (count > (sizeof(mdnie->path) - sizeof(MDNIE_SYSFS_PREFIX))) {
+ dev_err(dev, "file name %s is too long\n", mdnie->path);
+ return -ENOMEM;
+ }
+
+ memset(mdnie->path, 0, sizeof(mdnie->path));
+ snprintf(mdnie->path, sizeof(MDNIE_SYSFS_PREFIX) + count-1, "%s%s", MDNIE_SYSFS_PREFIX, buf);
+ dev_info(dev, "%s :: %s\n", __func__, mdnie->path);
+
+ mdnie_update(mdnie);
+ }
+
+ return count;
+}
+
+static ssize_t negative_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mdnie_info *mdnie = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", mdnie->negative);
+}
+
+static ssize_t negative_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct mdnie_info *mdnie = dev_get_drvdata(dev);
+ unsigned int value;
+ int ret;
+
+ ret = kstrtoul(buf, 0, (unsigned long *)&value);
+
+ dev_info(dev, "%s :: value=%d\n", __func__, value);
+
+ if (ret < 0)
+ return ret;
+ else {
+ if (mdnie->negative == value)
+ return count;
+
+ if (value >= NEGATIVE_MAX)
+ value = NEGATIVE_OFF;
+
+ value = (value) ? NEGATIVE_ON : NEGATIVE_OFF;
+
+ mutex_lock(&mdnie->lock);
+ mdnie->negative = value;
+ mutex_unlock(&mdnie->lock);
+
+ mdnie_update(mdnie);
+ }
+ return count;
+}
+
+static ssize_t accessibility_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mdnie_info *mdnie = dev_get_drvdata(dev);
+ char *pos = buf;
+ unsigned short *wbuf;
+ int i = 0;
+
+ pos += sprintf(pos, "%d, ", mdnie->accessibility);
+ if (mdnie->accessibility == COLOR_BLIND) {
+ if (!IS_ERR_OR_NULL(accessibility_table[mdnie->cabc][COLOR_BLIND].sequence)) {
+ wbuf = accessibility_table[mdnie->cabc][COLOR_BLIND].sequence;
+ while (wbuf[i] != END_SEQ) {
+ if (ADDRESS_IS_SCR_RGB(wbuf[i]))
+ pos += sprintf(pos, "0x%04x, ", wbuf[i+1]);
+ i += 2;
+ }
+ }
+ }
+ pos += sprintf(pos, "\n");
+
+ return pos - buf;
+}
+
+static ssize_t accessibility_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct mdnie_info *mdnie = dev_get_drvdata(dev);
+ unsigned int value, s[9], cabc, i, len = 0;
+ int ret;
+ unsigned short *wbuf;
+ char str[100] = {0,};
+
+ ret = sscanf(buf, "%d %x %x %x %x %x %x %x %x %x",
+ &value, &s[0], &s[1], &s[2], &s[3],
+ &s[4], &s[5], &s[6], &s[7], &s[8]);
+
+ dev_info(dev, "%s :: value=%d\n", __func__, value);
+
+ if (ret < 0)
+ return ret;
+ else {
+ if (value >= ACCESSIBILITY_MAX)
+ value = ACCESSIBILITY_OFF;
+
+ mutex_lock(&mdnie->lock);
+ mdnie->accessibility = value;
+ if (value == COLOR_BLIND) {
+ if (ret != 10) {
+ mutex_unlock(&mdnie->lock);
+ return -EINVAL;
+ }
+
+ for (cabc = 0; cabc < CABC_MAX; cabc++) {
+ wbuf = accessibility_table[cabc][COLOR_BLIND].sequence;
+ if (IS_ERR_OR_NULL(wbuf))
+ continue;
+ i = 0;
+ while (wbuf[i] != END_SEQ) {
+ if (ADDRESS_IS_SCR_RGB(wbuf[i]))
+ wbuf[i+1] = s[SCR_RGB_MASK(wbuf[i])];
+ i += 2;
+ }
+ }
+
+ i = 0;
+ len = sprintf(str + len, "%s :: ", __func__);
+ while (len < sizeof(str) && i < ARRAY_SIZE(s)) {
+ len += sprintf(str + len, "0x%04x, ", s[i]);
+ i++;
+ }
+ dev_info(dev, "%s\n", str);
+ }
+ mutex_unlock(&mdnie->lock);
+
+ mdnie_update(mdnie);
+ }
+ return count;
+}
+
+static ssize_t color_correct_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mdnie_info *mdnie = dev_get_drvdata(dev);
+ char *pos = buf;
+ int i;
+ int result[5] = {0,};
+
+ if (!mdnie->color_correction)
+ return -EINVAL;
+
+ for (i = 1; i < ARRAY_SIZE(result); i++)
+ pos += sprintf(pos, "F%d= %d, ", i, result[i]);
+ pos += sprintf(pos, "idx=%d\n", get_panel_color_position(mdnie, result));
+
+ return pos - buf;
+}
+
+#if defined(CONFIG_FB_EBOOK_PANEL_SCENARIO)
+static ssize_t ebook_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mdnie_info *mdnie = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", mdnie->ebook);
+}
+
+static ssize_t ebook_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct mdnie_info *mdnie = dev_get_drvdata(dev);
+ unsigned int value;
+ int ret;
+
+ ret = strict_strtoul(buf, 0, (unsigned long *)&value);
+
+ dev_info(dev, "%s :: value=%d\n", __func__, value);
+
+ if (ret < 0)
+ return ret;
+ else {
+ if (mdnie->ebook == value)
+ return count;
+
+ if (value >= EBOOK_MAX)
+ value = EBOOK_OFF;
+
+ value = (value) ? EBOOK_ON : EBOOK_OFF;
+
+ mutex_lock(&mdnie->lock);
+ mdnie->ebook = value;
+ mutex_unlock(&mdnie->lock);
+
+ mdnie_update(mdnie);
+ }
+ return count;
+}
+#endif
+
+static struct device_attribute mdnie_attributes[] = {
+ __ATTR(mode, 0664, mode_show, mode_store),
+ __ATTR(scenario, 0664, scenario_show, scenario_store),
+ __ATTR(outdoor, 0664, outdoor_show, outdoor_store),
+#if defined(CONFIG_FB_MDNIE_PWM)
+ __ATTR(cabc, 0664, cabc_show, cabc_store),
+#endif
+ __ATTR(tuning, 0664, tuning_show, tuning_store),
+ __ATTR(negative, 0664, negative_show, negative_store),
+ __ATTR(accessibility, 0664, accessibility_show, accessibility_store),
+ __ATTR(color_correct, 0444, color_correct_show, NULL),
+#if defined(CONFIG_FB_EBOOK_PANEL_SCENARIO)
+ __ATTR(ebook, 0664, ebook_show, ebook_store),
+#endif
+ __ATTR_NULL,
+};
+
+#ifdef CONFIG_PM
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+#if defined(CONFIG_FB_MDNIE_PWM)
+static void mdnie_early_suspend(struct early_suspend *h)
+{
+ struct mdnie_info *mdnie = container_of(h, struct mdnie_info, early_suspend);
+ struct lcd_platform_data *pd = mdnie->lcd_pd;
+
+ dev_info(mdnie->dev, "+%s\n", __func__);
+
+ mdnie->bd_enable = FALSE;
+
+ if (mdnie->enable)
+ mdnie_pwm_control(mdnie, 0);
+
+ if (pd && pd->power_on)
+ pd->power_on(NULL, 0);
+
+ dev_info(mdnie->dev, "-%s\n", __func__);
+
+ return;
+}
+#endif
+
+static void mdnie_late_resume(struct early_suspend *h)
+{
+ struct mdnie_info *mdnie = container_of(h, struct mdnie_info, early_suspend);
+#if defined(CONFIG_FB_MDNIE_PWM)
+ struct lcd_platform_data *pd = mdnie->lcd_pd;
+#endif
+
+ dev_info(mdnie->dev, "+%s\n", __func__);
+
+#if defined(CONFIG_FB_MDNIE_PWM)
+ if (mdnie->enable)
+ mdnie_pwm_control(mdnie, 0);
+
+ if (pd && pd->power_on)
+ pd->power_on(NULL, 1);
+
+ if (mdnie->enable) {
+ dev_info(&mdnie->bd->dev, "brightness=%d\n", mdnie->bd->props.brightness);
+ update_brightness(mdnie);
+ }
+
+ mdnie->bd_enable = TRUE;
+#endif
+
+ mdnie_update(mdnie);
+
+ dev_info(mdnie->dev, "-%s\n", __func__);
+
+ return;
+}
+#endif
+#endif
+
+static int mdnie_probe(struct platform_device *pdev)
+{
+#if defined(CONFIG_FB_MDNIE_PWM)
+ struct platform_mdnie_data *pdata = pdev->dev.platform_data;
+#endif
+ struct mdnie_info *mdnie;
+ int ret = 0;
+
+ mdnie_class = class_create(THIS_MODULE, dev_name(&pdev->dev));
+ if (IS_ERR_OR_NULL(mdnie_class)) {
+ pr_err("failed to create mdnie class\n");
+ ret = -EINVAL;
+ goto error0;
+ }
+
+ mdnie_class->dev_attrs = mdnie_attributes;
+
+ mdnie = kzalloc(sizeof(struct mdnie_info), GFP_KERNEL);
+ if (!mdnie) {
+ pr_err("failed to allocate mdnie\n");
+ ret = -ENOMEM;
+ goto error1;
+ }
+
+ mdnie->dev = device_create(mdnie_class, &pdev->dev, 0, &mdnie, "mdnie");
+ if (IS_ERR_OR_NULL(mdnie->dev)) {
+ pr_err("failed to create mdnie device\n");
+ ret = -EINVAL;
+ goto error2;
+ }
+
+#if defined(CONFIG_FB_MDNIE_PWM)
+ if (!pdata) {
+ pr_err("no platform data specified\n");
+ ret = -EINVAL;
+ goto error2;
+ }
+
+ mdnie->bd = backlight_device_register("panel", mdnie->dev,
+ mdnie, &mdnie_backlight_ops, NULL);
+ mdnie->bd->props.max_brightness = MAX_BRIGHTNESS_LEVEL;
+ mdnie->bd->props.brightness = DEFAULT_BRIGHTNESS;
+ mdnie->bd_enable = TRUE;
+ mdnie->lcd_pd = pdata->lcd_pd;
+
+ ret = device_create_file(&mdnie->bd->dev, &dev_attr_auto_brightness);
+ if (ret < 0)
+ dev_err(&mdnie->bd->dev, "failed to add sysfs entries, %d\n", __LINE__);
+#endif
+
+ mdnie->scenario = UI_MODE;
+ mdnie->mode = STANDARD;
+ mdnie->tone = TONE_NORMAL;
+ mdnie->outdoor = OUTDOOR_OFF;
+ mdnie->enable = TRUE;
+ mdnie->tuning = FALSE;
+ mdnie->negative = NEGATIVE_OFF;
+ mdnie->accessibility = ACCESSIBILITY_OFF;
+ mdnie->cabc = CABC_OFF;
+
+#if defined(CONFIG_FB_MDNIE_PWM)
+#if defined(CONFIG_FB_S5P_S6F1202A)
+ mdnie->cabc = CABC_ON;
+#endif
+ mdnie->power_lut_idx = LUT_LEVEL_MANUAL_AND_INDOOR;
+ mdnie->auto_brightness = 0;
+#endif
+
+#if defined(CONFIG_FB_EBOOK_PANEL_SCENARIO)
+ mdnie->ebook = EBOOK_OFF;
+#endif
+
+ mutex_init(&mdnie->lock);
+ mutex_init(&mdnie->dev_lock);
+
+ platform_set_drvdata(pdev, mdnie);
+ dev_set_drvdata(mdnie->dev, mdnie);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#if defined(CONFIG_FB_MDNIE_PWM)
+ mdnie->early_suspend.suspend = mdnie_early_suspend;
+#endif
+ mdnie->early_suspend.resume = mdnie_late_resume;
+ mdnie->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB - 1;
+ register_early_suspend(&mdnie->early_suspend);
+#endif
+
+
+#if defined(CONFIG_FB_MDNIE_PWM)
+ dev_info(mdnie->dev, "lcdtype = %d\n", pdata->display_type);
+ if (pdata->display_type > ARRAY_SIZE(backlight_table))
+ pdata->display_type = 0;
+ mdnie->backlight = &backlight_table[pdata->display_type];
+#endif
+
+#if defined(CONFIG_FB_S5P_S6F1202A)
+ if (pdata->display_type == 0) {
+ memcpy(tuning_table, tuning_table_hydis, sizeof(tuning_table));
+ memcpy(etc_table, etc_table_hydis, sizeof(etc_table));
+ memcpy(camera_table, camera_table_hydis, sizeof(camera_table));
+ } else if (pdata->display_type == 1) {
+ memcpy(tuning_table, tuning_table_sec, sizeof(tuning_table));
+ memcpy(etc_table, etc_table_sec, sizeof(etc_table));
+ memcpy(camera_table, camera_table_sec, sizeof(camera_table));
+ } else if (pdata->display_type == 2) {
+ memcpy(tuning_table, tuning_table_boe, sizeof(tuning_table));
+ memcpy(etc_table, etc_table_boe, sizeof(etc_table));
+ memcpy(camera_table, camera_table_boe, sizeof(camera_table));
+ }
+#endif
+
+ g_mdnie = mdnie;
+
+ mdnie_update(mdnie);
+
+ dev_info(mdnie->dev, "registered successfully\n");
+
+ return 0;
+
+error2:
+ kfree(mdnie);
+error1:
+ class_destroy(mdnie_class);
+error0:
+ return ret;
+}
+
+static int mdnie_remove(struct platform_device *pdev)
+{
+ struct mdnie_info *mdnie = dev_get_drvdata(&pdev->dev);
+
+#if defined(CONFIG_FB_MDNIE_PWM)
+ backlight_device_unregister(mdnie->bd);
+#endif
+ class_destroy(mdnie_class);
+ kfree(mdnie);
+
+ return 0;
+}
+
+static void mdnie_shutdown(struct platform_device *pdev)
+{
+#if defined(CONFIG_FB_MDNIE_PWM)
+ struct mdnie_info *mdnie = dev_get_drvdata(&pdev->dev);
+ struct lcd_platform_data *pd = NULL;
+ pd = mdnie->lcd_pd;
+
+ dev_info(mdnie->dev, "+%s\n", __func__);
+
+ mdnie->bd_enable = FALSE;
+
+ if (mdnie->enable)
+ mdnie_pwm_control(mdnie, 0);
+
+ if (pd && pd->power_on)
+ pd->power_on(NULL, 0);
+
+ dev_info(mdnie->dev, "-%s\n", __func__);
+#endif
+}
+
+
+static struct platform_driver mdnie_driver = {
+ .driver = {
+ .name = "mdnie",
+ .owner = THIS_MODULE,
+ },
+ .probe = mdnie_probe,
+ .remove = mdnie_remove,
+#ifndef CONFIG_HAS_EARLYSUSPEND
+ .suspend = mdnie_suspend,
+ .resume = mdnie_resume,
+#endif
+ .shutdown = mdnie_shutdown,
+};
+
+static int __init mdnie_init(void)
+{
+ return platform_driver_register(&mdnie_driver);
+}
+module_init(mdnie_init);
+
+static void __exit mdnie_exit(void)
+{
+ platform_driver_unregister(&mdnie_driver);
+}
+module_exit(mdnie_exit);
+
+MODULE_DESCRIPTION("mDNIe Driver");
+MODULE_LICENSE("GPL");
+