summaryrefslogtreecommitdiff
path: root/drivers/video/fbdev
diff options
context:
space:
mode:
authorAravind Venkateswaran <aravindh@codeaurora.org>2016-05-12 17:45:58 -0700
committerKyle Yan <kyan@codeaurora.org>2016-05-24 14:26:35 -0700
commit1d4e56bab0e14e1c4021b7750d5876cebdc5c3cf (patch)
treeae050227e1d29a03494e1e57a40c7f5289ef1194 /drivers/video/fbdev
parent703d92d617c0caa16cce718f73eb5e650513a88b (diff)
msm: mdss: dsi: add ulps support for DSI PHY v3
Implement the recommended programming sequence for configuring the DSI lanes to Ultra-Low Power State (ULPS) for the DSI PHY v3. Change-Id: I5dc7d8ed4407df5baa94e069b00897086bd02ab8 Signed-off-by: Aravind Venkateswaran <aravindh@codeaurora.org>
Diffstat (limited to 'drivers/video/fbdev')
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi_host.c9
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi_phy.h23
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi_phy_v3.c111
-rw-r--r--drivers/video/fbdev/msm/msm_mdss_io_8974.c151
4 files changed, 238 insertions, 56 deletions
diff --git a/drivers/video/fbdev/msm/mdss_dsi_host.c b/drivers/video/fbdev/msm/mdss_dsi_host.c
index e0a8ad44a008..39b014b90e28 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_host.c
+++ b/drivers/video/fbdev/msm/mdss_dsi_host.c
@@ -567,10 +567,13 @@ int mdss_dsi_wait_for_lane_idle(struct mdss_dsi_ctrl_pdata *ctrl)
pr_debug("%s: polling for lanes to be in stop state, mask=0x%08x\n",
__func__, stop_state_mask);
- rc = readl_poll_timeout(ctrl->ctrl_base + LANE_STATUS, val,
- (val & stop_state_mask), sleep_us, timeout_us);
+ if (ctrl->shared_data->phy_rev == DSI_PHY_REV_30)
+ rc = mdss_dsi_phy_v3_wait_for_lanes_stop_state(ctrl, &val);
+ else
+ rc = readl_poll_timeout(ctrl->ctrl_base + LANE_STATUS, val,
+ (val & stop_state_mask), sleep_us, timeout_us);
if (rc) {
- pr_err("%s: lanes not in stop state, LANE_STATUS=0x%08x\n",
+ pr_debug("%s: lanes not in stop state, LANE_STATUS=0x%08x\n",
__func__, val);
goto error;
}
diff --git a/drivers/video/fbdev/msm/mdss_dsi_phy.h b/drivers/video/fbdev/msm/mdss_dsi_phy.h
index 4cb78e378548..5fff3123b63f 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_phy.h
+++ b/drivers/video/fbdev/msm/mdss_dsi_phy.h
@@ -92,4 +92,27 @@ int mdss_dsi_phy_v3_regulator_disable(struct mdss_dsi_ctrl_pdata *ctrl);
*/
void mdss_dsi_phy_v3_toggle_resync_fifo(struct mdss_dsi_ctrl_pdata *ctrl);
+/**
+ * mdss_dsi_phy_v3_wait_for_lanes_stop_state() - Wait for DSI lanes to be in
+ * stop state
+ * @ctrl: pointer to DSI controller structure
+ * @lane_status: value of lane status register at the end of the poll
+ *
+ * This function waits for all the active DSI lanes to be in stop state by
+ * polling the lane status register. This function assumes that the bus clocks
+ * required to access the registers are already turned on.
+ */
+int mdss_dsi_phy_v3_wait_for_lanes_stop_state(struct mdss_dsi_ctrl_pdata *ctrl,
+ u32 *lane_status);
+
+/**
+ * mdss_dsi_phy_v3_ulps_config() - Program DSI lanes to enter/exit ULPS mode
+ * @ctrl: pointer to DSI controller structure
+ * @enable: true to enter ULPS, false to exit ULPS
+ *
+ * This function executes the necessary hardware programming sequence to
+ * enter/exit DSI Ultra-Low Power State (ULPS) for DSI PHY v3. This function
+ * assumes that the link and core clocks are already on.
+ */
+int mdss_dsi_phy_v3_ulps_config(struct mdss_dsi_ctrl_pdata *ctrl, bool enable);
#endif /* MDSS_DSI_PHY_H */
diff --git a/drivers/video/fbdev/msm/mdss_dsi_phy_v3.c b/drivers/video/fbdev/msm/mdss_dsi_phy_v3.c
index 0e10eb5d0cc9..0c4dcf9db216 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_phy_v3.c
+++ b/drivers/video/fbdev/msm/mdss_dsi_phy_v3.c
@@ -45,6 +45,8 @@
#define CMN_TIMING_CTRL_10 0x0D4
#define CMN_TIMING_CTRL_11 0x0D8
#define CMN_PHY_STATUS 0x0EC
+#define CMN_LANE_STATUS0 0x0F4
+#define CMN_LANE_STATUS1 0x0F8
#define LNX_CFG0(n) ((0x200 + (0x80 * (n))) + 0x00)
#define LNX_CFG1(n) ((0x200 + (0x80 * (n))) + 0x04)
@@ -62,6 +64,29 @@
#define DSI_PHY_W32(b, off, val) MIPI_OUTP((b) + (off), (val))
#define DSI_PHY_R32(b, off) MIPI_INP((b) + (off))
+static u32 __get_active_lanes_mask(struct mdss_dsi_ctrl_pdata *ctrl)
+{
+ struct mipi_panel_info *mipi;
+ u32 mask = 0;
+
+ mipi = &ctrl->panel_data.panel_info.mipi;
+
+ /* clock lane will always be programmed for ulps */
+ mask = BIT(4);
+
+ /* Mark all active data lanes */
+ if (mipi->data_lane0)
+ mask |= BIT(0);
+ if (mipi->data_lane1)
+ mask |= BIT(1);
+ if (mipi->data_lane2)
+ mask |= BIT(2);
+ if (mipi->data_lane3)
+ mask |= BIT(3);
+
+ return mask;
+}
+
static bool mdss_dsi_phy_v3_is_pll_on(struct mdss_dsi_ctrl_pdata *ctrl)
{
u32 data;
@@ -179,6 +204,92 @@ void mdss_dsi_phy_v3_toggle_resync_fifo(struct mdss_dsi_ctrl_pdata *ctrl)
wmb();
}
+int mdss_dsi_phy_v3_wait_for_lanes_stop_state(struct mdss_dsi_ctrl_pdata *ctrl,
+ u32 *lane_status)
+{
+ u32 stop_state_mask = 0;
+ u32 const sleep_us = 10;
+ u32 const timeout_us = 100;
+
+ if (!ctrl || !lane_status) {
+ pr_err("invalid input\n");
+ return -EINVAL;
+ }
+
+ stop_state_mask = __get_active_lanes_mask(ctrl);
+
+ return readl_poll_timeout(ctrl->phy_io.base + CMN_LANE_STATUS1,
+ *lane_status, (*lane_status == stop_state_mask), sleep_us,
+ timeout_us);
+}
+
+int mdss_dsi_phy_v3_ulps_config(struct mdss_dsi_ctrl_pdata *ctrl, bool enable)
+{
+ int rc = 0;
+ u32 active_lanes = 0;
+ u32 lane_status = 0;
+
+ if (!ctrl) {
+ pr_err("invalid input\n");
+ return -EINVAL;
+ }
+
+ active_lanes = __get_active_lanes_mask(ctrl);
+
+ pr_debug("configuring ulps (%s) for ctrl%d, active lanes=0x%08x\n",
+ (enable ? "on" : "off"), ctrl->ndx, active_lanes);
+
+ if (enable) {
+ /*
+ * ULPS Entry Request.
+ * Wait for a short duration to ensure that the lanes
+ * enter ULP state.
+ */
+ DSI_PHY_W32(ctrl->phy_io.base, CMN_DSI_LANE_CTRL1,
+ active_lanes);
+ usleep_range(100, 110);
+
+ /* Check to make sure that all active data lanes are in ULPS */
+ lane_status = DSI_PHY_R32(ctrl->phy_io.base, CMN_LANE_STATUS0);
+ if (lane_status & active_lanes) {
+ pr_err("ULPS entry req failed for ctrl%d. Lane status=0x%08x\n",
+ ctrl->ndx, lane_status);
+ rc = -EINVAL;
+ goto error;
+ }
+ } else {
+ /*
+ * ULPS Exit Request
+ * Hardware requirement is to wait for at least 1ms
+ */
+ DSI_PHY_W32(ctrl->phy_io.base, CMN_DSI_LANE_CTRL2,
+ active_lanes);
+ usleep_range(1000, 1010);
+
+ /*
+ * Sometimes when exiting ULPS, it is possible that some DSI
+ * lanes are not in the stop state which could lead to DSI
+ * commands not going through. To avoid this, force the lanes
+ * to be in stop state.
+ */
+ DSI_PHY_W32(ctrl->phy_io.base, CMN_DSI_LANE_CTRL3,
+ active_lanes);
+
+ DSI_PHY_W32(ctrl->phy_io.base, CMN_DSI_LANE_CTRL3, 0);
+ DSI_PHY_W32(ctrl->phy_io.base, CMN_DSI_LANE_CTRL2, 0);
+ DSI_PHY_W32(ctrl->phy_io.base, CMN_DSI_LANE_CTRL1, 0);
+
+ lane_status = DSI_PHY_R32(ctrl->phy_io.base, CMN_LANE_STATUS0);
+ }
+
+ pr_debug("DSI lane status = 0x%08x. Ulps %s\n", lane_status,
+ enable ? "enabled" : "disabled");
+
+error:
+ return rc;
+}
+
+
int mdss_dsi_phy_v3_shutdown(struct mdss_dsi_ctrl_pdata *ctrl)
{
/* ensure that the PLL is already off */
diff --git a/drivers/video/fbdev/msm/msm_mdss_io_8974.c b/drivers/video/fbdev/msm/msm_mdss_io_8974.c
index 55d3a6f46a81..909b3b5ccf0c 100644
--- a/drivers/video/fbdev/msm/msm_mdss_io_8974.c
+++ b/drivers/video/fbdev/msm/msm_mdss_io_8974.c
@@ -1739,18 +1739,19 @@ static bool mdss_dsi_is_ulps_req_valid(struct mdss_dsi_ctrl_pdata *ctrl,
}
/**
- * mdss_dsi_ulps_config() - Program DSI lanes to enter/exit ULPS mode
+ * mdss_dsi_ulps_config_default() - Program DSI lanes to enter/exit ULPS mode
* @ctrl: pointer to DSI controller structure
- * @enable: 1 to enter ULPS, 0 to exit ULPS
+ * @enable: true to enter ULPS, false to exit ULPS
*
- * This function executes the necessary programming sequence to enter/exit
- * DSI Ultra-Low Power State (ULPS). This function assumes that the link and
- * core clocks are already on.
+ * Executes the default hardware programming sequence to enter/exit DSI
+ * Ultra-Low Power State (ULPS). This function would be called whenever there
+ * are no hardware version sepcific functions for configuring ULPS mode. This
+ * function assumes that the link and core clocks are already on.
*/
-static int mdss_dsi_ulps_config(struct mdss_dsi_ctrl_pdata *ctrl,
- int enable)
+static int mdss_dsi_ulps_config_default(struct mdss_dsi_ctrl_pdata *ctrl,
+ bool enable)
{
- int ret = 0;
+ int rc = 0;
struct mdss_panel_data *pdata = NULL;
struct mdss_panel_info *pinfo;
struct mipi_panel_info *mipi;
@@ -1770,12 +1771,6 @@ static int mdss_dsi_ulps_config(struct mdss_dsi_ctrl_pdata *ctrl,
pinfo = &pdata->panel_info;
mipi = &pinfo->mipi;
- if (!mdss_dsi_is_ulps_req_valid(ctrl, enable)) {
- pr_debug("%s: skiping ULPS config for ctrl%d, enable=%d\n",
- __func__, ctrl->ndx, enable);
- return 0;
- }
-
/* clock lane will always be programmed for ulps */
active_lanes = BIT(4);
/*
@@ -1791,9 +1786,83 @@ static int mdss_dsi_ulps_config(struct mdss_dsi_ctrl_pdata *ctrl,
if (mipi->data_lane3)
active_lanes |= BIT(3);
- pr_debug("%s: configuring ulps (%s) for ctrl%d, active lanes=0x%08x,clamps=%s\n",
+ pr_debug("%s: configuring ulps (%s) for ctrl%d, active lanes=0x%08x\n",
+ __func__, (enable ? "on" : "off"), ctrl->ndx, active_lanes);
+
+ if (enable) {
+ /*
+ * ULPS Entry Request.
+ * Wait for a short duration to ensure that the lanes
+ * enter ULP state.
+ */
+ MIPI_OUTP(ctrl->ctrl_base + 0x0AC, active_lanes);
+ usleep_range(100, 110);
+
+ /* Check to make sure that all active data lanes are in ULPS */
+ lane_status = MIPI_INP(ctrl->ctrl_base + 0xA8);
+ if (lane_status & (active_lanes << 8)) {
+ pr_err("%s: ULPS entry req failed for ctrl%d. Lane status=0x%08x\n",
+ __func__, ctrl->ndx, lane_status);
+ rc = -EINVAL;
+ goto error;
+ }
+ } else {
+ /*
+ * ULPS Exit Request
+ * Hardware requirement is to wait for at least 1ms
+ */
+ MIPI_OUTP(ctrl->ctrl_base + 0x0AC, active_lanes << 8);
+ usleep_range(1000, 1010);
+
+ /*
+ * Sometimes when exiting ULPS, it is possible that some DSI
+ * lanes are not in the stop state which could lead to DSI
+ * commands not going through. To avoid this, force the lanes
+ * to be in stop state.
+ */
+ MIPI_OUTP(ctrl->ctrl_base + 0x0AC, active_lanes << 16);
+
+ MIPI_OUTP(ctrl->ctrl_base + 0x0AC, 0x0);
+
+ lane_status = MIPI_INP(ctrl->ctrl_base + 0xA8);
+ }
+
+ pr_debug("%s: DSI lane status = 0x%08x. Ulps %s\n", __func__,
+ lane_status, enable ? "enabled" : "disabled");
+
+error:
+ return rc;
+}
+
+/**
+ * mdss_dsi_ulps_config() - Program DSI lanes to enter/exit ULPS mode
+ * @ctrl: pointer to DSI controller structure
+ * @enable: 1 to enter ULPS, 0 to exit ULPS
+ *
+ * Execute the necessary programming sequence to enter/exit DSI Ultra-Low Power
+ * State (ULPS). This function the validity of the ULPS config request and
+ * executes and pre/post steps before/after the necessary hardware programming.
+ * This function assumes that the link and core clocks are already on.
+ */
+static int mdss_dsi_ulps_config(struct mdss_dsi_ctrl_pdata *ctrl,
+ int enable)
+{
+ int ret = 0;
+
+ if (!ctrl) {
+ pr_err("%s: invalid input\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!mdss_dsi_is_ulps_req_valid(ctrl, enable)) {
+ pr_debug("%s: skiping ULPS config for ctrl%d, enable=%d\n",
+ __func__, ctrl->ndx, enable);
+ return 0;
+ }
+
+ pr_debug("%s: configuring ulps (%s) for ctrl%d, clamps=%s\n",
__func__, (enable ? "on" : "off"), ctrl->ndx,
- active_lanes, ctrl->mmss_clamp ? "enabled" : "disabled");
+ ctrl->mmss_clamp ? "enabled" : "disabled");
if (enable && !ctrl->ulps) {
/*
@@ -1809,29 +1878,19 @@ static int mdss_dsi_ulps_config(struct mdss_dsi_ctrl_pdata *ctrl,
if (!ctrl->mmss_clamp) {
ret = mdss_dsi_wait_for_lane_idle(ctrl);
if (ret) {
- pr_warn("%s: lanes not idle, skip ulps\n",
+ pr_warn_ratelimited("%s: lanes not idle, skip ulps\n",
__func__);
ret = 0;
goto error;
}
}
- /*
- * ULPS Entry Request.
- * Wait for a short duration to ensure that the lanes
- * enter ULP state.
- */
- MIPI_OUTP(ctrl->ctrl_base + 0x0AC, active_lanes);
- usleep_range(100, 100);
-
- /* Check to make sure that all active data lanes are in ULPS */
- lane_status = MIPI_INP(ctrl->ctrl_base + 0xA8);
- if (lane_status & (active_lanes << 8)) {
- pr_err("%s: ULPS entry req failed for ctrl%d. Lane status=0x%08x\n",
- __func__, ctrl->ndx, lane_status);
- ret = -EINVAL;
+ if (ctrl->shared_data->phy_rev == DSI_PHY_REV_30)
+ ret = mdss_dsi_phy_v3_ulps_config(ctrl, true);
+ else
+ ret = mdss_dsi_ulps_config_default(ctrl, true);
+ if (ret)
goto error;
- }
ctrl->ulps = true;
} else if (!enable && ctrl->ulps) {
@@ -1842,22 +1901,12 @@ static int mdss_dsi_ulps_config(struct mdss_dsi_ctrl_pdata *ctrl,
*/
mdss_dsi_dln0_phy_err(ctrl, false);
- /*
- * ULPS Exit Request
- * Hardware requirement is to wait for at least 1ms
- */
- MIPI_OUTP(ctrl->ctrl_base + 0x0AC, active_lanes << 8);
- usleep_range(1000, 1000);
-
- /*
- * Sometimes when exiting ULPS, it is possible that some DSI
- * lanes are not in the stop state which could lead to DSI
- * commands not going through. To avoid this, force the lanes
- * to be in stop state.
- */
- MIPI_OUTP(ctrl->ctrl_base + 0x0AC, active_lanes << 16);
-
- MIPI_OUTP(ctrl->ctrl_base + 0x0AC, 0x0);
+ if (ctrl->shared_data->phy_rev == DSI_PHY_REV_30)
+ ret = mdss_dsi_phy_v3_ulps_config(ctrl, false);
+ else
+ ret = mdss_dsi_ulps_config_default(ctrl, false);
+ if (ret)
+ goto error;
/*
* Wait for a short duration before enabling
@@ -1865,7 +1914,6 @@ static int mdss_dsi_ulps_config(struct mdss_dsi_ctrl_pdata *ctrl,
*/
usleep_range(100, 100);
- lane_status = MIPI_INP(ctrl->ctrl_base + 0xA8);
ctrl->ulps = false;
} else {
pr_debug("%s: No change requested: %s -> %s\n", __func__,
@@ -1873,9 +1921,6 @@ static int mdss_dsi_ulps_config(struct mdss_dsi_ctrl_pdata *ctrl,
enable ? "enabled" : "disabled");
}
- pr_debug("%s: DSI lane status = 0x%08x. Ulps %s\n", __func__,
- lane_status, enable ? "enabled" : "disabled");
-
error:
return ret;
}
@@ -2204,7 +2249,7 @@ int mdss_dsi_post_clkon_cb(void *priv,
if (mmss_clamp)
mdss_dsi_ctrl_setup(ctrl);
- if (ctrl->ulps) {
+ if (ctrl->ulps && mmss_clamp) {
/*
* ULPS Entry Request. This is needed if the lanes were
* in ULPS prior to power collapse, since after