summaryrefslogtreecommitdiff
path: root/drivers/video/fbdev
diff options
context:
space:
mode:
authorSteve Kondik <steve@cyngn.com>2015-07-11 05:49:30 -0700
committerMichael Bestas <mkbestas@gmail.com>2020-04-18 04:02:31 +0300
commit5c268723ead051192652888914d352c9fff2bc98 (patch)
tree76475b3a8fcaea2d9752634a8d21f7c5106eb52f /drivers/video/fbdev
parent82cea59780524eaa4208c07eee3c53a6625b4c27 (diff)
video: mdss: LiveDisplay driver
* Bring in a lightweight version of the LiveDisplay driver which works for this chipset, and remove all the OEM spaghetti code. * This version of the driver does not include the generic RGB offset postprocessing, since we're going to do this using the official QDCM APIs. Change-Id: Ifcaaf93d56933d019d1f469fd70b8466cd4ad3e7
Diffstat (limited to 'drivers/video/fbdev')
-rw-r--r--drivers/video/fbdev/msm/Makefile2
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi.c4
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi_panel.c9
-rw-r--r--drivers/video/fbdev/msm/mdss_fb.c4
-rw-r--r--drivers/video/fbdev/msm/mdss_livedisplay.c612
-rw-r--r--drivers/video/fbdev/msm/mdss_livedisplay.h117
-rw-r--r--drivers/video/fbdev/msm/mdss_panel.h5
7 files changed, 751 insertions, 2 deletions
diff --git a/drivers/video/fbdev/msm/Makefile b/drivers/video/fbdev/msm/Makefile
index e101b873f361..dfbc604dafa5 100644
--- a/drivers/video/fbdev/msm/Makefile
+++ b/drivers/video/fbdev/msm/Makefile
@@ -64,3 +64,5 @@ obj-$(CONFIG_FB_MSM_QPIC_ILI_QVGA_PANEL) += qpic_panel_ili_qvga.o
obj-$(CONFIG_FB_MSM_MDSS) += mdss_fb.o mdss_util.o
obj-$(CONFIG_COMPAT) += mdss_compat_utils.o
+
+obj-$(CONFIG_FB_MSM_MDSS) += mdss_livedisplay.o
diff --git a/drivers/video/fbdev/msm/mdss_dsi.c b/drivers/video/fbdev/msm/mdss_dsi.c
index c2cfc8e0532e..56edef59f259 100644
--- a/drivers/video/fbdev/msm/mdss_dsi.c
+++ b/drivers/video/fbdev/msm/mdss_dsi.c
@@ -34,6 +34,7 @@
#include "mdss_debug.h"
#include "mdss_dsi_phy.h"
#include "mdss_dba_utils.h"
+#include "mdss_livedisplay.h"
#define CMDLINE_DSI_CTL_NUM_STRING_LEN 2
@@ -3113,6 +3114,9 @@ static int mdss_dsi_event_handler(struct mdss_panel_data *pdata,
rc);
}
break;
+ case MDSS_EVENT_UPDATE_LIVEDISPLAY:
+ rc = mdss_livedisplay_update(ctrl_pdata, (int)(unsigned long) arg);
+ break;
default:
pr_debug("%s: unhandled event=%d\n", __func__, event);
break;
diff --git a/drivers/video/fbdev/msm/mdss_dsi_panel.c b/drivers/video/fbdev/msm/mdss_dsi_panel.c
index bf695ae0beaf..2480e342ed44 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_panel.c
+++ b/drivers/video/fbdev/msm/mdss_dsi_panel.c
@@ -26,6 +26,7 @@
#include "mdss_dsi.h"
#include "mdss_dba_utils.h"
#include "mdss_debug.h"
+#include "mdss_livedisplay.h"
#define DT_CMD_HDR 6
#define DEFAULT_MDP_TRANSFER_TIME 14000
@@ -181,7 +182,7 @@ static void mdss_dsi_panel_apply_settings(struct mdss_dsi_ctrl_pdata *ctrl,
}
-static void mdss_dsi_panel_cmds_send(struct mdss_dsi_ctrl_pdata *ctrl,
+void mdss_dsi_panel_cmds_send(struct mdss_dsi_ctrl_pdata *ctrl,
struct dsi_panel_cmds *pcmds, u32 flags)
{
struct dcs_cmd_req cmdreq;
@@ -959,6 +960,10 @@ static int mdss_dsi_panel_on(struct mdss_panel_data *pdata)
/* Ensure low persistence mode is set as before */
mdss_dsi_panel_apply_display_setting(pdata, pinfo->persist_mode);
+ if (pdata->event_handler)
+ pdata->event_handler(pdata, MDSS_EVENT_UPDATE_LIVEDISPLAY,
+ (void *)(unsigned long) MODE_UPDATE_ALL);
+
end:
pr_debug("%s:-\n", __func__);
return ret;
@@ -2979,6 +2984,8 @@ static int mdss_panel_parse_dt(struct device_node *np,
pinfo->esc_clk_rate_hz = MDSS_DSI_MAX_ESC_CLK_RATE_HZ;
pr_debug("%s: esc clk %d\n", __func__, pinfo->esc_clk_rate_hz);
+ mdss_livedisplay_parse_dt(np, pinfo);
+
return 0;
error:
diff --git a/drivers/video/fbdev/msm/mdss_fb.c b/drivers/video/fbdev/msm/mdss_fb.c
index 31f4ef83b8b3..ce359b5c109b 100644
--- a/drivers/video/fbdev/msm/mdss_fb.c
+++ b/drivers/video/fbdev/msm/mdss_fb.c
@@ -56,6 +56,8 @@
#include "mdss_smmu.h"
#include "mdss_mdp.h"
+#include "mdss_livedisplay.h"
+
#ifdef CONFIG_FB_MSM_TRIPLE_BUFFER
#define MDSS_FB_NUM 3
#else
@@ -963,7 +965,7 @@ static int mdss_fb_create_sysfs(struct msm_fb_data_type *mfd)
rc = sysfs_create_group(&mfd->fbi->dev->kobj, &mdss_fb_attr_group);
if (rc)
pr_err("sysfs group creation failed, rc=%d\n", rc);
- return rc;
+ return mdss_livedisplay_create_sysfs(mfd);
}
static void mdss_fb_remove_sysfs(struct msm_fb_data_type *mfd)
diff --git a/drivers/video/fbdev/msm/mdss_livedisplay.c b/drivers/video/fbdev/msm/mdss_livedisplay.c
new file mode 100644
index 000000000000..d1c86dbcfbb4
--- /dev/null
+++ b/drivers/video/fbdev/msm/mdss_livedisplay.c
@@ -0,0 +1,612 @@
+/*
+ * 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_mdp_pp.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.
+ *
+ * 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.
+ *
+ * hbm: High Brightness Mode. Common for OLED panels. 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, u32 flags);
+
+static int parse_dsi_cmds(struct mdss_livedisplay_ctx *mlc,
+ 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;
+ }
+
+ pcmds->link_state = mlc->link_state;
+
+ 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;
+}
+
+/*
+ * Update all or a subset of parameters
+ */
+int mdss_livedisplay_update(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 ((mlc->caps & MODE_HIGH_BRIGHTNESS) && (types & MODE_HIGH_BRIGHTNESS))
+ len += mlc->hbm_enabled ? mlc->hbm_on_cmds_len : mlc->hbm_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;
+ }
+ }
+
+ // High brightness mode
+ if ((mlc->caps & MODE_HIGH_BRIGHTNESS) && (types & MODE_HIGH_BRIGHTNESS)) {
+ if (mlc->hbm_enabled) {
+ memcpy(cmd_buf + dlen, mlc->hbm_on_cmds, mlc->hbm_on_cmds_len);
+ dlen += mlc->hbm_on_cmds_len;
+ } else {
+ memcpy(cmd_buf + dlen, mlc->hbm_off_cmds, mlc->hbm_off_cmds_len);
+ dlen += mlc->hbm_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(mlc, &dsi_cmds, (const uint8_t *)cmd_buf, len);
+ if (ret == 0) {
+ mdss_dsi_panel_cmds_send(ctrl_pdata, &dsi_cmds,
+ CMD_REQ_COMMIT | CMD_CLK_CTRL);
+ } else {
+ pr_err("%s: error parsing DSI command! ret=%d", __func__, ret);
+ }
+
+ kfree(cmd_buf);
+
+ return ret;
+}
+
+int mdss_livedisplay_event(struct msm_fb_data_type *mfd, int types)
+{
+ int rc = 0;
+ struct mdss_panel_data *pdata;
+
+ pdata = dev_get_platdata(&mfd->pdev->dev);
+ if (!pdata)
+ return -ENODEV;
+
+ do {
+ if (pdata->event_handler)
+ rc = pdata->event_handler(pdata, MDSS_EVENT_UPDATE_LIVEDISPLAY,
+ (void *)(unsigned long) types);
+
+ pdata = pdata->next;
+ } while (!rc && pdata);
+
+ return rc;
+}
+
+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);
+
+ sscanf(buf, "%du", &level);
+ if (level >= CABC_OFF && level < CABC_MAX &&
+ level != mlc->cabc_level) {
+ mlc->cabc_level = level;
+ mdss_livedisplay_event(mfd, MODE_CABC);
+ }
+
+ 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);
+
+ sscanf(buf, "%du", &level);
+ if (level >= SRE_OFF && level < SRE_MAX &&
+ level != mlc->sre_level) {
+ mlc->sre_level = level;
+ mdss_livedisplay_event(mfd, MODE_SRE);
+ }
+
+ return count;
+}
+
+static ssize_t mdss_livedisplay_get_hbm(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->hbm_enabled);
+}
+
+static ssize_t mdss_livedisplay_set_hbm(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);
+
+ sscanf(buf, "%du", &value);
+ if ((value == 0 || value == 1)
+ && value != mlc->hbm_enabled) {
+ mlc->hbm_enabled = value;
+ mdss_livedisplay_event(mfd, MODE_HIGH_BRIGHTNESS);
+ }
+
+ 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);
+
+ sscanf(buf, "%du", &value);
+ if ((value == 0 || value == 1)
+ && value != mlc->ce_enabled) {
+ mlc->ce_enabled = value;
+ mdss_livedisplay_event(mfd, MODE_COLOR_ENHANCE);
+ }
+
+ 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);
+
+ sscanf(buf, "%du", &value);
+ if ((value == 0 || value == 1)
+ && value != mlc->aco_enabled) {
+ mlc->aco_enabled = value;
+ mdss_livedisplay_event(mfd, MODE_AUTO_CONTRAST);
+ }
+
+ 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);
+
+ sscanf(buf, "%du", &value);
+ if (value < 0 || value >= mlc->num_presets)
+ return -EINVAL;
+
+ mlc->preset = value;
+ mdss_livedisplay_event(mfd, MODE_PRESET);
+
+ 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 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(hbm, S_IRUGO | S_IWUSR | S_IWGRP, mdss_livedisplay_get_hbm, mdss_livedisplay_set_hbm);
+
+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];
+ const char *link_state;
+ uint32_t tmp = 0;
+
+ if (pinfo == NULL)
+ return -ENODEV;
+
+ mlc = kzalloc(sizeof(struct mdss_livedisplay_ctx), GFP_KERNEL);
+ mutex_init(&mlc->lock);
+
+ link_state = of_get_property(np, "cm,mdss-livedisplay-command-state", NULL);
+ if (link_state && !strcmp(link_state, "dsi_lp_mode"))
+ mlc->link_state = DSI_LP_MODE;
+ else
+ mlc->link_state = DSI_HS_MODE;
+
+ 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->hbm_on_cmds = of_get_property(np,
+ "cm,mdss-livedisplay-hbm-on-cmd", &mlc->hbm_on_cmds_len);
+ if (mlc->hbm_on_cmds_len) {
+ mlc->hbm_off_cmds = of_get_property(np,
+ "cm,mdss-livedisplay-hbm-off-cmd", &mlc->hbm_off_cmds_len);
+ if (mlc->hbm_off_cmds_len)
+ mlc->caps |= MODE_HIGH_BRIGHTNESS;
+ }
+
+ 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);
+
+ 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;
+
+ 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_HIGH_BRIGHTNESS) {
+ rc = sysfs_create_file(&mfd->fbi->dev->kobj, &dev_attr_hbm.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;
+}
+
diff --git a/drivers/video/fbdev/msm/mdss_livedisplay.h b/drivers/video/fbdev/msm/mdss_livedisplay.h
new file mode 100644
index 000000000000..099ebe20daf5
--- /dev/null
+++ b/drivers/video/fbdev/msm/mdss_livedisplay.h
@@ -0,0 +1,117 @@
+/*
+ * 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 *hbm_off_cmds;
+ const uint8_t *hbm_on_cmds;
+ unsigned int hbm_off_cmds_len;
+ unsigned int hbm_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;
+ bool hbm_enabled;
+
+ unsigned int link_state;
+
+ unsigned int num_presets;
+ unsigned int caps;
+
+ 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_HIGH_BRIGHTNESS = 0x20,
+ 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);
+int mdss_livedisplay_event(struct msm_fb_data_type *mfd, int types);
+
+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
diff --git a/drivers/video/fbdev/msm/mdss_panel.h b/drivers/video/fbdev/msm/mdss_panel.h
index 1e0ae618b501..767b6e631d78 100644
--- a/drivers/video/fbdev/msm/mdss_panel.h
+++ b/drivers/video/fbdev/msm/mdss_panel.h
@@ -309,6 +309,7 @@ enum mdss_intf_events {
MDSS_EVENT_AVR_MODE,
MDSS_EVENT_REGISTER_CLAMP_HANDLER,
MDSS_EVENT_DSI_DYNAMIC_BITCLK,
+ MDSS_EVENT_UPDATE_LIVEDISPLAY,
MDSS_EVENT_MAX,
};
@@ -761,6 +762,8 @@ struct mdss_dsi_dual_pu_roi {
bool enabled;
};
+struct mdss_livedisplay_ctx;
+
struct mdss_panel_hdr_properties {
bool hdr_enabled;
@@ -923,6 +926,8 @@ struct mdss_panel_info {
*/
u32 adjust_timer_delay_ms;
+ struct mdss_livedisplay_ctx *livedisplay;
+
/* debugfs structure for the panel */
struct mdss_panel_debugfs_info *debugfs_info;