diff --git a/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt b/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt index 52ffbe5c7207..b676efe97b8b 100644 --- a/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt +++ b/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt @@ -61,6 +61,30 @@ Required properties: transmitted byte 5, 6: 16 bits length in network byte order byte 7 and beyond: number byte of payload +- qcom,mdss-dsi-lp-mode-on: This is used to enable display low persistence mode. + A byte stream formed by multiple dcs packets base on + qcom dsi controller protocol. + byte 0: dcs data type + byte 1: set to indicate this is an individual packet + (no chain) + byte 2: virtual channel number + byte 3: expect ack from client (dcs read command) + byte 4: wait number of specified ms after dcs command + transmitted + byte 5, 6: 16 bits length in network byte order + byte 7 and beyond: number byte of payload +- qcom,mdss-dsi-lp-mode-off: This is used to disable display low persistence mode. + A byte stream formed by multiple dcs packets base on + qcom dsi controller protocol. + byte 0: dcs data type + byte 1: set to indicate this is an individual packet + (no chain) + byte 2: virtual channel number + byte 3: expect ack from client (dcs read command) + byte 4: wait number of specified ms after dcs command + transmitted + byte 5, 6: 16 bits length in network byte order + byte 7 and beyond: number byte of payload - qcom,mdss-dsi-post-panel-on-command: same as "qcom,mdss-dsi-on-command" except commands are sent after displaying an image. @@ -648,6 +672,9 @@ Example: qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; qcom,mdss-dsi-off-command = [22 01 00 00 00 00 00]; qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-lp-mode-on = [32 01 00 00 00 00 02 00 00 + 29 01 00 00 10 00 02 FF 99]; + qcom,mdss-dsi-lp-mode-off = [22 01 00 00 00 00 00]; qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; qcom,mdss-dsi-pan-enable-dynamic-fps; qcom,mdss-dsi-pan-fps-update = "dfps_suspend_resume_mode"; diff --git a/arch/arm/boot/dts/qcom/dsi-panel-s6e3ha3-amoled-dualmipi-wqhd-cmd.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-s6e3ha3-amoled-dualmipi-wqhd-cmd.dtsi index 7bd844ae6770..6d91e72851ec 100644 --- a/arch/arm/boot/dts/qcom/dsi-panel-s6e3ha3-amoled-dualmipi-wqhd-cmd.dtsi +++ b/arch/arm/boot/dts/qcom/dsi-panel-s6e3ha3-amoled-dualmipi-wqhd-cmd.dtsi @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * 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 @@ -57,6 +57,60 @@ 05 01 00 00 b4 00 02 10 00]; qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-lp-mode-on = [39 00 00 00 05 00 03 f0 5a 5a + 39 00 00 00 05 00 03 f1 5a 5a + 39 00 00 00 05 00 03 fc 5a 5a + 39 00 00 00 05 00 02 b0 17 + 39 00 00 00 05 00 02 cb 10 + 39 00 00 00 05 00 02 b0 2d + 39 00 00 00 05 00 02 cb cd + 39 00 00 00 05 00 02 b0 0e + 39 00 00 00 05 00 02 cb 02 + 39 00 00 00 05 00 02 b0 0f + 39 00 00 00 05 00 02 cb 09 + 39 00 00 00 05 00 02 b0 02 + 39 00 00 00 05 00 02 f2 c9 + 39 00 00 00 05 00 02 b0 03 + 39 00 00 00 05 00 02 f2 c0 + 39 00 00 00 05 00 02 b0 03 + 39 00 00 00 05 00 02 f4 aa + 39 00 00 00 05 00 02 b0 08 + 39 00 00 00 05 00 02 b1 30 + 39 00 00 00 05 00 02 b0 09 + 39 00 00 00 05 00 02 b1 0a + 39 00 00 00 05 00 02 b0 0d + 39 00 00 00 05 00 02 b1 10 + 39 00 00 00 05 00 02 b0 00 + 39 00 00 00 05 00 02 f7 03 + 39 00 00 00 05 00 02 fe 30 + 39 01 00 00 05 00 02 fe b0]; + qcom,mdss-dsi-lp-mode-off = [39 00 00 00 05 00 03 f0 5a 5a + 39 00 00 00 05 00 03 f1 5a 5a + 39 00 00 00 05 00 03 fc 5a 5a + 39 00 00 00 05 00 02 b0 2d + 39 00 00 00 05 00 02 cb 4d + 39 00 00 00 05 00 02 b0 17 + 39 00 00 00 05 00 02 cb 04 + 39 00 00 00 05 00 02 b0 0e + 39 00 00 00 05 00 02 cb 06 + 39 00 00 00 05 00 02 b0 0f + 39 00 00 00 05 00 02 cb 05 + 39 00 00 00 05 00 02 b0 02 + 39 00 00 00 05 00 02 f2 b8 + 39 00 00 00 05 00 02 b0 03 + 39 00 00 00 05 00 02 f2 80 + 39 00 00 00 05 00 02 b0 03 + 39 00 00 00 05 00 02 f4 8a + 39 00 00 00 05 00 02 b0 08 + 39 00 00 00 05 00 02 b1 10 + 39 00 00 00 05 00 02 b0 09 + 39 00 00 00 05 00 02 b1 0a + 39 00 00 00 05 00 02 b0 0d + 39 00 00 00 05 00 02 b1 80 + 39 00 00 00 05 00 02 b0 00 + 39 00 00 00 05 00 02 f7 03 + 39 00 00 00 05 00 02 fe 30 + 39 01 00 00 05 00 02 fe b0]; qcom,mdss-dsi-h-sync-pulse = <0>; qcom,mdss-dsi-traffic-mode = "non_burst_sync_event"; qcom,mdss-dsi-lane-map = "lane_map_0123"; diff --git a/drivers/video/fbdev/msm/mdss_dsi.h b/drivers/video/fbdev/msm/mdss_dsi.h index 0574410868bf..81d2723d0f2a 100644 --- a/drivers/video/fbdev/msm/mdss_dsi.h +++ b/drivers/video/fbdev/msm/mdss_dsi.h @@ -487,6 +487,8 @@ struct mdss_dsi_ctrl_pdata { struct dsi_panel_cmds post_dms_on_cmds; struct dsi_panel_cmds post_panel_on_cmds; struct dsi_panel_cmds off_cmds; + struct dsi_panel_cmds lp_on_cmds; + struct dsi_panel_cmds lp_off_cmds; struct dsi_panel_cmds status_cmds; u32 *status_valid_params; u32 *status_cmds_rlen; diff --git a/drivers/video/fbdev/msm/mdss_dsi_panel.c b/drivers/video/fbdev/msm/mdss_dsi_panel.c index 7f6cad3de18e..7cc9ce6e034d 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_panel.c +++ b/drivers/video/fbdev/msm/mdss_dsi_panel.c @@ -160,6 +160,27 @@ int mdss_dsi_panel_cmd_read(struct mdss_dsi_ctrl_pdata *ctrl, char cmd0, return mdss_dsi_cmdlist_put(ctrl, &cmdreq); } +static void mdss_dsi_panel_apply_settings(struct mdss_dsi_ctrl_pdata *ctrl, + struct dsi_panel_cmds *pcmds) +{ + struct dcs_cmd_req cmdreq; + struct mdss_panel_info *pinfo; + + pinfo = &(ctrl->panel_data.panel_info); + if ((pinfo->dcs_cmd_by_left) && (ctrl->ndx != DSI_CTRL_LEFT)) + return; + + memset(&cmdreq, 0, sizeof(cmdreq)); + cmdreq.cmds = pcmds->cmds; + cmdreq.cmds_cnt = pcmds->cmd_cnt; + cmdreq.flags = CMD_REQ_COMMIT; + cmdreq.rlen = 0; + cmdreq.cb = NULL; + + mdss_dsi_cmdlist_put(ctrl, &cmdreq); +} + + static void mdss_dsi_panel_cmds_send(struct mdss_dsi_ctrl_pdata *ctrl, struct dsi_panel_cmds *pcmds, u32 flags) { @@ -660,6 +681,38 @@ end: return 0; } +static int mdss_dsi_panel_apply_display_setting(struct mdss_panel_data *pdata, + u32 mode) +{ + struct mdss_dsi_ctrl_pdata *ctrl = NULL; + struct dsi_panel_cmds *lp_on_cmds; + struct dsi_panel_cmds *lp_off_cmds; + + if (pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return -EINVAL; + } + + ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata, + panel_data); + + lp_on_cmds = &ctrl->lp_on_cmds; + lp_off_cmds = &ctrl->lp_off_cmds; + + /* Apply display settings for low-persistence mode */ + if ((mode == MDSS_PANEL_LOW_PERSIST_MODE_ON) && + (lp_on_cmds->cmd_cnt)) + mdss_dsi_panel_apply_settings(ctrl, lp_on_cmds); + else if ((mode == MDSS_PANEL_LOW_PERSIST_MODE_OFF) && + (lp_on_cmds->cmd_cnt)) + mdss_dsi_panel_apply_settings(ctrl, lp_off_cmds); + else + return -EINVAL; + + pr_debug("%s: Persistence mode %d applied\n", __func__, mode); + return 0; +} + static void mdss_dsi_panel_switch_mode(struct mdss_panel_data *pdata, int mode) { @@ -822,6 +875,10 @@ static int mdss_dsi_panel_on(struct mdss_panel_data *pdata) if (ctrl->ds_registered) mdss_dba_utils_video_on(pinfo->dba_data, pinfo); + + /* Ensure low persistence mode is set as before */ + mdss_dsi_panel_apply_display_setting(pdata, pinfo->persist_mode); + end: pr_debug("%s:-\n", __func__); return ret; @@ -2046,6 +2103,12 @@ static int mdss_dsi_parse_panel_features(struct device_node *np, __func__, __LINE__); } + mdss_dsi_parse_dcs_cmds(np, &ctrl->lp_on_cmds, + "qcom,mdss-dsi-lp-mode-on", NULL); + + mdss_dsi_parse_dcs_cmds(np, &ctrl->lp_off_cmds, + "qcom,mdss-dsi-lp-mode-off", NULL); + return 0; } @@ -2796,12 +2859,15 @@ int mdss_dsi_panel_init(struct device_node *node, pinfo->dynamic_switch_pending = false; pinfo->is_lpm_mode = false; pinfo->esd_rdy = false; + pinfo->persist_mode = false; ctrl_pdata->on = mdss_dsi_panel_on; ctrl_pdata->post_panel_on = mdss_dsi_post_panel_on; ctrl_pdata->off = mdss_dsi_panel_off; ctrl_pdata->low_power_config = mdss_dsi_panel_low_power_config; ctrl_pdata->panel_data.set_backlight = mdss_dsi_panel_bl_ctrl; + ctrl_pdata->panel_data.apply_display_setting = + mdss_dsi_panel_apply_display_setting; ctrl_pdata->switch_mode = mdss_dsi_panel_switch_mode; return 0; diff --git a/drivers/video/fbdev/msm/mdss_fb.c b/drivers/video/fbdev/msm/mdss_fb.c index f20248e13cf8..cb0fcbafb8b4 100644 --- a/drivers/video/fbdev/msm/mdss_fb.c +++ b/drivers/video/fbdev/msm/mdss_fb.c @@ -1,8 +1,8 @@ /* * Core MDSS framebuffer driver. * + * Copyright (c) 2008-2017, The Linux Foundation. All rights reserved. * Copyright (C) 2007 Google Incorporated - * Copyright (c) 2008-2016, The Linux Foundation. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -823,6 +823,74 @@ static ssize_t mdss_fb_get_dfps_mode(struct device *dev, return ret; } +static ssize_t mdss_fb_change_persist_mode(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par; + struct mdss_panel_info *pinfo = NULL; + struct mdss_panel_data *pdata; + int ret = 0; + u32 persist_mode; + + if (!mfd || !mfd->panel_info) { + pr_err("%s: Panel info is NULL!\n", __func__); + return len; + } + + pinfo = mfd->panel_info; + + if (kstrtouint(buf, 0, &persist_mode)) { + pr_err("kstrtouint buf error!\n"); + return len; + } + + mutex_lock(&mfd->mdss_sysfs_lock); + if (mdss_panel_is_power_off(mfd->panel_power_state)) { + pinfo->persist_mode = persist_mode; + goto end; + } + + mutex_lock(&mfd->bl_lock); + + pdata = dev_get_platdata(&mfd->pdev->dev); + if ((pdata) && (pdata->apply_display_setting)) + ret = pdata->apply_display_setting(pdata, persist_mode); + + mutex_unlock(&mfd->bl_lock); + + if (!ret) { + pr_debug("%s: Persist mode %d\n", __func__, persist_mode); + pinfo->persist_mode = persist_mode; + } + +end: + mutex_unlock(&mfd->mdss_sysfs_lock); + + return len; +} + +static ssize_t mdss_fb_get_persist_mode(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_panel_data *pdata; + struct mdss_panel_info *pinfo; + int ret; + + pdata = dev_get_platdata(&mfd->pdev->dev); + if (!pdata) { + pr_err("no panel connected!\n"); + return -EINVAL; + } + pinfo = &pdata->panel_info; + + ret = scnprintf(buf, PAGE_SIZE, "%d\n", pinfo->persist_mode); + + return ret; +} + static DEVICE_ATTR(msm_fb_type, S_IRUGO, mdss_fb_get_type, NULL); static DEVICE_ATTR(msm_fb_split, S_IRUGO | S_IWUSR, mdss_fb_show_split, mdss_fb_store_split); @@ -841,6 +909,8 @@ static DEVICE_ATTR(msm_fb_dfps_mode, S_IRUGO | S_IWUSR, mdss_fb_get_dfps_mode, mdss_fb_change_dfps_mode); static DEVICE_ATTR(measured_fps, S_IRUGO | S_IWUSR | S_IWGRP, mdss_fb_get_fps_info, NULL); +static DEVICE_ATTR(msm_fb_persist_mode, S_IRUGO | S_IWUSR, + mdss_fb_get_persist_mode, mdss_fb_change_persist_mode); static struct attribute *mdss_fb_attrs[] = { &dev_attr_msm_fb_type.attr, &dev_attr_msm_fb_split.attr, @@ -853,6 +923,7 @@ static struct attribute *mdss_fb_attrs[] = { &dev_attr_msm_fb_panel_status.attr, &dev_attr_msm_fb_dfps_mode.attr, &dev_attr_measured_fps.attr, + &dev_attr_msm_fb_persist_mode.attr, NULL, }; @@ -1203,6 +1274,7 @@ static int mdss_fb_probe(struct platform_device *pdev) INIT_LIST_HEAD(&mfd->file_list); mutex_init(&mfd->bl_lock); + mutex_init(&mfd->mdss_sysfs_lock); mutex_init(&mfd->switch_lock); fbi_list[fbi_list_index++] = fbi; @@ -1971,6 +2043,8 @@ static int mdss_fb_blank(int blank_mode, struct fb_info *info) return ret; } + mutex_lock(&mfd->mdss_sysfs_lock); + if (mfd->op_enable == 0) { if (blank_mode == FB_BLANK_UNBLANK) mfd->suspend.panel_power_state = MDSS_PANEL_POWER_ON; @@ -1980,7 +2054,9 @@ static int mdss_fb_blank(int blank_mode, struct fb_info *info) mfd->suspend.panel_power_state = MDSS_PANEL_POWER_LP1; else mfd->suspend.panel_power_state = MDSS_PANEL_POWER_OFF; - return 0; + + ret = 0; + goto end; } pr_debug("mode: %d\n", blank_mode); @@ -1997,7 +2073,12 @@ static int mdss_fb_blank(int blank_mode, struct fb_info *info) if (pdata->panel_disable_mode) mdss_mdp_enable_panel_disable_mode(mfd, false); - return mdss_fb_blank_sub(blank_mode, info, mfd->op_enable); + ret = mdss_fb_blank_sub(blank_mode, info, mfd->op_enable); + +end: + mutex_unlock(&mfd->mdss_sysfs_lock); + + return ret; } static inline int mdss_fb_create_ion_client(struct msm_fb_data_type *mfd) diff --git a/drivers/video/fbdev/msm/mdss_fb.h b/drivers/video/fbdev/msm/mdss_fb.h index 1487c4e7f6e2..656b58eb62d7 100644 --- a/drivers/video/fbdev/msm/mdss_fb.h +++ b/drivers/video/fbdev/msm/mdss_fb.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2008-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2008-2017, The Linux Foundation. All rights reserved. * * 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 @@ -310,6 +310,7 @@ struct msm_fb_data_type { bool allow_bl_update; u32 bl_level_scaled; struct mutex bl_lock; + struct mutex mdss_sysfs_lock; bool ipc_resume; struct platform_device *pdev; diff --git a/drivers/video/fbdev/msm/mdss_panel.h b/drivers/video/fbdev/msm/mdss_panel.h index 4698d441f365..9c4c9fb3e906 100644 --- a/drivers/video/fbdev/msm/mdss_panel.h +++ b/drivers/video/fbdev/msm/mdss_panel.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2008-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2008-2017, The Linux Foundation. All rights reserved. * * 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 @@ -117,6 +117,11 @@ enum { MDSS_PANEL_BLANK_LOW_POWER, }; +enum { + MDSS_PANEL_LOW_PERSIST_MODE_OFF = 0, + MDSS_PANEL_LOW_PERSIST_MODE_ON, +}; + enum { MODE_GPIO_NOT_VALID = 0, MODE_SEL_DUAL_PORT, @@ -892,6 +897,9 @@ struct mdss_panel_info { /* debugfs structure for the panel */ struct mdss_panel_debugfs_info *debugfs_info; + /* persistence mode on/off */ + bool persist_mode; + /* stores initial adaptive variable refresh vtotal value */ u32 saved_avr_vtotal; @@ -936,6 +944,7 @@ struct mdss_panel_timing { struct mdss_panel_data { struct mdss_panel_info panel_info; void (*set_backlight) (struct mdss_panel_data *pdata, u32 bl_level); + int (*apply_display_setting)(struct mdss_panel_data *pdata, u32 mode); unsigned char *mmss_cc_base; /**