diff options
| -rw-r--r-- | Documentation/devicetree/bindings/fb/mdss-dsi-ctrl.txt | 7 | ||||
| -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 |
8 files changed, 372 insertions, 113 deletions
diff --git a/Documentation/devicetree/bindings/fb/mdss-dsi-ctrl.txt b/Documentation/devicetree/bindings/fb/mdss-dsi-ctrl.txt index 27e5a96111d9..972f85a2172a 100644 --- a/Documentation/devicetree/bindings/fb/mdss-dsi-ctrl.txt +++ b/Documentation/devicetree/bindings/fb/mdss-dsi-ctrl.txt @@ -6,7 +6,8 @@ are compatable with MIPI display serial interface specification. Required properties: - compatible: Must be "qcom,mdss-dsi-ctrl" - cell-index: Specifies the controller used among the two controllers. -- reg: offset and length of the register set for the device. +- reg: Offset and length of the register regions(s) for the device. +- reg-names: A list of strings that map in order to the list of regs. - vdd-supply: Phandle for vdd regulator device node. - vddio-supply: Phandle for vdd-io regulator device node. - vdda-supply: Phandle for vreg regulator device node. @@ -53,7 +54,9 @@ Example: compatible = "qcom,mdss-dsi-ctrl"; label = "MDSS DSI CTRL->0"; cell-index = <0>; - reg = <0xfd922800 0x600>; + reg = <0xfd922800 0x600>, + <0xfd828000 0x108>; + reg-names = "dsi_phys", "mmss_misc_phys"; vdd-supply = <&pm8226_l15>; vddio-supply = <&pm8226_l8>; vdda-supply = <&pm8226_l4>; 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; |
