diff options
| author | Aravind Venkateswaran <aravindh@codeaurora.org> | 2013-12-25 00:36:16 -0800 |
|---|---|---|
| committer | David Keitel <dkeitel@codeaurora.org> | 2016-03-23 20:27:24 -0700 |
| commit | 23c867e2cd6e415279da7bbb816714424cee35ba (patch) | |
| tree | 27a16317f588e7fc4a37c0342d2f5f5cf41bb18f /drivers | |
| parent | 9b89c7b7765c8966e81f9dc4ab99b6f8247bbd99 (diff) | |
msm: mdss: Add support for ULPS mode for DSI
For smart panels that can refresh display from their internal
RAMs, it is possible to configure the DSI clock and data lanes
in Ultra Low Power State (ULPS) during idle static screen
usecase. Add support for this feature.
Change-Id: I4e94d6a0201262f0675322efc9e39dd93c86edda
Signed-off-by: Aravind Venkateswaran <aravindh@codeaurora.org>
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_dsi.c | 294 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_dsi.h | 8 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_dsi_host.c | 6 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_dsi_panel.c | 75 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c | 52 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_panel.h | 7 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/msm_mdss_io_8974.c | 36 |
7 files changed, 367 insertions, 111 deletions
diff --git a/drivers/video/fbdev/msm/mdss_dsi.c b/drivers/video/fbdev/msm/mdss_dsi.c index 73da600c9275..3dc71bbb7d9c 100644 --- a/drivers/video/fbdev/msm/mdss_dsi.c +++ b/drivers/video/fbdev/msm/mdss_dsi.c @@ -72,18 +72,19 @@ static int mdss_dsi_panel_power_on(struct mdss_panel_data *pdata, int enable) goto error; } - ret = mdss_dsi_panel_reset(pdata, 1); - if (ret) { - pr_err("%s: Panel reset failed. rc=%d\n", - __func__, ret); - if (msm_dss_enable_vreg( - ctrl_pdata->power_data.vreg_config, - ctrl_pdata->power_data.num_vreg, 0)) - pr_err("Disable vregs failed\n"); - goto error; + if (!pdata->panel_info.mipi.lp11_init) { + ret = mdss_dsi_panel_reset(pdata, 1); + if (ret) { + pr_err("%s: Panel reset failed. rc=%d\n", + __func__, ret); + if (msm_dss_enable_vreg( + ctrl_pdata->power_data.vreg_config, + ctrl_pdata->power_data.num_vreg, 0)) + pr_err("Disable vregs failed\n"); + goto error; + } } } else { - ret = mdss_dsi_panel_reset(pdata, 0); if (ret) { pr_err("%s: Panel reset failed. rc=%d\n", @@ -346,72 +347,22 @@ static int mdss_dsi_off(struct mdss_panel_data *pdata) return ret; } -int mdss_dsi_on(struct mdss_panel_data *pdata) +static void __mdss_dsi_ctrl_setup(struct mdss_panel_data *pdata) { - int ret = 0; - u32 clk_rate; + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; struct mdss_panel_info *pinfo; struct mipi_panel_info *mipi; + u32 clk_rate; u32 hbp, hfp, vbp, vfp, hspw, vspw, width, height; u32 ystride, bpp, data, dst_bpp; u32 dummy_xres = 0, dummy_yres = 0; - struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; u32 hsync_period, vsync_period; - if (pdata == NULL) { - pr_err("%s: Invalid input data\n", __func__); - return -EINVAL; - } - - if (pdata->panel_info.panel_power_on) { - pr_warn("%s:%d Panel already on.\n", __func__, __LINE__); - return 0; - } - ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, panel_data); - pr_debug("%s+: ctrl=%p ndx=%d\n", - __func__, ctrl_pdata, ctrl_pdata->ndx); - pinfo = &pdata->panel_info; - ret = msm_dss_enable_vreg(ctrl_pdata->power_data.vreg_config, - ctrl_pdata->power_data.num_vreg, 1); - if (ret) { - pr_err("%s:Failed to enable vregs. rc=%d\n", __func__, ret); - return ret; - } - - if (!pdata->panel_info.mipi.lp11_init) { - ret = mdss_dsi_panel_reset(pdata, 1); - if (ret) { - pr_err("%s: Panel reset failed. rc=%d\n", - __func__, ret); - return ret; - } - } - ret = mdss_dsi_bus_clk_start(ctrl_pdata); - if (ret) { - pr_err("%s: failed to enable bus clocks. rc=%d\n", __func__, - ret); - ret = mdss_dsi_panel_power_on(pdata, 0); - if (ret) { - pr_err("%s: Panel reset failed. rc=%d\n", - __func__, ret); - return ret; - } - pdata->panel_info.panel_power_on = 0; - return ret; - } - - pdata->panel_info.panel_power_on = 1; - mdss_dsi_phy_sw_reset((ctrl_pdata->ctrl_base)); - mdss_dsi_phy_init(pdata); - mdss_dsi_bus_clk_stop(ctrl_pdata); - - mdss_dsi_clk_ctrl(ctrl_pdata, 1); - clk_rate = pdata->panel_info.clk_rate; clk_rate = min(clk_rate, pdata->panel_info.clk_max); @@ -441,7 +392,7 @@ int mdss_dsi_on(struct mdss_panel_data *pdata) vsync_period = vspw + vbp + height + dummy_yres + vfp; hsync_period = hspw + hbp + width + dummy_xres + hfp; - mipi = &pdata->panel_info.mipi; + mipi = &pdata->panel_info.mipi; if (pdata->panel_info.type == MIPI_VIDEO_PANEL) { MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x24, ((hspw + hbp + width + dummy_xres) << 16 | @@ -479,24 +430,195 @@ int mdss_dsi_on(struct mdss_panel_data *pdata) MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x64, data); MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x5C, data); } +} - mdss_dsi_sw_reset(pdata); - mdss_dsi_host_init(mipi, pdata); +static inline bool __mdss_dsi_ulps_feature_enabled( + struct mdss_panel_data *pdata) +{ + return pdata->panel_info.ulps_feature_enabled; +} - /* - * Issue hardware reset line after enabling the DSI clocks and data - * data lanes for LP11 init - */ - if (pdata->panel_info.mipi.lp11_init) { - ret = mdss_dsi_panel_reset(pdata, 1); +static int mdss_dsi_ulps_config_sub(struct mdss_dsi_ctrl_pdata *ctrl_pdata, + int enable) +{ + int ret = 0; + struct mdss_panel_data *pdata = NULL; + u32 lane_status = 0; + + if (!ctrl_pdata) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + pdata = &ctrl_pdata->panel_data; + + if (!__mdss_dsi_ulps_feature_enabled(pdata)) { + pr_debug("%s: ULPS feature not supported. enable=%d\n", + __func__, enable); + return -ENOTSUPP; + } + + if (enable && !ctrl_pdata->ulps) { + /* No need to configure ULPS mode when entering suspend state */ + if (!pdata->panel_info.panel_power_on) { + pr_err("%s: panel off. returning\n", __func__); + goto error; + } + + if (__mdss_dsi_clk_enabled(ctrl_pdata)) { + pr_err("%s: cannot enter ulps mode if dsi clocks are on\n", + __func__); + ret = -EPERM; + goto error; + } + + mdss_dsi_clk_ctrl(ctrl_pdata, 1); + /* + * ULPS Entry Request. + * Wait for a short duration to ensure that the lanes + * enter ULP state. + */ + MIPI_OUTP(ctrl_pdata->ctrl_base + 0x0AC, 0x01F); + usleep(100); + + /* Enable MMSS DSI Clamps */ + MIPI_OUTP(ctrl_pdata->mmss_misc_io.base + 0x14, 0x3FF); + MIPI_OUTP(ctrl_pdata->mmss_misc_io.base + 0x14, 0x83FF); + + wmb(); + + MIPI_OUTP(ctrl_pdata->mmss_misc_io.base + 0x108, 0x1); + /* disable DSI controller */ + mdss_dsi_controller_cfg(0, pdata); + + lane_status = MIPI_INP(ctrl_pdata->ctrl_base + 0xA8), + mdss_dsi_clk_ctrl(ctrl_pdata, 0); + ctrl_pdata->ulps = true; + } else if (ctrl_pdata->ulps) { + mdss_dsi_phy_init(pdata); + + __mdss_dsi_ctrl_setup(pdata); + mdss_dsi_sw_reset(pdata); + mdss_dsi_host_init(pdata); + mdss_dsi_op_mode_config(pdata->panel_info.mipi.mode, + pdata); + + /* Disable MMSS DSI Clamps */ + MIPI_OUTP(ctrl_pdata->mmss_misc_io.base + 0x14, 0x0); + + /* + * ULPS Exit Request + * Hardware requirement is to wait for at least 1ms + */ + MIPI_OUTP(ctrl_pdata->ctrl_base + 0x0AC, 0x1F00); + usleep(1000); + MIPI_OUTP(ctrl_pdata->ctrl_base + 0x0AC, 0x0); + + /* + * Wait for a short duration before enabling + * data transmission + */ + usleep(100); + + lane_status = MIPI_INP(ctrl_pdata->ctrl_base + 0xA8), + ctrl_pdata->ulps = false; + } + + pr_debug("%s: DSI lane status = 0x%08x. Ulps %s\n", __func__, + lane_status, enable ? "enabled" : "disabled"); + +error: + return ret; +} + +static int mdss_dsi_ulps_config(struct mdss_dsi_ctrl_pdata *ctrl, + int enable) +{ + int rc; + struct mdss_dsi_ctrl_pdata *sctrl = NULL; + + if (ctrl->flags & DSI_FLAG_CLOCK_MASTER) + sctrl = mdss_dsi_ctrl_slave(ctrl); + + if (sctrl) { + pr_debug("%s: configuring ulps (%s) for slave ctrl\n", + __func__, (enable ? "on" : "off")); + rc = mdss_dsi_ulps_config_sub(sctrl, enable); + if (rc) + return rc; + } + + pr_debug("%s: configuring ulps (%s) for master ctrl\n", + __func__, (enable ? "on" : "off")); + return mdss_dsi_ulps_config_sub(ctrl, enable); +} + +int mdss_dsi_on(struct mdss_panel_data *pdata) +{ + int ret = 0; + struct mdss_panel_info *pinfo; + struct mipi_panel_info *mipi; + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; + + if (pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return -EINVAL; + } + + if (pdata->panel_info.panel_power_on) { + pr_warn("%s:%d Panel already on.\n", __func__, __LINE__); + return 0; + } + + ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, + panel_data); + + pr_debug("%s+: ctrl=%p ndx=%d\n", + __func__, ctrl_pdata, ctrl_pdata->ndx); + + pinfo = &pdata->panel_info; + mipi = &pdata->panel_info.mipi; + + ret = mdss_dsi_panel_power_on(pdata, 1); + if (ret) { + pr_err("%s:Panel power on failed. rc=%d\n", __func__, ret); + return ret; + } + + ret = mdss_dsi_bus_clk_start(ctrl_pdata); + if (ret) { + pr_err("%s: failed to enable bus clocks. rc=%d\n", __func__, + ret); + ret = mdss_dsi_panel_power_on(pdata, 0); if (ret) { pr_err("%s: Panel reset failed. rc=%d\n", __func__, ret); return ret; } + pdata->panel_info.panel_power_on = 0; + return ret; } - if (pdata->panel_info.mipi.init_delay) - usleep(pdata->panel_info.mipi.init_delay); + pdata->panel_info.panel_power_on = 1; + + mdss_dsi_phy_sw_reset((ctrl_pdata->ctrl_base)); + mdss_dsi_phy_init(pdata); + mdss_dsi_bus_clk_stop(ctrl_pdata); + + mdss_dsi_clk_ctrl(ctrl_pdata, 1); + + __mdss_dsi_ctrl_setup(pdata); + mdss_dsi_sw_reset(pdata); + mdss_dsi_host_init(pdata); + + /* + * Issue hardware reset line after enabling the DSI clocks and data + * data lanes for LP11 init + */ + if (mipi->lp11_init) + mdss_dsi_panel_reset(pdata, 1); + + if (mipi->init_delay) + usleep(mipi->init_delay); if (mipi->force_clk_lane_hs) { u32 tmp; @@ -570,6 +692,17 @@ static int mdss_dsi_blank(struct mdss_panel_data *pdata) panel_data); mipi = &pdata->panel_info.mipi; + if (__mdss_dsi_ulps_feature_enabled(pdata) && + (ctrl_pdata->ulps)) { + /* Disable ULPS mode before blanking the panel */ + ret = mdss_dsi_ulps_config(ctrl_pdata, 0); + if (ret) { + pr_err("%s: failed to exit ULPS mode. rc=%d\n", + __func__, ret); + return ret; + } + } + mdss_dsi_op_mode_config(DSI_CMD_MODE, pdata); if (pdata->panel_info.type == MIPI_CMD_PANEL) { @@ -616,7 +749,7 @@ int mdss_dsi_cont_splash_on(struct mdss_panel_data *pdata) "Incorrect Ctrl state=0x%x\n", ctrl_pdata->ctrl_state); mdss_dsi_sw_reset(pdata); - mdss_dsi_host_init(mipi, pdata); + mdss_dsi_host_init(pdata); mdss_dsi_op_mode_config(mipi->mode, pdata); if (ctrl_pdata->on_cmds.link_state == DSI_LP_MODE) { @@ -824,6 +957,9 @@ static int mdss_dsi_event_handler(struct mdss_panel_data *pdata, case MDSS_EVENT_ENABLE_PARTIAL_UPDATE: rc = mdss_dsi_ctl_partial_update(pdata); break; + case MDSS_EVENT_DSI_ULPS_CTRL: + rc = mdss_dsi_ulps_config(ctrl_pdata, (int)arg); + break; default: pr_debug("%s: unhandled event=%d\n", __func__, event); break; @@ -1054,6 +1190,7 @@ static int mdss_dsi_ctrl_remove(struct platform_device *pdev) pr_err("%s: failed to de-init vregs\n", __func__); mdss_dsi_put_dt_vreg_data(&pdev->dev, &ctrl_pdata->power_data); mfd = platform_get_drvdata(pdev); + msm_dss_iounmap(&ctrl_pdata->mmss_misc_io); return 0; } @@ -1113,6 +1250,13 @@ int mdss_dsi_retrieve_ctrl_resources(struct platform_device *pdev, int mode, __func__, (int) (unsigned long) ctrl->ctrl_base, ctrl->reg_size); + rc = msm_dss_ioremap_byname(pdev, &ctrl->mmss_misc_io, + "mmss_misc_phys"); + if (rc) { + pr_err("%s:%d mmss_misc IO remap failed\n", __func__, __LINE__); + return rc; + } + return 0; } diff --git a/drivers/video/fbdev/msm/mdss_dsi.h b/drivers/video/fbdev/msm/mdss_dsi.h index 89a2178b49ef..f84e32ca293d 100644 --- a/drivers/video/fbdev/msm/mdss_dsi.h +++ b/drivers/video/fbdev/msm/mdss_dsi.h @@ -234,6 +234,7 @@ struct mdss_dsi_ctrl_pdata { int (*cmdlist_commit)(struct mdss_dsi_ctrl_pdata *ctrl, int from_mdp); struct mdss_panel_data panel_data; unsigned char *ctrl_base; + struct dss_io_data mmss_misc_io; int reg_size; u32 clk_cnt; int clk_cnt_sub; @@ -241,6 +242,7 @@ struct mdss_dsi_ctrl_pdata { struct clk *mdp_core_clk; struct clk *ahb_clk; struct clk *axi_clk; + struct clk *mmss_misc_ahb_clk; struct clk *byte_clk; struct clk *esc_clk; struct clk *pixel_clk; @@ -284,6 +286,8 @@ struct mdss_dsi_ctrl_pdata { struct mutex mutex; struct mutex cmd_mutex; + bool ulps; + struct dsi_buf tx_buf; struct dsi_buf rx_buf; }; @@ -297,8 +301,7 @@ int mdss_dsi_cmds_tx(struct mdss_dsi_ctrl_pdata *ctrl, int mdss_dsi_cmds_rx(struct mdss_dsi_ctrl_pdata *ctrl, struct dsi_cmd_desc *cmds, int rlen); -void mdss_dsi_host_init(struct mipi_panel_info *pinfo, - struct mdss_panel_data *pdata); +void mdss_dsi_host_init(struct mdss_panel_data *pdata); void mdss_dsi_op_mode_config(int mode, struct mdss_panel_data *pdata); void mdss_dsi_cmd_mode_ctrl(int enable); @@ -345,6 +348,7 @@ void mdss_dsi_wait4video_done(struct mdss_dsi_ctrl_pdata *ctrl); int mdss_dsi_cmdlist_commit(struct mdss_dsi_ctrl_pdata *ctrl, int from_mdp); void mdss_dsi_cmdlist_kickoff(int intf); int mdss_dsi_bta_status_check(struct mdss_dsi_ctrl_pdata *ctrl); +bool __mdss_dsi_clk_enabled(struct mdss_dsi_ctrl_pdata *ctrl); int mdss_dsi_panel_init(struct device_node *node, struct mdss_dsi_ctrl_pdata *ctrl_pdata, diff --git a/drivers/video/fbdev/msm/mdss_dsi_host.c b/drivers/video/fbdev/msm/mdss_dsi_host.c index 8b3b29fcd595..11e6c984440a 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_host.c +++ b/drivers/video/fbdev/msm/mdss_dsi_host.c @@ -252,12 +252,12 @@ void mdss_dsi_cmd_test_pattern(struct mdss_dsi_ctrl_pdata *ctrl) MIPI_OUTP((ctrl->ctrl_base) + 0x015c, 0x0); } -void mdss_dsi_host_init(struct mipi_panel_info *pinfo, - struct mdss_panel_data *pdata) +void mdss_dsi_host_init(struct mdss_panel_data *pdata) { u32 dsi_ctrl, intr_ctrl; u32 data; struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; + struct mipi_panel_info *pinfo = NULL; if (pdata == NULL) { pr_err("%s: Invalid input data\n", __func__); @@ -267,6 +267,8 @@ void mdss_dsi_host_init(struct mipi_panel_info *pinfo, ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, panel_data); + pinfo = &pdata->panel_info.mipi; + pinfo->rgb_swap = DSI_RGB_SWAP_RGB; if (pinfo->mode == DSI_VIDEO_MODE) { diff --git a/drivers/video/fbdev/msm/mdss_dsi_panel.c b/drivers/video/fbdev/msm/mdss_dsi_panel.c index 368b822fad4d..d040955254e6 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_panel.c +++ b/drivers/video/fbdev/msm/mdss_dsi_panel.c @@ -737,6 +737,35 @@ static int mdss_dsi_parse_reset_seq(struct device_node *np, return 0; } +static int mdss_dsi_parse_panel_features(struct device_node *np, + struct mdss_dsi_ctrl_pdata *ctrl) +{ + struct mdss_panel_info *pinfo; + + if (!np || !ctrl) { + pr_err("%s: Invalid arguments\n", __func__); + return -ENODEV; + } + + pinfo = &ctrl->panel_data.panel_info; + + pinfo->cont_splash_enabled = of_property_read_bool(np, + "qcom,cont-splash-enabled"); + + pinfo->partial_update_enabled = of_property_read_bool(np, + "qcom,partial-update-enabled"); + pr_info("%s:%d Partial update %s\n", __func__, __LINE__, + (pinfo->partial_update_enabled ? "enabled" : "disabled")); + if (pinfo->partial_update_enabled) + ctrl->partial_update_fnc = mdss_dsi_panel_partial_update; + + pinfo->ulps_feature_enabled = of_property_read_bool(np, + "qcom,ulps-enabled"); + pr_info("%s: ulps feature %s", __func__, + (pinfo->ulps_feature_enabled ? "enabled" : "disabled")); + + return 0; +} static int mdss_panel_parse_dt(struct device_node *np, struct mdss_dsi_ctrl_pdata *ctrl_pdata) @@ -1019,6 +1048,12 @@ static int mdss_panel_parse_dt(struct device_node *np, mdss_dsi_parse_dcs_cmds(np, &ctrl_pdata->off_cmds, "qcom,mdss-dsi-off-command", "qcom,mdss-dsi-off-command-state"); + rc = mdss_dsi_parse_panel_features(np, ctrl_pdata); + if (rc) { + pr_err("%s: failed to parse panel features\n", __func__); + goto error; + } + return 0; error: @@ -1031,14 +1066,15 @@ int mdss_dsi_panel_init(struct device_node *node, { int rc = 0; static const char *panel_name; - bool cont_splash_enabled; - bool partial_update_enabled; + struct mdss_panel_info *pinfo; - if (!node) { - pr_err("%s: no panel node\n", __func__); + if (!node || !ctrl_pdata) { + pr_err("%s: Invalid arguments\n", __func__); return -ENODEV; } + pinfo = &ctrl_pdata->panel_data.panel_info; + pr_debug("%s:%d\n", __func__, __LINE__); panel_name = of_get_property(node, "qcom,mdss-dsi-panel-name", NULL); if (!panel_name) @@ -1053,33 +1089,10 @@ int mdss_dsi_panel_init(struct device_node *node, return rc; } - if (cmd_cfg_cont_splash) - cont_splash_enabled = of_property_read_bool(node, - "qcom,cont-splash-enabled"); - else - cont_splash_enabled = false; - if (!cont_splash_enabled) { - pr_info("%s:%d Continuous splash flag not found.\n", - __func__, __LINE__); - ctrl_pdata->panel_data.panel_info.cont_splash_enabled = 0; - } else { - pr_info("%s:%d Continuous splash flag enabled.\n", - __func__, __LINE__); - - ctrl_pdata->panel_data.panel_info.cont_splash_enabled = 1; - } - - partial_update_enabled = of_property_read_bool(node, - "qcom,partial-update-enabled"); - if (partial_update_enabled) { - pr_info("%s: Partial update enabled.\n", __func__); - ctrl_pdata->panel_data.panel_info.partial_update_enabled = 1; - ctrl_pdata->partial_update_fnc = mdss_dsi_panel_partial_update; - } else { - pr_info("%s: Partial update disabled.\n", __func__); - ctrl_pdata->panel_data.panel_info.partial_update_enabled = 0; - ctrl_pdata->partial_update_fnc = NULL; - } + if (!cmd_cfg_cont_splash) + pinfo->cont_splash_enabled = false; + pr_info("%s: Continuous splash %s", __func__, + pinfo->cont_splash_enabled ? "enabled" : "disabled"); ctrl_pdata->on = mdss_dsi_panel_on; ctrl_pdata->off = mdss_dsi_panel_off; diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c index 6205445dbd96..72b3999f2c94 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c +++ b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c @@ -24,6 +24,7 @@ #define KOFF_TIMEOUT msecs_to_jiffies(84) #define STOP_TIMEOUT msecs_to_jiffies(16 * (VSYNC_EXPIRE_TICK + 2)) +#define ULPS_ENTER_TIME msecs_to_jiffies(100) struct mdss_mdp_cmd_ctx { struct mdss_mdp_ctl *ctl; @@ -40,9 +41,11 @@ struct mdss_mdp_cmd_ctx { struct mutex clk_mtx; spinlock_t clk_lock; struct work_struct clk_work; + struct delayed_work ulps_work; struct work_struct pp_done_work; atomic_t pp_done_cnt; struct mdss_panel_recovery recovery; + bool ulps; }; struct mdss_mdp_cmd_ctx mdss_mdp_cmd_ctx_list[MAX_SESSIONS]; @@ -180,8 +183,19 @@ static inline void mdss_mdp_cmd_clk_on(struct mdss_mdp_cmd_ctx *ctx) mutex_lock(&ctx->clk_mtx); if (!ctx->clk_enabled) { ctx->clk_enabled = 1; + if (cancel_delayed_work_sync(&ctx->ulps_work)) + pr_debug("deleted pending ulps work\n"); mdss_mdp_ctl_intf_event (ctx->ctl, MDSS_EVENT_PANEL_CLK_CTRL, (void *)1); + + if (ctx->ulps) { + if (mdss_mdp_cmd_tearcheck_setup(ctx->ctl)) + pr_warn("tearcheck setup failed\n"); + mdss_mdp_ctl_intf_event(ctx->ctl, + MDSS_EVENT_DSI_ULPS_CTRL, (void *)0); + ctx->ulps = false; + } + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); mdss_mdp_hist_intr_setup(&mdata->hist_intr, MDSS_IRQ_RESUME); } @@ -211,6 +225,8 @@ static inline void mdss_mdp_cmd_clk_off(struct mdss_mdp_cmd_ctx *ctx) mdss_mdp_ctl_intf_event (ctx->ctl, MDSS_EVENT_PANEL_CLK_CTRL, (void *)0); mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); + if (ctx->panel_on) + schedule_delayed_work(&ctx->ulps_work, ULPS_ENTER_TIME); } mutex_unlock(&ctx->clk_mtx); } @@ -345,6 +361,36 @@ static void clk_ctrl_work(struct work_struct *work) mdss_mdp_cmd_clk_off(ctx); } +static void __mdss_mdp_cmd_ulps_work(struct work_struct *work) +{ + struct delayed_work *dw = to_delayed_work(work); + struct mdss_mdp_cmd_ctx *ctx = + container_of(dw, struct mdss_mdp_cmd_ctx, ulps_work); + + if (!ctx) { + pr_err("%s: invalid ctx\n", __func__); + return; + } + + mutex_lock(&ctx->clk_mtx); + if (ctx->clk_enabled) { + mutex_unlock(&ctx->clk_mtx); + pr_warn("Cannot enter ulps mode if DSI clocks are on\n"); + return; + } + mutex_unlock(&ctx->clk_mtx); + + if (!ctx->panel_on) { + pr_err("Panel is off. skipping ULPS configuration\n"); + return; + } + + if (!mdss_mdp_ctl_intf_event(ctx->ctl, MDSS_EVENT_DSI_ULPS_CTRL, + (void *)1)) { + ctx->ulps = true; + } +} + static int mdss_mdp_cmd_add_vsync_handler(struct mdss_mdp_ctl *ctl, struct mdss_mdp_vsync_handler *handle) { @@ -561,11 +607,14 @@ int mdss_mdp_cmd_stop(struct mdss_mdp_ctl *ctl) if (cancel_work_sync(&ctx->clk_work)) pr_debug("no pending clk work\n"); + if (cancel_delayed_work_sync(&ctx->ulps_work)) + pr_debug("deleted pending ulps work\n"); + + ctx->panel_on = 0; mdss_mdp_cmd_clk_off(ctx); flush_work(&ctx->pp_done_work); - ctx->panel_on = 0; mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_PING_PONG_RD_PTR, ctx->pp_num, NULL, NULL); @@ -633,6 +682,7 @@ int mdss_mdp_cmd_start(struct mdss_mdp_ctl *ctl) spin_lock_init(&ctx->clk_lock); mutex_init(&ctx->clk_mtx); INIT_WORK(&ctx->clk_work, clk_ctrl_work); + INIT_DELAYED_WORK(&ctx->ulps_work, __mdss_mdp_cmd_ulps_work); INIT_WORK(&ctx->pp_done_work, pingpong_done_work); atomic_set(&ctx->pp_done_cnt, 0); INIT_LIST_HEAD(&ctx->vsync_handlers); diff --git a/drivers/video/fbdev/msm/mdss_panel.h b/drivers/video/fbdev/msm/mdss_panel.h index 46508f949f67..a32b695a0c37 100644 --- a/drivers/video/fbdev/msm/mdss_panel.h +++ b/drivers/video/fbdev/msm/mdss_panel.h @@ -127,6 +127,11 @@ struct mdss_panel_recovery { - 1 clock enable * @MDSS_EVENT_DSI_CMDLIST_KOFF: acquire dsi_mdp_busy lock before kickoff. * @MDSS_EVENT_ENABLE_PARTIAL_UPDATE: Event to update ROI of the panel. + * @MDSS_EVENT_DSI_ULPS_CTRL: Event to configure Ultra Lower Power Saving + * mode for the DSI data and clock lanes. The + * event arguments can have one of these values: + * - 0: Disable ULPS mode + * - 1: Enable ULPS mode */ enum mdss_intf_events { MDSS_EVENT_RESET = 1, @@ -145,6 +150,7 @@ enum mdss_intf_events { MDSS_EVENT_PANEL_CLK_CTRL, MDSS_EVENT_DSI_CMDLIST_KOFF, MDSS_EVENT_ENABLE_PARTIAL_UPDATE, + MDSS_EVENT_DSI_ULPS_CTRL, }; struct lcd_panel_info { @@ -310,6 +316,7 @@ struct mdss_panel_info { int pwm_lpg_chan; int pwm_period; bool dynamic_fps; + bool ulps_feature_enabled; char dfps_update; int new_fps; u32 mode_gpio_state; diff --git a/drivers/video/fbdev/msm/msm_mdss_io_8974.c b/drivers/video/fbdev/msm/msm_mdss_io_8974.c index dccc7fbeabb6..c363edd1013a 100644 --- a/drivers/video/fbdev/msm/msm_mdss_io_8974.c +++ b/drivers/video/fbdev/msm/msm_mdss_io_8974.c @@ -65,6 +65,16 @@ int mdss_dsi_clk_init(struct platform_device *pdev, goto mdss_dsi_clk_err; } + if (ctrl->panel_data.panel_info.type == MIPI_CMD_PANEL) { + ctrl->mmss_misc_ahb_clk = clk_get(dev, "core_mmss_clk"); + if (IS_ERR(ctrl->mmss_misc_ahb_clk)) { + rc = PTR_ERR(ctrl->mmss_misc_ahb_clk); + pr_err("%s: Unable to get mmss misc ahb clk. rc=%d\n", + __func__, rc); + goto mdss_dsi_clk_err; + } + } + ctrl->byte_clk = clk_get(dev, "byte_clk"); if (IS_ERR(ctrl->byte_clk)) { rc = PTR_ERR(ctrl->byte_clk); @@ -115,6 +125,8 @@ void mdss_dsi_clk_deinit(struct mdss_dsi_ctrl_pdata *ctrl) clk_put(ctrl->esc_clk); if (ctrl->pixel_clk) clk_put(ctrl->pixel_clk); + if (ctrl->mmss_misc_ahb_clk) + clk_put(ctrl->mmss_misc_ahb_clk); if (ctrl->axi_clk) clk_put(ctrl->axi_clk); if (ctrl->ahb_clk) @@ -285,12 +297,26 @@ int mdss_dsi_bus_clk_start(struct mdss_dsi_ctrl_pdata *ctrl_pdata) goto error; } + if (ctrl_pdata->mmss_misc_ahb_clk) { + rc = clk_prepare_enable(ctrl_pdata->mmss_misc_ahb_clk); + if (rc) { + pr_err("%s: failed to enable mmss misc ahb clk.rc=%d\n", + __func__, rc); + clk_disable_unprepare(ctrl_pdata->axi_clk); + clk_disable_unprepare(ctrl_pdata->ahb_clk); + clk_disable_unprepare(ctrl_pdata->mdp_core_clk); + goto error; + } + } + error: return rc; } void mdss_dsi_bus_clk_stop(struct mdss_dsi_ctrl_pdata *ctrl_pdata) { + if (ctrl_pdata->mmss_misc_ahb_clk) + clk_disable_unprepare(ctrl_pdata->mmss_misc_ahb_clk); clk_disable_unprepare(ctrl_pdata->axi_clk); clk_disable_unprepare(ctrl_pdata->ahb_clk); clk_disable_unprepare(ctrl_pdata->mdp_core_clk); @@ -517,6 +543,16 @@ static void mdss_dsi_clk_ctrl_sub(struct mdss_dsi_ctrl_pdata *ctrl, int enable) static DEFINE_MUTEX(dsi_clk_lock); /* per system */ +bool __mdss_dsi_clk_enabled(struct mdss_dsi_ctrl_pdata *ctrl) +{ + bool enabled; + mutex_lock(&dsi_clk_lock); + enabled = ctrl->clk_cnt ? true : false; + mutex_unlock(&dsi_clk_lock); + + return enabled; +} + void mdss_dsi_clk_ctrl(struct mdss_dsi_ctrl_pdata *ctrl, int enable) { int changed = 0; |
