summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/fb/mdss-dsi-ctrl.txt7
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi.c294
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi.h8
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi_host.c6
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi_panel.c75
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c52
-rw-r--r--drivers/video/fbdev/msm/mdss_panel.h7
-rw-r--r--drivers/video/fbdev/msm/msm_mdss_io_8974.c36
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;