@@ -0,0 +1,678 @@
/*
* Copyright (c) 2015 The CyanogenMod Project
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/

#include <linux/err.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/sysfs.h>

#include "mdss_dsi.h"
#include "mdss_fb.h"
#include "mdss_mdp.h"
#include "mdss_livedisplay.h"

/*
* LiveDisplay is the display management service in CyanogenMod. It uses
* various capabilities of the hardware and software in order to
* optimize the experience for ambient conditions and time of day.
*
* This module is initialized by mdss_fb for each panel, and creates
* several new controls in /sys/class/graphics/fbX based on the
* configuration in the devicetree.
*
* rgb: Always available with MDSS. Used for color temperature and
* user-level calibration. Takes a string of "r g b".
*
* cabc: Content Adaptive Backlight Control. Must be configured
* in the panel devicetree. Up to three levels.
* sre: Sunlight Readability Enhancement. Must be configured in
* the panel devicetree. Up to three levels.
* aco: Automatic Contrast Optimization. Must be configured in
* the panel devicetree. Boolean.
*
* preset: Arbitrary DSI commands, up to 10 may be configured.
* Useful for gamma calibration.
*
* color_enhance: Hardware color enhancement. Must be configured
* in the panel devicetree. Boolean.
*/

extern void mdss_dsi_panel_cmds_send(struct mdss_dsi_ctrl_pdata *ctrl,
struct dsi_panel_cmds *pcmds);

static int parse_dsi_cmds(struct dsi_panel_cmds *pcmds, const uint8_t *cmd, int blen)
{
int len;
char *buf, *bp;
struct dsi_ctrl_hdr *dchdr;
int i, cnt;

buf = kzalloc(blen, GFP_KERNEL);
if (!buf)
return -ENOMEM;

memcpy(buf, cmd, blen);

/* scan dcs commands */
bp = buf;
len = blen;
cnt = 0;
while (len >= sizeof(*dchdr)) {
dchdr = (struct dsi_ctrl_hdr *)bp;
dchdr->dlen = ntohs(dchdr->dlen);
if (dchdr->dlen > len) {
pr_err("%s: dtsi cmd=%x error, len=%d\n",
__func__, dchdr->dtype, dchdr->dlen);
goto exit_free;
}
bp += sizeof(*dchdr);
len -= sizeof(*dchdr);
bp += dchdr->dlen;
len -= dchdr->dlen;
cnt++;
}

if (len != 0) {
pr_err("%s: dcs_cmd=%x len=%d error!\n",
__func__, buf[0], blen);
goto exit_free;
}

pcmds->cmds = kzalloc(cnt * sizeof(struct dsi_cmd_desc),
GFP_KERNEL);
if (!pcmds->cmds)
goto exit_free;

pcmds->cmd_cnt = cnt;
pcmds->buf = buf;
pcmds->blen = blen;

bp = buf;
len = blen;
for (i = 0; i < cnt; i++) {
dchdr = (struct dsi_ctrl_hdr *)bp;
len -= sizeof(*dchdr);
bp += sizeof(*dchdr);
pcmds->cmds[i].dchdr = *dchdr;
pcmds->cmds[i].payload = bp;
bp += dchdr->dlen;
len -= dchdr->dlen;
}

/*Set default link state to HS Mode*/
pcmds->link_state = DSI_HS_MODE;

pr_debug("%s: dcs_cmd=%x len=%d, cmd_cnt=%d link_state=%d\n", __func__,
pcmds->buf[0], pcmds->blen, pcmds->cmd_cnt, pcmds->link_state);

return 0;

exit_free:
kfree(buf);
return -ENOMEM;
}

/**
* simple color temperature interface using polynomial color correction
*
* input values are r/g/b adjustments from 0-32768 representing 0 -> 1
*
* example adjustment @ 3500K:
* 1.0000 / 0.5515 / 0.2520 = 32768 / 25828 / 17347
*
* reference chart:
* http://www.vendian.org/mncharity/dir3/blackbody/UnstableURLs/bbr_color.html
*/
static int mdss_livedisplay_set_rgb_locked(struct msm_fb_data_type *mfd)
{
static struct mdp_pcc_cfg_data pcc_cfg;
struct mdss_livedisplay_ctx *mlc;

mlc = get_ctx(mfd);

if (mlc == NULL)
return -ENODEV;

pr_info("%s: r=%d g=%d b=%d\n", __func__, mlc->r, mlc->g, mlc->b);

memset(&pcc_cfg, 0, sizeof(struct mdp_pcc_cfg_data));

pcc_cfg.block = mfd->index + MDP_LOGICAL_BLOCK_DISP_0;
if (mlc->r == 32768 && mlc->g == 32768 && mlc->b == 32768)
pcc_cfg.ops = MDP_PP_OPS_DISABLE;
else
pcc_cfg.ops = MDP_PP_OPS_ENABLE;
pcc_cfg.ops |= MDP_PP_OPS_WRITE;
pcc_cfg.r.r = mlc->r;
pcc_cfg.g.g = mlc->g;
pcc_cfg.b.b = mlc->b;

return mdss_mdp_user_pcc_config(&pcc_cfg);
}

/*
* Update all or a subset of parameters
*/
static int mdss_livedisplay_update_locked(struct mdss_dsi_ctrl_pdata *ctrl_pdata,
int types)
{
int ret = 0;
struct mdss_panel_info *pinfo = NULL;
struct mdss_livedisplay_ctx *mlc = NULL;
unsigned int len = 0, dlen = 0;
struct dsi_panel_cmds dsi_cmds;
uint8_t cabc_value = 0;
uint8_t *cmd_buf;

if (ctrl_pdata == NULL)
return -ENODEV;

pinfo = &(ctrl_pdata->panel_data.panel_info);
if (pinfo == NULL)
return -ENODEV;

mlc = pinfo->livedisplay;
if (mlc == NULL)
return -ENODEV;

if (!mlc->caps || !mdss_panel_is_power_on_interactive(pinfo->panel_power_state))
return 0;

// First find the length of the command array
if ((mlc->caps & MODE_PRESET) && (types & MODE_PRESET))
len += mlc->presets_len[mlc->preset];

if ((mlc->caps & MODE_COLOR_ENHANCE) && (types & MODE_COLOR_ENHANCE))
len += mlc->ce_enabled ? mlc->ce_on_cmds_len : mlc->ce_off_cmds_len;

if (is_cabc_cmd(types) && is_cabc_cmd(mlc->caps)) {

// The CABC command on most modern panels is also responsible for
// other features such as SRE and ACO. The register fields are bits
// and are OR'd together and sent in a single DSI command.
if (mlc->cabc_level == CABC_UI)
cabc_value |= mlc->cabc_ui_value;
else if (mlc->cabc_level == CABC_IMAGE)
cabc_value |= mlc->cabc_image_value;
else if (mlc->cabc_level == CABC_VIDEO)
cabc_value |= mlc->cabc_video_value;

if (mlc->sre_level == SRE_WEAK)
cabc_value |= mlc->sre_weak_value;
else if (mlc->sre_level == SRE_MEDIUM)
cabc_value |= mlc->sre_medium_value;
else if (mlc->sre_level == SRE_STRONG)
cabc_value |= mlc->sre_strong_value;

if (mlc->aco_enabled)
cabc_value |= mlc->aco_value;

len += mlc->cabc_cmds_len;

pr_info("%s cabc=%d sre=%d aco=%d cmd=%d\n", __func__,
mlc->cabc_level, mlc->sre_level, mlc->aco_enabled,
cabc_value);
}

len += mlc->post_cmds_len;

if (len == 0)
return 0;

memset(&dsi_cmds, 0, sizeof(struct dsi_panel_cmds));
cmd_buf = kzalloc(len + 1, GFP_KERNEL);
if (!cmd_buf)
return -ENOMEM;

// Build the command as a single chain, preset first
if ((mlc->caps & MODE_PRESET) && (types & MODE_PRESET)) {
memcpy(cmd_buf, mlc->presets[mlc->preset], mlc->presets_len[mlc->preset]);
dlen += mlc->presets_len[mlc->preset];
}

// Color enhancement
if ((mlc->caps & MODE_COLOR_ENHANCE) && (types & MODE_COLOR_ENHANCE)) {
if (mlc->ce_enabled) {
memcpy(cmd_buf + dlen, mlc->ce_on_cmds, mlc->ce_on_cmds_len);
dlen += mlc->ce_on_cmds_len;
} else {
memcpy(cmd_buf + dlen, mlc->ce_off_cmds, mlc->ce_off_cmds_len);
dlen += mlc->ce_off_cmds_len;
}
}

// CABC/SRE/ACO features
if (is_cabc_cmd(types) && mlc->cabc_cmds_len) {
memcpy(cmd_buf + dlen, mlc->cabc_cmds, mlc->cabc_cmds_len);
dlen += mlc->cabc_cmds_len;
// The CABC command parameter is the last value in the sequence
cmd_buf[dlen - 1] = cabc_value;
}

// And the post_cmd, can be used to turn on the panel
if (mlc->post_cmds_len) {
memcpy(cmd_buf + dlen, mlc->post_cmds, mlc->post_cmds_len);
dlen += mlc->post_cmds_len;
}

// Parse the command and send it
ret = parse_dsi_cmds(&dsi_cmds, (const uint8_t *)cmd_buf, len);
if (ret == 0) {
mdss_dsi_panel_cmds_send(ctrl_pdata, &dsi_cmds);
} else {
pr_err("%s: error parsing DSI command! ret=%d", __func__, ret);
}

kfree(cmd_buf);

// Restore saved RGB settings
mdss_livedisplay_set_rgb_locked(mlc->mfd);

return ret;
}

int mdss_livedisplay_update(struct mdss_dsi_ctrl_pdata *ctrl_pdata,
int types)
{
struct mdss_panel_info *pinfo;
struct mdss_livedisplay_ctx *mlc;
int ret = 0;

pinfo = &(ctrl_pdata->panel_data.panel_info);
if (pinfo == NULL)
return -ENODEV;

mlc = pinfo->livedisplay;
if (mlc == NULL)
return -ENODEV;

if (mlc->mfd == NULL)
return -ENODEV;

mutex_lock(&mlc->lock);
ret = mdss_livedisplay_update_locked(ctrl_pdata, types);
mutex_unlock(&mlc->lock);

return ret;
}

static ssize_t mdss_livedisplay_get_cabc(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct fb_info *fbi = dev_get_drvdata(dev);
struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par;
struct mdss_livedisplay_ctx *mlc = get_ctx(mfd);

return sprintf(buf, "%d\n", mlc->cabc_level);
}

static ssize_t mdss_livedisplay_set_cabc(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int level = 0;
struct fb_info *fbi = dev_get_drvdata(dev);
struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par;
struct mdss_livedisplay_ctx *mlc = get_ctx(mfd);

mutex_lock(&mlc->lock);

sscanf(buf, "%du", &level);
if (level >= CABC_OFF && level < CABC_MAX &&
level != mlc->cabc_level) {
mlc->cabc_level = level;
mdss_livedisplay_update_locked(get_ctrl(mfd), MODE_CABC);
}

mutex_unlock(&mlc->lock);

return count;
}

static ssize_t mdss_livedisplay_get_sre(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct fb_info *fbi = dev_get_drvdata(dev);
struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par;
struct mdss_livedisplay_ctx *mlc = get_ctx(mfd);

return sprintf(buf, "%d\n", mlc->sre_level);
}

static ssize_t mdss_livedisplay_set_sre(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int level = 0;
struct fb_info *fbi = dev_get_drvdata(dev);
struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par;
struct mdss_livedisplay_ctx *mlc = get_ctx(mfd);

mutex_lock(&mlc->lock);

sscanf(buf, "%du", &level);
if (level >= SRE_OFF && level < SRE_MAX &&
level != mlc->sre_level) {
mlc->sre_level = level;
mdss_livedisplay_update_locked(get_ctrl(mfd), MODE_SRE);
}

mutex_unlock(&mlc->lock);

return count;
}

static ssize_t mdss_livedisplay_get_color_enhance(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct fb_info *fbi = dev_get_drvdata(dev);
struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par;
struct mdss_livedisplay_ctx *mlc = get_ctx(mfd);

return sprintf(buf, "%d\n", mlc->ce_enabled);
}

static ssize_t mdss_livedisplay_set_color_enhance(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int value = 0;
struct fb_info *fbi = dev_get_drvdata(dev);
struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par;
struct mdss_livedisplay_ctx *mlc = get_ctx(mfd);

mutex_lock(&mlc->lock);

sscanf(buf, "%du", &value);
if ((value == 0 || value == 1)
&& value != mlc->ce_enabled) {
mlc->ce_enabled = value;
mdss_livedisplay_update_locked(get_ctrl(mfd), MODE_COLOR_ENHANCE);
}

mutex_unlock(&mlc->lock);

return count;
}

static ssize_t mdss_livedisplay_get_aco(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct fb_info *fbi = dev_get_drvdata(dev);
struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par;
struct mdss_livedisplay_ctx *mlc = get_ctx(mfd);

return sprintf(buf, "%d\n", mlc->aco_enabled);
}

static ssize_t mdss_livedisplay_set_aco(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int value = 0;
struct fb_info *fbi = dev_get_drvdata(dev);
struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par;
struct mdss_livedisplay_ctx *mlc = get_ctx(mfd);

mutex_lock(&mlc->lock);

sscanf(buf, "%du", &value);
if ((value == 0 || value == 1)
&& value != mlc->aco_enabled) {
mlc->aco_enabled = value;
mdss_livedisplay_update_locked(get_ctrl(mfd), MODE_AUTO_CONTRAST);
}

mutex_unlock(&mlc->lock);

return count;
}

static ssize_t mdss_livedisplay_get_preset(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct fb_info *fbi = dev_get_drvdata(dev);
struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par;
struct mdss_livedisplay_ctx *mlc = get_ctx(mfd);

return sprintf(buf, "%d\n", mlc->preset);
}

static ssize_t mdss_livedisplay_set_preset(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int value = 0;
struct fb_info *fbi = dev_get_drvdata(dev);
struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par;
struct mdss_livedisplay_ctx *mlc = get_ctx(mfd);

mutex_lock(&mlc->lock);

sscanf(buf, "%du", &value);
if (value < 0 || value >= mlc->num_presets)
return -EINVAL;

mlc->preset = value;
mdss_livedisplay_update_locked(get_ctrl(mfd), MODE_PRESET);

mutex_unlock(&mlc->lock);

return count;
}

static ssize_t mdss_livedisplay_get_num_presets(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct fb_info *fbi = dev_get_drvdata(dev);
struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par;
struct mdss_livedisplay_ctx *mlc = get_ctx(mfd);

return sprintf(buf, "%d\n", mlc->num_presets);
}

static ssize_t mdss_livedisplay_get_rgb(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct fb_info *fbi = dev_get_drvdata(dev);
struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par;
struct mdss_livedisplay_ctx *mlc;

if (mfd == NULL)
return -ENODEV;

mlc = get_ctx(mfd);

return scnprintf(buf, PAGE_SIZE, "%d %d %d\n",
mlc->r, mlc->g, mlc->b);
}

static ssize_t mdss_livedisplay_set_rgb(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
uint32_t r = 0, g = 0, b = 0;
struct fb_info *fbi = dev_get_drvdata(dev);
struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par;
struct mdss_panel_data *pdata;
struct mdss_livedisplay_ctx *mlc;
int ret = -EINVAL;

if (mfd == NULL)
return -ENODEV;

if (count > 19)
return -EINVAL;

mlc = get_ctx(mfd);
pdata = dev_get_platdata(&mfd->pdev->dev);

sscanf(buf, "%d %d %d", &r, &g, &b);

if (r < 0 || r > 32768)
return -EINVAL;
if (g < 0 || g > 32768)
return -EINVAL;
if (b < 0 || b > 32768)
return -EINVAL;

mutex_lock(&mlc->lock);

mlc->r = r;
mlc->g = g;
mlc->b = b;

if (!mdss_panel_is_power_on_interactive(mfd->panel_power_state) ||
(mdss_livedisplay_set_rgb_locked(mfd) == 0))
ret = count;

mutex_unlock(&mlc->lock);

return ret;
}

static DEVICE_ATTR(cabc, S_IRUGO | S_IWUSR | S_IWGRP, mdss_livedisplay_get_cabc, mdss_livedisplay_set_cabc);
static DEVICE_ATTR(sre, S_IRUGO | S_IWUSR | S_IWGRP, mdss_livedisplay_get_sre, mdss_livedisplay_set_sre);
static DEVICE_ATTR(color_enhance, S_IRUGO | S_IWUSR | S_IWGRP, mdss_livedisplay_get_color_enhance, mdss_livedisplay_set_color_enhance);
static DEVICE_ATTR(aco, S_IRUGO | S_IWUSR | S_IWGRP, mdss_livedisplay_get_aco, mdss_livedisplay_set_aco);
static DEVICE_ATTR(preset, S_IRUGO | S_IWUSR | S_IWGRP, mdss_livedisplay_get_preset, mdss_livedisplay_set_preset);
static DEVICE_ATTR(num_presets, S_IRUGO, mdss_livedisplay_get_num_presets, NULL);
static DEVICE_ATTR(rgb, S_IRUGO | S_IWUSR | S_IWGRP, mdss_livedisplay_get_rgb, mdss_livedisplay_set_rgb);

int mdss_livedisplay_parse_dt(struct device_node *np, struct mdss_panel_info *pinfo)
{
int rc = 0, i = 0;
struct mdss_livedisplay_ctx *mlc;
char preset_name[64];
uint32_t tmp = 0;

if (pinfo == NULL)
return -ENODEV;

mlc = kzalloc(sizeof(struct mdss_livedisplay_ctx), GFP_KERNEL);
mutex_init(&mlc->lock);

mlc->cabc_cmds = of_get_property(np,
"cm,mdss-livedisplay-cabc-cmd", &mlc->cabc_cmds_len);

if (mlc->cabc_cmds_len > 0) {
rc = of_property_read_u32(np, "cm,mdss-livedisplay-cabc-ui-value", &tmp);
if (rc == 0) {
mlc->caps |= MODE_CABC;
mlc->cabc_ui_value = (uint8_t)(tmp & 0xFF);
of_property_read_u32(np, "cm,mdss-livedisplay-cabc-image-value", &tmp);
mlc->cabc_image_value = (uint8_t)(tmp & 0xFF);
of_property_read_u32(np, "cm,mdss-livedisplay-cabc-video-value", &tmp);
mlc->cabc_video_value = (uint8_t)(tmp & 0xFF);
}
rc = of_property_read_u32(np, "cm,mdss-livedisplay-sre-medium-value", &tmp);
if (rc == 0) {
mlc->caps |= MODE_SRE;
mlc->sre_medium_value = (uint8_t)(tmp & 0xFF);
of_property_read_u32(np, "cm,mdss-livedisplay-sre-weak-value", &tmp);
mlc->sre_weak_value = (uint8_t)(tmp & 0xFF);
of_property_read_u32(np, "cm,mdss-livedisplay-sre-strong-value", &tmp);
mlc->sre_strong_value = (uint8_t)(tmp & 0xFF);
}
rc = of_property_read_u32(np, "cm,mdss-livedisplay-aco-value", &tmp);
if (rc == 0) {
mlc->caps |= MODE_AUTO_CONTRAST;
mlc->aco_value = (uint8_t)(tmp & 0xFF);
}
}

mlc->ce_on_cmds = of_get_property(np,
"cm,mdss-livedisplay-color-enhance-on", &mlc->ce_on_cmds_len);
if (mlc->ce_on_cmds_len) {
mlc->ce_off_cmds = of_get_property(np,
"cm,mdss-livedisplay-color-enhance-off", &mlc->ce_off_cmds_len);
if (mlc->ce_off_cmds_len)
mlc->caps |= MODE_COLOR_ENHANCE;
}

for (i = 0; i < MAX_PRESETS; i++) {
memset(preset_name, 0, sizeof(preset_name));
snprintf(preset_name, 64, "%s-%d", "cm,mdss-livedisplay-preset", i);
mlc->presets[mlc->num_presets] = of_get_property(np, preset_name,
&mlc->presets_len[mlc->num_presets]);
if (mlc->presets_len[mlc->num_presets] > 0)
mlc->num_presets++;
}

if (mlc->num_presets)
mlc->caps |= MODE_PRESET;

mlc->post_cmds = of_get_property(np,
"cm,mdss-livedisplay-post-cmd", &mlc->post_cmds_len);

mlc->r = mlc->g = mlc->b = 32768;

pinfo->livedisplay = mlc;
return 0;
}

int mdss_livedisplay_create_sysfs(struct msm_fb_data_type *mfd)
{
int rc = 0;
struct mdss_livedisplay_ctx *mlc = get_ctx(mfd);

if (mlc == NULL)
return 0;

rc = sysfs_create_file(&mfd->fbi->dev->kobj, &dev_attr_rgb.attr);
if (rc)
goto sysfs_err;

if (mlc->caps & MODE_CABC) {
rc = sysfs_create_file(&mfd->fbi->dev->kobj, &dev_attr_cabc.attr);
if (rc)
goto sysfs_err;
}

if (mlc->caps & MODE_SRE) {
rc = sysfs_create_file(&mfd->fbi->dev->kobj, &dev_attr_sre.attr);
if (rc)
goto sysfs_err;
}

if (mlc->caps & MODE_AUTO_CONTRAST) {
rc = sysfs_create_file(&mfd->fbi->dev->kobj, &dev_attr_aco.attr);
if (rc)
goto sysfs_err;
}

if (mlc->caps & MODE_COLOR_ENHANCE) {
rc = sysfs_create_file(&mfd->fbi->dev->kobj, &dev_attr_color_enhance.attr);
if (rc)
goto sysfs_err;
}

if (mlc->caps & MODE_PRESET) {
rc = sysfs_create_file(&mfd->fbi->dev->kobj, &dev_attr_preset.attr);
if (rc)
goto sysfs_err;
rc = sysfs_create_file(&mfd->fbi->dev->kobj, &dev_attr_num_presets.attr);
if (rc)
goto sysfs_err;
}

mlc->mfd = mfd;

return rc;

sysfs_err:
pr_err("%s: sysfs creation failed, rc=%d", __func__, rc);
return rc;
}

@@ -0,0 +1,108 @@
/*
* Copyright (c) 2015 The CyanogenMod Project
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/

#ifndef MDSS_LIVEDISPLAY_H
#define MDSS_LIVEDISPLAY_H

#include <linux/of.h>
#include <linux/sysfs.h>

#include "mdss_dsi.h"
#include "mdss_fb.h"

#define MAX_PRESETS 10

struct mdss_livedisplay_ctx {
uint8_t cabc_ui_value;
uint8_t cabc_image_value;
uint8_t cabc_video_value;
uint8_t sre_weak_value;
uint8_t sre_medium_value;
uint8_t sre_strong_value;
uint8_t aco_value;

const uint8_t *ce_off_cmds;
const uint8_t *ce_on_cmds;
unsigned int ce_off_cmds_len;
unsigned int ce_on_cmds_len;

const uint8_t *presets[MAX_PRESETS];
unsigned int presets_len[MAX_PRESETS];

const uint8_t *cabc_cmds;
unsigned int cabc_cmds_len;

const uint8_t *post_cmds;
unsigned int post_cmds_len;

unsigned int preset;
unsigned int cabc_level;
unsigned int sre_level;
bool aco_enabled;
bool ce_enabled;

unsigned int num_presets;
unsigned int caps;

uint32_t r, g, b;
struct msm_fb_data_type *mfd;

struct mutex lock;
};

enum {
CABC_OFF,
CABC_UI,
CABC_IMAGE,
CABC_VIDEO,
CABC_MAX
};

enum {
SRE_OFF,
SRE_WEAK,
SRE_MEDIUM,
SRE_STRONG,
SRE_MAX
};

enum {
MODE_CABC = 0x01,
MODE_SRE = 0x02,
MODE_AUTO_CONTRAST = 0x04,
MODE_COLOR_ENHANCE = 0x08,
MODE_PRESET = 0x10,
MODE_UPDATE_ALL = 0xFF,
};

int mdss_livedisplay_update(struct mdss_dsi_ctrl_pdata *ctrl_pdata, int types);
int mdss_livedisplay_parse_dt(struct device_node *np, struct mdss_panel_info *pinfo);
int mdss_livedisplay_create_sysfs(struct msm_fb_data_type *mfd);

static inline bool is_cabc_cmd(unsigned int value)
{
return (value & MODE_CABC) || (value & MODE_SRE) || (value & MODE_AUTO_CONTRAST);
}

static inline struct mdss_livedisplay_ctx* get_ctx(struct msm_fb_data_type *mfd)
{
return mfd->panel_info->livedisplay;
}

static inline struct mdss_dsi_ctrl_pdata* get_ctrl(struct msm_fb_data_type *mfd)
{
struct mdss_panel_data *pdata = dev_get_platdata(&mfd->pdev->dev);
return container_of(pdata, struct mdss_dsi_ctrl_pdata, panel_data);
}

#endif