diff --git a/drivers/video/fbdev/msm/mdss_dsi.h b/drivers/video/fbdev/msm/mdss_dsi.h index 26c303ab7a30..f3f708448ebd 100644 --- a/drivers/video/fbdev/msm/mdss_dsi.h +++ b/drivers/video/fbdev/msm/mdss_dsi.h @@ -560,6 +560,7 @@ void mdss_dsi_clk_req(struct mdss_dsi_ctrl_pdata *ctrl, void mdss_dsi_controller_cfg(int enable, struct mdss_panel_data *pdata); void mdss_dsi_sw_reset(struct mdss_dsi_ctrl_pdata *ctrl_pdata, bool restore); +int mdss_dsi_wait_for_lane_idle(struct mdss_dsi_ctrl_pdata *ctrl); irqreturn_t mdss_dsi_isr(int irq, void *ptr); irqreturn_t hw_vsync_handler(int irq, void *data); diff --git a/drivers/video/fbdev/msm/mdss_dsi_host.c b/drivers/video/fbdev/msm/mdss_dsi_host.c index 5f9c936b54ba..df32e5d9e2f0 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_host.c +++ b/drivers/video/fbdev/msm/mdss_dsi_host.c @@ -34,6 +34,9 @@ #define DMA_TX_TIMEOUT 200 #define DMA_TPG_FIFO_LEN 64 +#define FIFO_STATUS 0x0C +#define LANE_STATUS 0xA8 + struct mdss_dsi_ctrl_pdata *ctrl_list[DSI_CTRL_MAX]; struct mdss_hw mdss_dsi0_hw = { @@ -517,6 +520,73 @@ void mdss_dsi_sw_reset(struct mdss_dsi_ctrl_pdata *ctrl, bool restore) spin_unlock_irqrestore(&ctrl->mdp_lock, flag); } +/** + * mdss_dsi_wait_for_lane_idle() - Wait for DSI lanes to be idle + * @ctrl: pointer to DSI controller structure + * + * This function waits for all the active DSI lanes to be idle by polling all + * the *FIFO_EMPTY bits and polling the lane status to ensure that all the lanes + * are in stop state. This function assumes that the bus clocks required to + * access the registers are already turned on. + */ +int mdss_dsi_wait_for_lane_idle(struct mdss_dsi_ctrl_pdata *ctrl) +{ + int rc; + u32 val; + u32 fifo_empty_mask = 0; + u32 stop_state_mask = 0; + struct mipi_panel_info *mipi; + u32 const sleep_us = 10; + u32 const timeout_us = 100; + + if (!ctrl) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + mipi = &ctrl->panel_data.panel_info.mipi; + + if (mipi->data_lane0) { + stop_state_mask |= BIT(0); + fifo_empty_mask |= (BIT(12) | BIT(16)); + } + if (mipi->data_lane1) { + stop_state_mask |= BIT(1); + fifo_empty_mask |= BIT(20); + } + if (mipi->data_lane2) { + stop_state_mask |= BIT(2); + fifo_empty_mask |= BIT(24); + } + if (mipi->data_lane3) { + stop_state_mask |= BIT(3); + fifo_empty_mask |= BIT(28); + } + + pr_debug("%s: polling for fifo empty, mask=0x%08x\n", __func__, + fifo_empty_mask); + rc = readl_poll_timeout(ctrl->ctrl_base + FIFO_STATUS, val, + (val & fifo_empty_mask), sleep_us, timeout_us); + if (rc) { + pr_err("%s: fifo not empty, FIFO_STATUS=0x%08x\n", + __func__, val); + goto error; + } + + 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 (rc) { + pr_err("%s: lanes not in stop state, LANE_STATUS=0x%08x\n", + __func__, val); + goto error; + } + +error: + return rc; +} + static void mdss_dsi_cfg_lane_ctrl(struct mdss_dsi_ctrl_pdata *ctrl, u32 bits, int set) { diff --git a/drivers/video/fbdev/msm/msm_mdss_io_8974.c b/drivers/video/fbdev/msm/msm_mdss_io_8974.c index 6135a81d94f6..7cc2717a6df5 100644 --- a/drivers/video/fbdev/msm/msm_mdss_io_8974.c +++ b/drivers/video/fbdev/msm/msm_mdss_io_8974.c @@ -1621,6 +1621,26 @@ static int mdss_dsi_ulps_config(struct mdss_dsi_ctrl_pdata *ctrl, active_lanes, ctrl->mmss_clamp ? "enabled" : "disabled"); if (enable && !ctrl->ulps) { + /* + * Ensure that the lanes are idle prior to placing a ULPS entry + * request. This is needed to ensure that there is no overlap + * between any HS or LP commands being sent out on the lane and + * a potential ULPS entry request. + * + * This check needs to be avoided when we are resuming from idle + * power collapse and just restoring the controller state to + * ULPS with the clamps still in place. + */ + if (!ctrl->mmss_clamp) { + ret = mdss_dsi_wait_for_lane_idle(ctrl); + if (ret) { + pr_warn("%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