diff options
author | Aravind Venkateswaran <aravindh@codeaurora.org> | 2016-05-12 17:45:58 -0700 |
---|---|---|
committer | Kyle Yan <kyan@codeaurora.org> | 2016-05-24 14:26:35 -0700 |
commit | 1d4e56bab0e14e1c4021b7750d5876cebdc5c3cf (patch) | |
tree | ae050227e1d29a03494e1e57a40c7f5289ef1194 /drivers | |
parent | 703d92d617c0caa16cce718f73eb5e650513a88b (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')
-rw-r--r-- | drivers/video/fbdev/msm/mdss_dsi_host.c | 9 | ||||
-rw-r--r-- | drivers/video/fbdev/msm/mdss_dsi_phy.h | 23 | ||||
-rw-r--r-- | drivers/video/fbdev/msm/mdss_dsi_phy_v3.c | 111 | ||||
-rw-r--r-- | drivers/video/fbdev/msm/msm_mdss_io_8974.c | 151 |
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 |