From 3829ba593ddc0af7c340e9cb2d47e349207ba76b Mon Sep 17 00:00:00 2001 From: Ingrid Gallardo Date: Wed, 27 Jul 2016 16:53:08 -0700 Subject: msm: mdss: add sysfs node to disable panel For power measurements, it is needed to disable the panel in order to determine the delta between the consumption with and without the panel and it's dependencies. This change adds a sysfs note to disable the panel and dependencies without disturbing any of the dsi/mdp configurations. CRs-Fixed: 878591 Change-Id: I57c1f49aa57f645cb96714edd2f297fa06187c03 Signed-off-by: Ingrid Gallardo --- drivers/video/fbdev/msm/mdss_dsi.c | 23 +++++- drivers/video/fbdev/msm/mdss_dsi.h | 1 + drivers/video/fbdev/msm/mdss_dsi_panel.c | 5 ++ drivers/video/fbdev/msm/mdss_dsi_status.c | 10 +++ drivers/video/fbdev/msm/mdss_fb.c | 3 + drivers/video/fbdev/msm/mdss_mdp.h | 4 ++ drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c | 87 +++++++++++++++++++++- drivers/video/fbdev/msm/mdss_mdp_overlay.c | 107 ++++++++++++++++++++++++++++ drivers/video/fbdev/msm/mdss_panel.h | 9 +++ 9 files changed, 245 insertions(+), 4 deletions(-) diff --git a/drivers/video/fbdev/msm/mdss_dsi.c b/drivers/video/fbdev/msm/mdss_dsi.c index c145f72c3c70..66cd99720afa 100644 --- a/drivers/video/fbdev/msm/mdss_dsi.c +++ b/drivers/video/fbdev/msm/mdss_dsi.c @@ -357,7 +357,7 @@ static int mdss_dsi_panel_power_lp(struct mdss_panel_data *pdata, int enable) static int mdss_dsi_panel_power_ctrl(struct mdss_panel_data *pdata, int power_state) { - int ret; + int ret = 0; struct mdss_panel_info *pinfo; if (pdata == NULL) { @@ -383,7 +383,11 @@ static int mdss_dsi_panel_power_ctrl(struct mdss_panel_data *pdata, switch (power_state) { case MDSS_PANEL_POWER_OFF: - ret = mdss_dsi_panel_power_off(pdata); + case MDSS_PANEL_POWER_LCD_DISABLED: + /* if LCD has not been disabled, then disable it now */ + if ((pinfo->panel_power_state != MDSS_PANEL_POWER_LCD_DISABLED) + && (pinfo->panel_power_state != MDSS_PANEL_POWER_OFF)) + ret = mdss_dsi_panel_power_off(pdata); break; case MDSS_PANEL_POWER_ON: if (mdss_dsi_is_panel_on_lp(pdata)) @@ -2469,6 +2473,7 @@ static int mdss_dsi_event_handler(struct mdss_panel_data *pdata, int power_state; u32 mode; struct mdss_panel_info *pinfo; + int ret; if (pdata == NULL) { pr_err("%s: Invalid input data\n", __func__); @@ -2529,6 +2534,20 @@ static int mdss_dsi_event_handler(struct mdss_panel_data *pdata, rc = mdss_dsi_blank(pdata, power_state); rc = mdss_dsi_off(pdata, power_state); break; + case MDSS_EVENT_DISABLE_PANEL: + /* disable esd thread */ + disable_esd_thread(); + + /* disable backlight */ + ctrl_pdata->panel_data.set_backlight(pdata, 0); + + /* send the off commands */ + ctrl_pdata->off(pdata); + + /* disable panel power */ + ret = mdss_dsi_panel_power_ctrl(pdata, + MDSS_PANEL_POWER_LCD_DISABLED); + break; case MDSS_EVENT_CONT_SPLASH_FINISH: if (ctrl_pdata->off_cmds.link_state == DSI_LP_MODE) rc = mdss_dsi_blank(pdata, MDSS_PANEL_POWER_OFF); diff --git a/drivers/video/fbdev/msm/mdss_dsi.h b/drivers/video/fbdev/msm/mdss_dsi.h index bd1854092c6a..7091dc2f38b9 100644 --- a/drivers/video/fbdev/msm/mdss_dsi.h +++ b/drivers/video/fbdev/msm/mdss_dsi.h @@ -614,6 +614,7 @@ int mdss_dsi_wait_for_lane_idle(struct mdss_dsi_ctrl_pdata *ctrl); irqreturn_t mdss_dsi_isr(int irq, void *ptr); irqreturn_t hw_vsync_handler(int irq, void *data); +void disable_esd_thread(void); void mdss_dsi_irq_handler_config(struct mdss_dsi_ctrl_pdata *ctrl_pdata); void mdss_dsi_set_tx_power_mode(int mode, struct mdss_panel_data *pdata); diff --git a/drivers/video/fbdev/msm/mdss_dsi_panel.c b/drivers/video/fbdev/msm/mdss_dsi_panel.c index e8d68059581f..8ffba091e2b2 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_panel.c +++ b/drivers/video/fbdev/msm/mdss_dsi_panel.c @@ -667,6 +667,11 @@ static void mdss_dsi_panel_bl_ctrl(struct mdss_panel_data *pdata, * for the backlight brightness. If the brightness is less * than it, the controller can malfunction. */ + pr_debug("%s: bl_level:%d\n", __func__, bl_level); + + /* do not allow backlight to change when panel in disable mode */ + if (pdata->panel_disable_mode && (bl_level != 0)) + return; if ((bl_level < pdata->panel_info.bl_min) && (bl_level != 0)) bl_level = pdata->panel_info.bl_min; diff --git a/drivers/video/fbdev/msm/mdss_dsi_status.c b/drivers/video/fbdev/msm/mdss_dsi_status.c index bf545ae311f2..4208c2c43efb 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_status.c +++ b/drivers/video/fbdev/msm/mdss_dsi_status.c @@ -100,6 +100,16 @@ irqreturn_t hw_vsync_handler(int irq, void *data) return IRQ_HANDLED; } +/* + * disable_esd_thread() - Cancels work item for the esd check. + */ +void disable_esd_thread(void) +{ + if (pstatus_data && + cancel_delayed_work(&pstatus_data->check_status)) + pr_debug("esd thread killed\n"); +} + /* * fb_event_callback() - Call back function for the fb_register_client() * notifying events diff --git a/drivers/video/fbdev/msm/mdss_fb.c b/drivers/video/fbdev/msm/mdss_fb.c index 50c7015c6731..fc8d3898351e 100644 --- a/drivers/video/fbdev/msm/mdss_fb.c +++ b/drivers/video/fbdev/msm/mdss_fb.c @@ -1950,6 +1950,9 @@ static int mdss_fb_blank(int blank_mode, struct fb_info *info) pdata->panel_info.is_lpm_mode = false; } + if (pdata->panel_disable_mode) + mdss_mdp_enable_panel_disable_mode(mfd, false); + return mdss_fb_blank_sub(blank_mode, info, mfd->op_enable); } diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h index 921391dc4bde..0085163ada52 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.h +++ b/drivers/video/fbdev/msm/mdss_mdp.h @@ -394,6 +394,8 @@ struct mdss_mdp_ctl_intfs_ops { enum dynamic_switch_modes mode, bool pre); /* called before do any register programming from commit thread */ void (*pre_programming)(struct mdss_mdp_ctl *ctl); + /* called to do any interface programming for the panel disable mode */ + void (*panel_disable_cfg)(struct mdss_mdp_ctl *ctl, bool disable); /* to update lineptr, [1..yres] - enable, 0 - disable */ int (*update_lineptr)(struct mdss_mdp_ctl *ctl, bool enable); @@ -1863,6 +1865,8 @@ int mdss_mdp_cmd_set_autorefresh_mode(struct mdss_mdp_ctl *ctl, int frame_cnt); int mdss_mdp_cmd_get_autorefresh_mode(struct mdss_mdp_ctl *ctl); int mdss_mdp_ctl_cmd_set_autorefresh(struct mdss_mdp_ctl *ctl, int frame_cnt); int mdss_mdp_ctl_cmd_get_autorefresh(struct mdss_mdp_ctl *ctl); +int mdss_mdp_enable_panel_disable_mode(struct msm_fb_data_type *mfd, + bool disable_panel); int mdss_mdp_pp_get_version(struct mdp_pp_feature_version *version); int mdss_mdp_layer_pre_commit_cwb(struct msm_fb_data_type *mfd, struct mdp_layer_commit_v1 *commit); diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c index 2c2dc6f18fd9..72d6175686b7 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c +++ b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c @@ -295,9 +295,9 @@ static int mdss_mdp_cmd_tearcheck_cfg(struct mdss_mdp_mixer *mixer, __func__, pinfo->yres, vclks_line, te->sync_cfg_height, te->vsync_init_val, te->rd_ptr_irq, te->start_pos, te->wr_ptr_irq); - pr_debug("thrd_start =%d thrd_cont=%d pp_split=%d\n", + pr_debug("thrd_start =%d thrd_cont=%d pp_split=%d hw_vsync_mode:%d\n", te->sync_threshold_start, te->sync_threshold_continue, - ctx->pingpong_split_slave); + ctx->pingpong_split_slave, pinfo->mipi.hw_vsync_mode); pingpong_base = mixer->pingpong_base; @@ -2129,6 +2129,88 @@ static int mdss_mdp_cmd_panel_on(struct mdss_mdp_ctl *ctl, return rc; } +/* + * This function will be called from the sysfs node to tear down or restore + * any dependencies of the interface to disable the panel + */ +void mdss_mdp_cmd_panel_disable_cfg(struct mdss_mdp_ctl *ctl, + bool disable) +{ + struct mdss_panel_info *pinfo, *spinfo = NULL; + struct mdss_mdp_cmd_ctx *ctx, *sctx = NULL; + + pinfo = &ctl->panel_data->panel_info; + mutex_lock(&ctl->offlock); + + if ((pinfo->sim_panel_mode == SIM_MODE) || + ((!ctl->panel_data->panel_disable_mode) && + (pinfo->mipi.hw_vsync_mode == 0))) { + pr_err("te already in simulaiton mode\n"); + goto exit; + } + + ctx = (struct mdss_mdp_cmd_ctx *)ctl->intf_ctx[MASTER_CTX]; + if (is_pingpong_split(ctl->mfd)) { + sctx = (struct mdss_mdp_cmd_ctx *)ctl->intf_ctx[SLAVE_CTX]; + } else if (ctl->mfd->split_mode == MDP_DUAL_LM_DUAL_DISPLAY) { + struct mdss_mdp_ctl *sctl = mdss_mdp_get_split_ctl(ctl); + + if (sctl) { + sctx = (struct mdss_mdp_cmd_ctx *) + sctl->intf_ctx[MASTER_CTX]; + spinfo = &sctl->panel_data->panel_info; + } + } + + if (disable) { + /* cache the te params */ + memcpy(&pinfo->te_cached, &pinfo->te, + sizeof(struct mdss_mdp_pp_tear_check)); + pinfo->mipi.hw_vsync_mode = 0; + + if (spinfo) { + spinfo->mipi.hw_vsync_mode = 0; + memcpy(&spinfo->te_cached, &spinfo->te, + sizeof(struct mdss_mdp_pp_tear_check)); + } + + pr_debug("%s: update info\n", __func__); + /* update the te information to use sim mode */ + mdss_panel_override_te_params(pinfo); + if (spinfo) + mdss_panel_override_te_params(spinfo); + + pr_debug("%s: reconfig tear check\n", __func__); + /* reconfigure tear check, remove dependency to external te */ + if (mdss_mdp_cmd_tearcheck_setup(ctx, false)) { + pr_warn("%s: ctx%d tearcheck setup failed\n", __func__, + ctx->current_pp_num); + } else { + if (sctx && mdss_mdp_cmd_tearcheck_setup(sctx, false)) + pr_warn("%s: ctx%d tearcheck setup failed\n", + __func__, sctx->current_pp_num); + } + } else { + /* + * restore the information in the panel information, + * the actual programming will happen during restore + */ + pr_debug("%s: reset tear check\n", __func__); + memcpy(&pinfo->te, &pinfo->te_cached, + sizeof(struct mdss_mdp_pp_tear_check)); + pinfo->mipi.hw_vsync_mode = 1; + + if (spinfo) { + spinfo->mipi.hw_vsync_mode = 1; + memcpy(&spinfo->te, &spinfo->te_cached, + sizeof(struct mdss_mdp_pp_tear_check)); + } + } + +exit: + mutex_unlock(&ctl->offlock); +} + /* * This function will be called from the sysfs node to enable and disable the * feature with master ctl only. @@ -3481,6 +3563,7 @@ int mdss_mdp_cmd_start(struct mdss_mdp_ctl *ctl) ctl->ops.reconfigure = mdss_mdp_cmd_reconfigure; ctl->ops.pre_programming = mdss_mdp_cmd_pre_programming; ctl->ops.update_lineptr = mdss_mdp_cmd_update_lineptr; + ctl->ops.panel_disable_cfg = mdss_mdp_cmd_panel_disable_cfg; pr_debug("%s:-\n", __func__); return 0; diff --git a/drivers/video/fbdev/msm/mdss_mdp_overlay.c b/drivers/video/fbdev/msm/mdss_mdp_overlay.c index 9dda467e53cc..965d4a6cfb5e 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_overlay.c +++ b/drivers/video/fbdev/msm/mdss_mdp_overlay.c @@ -3243,6 +3243,110 @@ static ssize_t mdss_mdp_dyn_pu_store(struct device *dev, return count; } + +static ssize_t mdss_mdp_panel_disable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = 0; + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par; + struct mdss_mdp_ctl *ctl; + struct mdss_panel_data *pdata; + + if (!mfd) { + pr_err("Invalid mfd structure\n"); + return -EINVAL; + } + + ctl = mfd_to_ctl(mfd); + if (!ctl) { + pr_err("Invalid ctl structure\n"); + return -EINVAL; + } + + pdata = dev_get_platdata(&mfd->pdev->dev); + + ret = snprintf(buf, PAGE_SIZE, "%d\n", + pdata->panel_disable_mode); + + return ret; +} + +int mdss_mdp_enable_panel_disable_mode(struct msm_fb_data_type *mfd, + bool disable_panel) +{ + struct mdss_mdp_ctl *ctl; + int ret = 0; + struct mdss_panel_data *pdata; + + ctl = mfd_to_ctl(mfd); + if (!ctl) { + pr_err("Invalid ctl structure\n"); + ret = -EINVAL; + return ret; + } + + pdata = dev_get_platdata(&mfd->pdev->dev); + + pr_debug("config panel %d\n", disable_panel); + if (disable_panel) { + /* first set the flag that we enter this mode */ + pdata->panel_disable_mode = true; + + /* + * setup any interface config that needs to change before + * disabling the panel + */ + if (ctl->ops.panel_disable_cfg) + ctl->ops.panel_disable_cfg(ctl, disable_panel); + + /* disable panel */ + ret = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_DISABLE_PANEL, + NULL, CTL_INTF_EVENT_FLAG_DEFAULT); + if (ret) + pr_err("failed to disable panel! %d\n", ret); + } else { + /* restore any interface configuration */ + if (ctl->ops.panel_disable_cfg) + ctl->ops.panel_disable_cfg(ctl, disable_panel); + + /* + * no other action is needed when reconfiguring, since all the + * re-configuration will happen during restore + */ + pdata->panel_disable_mode = false; + } + + return ret; +} + +static ssize_t mdss_mdp_panel_disable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + int disable_panel, rc; + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par; + + if (!mfd) { + pr_err("Invalid mfd structure\n"); + rc = -EINVAL; + return rc; + } + + rc = kstrtoint(buf, 10, &disable_panel); + if (rc) { + pr_err("kstrtoint failed. rc=%d\n", rc); + return rc; + } + + pr_debug("disable panel: %d ++\n", disable_panel); + /* we only support disabling the panel from sysfs */ + if (disable_panel) + mdss_mdp_enable_panel_disable_mode(mfd, true); + + return len; +} + static ssize_t mdss_mdp_cmd_autorefresh_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -3433,6 +3537,8 @@ static DEVICE_ATTR(msm_misr_en, S_IRUGO | S_IWUSR, mdss_mdp_misr_show, mdss_mdp_misr_store); static DEVICE_ATTR(msm_cmd_autorefresh_en, S_IRUGO | S_IWUSR, mdss_mdp_cmd_autorefresh_show, mdss_mdp_cmd_autorefresh_store); +static DEVICE_ATTR(msm_disable_panel, S_IRUGO | S_IWUSR, + mdss_mdp_panel_disable_show, mdss_mdp_panel_disable_store); static DEVICE_ATTR(vsync_event, S_IRUGO, mdss_mdp_vsync_show_event, NULL); static DEVICE_ATTR(lineptr_event, S_IRUGO, mdss_mdp_lineptr_show_event, NULL); static DEVICE_ATTR(lineptr_value, S_IRUGO | S_IWUSR | S_IWGRP, @@ -3454,6 +3560,7 @@ static struct attribute *mdp_overlay_sysfs_attrs[] = { &dev_attr_dyn_pu.attr, &dev_attr_msm_misr_en.attr, &dev_attr_msm_cmd_autorefresh_en.attr, + &dev_attr_msm_disable_panel.attr, &dev_attr_hist_event.attr, &dev_attr_bl_event.attr, &dev_attr_ad_event.attr, diff --git a/drivers/video/fbdev/msm/mdss_panel.h b/drivers/video/fbdev/msm/mdss_panel.h index b2b647dcc017..a633528b5373 100644 --- a/drivers/video/fbdev/msm/mdss_panel.h +++ b/drivers/video/fbdev/msm/mdss_panel.h @@ -104,6 +104,7 @@ enum { MDSS_PANEL_POWER_ON, MDSS_PANEL_POWER_LP1, MDSS_PANEL_POWER_LP2, + MDSS_PANEL_POWER_LCD_DISABLED, }; enum { @@ -265,6 +266,7 @@ enum mdss_intf_events { MDSS_EVENT_DSI_RESET_WRITE_PTR, MDSS_EVENT_PANEL_TIMING_SWITCH, MDSS_EVENT_DEEP_COLOR, + MDSS_EVENT_DISABLE_PANEL, MDSS_EVENT_MAX, }; @@ -692,6 +694,7 @@ struct mdss_panel_info { char panel_name[MDSS_MAX_PANEL_LEN]; struct mdss_mdp_pp_tear_check te; + struct mdss_mdp_pp_tear_check te_cached; /* * Value of 2 only when single DSI is configured with 2 DSC @@ -789,6 +792,12 @@ struct mdss_panel_data { /* To store dsc cfg name passed by bootloader */ char dsc_cfg_np_name[MDSS_MAX_PANEL_LEN]; struct mdss_panel_data *next; + + /* + * Set when the power of the panel is disabled while dsi/mdp + * are still on; panel will recover after unblank + */ + bool panel_disable_mode; }; struct mdss_panel_debugfs_info { -- cgit v1.2.3