diff options
| author | Steve Kondik <steve@cyngn.com> | 2015-07-11 05:49:30 -0700 |
|---|---|---|
| committer | Michael Bestas <mkbestas@gmail.com> | 2020-04-18 04:02:31 +0300 |
| commit | 5c268723ead051192652888914d352c9fff2bc98 (patch) | |
| tree | 76475b3a8fcaea2d9752634a8d21f7c5106eb52f /drivers/video/fbdev | |
| parent | 82cea59780524eaa4208c07eee3c53a6625b4c27 (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/Makefile | 2 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_dsi.c | 4 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_dsi_panel.c | 9 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_fb.c | 4 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_livedisplay.c | 612 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_livedisplay.h | 117 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_panel.h | 5 |
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; |
