lineage_kernel_xcoverpro/drivers/video/fbdev/exynos/dpu20/panels/mdnie.c

1284 lines
34 KiB
C
Raw Permalink Normal View History

2023-06-18 22:53:49 +00:00
/*
* Copyright (c) Samsung Electronics Co., Ltd.
*
* 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/delay.h>
#include <linux/lcd.h>
#include <linux/fb.h>
#include <linux/pm_runtime.h>
#include "../decon_notify.h"
#include "mdnie.h"
#include "dd.h"
#ifdef CONFIG_DISPLAY_USE_INFO
#include "dpui.h"
#endif
#define IS_DMB(idx) (idx == DMB_NORMAL_MODE)
#define IS_SCENARIO(idx) (idx < SCENARIO_MAX && !(idx > VIDEO_NORMAL_MODE && idx < CAMERA_MODE))
#define IS_ACCESSIBILITY(idx) (idx && idx < ACCESSIBILITY_MAX)
#define IS_COLOR_LENS(idx) (idx && idx < COLOR_LENS_MAX)
#define IS_HBM(idx) (idx && idx < HBM_MAX)
#define IS_HMT(idx) (idx && idx < HMT_MDNIE_MAX)
#define IS_NIGHT_MODE(idx) (idx && idx < NIGHT_MODE_MAX)
#define IS_LIGHT_NOTIFICATION(idx) (idx && idx < LIGHT_NOTIFICATION_MAX)
#define IS_HDR(idx) (idx && idx < HDR_MAX)
#define SCENARIO_IS_VALID(idx) (IS_DMB(idx) || IS_SCENARIO(idx))
#define WRGB_IS_VALID(_x) ((_x <= 0) && (_x >= -30))
/* Split 16 bit as 8bit x 2 */
#define GET_MSB_8BIT(x) ((x >> 8) & (BIT(8) - 1))
#define GET_LSB_8BIT(x) ((x >> 0) & (BIT(8) - 1))
static struct class *mdnie_class;
/* Do not call mdnie write directly */
static int mdnie_write(struct mdnie_info *mdnie, struct mdnie_table *table, unsigned int num)
{
int ret = 0;
if (mdnie->enable)
ret = mdnie->ops.write(mdnie->data, table->seq, num);
return ret;
}
static int mdnie_write_table(struct mdnie_info *mdnie, struct mdnie_table *table)
{
int i, ret = 0;
struct mdnie_table *buf = NULL;
for (i = 0; table->seq[i].len; i++) {
if (IS_ERR_OR_NULL(table->seq[i].cmd)) {
dev_info(mdnie->dev, "mdnie sequence %s %dth is null\n", table->name, i);
return -EPERM;
}
}
mutex_lock(&mdnie->dev_lock);
buf = table;
ret = mdnie_write(mdnie, buf, i);
mutex_unlock(&mdnie->dev_lock);
return ret;
}
static struct mdnie_table *mdnie_find_table(struct mdnie_info *mdnie)
{
struct mdnie_table *table = NULL;
struct mdnie_trans_info *trans_info = mdnie->tune->trans_info;
mutex_lock(&mdnie->lock);
if (IS_LIGHT_NOTIFICATION(mdnie->light_notification)) {
table = mdnie->tune->light_notification_table ? &mdnie->tune->light_notification_table[mdnie->light_notification] : NULL;
goto exit;
} else if (IS_ACCESSIBILITY(mdnie->accessibility)) {
table = mdnie->tune->accessibility_table ? &mdnie->tune->accessibility_table[mdnie->accessibility] : NULL;
goto exit;
} else if (IS_COLOR_LENS(mdnie->color_lens)) {
table = mdnie->tune->lens_table ? &mdnie->tune->lens_table[mdnie->color_lens] : NULL;
goto exit;
} else if (IS_HMT(mdnie->hmt_mode)) {
table = mdnie->tune->hmt_table ? &mdnie->tune->hmt_table[mdnie->hmt_mode] : NULL;
goto exit;
} else if (IS_NIGHT_MODE(mdnie->night_mode)) {
table = mdnie->tune->night_table ? &mdnie->tune->night_table[mdnie->night_mode] : NULL;
goto exit;
} else if (IS_HBM(mdnie->hbm)) {
table = mdnie->tune->hbm_table ? &mdnie->tune->hbm_table[mdnie->hbm] : NULL;
goto exit;
} else if (IS_HDR(mdnie->hdr)) {
table = mdnie->tune->hdr_table ? &mdnie->tune->hdr_table[mdnie->hdr] : NULL;
goto exit;
} else if (IS_DMB(mdnie->scenario)) {
table = mdnie->tune->dmb_table ? &mdnie->tune->dmb_table[mdnie->mode] : NULL;
goto exit;
} else if (IS_SCENARIO(mdnie->scenario)) {
table = mdnie->tune->main_table ? &mdnie->tune->main_table[mdnie->scenario][mdnie->mode] : NULL;
goto exit;
}
exit:
if (trans_info->enable && mdnie->disable_trans_dimming && (table != NULL)) {
dev_info(mdnie->dev, "%s: disable_trans_dimming=%d\n", __func__, mdnie->disable_trans_dimming);
memcpy(&(mdnie->table_buffer), table, sizeof(struct mdnie_table));
memcpy(mdnie->sequence_buffer, table->seq[trans_info->index].cmd, table->seq[trans_info->index].len);
mdnie->table_buffer.seq[trans_info->index].cmd = mdnie->sequence_buffer;
mdnie->table_buffer.seq[trans_info->index].cmd[trans_info->offset] = 0x0;
mutex_unlock(&mdnie->lock);
return &(mdnie->table_buffer);
}
mutex_unlock(&mdnie->lock);
return table;
}
static void mdnie_update_sequence(struct mdnie_info *mdnie, struct mdnie_table *table)
{
mdnie_renew_table(mdnie, table);
mdnie_write_table(mdnie, table);
}
void mdnie_update(struct mdnie_info *mdnie)
{
struct mdnie_table *table = NULL;
struct mdnie_scr_info *scr_info = mdnie->tune->scr_info;
if (!mdnie->enable) {
dev_err(mdnie->dev, "mdnie state is off\n");
return;
}
table = mdnie_find_table(mdnie);
if (!IS_ERR_OR_NULL(table) && !IS_ERR_OR_NULL(table->name)) {
mdnie_update_sequence(mdnie, table);
dev_info(mdnie->dev, "%s\n", table->name);
mdnie->wrgb_current.r = table->seq[scr_info->index].cmd[scr_info->wr];
mdnie->wrgb_current.g = table->seq[scr_info->index].cmd[scr_info->wg];
mdnie->wrgb_current.b = table->seq[scr_info->index].cmd[scr_info->wb];
}
}
static void update_color_position(struct mdnie_info *mdnie, unsigned int idx)
{
u8 mode, scenario;
mdnie_t *wbuf;
struct mdnie_scr_info *scr_info = mdnie->tune->scr_info;
dev_info(mdnie->dev, "%s: %d\n", __func__, idx);
mutex_lock(&mdnie->lock);
for (mode = 0; mode < MODE_MAX; mode++) {
for (scenario = 0; scenario <= EMAIL_MODE; scenario++) {
wbuf = mdnie->tune->main_table[scenario][mode].seq[scr_info->index].cmd;
if (IS_ERR_OR_NULL(wbuf))
continue;
if (scenario != EBOOK_MODE && mode != EBOOK) {
wbuf[scr_info->wr] = mdnie->tune->coordinate_table[mode][idx * 3 + 0];
wbuf[scr_info->wg] = mdnie->tune->coordinate_table[mode][idx * 3 + 1];
wbuf[scr_info->wb] = mdnie->tune->coordinate_table[mode][idx * 3 + 2];
#ifdef CONFIG_SEC_FACTORY
if (mode == AUTO) {
wbuf[scr_info->wr] = mdnie->tune->coordinate_table[mode][idx * 3 + 0] + mdnie->wrgb_balance.r;
wbuf[scr_info->wg] = mdnie->tune->coordinate_table[mode][idx * 3 + 1] + mdnie->wrgb_balance.g;
wbuf[scr_info->wb] = mdnie->tune->coordinate_table[mode][idx * 3 + 2] + mdnie->wrgb_balance.b;
}
#endif
}
if (mode == AUTO && scenario == UI_MODE) {
mdnie->wrgb_default.r = mdnie->tune->coordinate_table[mode][idx * 3 + 0];
mdnie->wrgb_default.g = mdnie->tune->coordinate_table[mode][idx * 3 + 1];
mdnie->wrgb_default.b = mdnie->tune->coordinate_table[mode][idx * 3 + 2];
dev_info(mdnie->dev, "%s: %d, %d, %d\n",
__func__, mdnie->wrgb_default.r, mdnie->wrgb_default.g, mdnie->wrgb_default.b);
}
}
}
mutex_unlock(&mdnie->lock);
}
static int mdnie_calibration(int *r)
{
int ret = 0;
if (r[1] > 0) {
if (r[3] > 0)
ret = 3;
else
ret = (r[4] < 0) ? 1 : 2;
} else {
if (r[2] < 0) {
if (r[3] > 0)
ret = 9;
else
ret = (r[4] < 0) ? 7 : 8;
} else {
if (r[3] > 0)
ret = 6;
else
ret = (r[4] < 0) ? 4 : 5;
}
}
pr_info("%d, %d, %d, %d, tune%d\n", r[1], r[2], r[3], r[4], ret);
return ret;
}
static int get_panel_coordinate(struct mdnie_info *mdnie, int *result)
{
int ret = 0;
unsigned short x, y;
x = mdnie->coordinate[0];
y = mdnie->coordinate[1];
if (!(x || y)) {
dev_info(mdnie->dev, "%s: %d, %d\n", __func__, x, y);
ret = -EINVAL;
goto skip_color_correction;
}
result[COLOR_OFFSET_FUNC_F1] = mdnie->tune->color_offset[COLOR_OFFSET_FUNC_F1](x, y);
result[COLOR_OFFSET_FUNC_F2] = mdnie->tune->color_offset[COLOR_OFFSET_FUNC_F2](x, y);
result[COLOR_OFFSET_FUNC_F3] = mdnie->tune->color_offset[COLOR_OFFSET_FUNC_F3](x, y);
result[COLOR_OFFSET_FUNC_F4] = mdnie->tune->color_offset[COLOR_OFFSET_FUNC_F4](x, y);
ret = mdnie_calibration(result);
dev_info(mdnie->dev, "%s: %d, %d, %d\n", __func__, x, y, ret);
skip_color_correction:
mdnie->color_correction = 1;
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 ret, idx, result[COLOR_OFFSET_FUNC_MAX] = {0,};
ret = kstrtouint(buf, 0, &value);
if (ret < 0)
return ret;
dev_info(dev, "%s: %d\n", __func__, value);
if (value >= MODE_MAX)
return -EINVAL;
mutex_lock(&mdnie->lock);
mdnie->mode = value;
mutex_unlock(&mdnie->lock);
if (!mdnie->color_correction) {
idx = get_panel_coordinate(mdnie, result);
if (idx > 0)
update_color_position(mdnie, idx);
}
mdnie_update(mdnie);
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 = 0;
int ret;
ret = kstrtouint(buf, 0, &value);
if (ret < 0)
return ret;
dev_info(dev, "%s: %d\n", __func__, value);
if (!SCENARIO_IS_VALID(value))
value = UI_MODE;
mutex_lock(&mdnie->lock);
mdnie->scenario = 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);
return sprintf(buf, "%d\n", mdnie->accessibility);
}
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 = 0, s[12] = {0, }, i = 0;
int ret;
mdnie_t *wbuf;
struct mdnie_scr_info *scr_info = mdnie->tune->scr_info;
ret = sscanf(buf, "%8d %8x %8x %8x %8x %8x %8x %8x %8x %8x %8x %8x %8x",
&value, &s[0], &s[1], &s[2], &s[3],
&s[4], &s[5], &s[6], &s[7], &s[8], &s[9], &s[10], &s[11]);
if (ret < 0)
return ret;
dev_info(dev, "%s: %d, %d\n", __func__, value, ret);
if (value >= ACCESSIBILITY_MAX)
return -EINVAL;
mutex_lock(&mdnie->lock);
mdnie->accessibility = value;
if (value == COLOR_BLIND) {
if (ret > ARRAY_SIZE(s) + 1) {
mutex_unlock(&mdnie->lock);
return -EINVAL;
}
wbuf = &mdnie->tune->accessibility_table[value].seq[scr_info->index].cmd[scr_info->cr];
while (i < ret - 1) {
wbuf[i * 2 + 0] = GET_LSB_8BIT(s[i]);
wbuf[i * 2 + 1] = GET_MSB_8BIT(s[i]);
i++;
}
dev_info(dev, "%s: %s\n", __func__, buf);
}
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, idx, result[COLOR_OFFSET_FUNC_MAX] = {0,};
if (!mdnie->color_correction)
return -EINVAL;
idx = get_panel_coordinate(mdnie, result);
for (i = COLOR_OFFSET_FUNC_F1; i < COLOR_OFFSET_FUNC_MAX; i++)
pos += sprintf(pos, "f%d: %d, ", i, result[i]);
pos += sprintf(pos, "tune%d\n", idx);
return pos - buf;
}
static ssize_t color_coordinate_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mdnie_info *mdnie = dev_get_drvdata(dev);
sprintf(buf, "%d, %d\n", mdnie->coordinate[0], mdnie->coordinate[1]);
return strlen(buf);
}
static ssize_t color_coordinate_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct mdnie_info *mdnie = dev_get_drvdata(dev);
int ret, idx, result[COLOR_OFFSET_FUNC_MAX] = {0,};
ret = sscanf(buf, "%8d %8d", &mdnie->coordinate[0], &mdnie->coordinate[1]);
if (ret < 0)
return ret;
dev_info(dev, "%s: %d, %d\n", __func__, mdnie->coordinate[0], mdnie->coordinate[1]);
idx = get_panel_coordinate(mdnie, result);
if (idx > 0)
update_color_position(mdnie, idx);
mdnie_update(mdnie);
return count;
}
static ssize_t bypass_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mdnie_info *mdnie = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", mdnie->bypass);
}
static ssize_t bypass_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct mdnie_info *mdnie = dev_get_drvdata(dev);
struct mdnie_table *table = NULL;
unsigned int value = 0;
int ret;
ret = kstrtouint(buf, 0, &value);
if (ret < 0)
return ret;
dev_info(dev, "%s: %d\n", __func__, value);
if (value >= BYPASS_MAX)
return -EINVAL;
value = (value) ? BYPASS_ON : BYPASS_OFF;
mutex_lock(&mdnie->lock);
mdnie->bypass = value;
mutex_unlock(&mdnie->lock);
table = &mdnie->tune->bypass_table[value];
if (!IS_ERR_OR_NULL(table)) {
mdnie_write_table(mdnie, table);
dev_info(mdnie->dev, "%s\n", table->name);
}
return count;
}
static ssize_t lux_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mdnie_info *mdnie = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", mdnie->hbm);
}
static ssize_t lux_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct mdnie_info *mdnie = dev_get_drvdata(dev);
unsigned int hbm = 0, update = 0;
int ret, value = 0;
ret = kstrtoint(buf, 0, &value);
if (ret < 0)
return ret;
if (!mdnie->tune->get_hbm_index)
return count;
mutex_lock(&mdnie->lock);
hbm = mdnie->tune->get_hbm_index(value);
update = (mdnie->hbm != hbm) ? 1 : 0;
mdnie->hbm = update ? hbm : mdnie->hbm;
mutex_unlock(&mdnie->lock);
if (update) {
dev_info(dev, "%s: %d\n", __func__, value);
mdnie_update(mdnie);
}
return count;
}
static ssize_t sensorRGB_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mdnie_info *mdnie = dev_get_drvdata(dev);
return sprintf(buf, "%d %d %d\n", mdnie->wrgb_current.r, mdnie->wrgb_current.g, mdnie->wrgb_current.b);
}
static ssize_t sensorRGB_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct mdnie_info *mdnie = dev_get_drvdata(dev);
struct mdnie_table *table = NULL;
unsigned int white_r = 0, white_g = 0, white_b = 0;
int ret;
struct mdnie_scr_info *scr_info = mdnie->tune->scr_info;
ret = sscanf(buf, "%8d %8d %8d", &white_r, &white_g, &white_b);
if (ret < 0)
return ret;
if (mdnie->enable
&& mdnie->accessibility == ACCESSIBILITY_OFF
&& !mdnie->ldu
&& mdnie->mode == AUTO
&& (mdnie->scenario == BROWSER_MODE || mdnie->scenario == EBOOK_MODE)) {
dev_info(dev, "%s: %d, %d, %d\n", __func__, white_r, white_g, white_b);
table = mdnie_find_table(mdnie);
memcpy(&mdnie->table_buffer, table, sizeof(struct mdnie_table));
memcpy(&mdnie->sequence_buffer, table->seq[scr_info->index].cmd, table->seq[scr_info->index].len);
mdnie->table_buffer.seq[scr_info->index].cmd = mdnie->sequence_buffer;
mdnie->table_buffer.seq[scr_info->index].cmd[scr_info->wr] = mdnie->wrgb_current.r = (unsigned char)white_r;
mdnie->table_buffer.seq[scr_info->index].cmd[scr_info->wg] = mdnie->wrgb_current.g = (unsigned char)white_g;
mdnie->table_buffer.seq[scr_info->index].cmd[scr_info->wb] = mdnie->wrgb_current.b = (unsigned char)white_b;
mdnie_update_sequence(mdnie, &mdnie->table_buffer);
}
return count;
}
static ssize_t whiteRGB_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mdnie_info *mdnie = dev_get_drvdata(dev);
return sprintf(buf, "%d %d %d\n", mdnie->wrgb_balance.r, mdnie->wrgb_balance.g, mdnie->wrgb_balance.b);
}
static ssize_t whiteRGB_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct mdnie_info *mdnie = dev_get_drvdata(dev);
mdnie_t *wbuf;
u8 scenario;
int white_r = 0, white_g = 0, white_b = 0;
int ret;
struct mdnie_scr_info *scr_info = mdnie->tune->scr_info;
ret = sscanf(buf, "%8d %8d %8d", &white_r, &white_g, &white_b);
if (ret < 0)
return ret;
dev_info(dev, "%s: %d, %d, %d\n", __func__, white_r, white_g, white_b);
if (!WRGB_IS_VALID(white_r) || !WRGB_IS_VALID(white_g) || !WRGB_IS_VALID(white_b))
return count;
if (mdnie->mode != AUTO)
return count;
mutex_lock(&mdnie->lock);
if (!mdnie->ldu) {
mdnie->wrgb_ldu.r = mdnie->wrgb_default.r;
mdnie->wrgb_ldu.g = mdnie->wrgb_default.g;
mdnie->wrgb_ldu.b = mdnie->wrgb_default.b;
}
for (scenario = 0; scenario < SCENARIO_MAX; scenario++) {
wbuf = mdnie->tune->main_table[scenario][mdnie->mode].seq[scr_info->index].cmd;
if (IS_ERR_OR_NULL(wbuf))
continue;
if (scenario != EBOOK_MODE) {
wbuf[scr_info->wr] = (unsigned char)(mdnie->wrgb_ldu.r + white_r);
wbuf[scr_info->wg] = (unsigned char)(mdnie->wrgb_ldu.g + white_g);
wbuf[scr_info->wb] = (unsigned char)(mdnie->wrgb_ldu.b + white_b);
mdnie->wrgb_balance.r = white_r;
mdnie->wrgb_balance.g = white_g;
mdnie->wrgb_balance.b = white_b;
}
}
if (!IS_ERR_OR_NULL(mdnie->tune->dmb_table)) {
wbuf = mdnie->tune->dmb_table[mdnie->mode].seq[scr_info->index].cmd;
if (!IS_ERR_OR_NULL(wbuf)) {
wbuf[scr_info->wr] = (unsigned char)(mdnie->wrgb_ldu.r + white_r);
wbuf[scr_info->wg] = (unsigned char)(mdnie->wrgb_ldu.g + white_g);
wbuf[scr_info->wb] = (unsigned char)(mdnie->wrgb_ldu.b + white_b);
mdnie->wrgb_balance.r = white_r;
mdnie->wrgb_balance.g = white_g;
mdnie->wrgb_balance.b = white_b;
}
}
mutex_unlock(&mdnie->lock);
mdnie_update(mdnie);
return count;
}
static ssize_t night_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mdnie_info *mdnie = dev_get_drvdata(dev);
return sprintf(buf, "%d %d\n", mdnie->night_mode, mdnie->night_mode_level);
}
static ssize_t night_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 enable = 0, level = 0, base_index;
int i;
int ret;
mdnie_t *wbuf;
struct mdnie_scr_info *scr_info = mdnie->tune->scr_info;
ret = sscanf(buf, "%8d %8d", &enable, &level);
if (ret < 0)
return ret;
dev_info(dev, "%s: %d, %d\n", __func__, enable, level);
if (IS_ERR_OR_NULL(mdnie->tune->night_table) || IS_ERR_OR_NULL(mdnie->tune->night_info))
return count;
if (!mdnie->tune->night_info->max_w || !mdnie->tune->night_info->max_h)
return count;
if (enable >= NIGHT_MODE_MAX)
return -EINVAL;
if (level >= mdnie->tune->night_info->max_h)
return -EINVAL;
mutex_lock(&mdnie->lock);
if (enable) {
wbuf = &mdnie->tune->night_table[enable].seq[scr_info->index].cmd[scr_info->cr];
base_index = mdnie->tune->night_info->max_w * level;
for (i = 0; i < mdnie->tune->night_info->max_w; i++)
wbuf[i] = mdnie->tune->night_mode_table[mdnie->mode][base_index + i];
}
mdnie->night_mode = enable;
mdnie->night_mode_level = level;
mutex_unlock(&mdnie->lock);
mdnie_update(mdnie);
return count;
}
static ssize_t mdnie_ldu_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mdnie_info *mdnie = dev_get_drvdata(dev);
return sprintf(buf, "%d %d %d\n", mdnie->wrgb_current.r, mdnie->wrgb_current.g, mdnie->wrgb_current.b);
}
static ssize_t mdnie_ldu_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct mdnie_info *mdnie = dev_get_drvdata(dev);
mdnie_t *wbuf;
u8 mode, scenario;
unsigned int idx = 0;
int ret;
struct mdnie_scr_info *scr_info = mdnie->tune->scr_info;
ret = kstrtouint(buf, 0, &idx);
if (ret < 0)
return ret;
dev_info(dev, "%s: %d\n", __func__, idx);
if (idx >= MODE_MAX)
return -EINVAL;
if (IS_ERR_OR_NULL(mdnie->tune->adjust_ldu_table))
return count;
mutex_lock(&mdnie->lock);
mdnie->ldu = idx;
for (mode = 0; mode < MODE_MAX; mode++) {
for (scenario = 0; scenario <= EMAIL_MODE; scenario++) {
wbuf = mdnie->tune->main_table[scenario][mode].seq[scr_info->index].cmd;
if (IS_ERR_OR_NULL(wbuf))
continue;
if (scenario != EBOOK_MODE && mode != EBOOK) {
if (mode == AUTO) {
wbuf[scr_info->wr] = mdnie->tune->adjust_ldu_table[mode][idx * 3 + 0] + mdnie->wrgb_balance.r;
wbuf[scr_info->wg] = mdnie->tune->adjust_ldu_table[mode][idx * 3 + 1] + mdnie->wrgb_balance.g;
wbuf[scr_info->wb] = mdnie->tune->adjust_ldu_table[mode][idx * 3 + 2] + mdnie->wrgb_balance.b;
mdnie->wrgb_ldu.r = mdnie->tune->adjust_ldu_table[mode][idx * 3 + 0];
mdnie->wrgb_ldu.g = mdnie->tune->adjust_ldu_table[mode][idx * 3 + 1];
mdnie->wrgb_ldu.b = mdnie->tune->adjust_ldu_table[mode][idx * 3 + 2];
} else {
wbuf[scr_info->wr] = mdnie->tune->adjust_ldu_table[mode][idx * 3 + 0];
wbuf[scr_info->wg] = mdnie->tune->adjust_ldu_table[mode][idx * 3 + 1];
wbuf[scr_info->wb] = mdnie->tune->adjust_ldu_table[mode][idx * 3 + 2];
}
}
}
}
mutex_unlock(&mdnie->lock);
mdnie_update(mdnie);
return count;
}
static ssize_t light_notification_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mdnie_info *mdnie = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", mdnie->light_notification);
}
static ssize_t light_notification_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 ret;
ret = kstrtouint(buf, 0, &value);
if (ret < 0)
return ret;
dev_info(dev, "%s: %d\n", __func__, value);
if (value >= LIGHT_NOTIFICATION_MAX)
return -EINVAL;
value = (value) ? LIGHT_NOTIFICATION_ON : LIGHT_NOTIFICATION_OFF;
mutex_lock(&mdnie->lock);
mdnie->light_notification = value;
mutex_unlock(&mdnie->lock);
mdnie_update(mdnie);
return count;
}
static ssize_t color_lens_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mdnie_info *mdnie = dev_get_drvdata(dev);
return sprintf(buf, "%d %d %d\n", mdnie->color_lens, mdnie->color_lens_color, mdnie->color_lens_level);
}
static ssize_t color_lens_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct mdnie_info *mdnie = dev_get_drvdata(dev);
unsigned int enable = 0, color = 0, level = 0, base_index;
int i;
int ret;
mdnie_t *wbuf;
struct mdnie_scr_info *scr_info = mdnie->tune->scr_info;
ret = sscanf(buf, "%8d %8d %8d", &enable, &color, &level);
if (ret < 0)
return ret;
dev_info(dev, "%s: %d, %d, %d\n", __func__, enable, color, level);
if (IS_ERR_OR_NULL(mdnie->tune->color_lens_table) || IS_ERR_OR_NULL(mdnie->tune->color_lens_info))
return count;
if (!mdnie->tune->color_lens_info->max_color || !mdnie->tune->color_lens_info->max_level || !mdnie->tune->color_lens_info->max_w)
return count;
if (enable >= COLOR_LENS_MAX)
return -EINVAL;
if ((color >= mdnie->tune->color_lens_info->max_color) || (level >= mdnie->tune->color_lens_info->max_level))
return -EINVAL;
mutex_lock(&mdnie->lock);
if (enable) {
wbuf = &mdnie->tune->lens_table[enable].seq[scr_info->index].cmd[scr_info->cr];
base_index = (mdnie->tune->color_lens_info->max_level * mdnie->tune->color_lens_info->max_w * color) + (mdnie->tune->color_lens_info->max_w * level);
for (i = 0; i < mdnie->tune->color_lens_info->max_w; i++)
wbuf[i] = mdnie->tune->color_lens_table[base_index + i];
}
mdnie->color_lens = enable;
mdnie->color_lens_color = color;
mdnie->color_lens_level = level;
mutex_unlock(&mdnie->lock);
mdnie_update(mdnie);
return count;
}
static ssize_t hdr_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mdnie_info *mdnie = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", mdnie->hdr);
}
static ssize_t hdr_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 ret;
ret = kstrtouint(buf, 0, &value);
if (ret < 0)
return ret;
dev_info(dev, "%s: %d\n", __func__, value);
if (value >= HDR_MAX)
return -EINVAL;
mutex_lock(&mdnie->lock);
mdnie->hdr = value;
mutex_unlock(&mdnie->lock);
mdnie_update(mdnie);
return count;
}
#ifdef CONFIG_LCD_HMT
static ssize_t hmtColorTemp_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mdnie_info *mdnie = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", mdnie->hmt_mode);
}
static ssize_t hmtColorTemp_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 ret;
ret = kstrtouint(buf, 0, &value);
if (ret < 0)
return ret;
if (value != mdnie->hmt_mode && value < HMT_MDNIE_MAX) {
mutex_lock(&mdnie->lock);
mdnie->hmt_mode = value;
mutex_unlock(&mdnie->lock);
mdnie_update(mdnie);
}
return count;
}
#endif
static DEVICE_ATTR(mode, 0664, mode_show, mode_store);
static DEVICE_ATTR(scenario, 0664, scenario_show, scenario_store);
static DEVICE_ATTR(accessibility, 0664, accessibility_show, accessibility_store);
static DEVICE_ATTR(color_correct, 0444, color_correct_show, NULL);
static DEVICE_ATTR(color_coordinate, 0000, color_coordinate_show, color_coordinate_store);
static DEVICE_ATTR(bypass, 0664, bypass_show, bypass_store);
static DEVICE_ATTR(lux, 0000, lux_show, lux_store);
static DEVICE_ATTR(sensorRGB, 0664, sensorRGB_show, sensorRGB_store);
static DEVICE_ATTR(whiteRGB, 0664, whiteRGB_show, whiteRGB_store);
static DEVICE_ATTR(night_mode, 0664, night_mode_show, night_mode_store);
static DEVICE_ATTR(mdnie_ldu, 0664, mdnie_ldu_show, mdnie_ldu_store);
static DEVICE_ATTR(light_notification, 0664, light_notification_show, light_notification_store);
static DEVICE_ATTR(color_lens, 0664, color_lens_show, color_lens_store);
static DEVICE_ATTR(hdr, 0664, hdr_show, hdr_store);
#ifdef CONFIG_LCD_HMT
static DEVICE_ATTR(hmt_color_temperature, 0664, hmtColorTemp_show, hmtColorTemp_store);
#endif
static struct attribute *mdnie_attrs[] = {
&dev_attr_mode.attr,
&dev_attr_scenario.attr,
&dev_attr_color_correct.attr,
&dev_attr_color_coordinate.attr,
&dev_attr_bypass.attr,
&dev_attr_lux.attr,
&dev_attr_light_notification.attr,
&dev_attr_hdr.attr,
#ifdef CONFIG_LCD_HMT
&dev_attr_hmt_color_temperature.attr,
#endif
NULL,
};
ATTRIBUTE_GROUPS(mdnie);
static const struct attribute *mdnie_scr_attrs[] = {
&dev_attr_accessibility.attr,
&dev_attr_sensorRGB.attr,
&dev_attr_whiteRGB.attr,
&dev_attr_night_mode.attr,
&dev_attr_mdnie_ldu.attr,
&dev_attr_color_lens.attr,
NULL,
};
static int fb_notifier_callback(struct notifier_block *self,
unsigned long event, void *data)
{
struct mdnie_info *mdnie;
struct fb_event *evdata = data;
int fb_blank;
switch (event) {
case FB_EVENT_BLANK:
case DECON_EVENT_DOZE:
break;
default:
return NOTIFY_DONE;
}
mdnie = container_of(self, struct mdnie_info, fb_notif);
fb_blank = *(int *)evdata->data;
dev_info(mdnie->dev, "%s: event: %lu, blank: %d\n", __func__, event, fb_blank);
if (evdata->info->node)
return NOTIFY_DONE;
if (event == DECON_EVENT_DOZE) {
mutex_lock(&mdnie->lock);
mdnie->lpm = 1;
mutex_unlock(&mdnie->lock);
} else if (event == FB_EVENT_BLANK) {
mutex_lock(&mdnie->lock);
mdnie->lpm = 0;
mutex_unlock(&mdnie->lock);
}
if (fb_blank == FB_BLANK_UNBLANK) {
mutex_lock(&mdnie->lock);
mdnie->light_notification = LIGHT_NOTIFICATION_OFF;
mdnie->enable = 1;
mutex_unlock(&mdnie->lock);
mdnie_update(mdnie);
if (mdnie->tune->trans_info->enable)
mdnie->disable_trans_dimming = 0;
} else if (fb_blank == FB_BLANK_POWERDOWN) {
mutex_lock(&mdnie->lock);
mdnie->enable = 0;
if (mdnie->tune->trans_info->enable)
mdnie->disable_trans_dimming = 1;
mutex_unlock(&mdnie->lock);
}
return NOTIFY_DONE;
}
static int mdnie_register_fb(struct mdnie_info *mdnie)
{
memset(&mdnie->fb_notif, 0, sizeof(mdnie->fb_notif));
mdnie->fb_notif.notifier_call = fb_notifier_callback;
return decon_register_notifier(&mdnie->fb_notif);
}
#ifdef CONFIG_DISPLAY_USE_INFO
static int dpui_notifier_callback(struct notifier_block *self,
unsigned long event, void *data)
{
struct mdnie_info *mdnie;
char tbuf[MAX_DPUI_VAL_LEN];
int size;
mdnie = container_of(self, struct mdnie_info, dpui_notif);
mutex_lock(&mdnie->lock);
size = snprintf(tbuf, MAX_DPUI_VAL_LEN, "%d", mdnie->coordinate[0]);
set_dpui_field(DPUI_KEY_WCRD_X, tbuf, size);
size = snprintf(tbuf, MAX_DPUI_VAL_LEN, "%d", mdnie->coordinate[1]);
set_dpui_field(DPUI_KEY_WCRD_Y, tbuf, size);
size = snprintf(tbuf, MAX_DPUI_VAL_LEN, "%d", mdnie->wrgb_balance.r);
set_dpui_field(DPUI_KEY_WOFS_R, tbuf, size);
size = snprintf(tbuf, MAX_DPUI_VAL_LEN, "%d", mdnie->wrgb_balance.g);
set_dpui_field(DPUI_KEY_WOFS_G, tbuf, size);
size = snprintf(tbuf, MAX_DPUI_VAL_LEN, "%d", mdnie->wrgb_balance.b);
set_dpui_field(DPUI_KEY_WOFS_B, tbuf, size);
mutex_unlock(&mdnie->lock);
return NOTIFY_DONE;
}
static int mdnie_register_dpui(struct mdnie_info *mdnie)
{
memset(&mdnie->dpui_notif, 0, sizeof(mdnie->dpui_notif));
mdnie->dpui_notif.notifier_call = dpui_notifier_callback;
return dpui_logging_register(&mdnie->dpui_notif, DPUI_TYPE_PANEL);
}
#endif /* CONFIG_DISPLAY_USE_INFO */
static struct mdnie_scr_info default_scr_info;
static struct mdnie_night_info default_night_info;
static struct mdnie_trans_info default_trans_info;
static int mdnie_check_info(struct mdnie_info *mdnie)
{
struct mdnie_scr_info *scr_info = mdnie->tune->scr_info;
struct mdnie_night_info *night_info = mdnie->tune->night_info;
struct mdnie_trans_info *trans_info = mdnie->tune->trans_info;
struct mdnie_table *table = NULL;
unsigned int index = 0, limit = 0;
int ret = 0;
table = mdnie->tune->main_table ? &mdnie->tune->main_table[mdnie->scenario][mdnie->mode] : NULL;
if (!table) {
pr_err("%s: failed to get initial mdnie table\n", __func__);
ret = -EINVAL;
goto exit;
}
if (scr_info && (scr_info->cr || scr_info->wr || scr_info->wg || scr_info->wb)) {
index = scr_info->index;
limit = max(scr_info->cr, max3(scr_info->wr, scr_info->wg, scr_info->wb));
if (index >= MDNIE_IDX_MAX) {
pr_err("%s: invalid scr_info index. %d\n", __func__, index);
ret = -EINVAL;
goto exit;
}
if (limit >= table->seq[index].len) {
pr_err("%s: invalid scr_info limit. %d, %d\n", __func__, limit, table->seq[index].len);
ret = -EINVAL;
goto exit;
}
}
if (scr_info && night_info && night_info->max_w) {
index = scr_info->index;
limit = scr_info->cr + night_info->max_w - 1;
if (index >= MDNIE_IDX_MAX) {
pr_err("%s: invalid night_info index. %d\n", __func__, index);
ret = -EINVAL;
goto exit;
}
if (limit >= table->seq[index].len) {
pr_err("%s: invalid night_info offset. %d, %d\n", __func__, limit, table->seq[index].len);
ret = -EINVAL;
goto exit;
}
}
if (trans_info && trans_info->enable) {
index = trans_info->index;
limit = trans_info->offset;
if (index >= MDNIE_IDX_MAX) {
pr_err("%s: invalid trans_info index. %d\n", __func__, index);
ret = -EINVAL;
goto exit;
}
if (limit >= table->seq[index].len) {
pr_err("%s: invalid trans_info offset. %d, %d\n", __func__, limit, table->seq[index].len);
ret = -EINVAL;
goto exit;
}
}
exit:
if (ret < 0)
pr_info("%s: skip to use mdnie\n", __func__);
else {
if (!scr_info) {
pr_info("%s: mdnie tune scr info as default\n", __func__);
mdnie->tune->scr_info = &default_scr_info;
}
if (!night_info) {
pr_info("%s: mdnie tune night info as default\n", __func__);
mdnie->tune->night_info = &default_night_info;
}
if (!trans_info) {
pr_info("%s: mdnie tune trans info as default\n", __func__);
mdnie->tune->trans_info = &default_trans_info;
}
}
return ret;
}
int mdnie_register(struct device *p, void *data, mdnie_w w, mdnie_r r,
unsigned int *coordinate, struct mdnie_tune *tune)
{
int ret = 0;
struct mdnie_info *mdnie;
static unsigned int mdnie_no;
if (!tune) {
pr_err("failed to get mdnie tune\n");
goto exit0;
}
mdnie = kzalloc(sizeof(struct mdnie_info), GFP_KERNEL);
if (!mdnie) {
pr_err("failed to allocate mdnie\n");
ret = -ENOMEM;
goto exit0;
}
mdnie->scenario = UI_MODE;
mdnie->mode = AUTO;
mdnie->accessibility = ACCESSIBILITY_OFF;
mdnie->bypass = BYPASS_OFF;
mdnie->night_mode = NIGHT_MODE_OFF;
mdnie->light_notification = LIGHT_NOTIFICATION_OFF;
mdnie->color_lens = COLOR_LENS_OFF;
mdnie->wrgb_default.r = mdnie->wrgb_ldu.r = 255;
mdnie->wrgb_default.g = mdnie->wrgb_ldu.r = 255;
mdnie->wrgb_default.b = mdnie->wrgb_ldu.r = 255;
mdnie->data = data;
mdnie->ops.write = w;
mdnie->ops.read = r;
mdnie->coordinate[0] = coordinate ? coordinate[0] : 0;
mdnie->coordinate[1] = coordinate ? coordinate[1] : 0;
mdnie->tune = tune;
ret = mdnie_check_info(mdnie);
if (ret < 0)
goto exit1;
if (IS_ERR_OR_NULL(mdnie_class)) {
mdnie_class = class_create(THIS_MODULE, "mdnie");
if (IS_ERR_OR_NULL(mdnie_class)) {
pr_err("failed to create mdnie class\n");
ret = -EINVAL;
goto exit1;
}
mdnie_class->dev_groups = mdnie_groups;
}
mdnie->dev = device_create(mdnie_class, p, 0, &mdnie, !mdnie_no ? "mdnie" : "mdnie%d", mdnie_no);
if (IS_ERR_OR_NULL(mdnie->dev)) {
pr_err("failed to create mdnie device\n");
ret = -EINVAL;
goto exit2;
}
if (tune->scr_info->cr && tune->scr_info->wr && tune->scr_info->wg && tune->scr_info->wb) {
ret = sysfs_create_files(&mdnie->dev->kobj, mdnie_scr_attrs);
if (ret < 0) {
pr_err("failed to create mdnie scr attributes\n");
goto exit3;
}
}
mutex_init(&mdnie->lock);
mutex_init(&mdnie->dev_lock);
dev_set_drvdata(mdnie->dev, mdnie);
mdnie_register_fb(mdnie);
#ifdef CONFIG_DISPLAY_USE_INFO
mdnie_register_dpui(mdnie);
#endif
mdnie->enable = 1;
init_debugfs_mdnie(mdnie, mdnie_no);
mdnie_update(mdnie);
dev_info(mdnie->dev, "registered successfully\n");
mdnie_no++;
return 0;
exit3:
device_unregister(mdnie->dev);
exit2:
class_destroy(mdnie_class);
exit1:
kfree(mdnie);
exit0:
return ret;
}
static int attr_find_and_store(struct device *dev,
const char *name, const char *buf, size_t size)
{
struct device_attribute *dev_attr;
struct kernfs_node *kn;
struct attribute *attr;
kn = kernfs_find_and_get(dev->kobj.sd, name);
if (!kn) {
dev_info(dev, "%s: not found: %s\n", __func__, name);
return 0;
}
attr = kn->priv;
dev_attr = container_of(attr, struct device_attribute, attr);
if (dev_attr && dev_attr->store)
dev_attr->store(dev, dev_attr, buf, size);
kernfs_put(kn);
return 0;
}
ssize_t attr_store_for_each(struct class *cls,
const char *name, const char *buf, size_t size)
{
struct class_dev_iter iter;
struct device *dev;
int error = 0;
struct class *class = cls;
if (!class)
return -EINVAL;
if (!class->p) {
WARN(1, "%s called for class '%s' before it was initialized",
__func__, class->name);
return -EINVAL;
}
class_dev_iter_init(&iter, class, NULL, NULL);
while ((dev = class_dev_iter_next(&iter))) {
error = attr_find_and_store(dev, name, buf, size);
if (error)
break;
}
class_dev_iter_exit(&iter);
return error;
}
struct class *get_mdnie_class(void)
{
return mdnie_class;
}