Пример #1
0
static int mdss_dsi_blank(struct mdss_panel_data *pdata)
{
	int ret = 0;
	struct mipi_panel_info *mipi;
	struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;

	pr_debug("%s+:\n", __func__);

	if (pdata == NULL) {
		pr_err("%s: Invalid input data\n", __func__);
		return -EINVAL;
	}

	ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata,
				panel_data);
	mipi = &pdata->panel_info.mipi;

	if (pdata->panel_info.type == MIPI_VIDEO_PANEL &&
			ctrl_pdata->off_cmds.link_state == DSI_LP_MODE) {
		mdss_dsi_sw_reset(pdata);
		mdss_dsi_host_init(pdata);
	}

	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) {
		if (mipi->vsync_enable && mipi->hw_vsync_mode
			&& gpio_is_valid(ctrl_pdata->disp_te_gpio)) {
			mdss_dsi_set_tear_off(ctrl_pdata);
		}
	}

	if (ctrl_pdata->ctrl_state & CTRL_STATE_PANEL_INIT) {
		ret = ctrl_pdata->off(pdata);
		if (ret) {
			pr_err("%s: Panel OFF failed\n", __func__);
			return ret;
		}
		ctrl_pdata->ctrl_state &= ~CTRL_STATE_PANEL_INIT;
	}
	pr_debug("%s-:End\n", __func__);
	return ret;
}
Пример #2
0
static int mdss_dsi_event_handler(struct mdss_panel_data *pdata,
				  int event, void *arg)
{
	int rc = 0;
	struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;

	if (pdata == NULL) {
		pr_err("%s: Invalid input data\n", __func__);
		return -EINVAL;
	}
	ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata,
				panel_data);
	pr_debug("%s+:event=%d\n", __func__, event);

	switch (event) {
	case MDSS_EVENT_UNBLANK:
		rc = mdss_dsi_on(pdata);
		mdss_dsi_op_mode_config(pdata->panel_info.mipi.mode,
							pdata);
		if (ctrl_pdata->on_cmds.link_state == DSI_LP_MODE)
			rc = mdss_dsi_unblank(pdata);
		break;
	case MDSS_EVENT_PANEL_ON:
		ctrl_pdata->ctrl_state |= CTRL_STATE_MDP_ACTIVE;
		if (ctrl_pdata->on_cmds.link_state == DSI_HS_MODE)
			rc = mdss_dsi_unblank(pdata);
		break;
	case MDSS_EVENT_BLANK:
		if (ctrl_pdata->off_cmds.link_state == DSI_HS_MODE)
			rc = mdss_dsi_blank(pdata);
		break;
	case MDSS_EVENT_PANEL_OFF:
		ctrl_pdata->ctrl_state &= ~CTRL_STATE_MDP_ACTIVE;
		if (ctrl_pdata->off_cmds.link_state == DSI_LP_MODE)
			rc = mdss_dsi_blank(pdata);
		rc = mdss_dsi_off(pdata);
		break;
	case MDSS_EVENT_CONT_SPLASH_FINISH:
		if (ctrl_pdata->off_cmds.link_state == DSI_LP_MODE)
			rc = mdss_dsi_blank(pdata);
		ctrl_pdata->ctrl_state &= ~CTRL_STATE_MDP_ACTIVE;
		rc = mdss_dsi_cont_splash_on(pdata);
		break;
	case MDSS_EVENT_PANEL_CONT_SPLASH_FINISH:
		if (ctrl_pdata->cont_splash_on)
			rc = ctrl_pdata->cont_splash_on(pdata);
		break;
	case MDSS_EVENT_PANEL_CLK_CTRL:
		mdss_dsi_clk_req(ctrl_pdata, (int)arg);
		break;
	case MDSS_EVENT_DSI_CMDLIST_KOFF:
		mdss_dsi_cmdlist_commit(ctrl_pdata, 1, NULL);
		break;
	case MDSS_EVENT_PANEL_UPDATE_FPS:
		if (arg != NULL) {
			rc = mdss_dsi_dfps_config(pdata, (int)arg);
			pr_debug("%s:update fps to = %d\n",
				__func__, (int)arg);
		}
		break;
	case MDSS_EVENT_CONT_SPLASH_BEGIN:
		/*
		 * TODO: Stubbed out for now until we can remove whole
		 * black screen when kernel starts.
		 */
		break;
	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;
	case MDSS_EVENT_REGISTER_RECOVERY_HANDLER:
		rc = mdss_dsi_register_recovery_handler(ctrl_pdata,
			(struct mdss_panel_recovery *)arg);
		break;
	case MDSS_EVENT_SET_CABC:
		if (ctrl_pdata->set_cabc)
			rc = ctrl_pdata->set_cabc(ctrl_pdata, (int)arg);
		break;
	case MDSS_EVENT_ENABLE_TE:
		rc = mdss_dsi_hndl_enable_te(ctrl_pdata, (int) arg);
		break;
	case MDSS_EVENT_ENABLE_HBM:
		rc = mdss_dsi_hndl_enable_hbm(ctrl_pdata, (int) arg);
		break;
	default:
		pr_debug("%s: unhandled event=%d\n", __func__, event);
		break;
	}
	pr_debug("%s-:event=%d, rc=%d\n", __func__, event, rc);
	return rc;
}
static int mdss_dsi_clk_ctrl_sub(struct mdss_dsi_ctrl_pdata *ctrl,
	u8 clk_type, int enable)
{
	int rc = 0;
	struct mdss_panel_data *pdata;

	if (!ctrl) {
		pr_err("%s: Invalid arg\n", __func__);
		return -EINVAL;
	}

	pdata = &ctrl->panel_data;

	pr_debug("%s: ndx=%d clk_type=%08x enable=%d\n", __func__,
		ctrl->ndx, clk_type, enable);

	if (enable) {
		if (clk_type & DSI_BUS_CLKS) {
			rc = mdss_dsi_core_power_ctrl(ctrl, enable);
			if (rc) {
				pr_err("%s: Failed to enable core power. rc=%d\n",
					__func__, rc);
				goto error;
			}
		}
		if (clk_type & DSI_LINK_CLKS) {
			rc = mdss_dsi_link_clk_start(ctrl);
			if (rc) {
				pr_err("%s: Failed to start link clocks. rc=%d\n",
					__func__, rc);
				goto error_link_clk_start;
			}
			/* Disable ULPS, if enabled */
			if (ctrl->ulps) {
				rc = mdss_dsi_ulps_config(ctrl, 0);
				if (rc) {
					pr_err("%s: Failed to exit ulps. rc=%d\n",
						__func__, rc);
					goto error_ulps_exit;
				}
			}
		}
	} else {
		if (clk_type & DSI_LINK_CLKS) {
			/*
			 * If ULPS feature is enabled, enter ULPS first.
			 * If ULPS during suspend is not enabled, no need
			 * to enable ULPS when turning off the clocks
			 * while blanking the panel.
			 */
			if (((mdss_dsi_ulps_feature_enabled(pdata)) &&
				(pdata->panel_info.blank_state !=
				 MDSS_PANEL_BLANK_BLANK)) ||
				(pdata->panel_info.ulps_suspend_enabled))
				mdss_dsi_ulps_config(ctrl, 1);

			mdss_dsi_link_clk_stop(ctrl);
		}
		if (clk_type & DSI_BUS_CLKS) {
			rc = mdss_dsi_core_power_ctrl(ctrl, enable);
			if (rc) {
				pr_err("%s: Failed to disable core power. rc=%d\n",
					__func__, rc);
			}
		}
	}

	return rc;

error_ulps_exit:
	mdss_dsi_link_clk_stop(ctrl);
error_link_clk_start:
	if ((clk_type & DSI_BUS_CLKS) &&
		(mdss_dsi_core_power_ctrl(ctrl, !enable)))
		pr_warn("%s: Failed to disable core power. rc=%d\n",
			__func__, rc);
error:
	return rc;
}
/**
 * mdss_dsi_core_power_ctrl() - Enable/disable DSI core power
 * @ctrl: pointer to DSI controller structure
 * @enable: 1 to enable power, 0 to disable power
 *
 * When all DSI bus clocks are disabled, DSI core power module can be turned
 * off to save any leakage current. This function implements the necessary
 * programming sequence for the same. For command mode panels, the core power
 * can be turned off for idle-screen usecases, where additional programming is
 * needed to clamp DSI phy.
 */
static int mdss_dsi_core_power_ctrl(struct mdss_dsi_ctrl_pdata *ctrl,
	int enable)
{
	int rc = 0;
	struct mdss_panel_data *pdata = NULL;

	if (!ctrl) {
		pr_err("%s: invalid input\n", __func__);
		return -EINVAL;
	}

	pdata = &ctrl->panel_data;
	if (!pdata) {
		pr_err("%s: Invalid panel data\n", __func__);
		return -EINVAL;
	}

	if (enable) {
		if (!ctrl->core_power) {
			/* enable mdss gdsc */
			pr_debug("%s: Enable MDP FS\n", __func__);
			rc = msm_dss_enable_vreg(
				ctrl->power_data[DSI_CORE_PM].vreg_config,
				ctrl->power_data[DSI_CORE_PM].num_vreg, 1);
			if (rc) {
				pr_err("%s: failed to enable vregs for %s\n",
					__func__,
					__mdss_dsi_pm_name(DSI_CORE_PM));
				goto error;
			}
			ctrl->core_power = true;
		}

		rc = mdss_dsi_bus_clk_start(ctrl);
		if (rc) {
			pr_err("%s: Failed to start bus clocks. rc=%d\n",
				__func__, rc);
			goto error_bus_clk_start;
		}

		/*
		 * Phy software reset should not be done for:
		 * 1.) Idle screen power collapse use-case. Issue a phy software
		 *     reset only when unblanking the panel in this case.
		 * 2.) When ULPS during suspend is enabled.
		 */
		if (pdata->panel_info.blank_state == MDSS_PANEL_BLANK_BLANK &&
			!pdata->panel_info.ulps_suspend_enabled)
			mdss_dsi_phy_sw_reset(ctrl);

		/*
		 * Phy and controller setup need not be done during bootup
		 * when continuous splash screen is enabled.
		 */
		if (!pdata->panel_info.cont_splash_enabled) {
			mdss_dsi_phy_init(ctrl);
			mdss_dsi_ctrl_setup(ctrl);
		}

		if (ctrl->ulps) {
			/*
			 * ULPS Entry Request. This is needed if the lanes were
			 * in ULPS prior to power collapse, since after
			 * power collapse and reset, the DSI controller resets
			 * back to idle state and not ULPS. This ulps entry
			 * request will transition the state of the DSI
			 * controller to ULPS which will match the state of the
			 * DSI phy. This needs to be done prior to disabling
			 * the DSI clamps.
			 *
			 * Also, reset the ulps flag so that ulps_config
			 * function would reconfigure the controller state to
			 * ULPS.
			 */
			ctrl->ulps = false;
			rc = mdss_dsi_ulps_config(ctrl, 1);
			if (rc) {
				pr_err("%s: Failed to enter ULPS. rc=%d\n",
					__func__, rc);
				goto error_ulps;
			}
		}

		rc = mdss_dsi_clamp_ctrl(ctrl, 0);
		if (rc) {
			pr_err("%s: Failed to disable dsi clamps. rc=%d\n",
				__func__, rc);
			goto error_ulps;
		}
	} else {
		/*
		 * Enable DSI clamps only if entering idle power collapse or
		 * when ULPS during suspend is enabled.
		 */
		if ((pdata->panel_info.blank_state != MDSS_PANEL_BLANK_BLANK) ||
			pdata->panel_info.ulps_suspend_enabled) {
			rc = mdss_dsi_clamp_ctrl(ctrl, 1);
			if (rc)
				pr_err("%s: Failed to enable dsi clamps. rc=%d\n",
					__func__, rc);
		}

		/*
		 * disable bus clocks irrespective of whether dsi phy was
		 * successfully clamped or not
		 */
		mdss_dsi_bus_clk_stop(ctrl);

		/* disable mdss gdsc only if dsi phy was successfully clamped*/
		if (rc) {
			pr_debug("%s: leaving mdss gdsc on\n", __func__);
		} else {
			pr_debug("%s: Disable MDP FS\n", __func__);
			rc = msm_dss_enable_vreg(
				ctrl->power_data[DSI_CORE_PM].vreg_config,
				ctrl->power_data[DSI_CORE_PM].num_vreg, 0);
			if (rc) {
				pr_warn("%s: failed to disable vregs for %s\n",
					__func__,
					__mdss_dsi_pm_name(DSI_CORE_PM));
				rc = 0;
			} else {
				ctrl->core_power = false;
			}
		}
	}
	return rc;

error_ulps:
	mdss_dsi_bus_clk_stop(ctrl);
error_bus_clk_start:
	if (msm_dss_enable_vreg(ctrl->power_data[DSI_CORE_PM].vreg_config,
		ctrl->power_data[DSI_CORE_PM].num_vreg, 0))
		pr_warn("%s: failed to disable vregs for %s\n",
			__func__, __mdss_dsi_pm_name(DSI_CORE_PM));
	else
		ctrl->core_power = false;
error:
	return rc;
}
static int mdss_dsi_clk_ctrl_sub(struct mdss_dsi_ctrl_pdata *ctrl,
	u8 clk_type, int enable)
{
	int rc = 0;
	struct mdss_panel_data *pdata;

	if (!ctrl) {
		pr_err("%s: Invalid arg\n", __func__);
		return -EINVAL;
	}

	pdata = &ctrl->panel_data;

	pr_debug("%s: ndx=%d clk_type=%08x enable=%d\n", __func__,
		ctrl->ndx, clk_type, enable);

	if (enable) {
		if (clk_type & DSI_BUS_CLKS) {
			/* enable mdss gdsc */
			pr_debug("%s: Enable MDP FS\n", __func__);
			rc = msm_dss_enable_vreg(
				ctrl->power_data[DSI_CORE_PM].vreg_config,
				ctrl->power_data[DSI_CORE_PM].num_vreg, 1);
			if (rc) {
				pr_err("%s: failed to enable vregs for %s\n",
					__func__,
					__mdss_dsi_pm_name(DSI_CORE_PM));
				goto error;
			}

			rc = mdss_dsi_bus_clk_start(ctrl);
			if (rc) {
				pr_err("Failed to start bus clocks. rc=%d\n",
					rc);
				goto error_vreg;
			}
		}
		if (clk_type & DSI_LINK_CLKS) {
			rc = mdss_dsi_link_clk_start(ctrl);
			if (rc) {
				pr_err("Failed to start link clocks. rc=%d\n",
					rc);
				goto error_link_clk_start;
			}
			/* Disable ULPS, if enabled */
			if (ctrl->ulps) {
				rc = mdss_dsi_ulps_config(ctrl, 0);
				if (rc) {
					pr_err("Failed to exit ulps. rc=%d\n",
						rc);
					goto error_ulps_exit;
				}
			}
		}
	} else {
		if (clk_type & DSI_LINK_CLKS) {
			/*
			 * If ULPS feature is enabled, enter ULPS first.
			 * No need to enable ULPS when turning off clocks
			 * while blanking the panel.
			 */
			if ((mdss_dsi_ulps_feature_enabled(pdata)) &&
				(pdata->panel_info.panel_power_on))
				mdss_dsi_ulps_config(ctrl, 1);
			mdss_dsi_link_clk_stop(ctrl);
		}
		if (clk_type & DSI_BUS_CLKS) {
			mdss_dsi_bus_clk_stop(ctrl);

			/* disable mdss gdsc */
			pr_debug("%s: Disable MDP FS\n", __func__);
			rc = msm_dss_enable_vreg(
				ctrl->power_data[DSI_CORE_PM].vreg_config,
				ctrl->power_data[DSI_CORE_PM].num_vreg, 0);
			if (rc) {
				pr_warn("%s: failed to disable vregs for %s\n",
					__func__,
					__mdss_dsi_pm_name(DSI_CORE_PM));
				rc = 0;
			}
		}
	}

	return rc;

error_ulps_exit:
	mdss_dsi_link_clk_stop(ctrl);
error_link_clk_start:
	if (clk_type & DSI_BUS_CLKS)
		mdss_dsi_bus_clk_stop(ctrl);
error_vreg:
	if ((clk_type & DSI_BUS_CLKS) &&
		(msm_dss_enable_vreg(ctrl->power_data[DSI_CORE_PM].vreg_config,
			ctrl->power_data[DSI_CORE_PM].num_vreg, 0))) {
		pr_warn("%s: failed to disable vregs for %s\n", __func__,
			__mdss_dsi_pm_name(DSI_CORE_PM));
	}
error:
	return rc;
}
static int mdss_dsi_event_handler(struct mdss_panel_data *pdata,
				  int event, void *arg)
{
	int rc = 0;
	struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;

	if (pdata == NULL) {
		pr_err("%s: Invalid input data\n", __func__);
		return -EINVAL;
	}
	ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata,
				panel_data);
	pr_debug("%s+:event=%d\n", __func__, event);

	MDSS_XLOG(event, arg, ctrl_pdata->ndx, 0x3333);

	switch (event) {
	case MDSS_EVENT_UNBLANK:
		rc = mdss_dsi_on(pdata);
		mdss_dsi_op_mode_config(pdata->panel_info.mipi.mode,
							pdata);
		if (ctrl_pdata->on_cmds.link_state == DSI_LP_MODE)
			rc = mdss_dsi_unblank(pdata);
		break;
	case MDSS_EVENT_PANEL_ON:
		ctrl_pdata->ctrl_state |= CTRL_STATE_MDP_ACTIVE;
		if (ctrl_pdata->on_cmds.link_state == DSI_HS_MODE)
			rc = mdss_dsi_unblank(pdata);
		break;
	case MDSS_EVENT_BLANK:
		if (ctrl_pdata->off_cmds.link_state == DSI_HS_MODE)
			rc = mdss_dsi_blank(pdata);
		break;
	case MDSS_EVENT_PANEL_OFF:
		ctrl_pdata->ctrl_state &= ~CTRL_STATE_MDP_ACTIVE;
		if (ctrl_pdata->off_cmds.link_state == DSI_LP_MODE)
			rc = mdss_dsi_blank(pdata);
		rc = mdss_dsi_off(pdata);
		break;
	case MDSS_EVENT_CONT_SPLASH_FINISH:
		if (ctrl_pdata->off_cmds.link_state == DSI_LP_MODE)
			rc = mdss_dsi_blank(pdata);
		ctrl_pdata->ctrl_state &= ~CTRL_STATE_MDP_ACTIVE;
		rc = mdss_dsi_cont_splash_on(pdata);
		break;
	case MDSS_EVENT_PANEL_CLK_CTRL:
		mdss_dsi_clk_req(ctrl_pdata, (int)arg);
		break;
	case MDSS_EVENT_DSI_CMDLIST_KOFF:
		mdss_dsi_cmdlist_commit(ctrl_pdata, 1);
		break;
	case MDSS_EVENT_PANEL_UPDATE_FPS:
		if (arg != NULL) {
			rc = mdss_dsi_dfps_config(pdata, (int)arg);
			pr_debug("%s:update fps to = %d\n",
				__func__, (int)arg);
		}
		break;
	case MDSS_EVENT_CONT_SPLASH_BEGIN:
		if (ctrl_pdata->off_cmds.link_state == DSI_HS_MODE) {
			/* Panel is Enabled in Bootloader */
			rc = mdss_dsi_blank(pdata);
		}
		break;
	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;
	case MDSS_EVENT_REGISTER_RECOVERY_HANDLER:
		rc = mdss_dsi_register_recovery_handler(ctrl_pdata,
			(struct mdss_panel_recovery *)arg);
		break;
	case MDSS_EVENT_DSI_DYNAMIC_SWITCH:
		rc = mdss_dsi_update_panel_config(ctrl_pdata,
					(int)(unsigned long) arg);
		break;
	default:
		pr_debug("%s: unhandled event=%d\n", __func__, event);
		break;
	}
	pr_debug("%s-:event=%d, rc=%d\n", __func__, event, rc);
	return rc;
}
static int mdss_dsi_clk_ctrl_sub(struct mdss_dsi_ctrl_pdata *ctrl,
	u8 clk_type, int enable)
{
	int rc = 0;
	struct mdss_panel_data *pdata;

	if (!ctrl) {
		pr_err("%s: Invalid arg\n", __func__);
		return -EINVAL;
	}

	pdata = &ctrl->panel_data;

	pr_debug("%s: ndx=%d clk_type=%08x enable=%d\n", __func__,
		ctrl->ndx, clk_type, enable);

	if (enable) {
		if (clk_type & DSI_BUS_CLKS) {
			/* enable mdss gdsc */
			pr_debug("%s: Enable MDP FS\n", __func__);
			rc = msm_dss_enable_vreg(
				ctrl->power_data[DSI_CORE_PM].vreg_config,
				ctrl->power_data[DSI_CORE_PM].num_vreg, 1);
			if (rc) {
				pr_err("%s: failed to enable vregs for %s\n",
					__func__,
					__mdss_dsi_pm_name(DSI_CORE_PM));
				goto error;
			}

			rc = mdss_dsi_bus_clk_start(ctrl);
			if (rc) {
				pr_err("Failed to start bus clocks. rc=%d\n",
					rc);
				goto error_vreg;
			}
		}
		if (clk_type & DSI_LINK_CLKS) {
			if (ctrl->mmss_clamp) {
				mdss_dsi_phy_init(pdata);
				mdss_dsi_ctrl_setup(pdata);
				mdss_dsi_host_init(pdata);
				mdss_dsi_op_mode_config(pdata->panel_info.mipi.mode,
					pdata);
				/*
				 * ULPS Entry Request. This is needed because, after power
				 * collapse and reset, the DSI controller resets back to
				 * idle state and not ULPS.
				 */
				mdss_dsi_ulps_config(ctrl, 1);
				mdss_dsi_clamp_ctrl(ctrl, 0);
			}
			rc = mdss_dsi_link_clk_start(ctrl);
			if (rc) {
				pr_err("Failed to start link clocks. rc=%d\n",
					rc);
				goto error_link_clk_start;
			}
			/* Disable ULPS, if enabled */
			if (ctrl->ulps) {
				rc = mdss_dsi_ulps_config(ctrl, 0);
				if (rc) {
					pr_err("Failed to exit ulps. rc=%d\n",
						rc);
					goto error_ulps_exit;
				}
			}
		}
	} else {
		if (clk_type & DSI_LINK_CLKS) {
			/*
			 * If ULPS feature is enabled, enter ULPS first.
			 * No need to enable ULPS when turning off clocks
			 * while blanking the panel.
			 */
			if (((mdss_dsi_ulps_feature_enabled(pdata)) &&
				(pdata->panel_info.panel_power_on)) ||
				(pdata->panel_info.ulps_suspend_enabled &&
				  !pdata->panel_info.panel_power_on)) {
				mdss_dsi_ulps_config(ctrl, 1);
				mdss_dsi_link_clk_stop(ctrl);
				mdss_dsi_clamp_ctrl(ctrl, 1);
			} else {
				mdss_dsi_link_clk_stop(ctrl);
			}
		}
		if (clk_type & DSI_BUS_CLKS) {
			mdss_dsi_bus_clk_stop(ctrl);

			/* disable mdss gdsc */
			pr_debug("%s: Disable MDP FS\n", __func__);
			rc = msm_dss_enable_vreg(
				ctrl->power_data[DSI_CORE_PM].vreg_config,
				ctrl->power_data[DSI_CORE_PM].num_vreg, 0);
			if (rc) {
				pr_warn("%s: failed to disable vregs for %s\n",
					__func__,
					__mdss_dsi_pm_name(DSI_CORE_PM));
				rc = 0;
			}
		}
	}

	return rc;

error_ulps_exit:
	mdss_dsi_link_clk_stop(ctrl);
error_link_clk_start:
	if (clk_type & DSI_BUS_CLKS)
		mdss_dsi_bus_clk_stop(ctrl);
error_vreg:
	if ((clk_type & DSI_BUS_CLKS) &&
		(msm_dss_enable_vreg(ctrl->power_data[DSI_CORE_PM].vreg_config,
			ctrl->power_data[DSI_CORE_PM].num_vreg, 0))) {
		pr_warn("%s: failed to disable vregs for %s\n", __func__,
			__mdss_dsi_pm_name(DSI_CORE_PM));
	}
error:
	return rc;
}
/**
 * mdss_dsi_core_power_ctrl() - Enable/disable DSI core power
 * @ctrl: pointer to DSI controller structure
 * @enable: 1 to enable power, 0 to disable power
 *
 * When all DSI bus clocks are disabled, DSI core power module can be turned
 * off to save any leakage current. This function implements the necessary
 * programming sequence for the same. For command mode panels, the core power
 * can be turned off for idle-screen usecases, where additional programming is
 * needed to clamp DSI phy.
 */
static int mdss_dsi_core_power_ctrl(struct mdss_dsi_ctrl_pdata *ctrl,
	int enable)
{
	int rc = 0;
	struct mdss_panel_data *pdata = NULL;
	u32 ctrl_rev;

	if (!ctrl) {
		pr_err("%s: invalid input\n", __func__);
		return -EINVAL;
	}

	pdata = &ctrl->panel_data;
	if (!pdata) {
		pr_err("%s: Invalid panel data\n", __func__);
		return -EINVAL;
	}

	if (enable) {
		if (!ctrl->core_power) {
			/* enable mdss gdsc */
			pr_debug("%s: Enable MDP FS\n", __func__);
			rc = msm_dss_enable_vreg(
				ctrl->power_data[DSI_CORE_PM].vreg_config,
				ctrl->power_data[DSI_CORE_PM].num_vreg, 1);
			if (rc) {
				pr_err("%s: failed to enable vregs for %s\n",
					__func__,
					__mdss_dsi_pm_name(DSI_CORE_PM));
				goto error;
			}
			ctrl->core_power = true;
		}

		/*
		 * temp workaround until framework issues pertaining to LP2
		 * power state transitions are fixed. For now, if we intend to
		 * send a frame update when in LP1, we have to explicitly exit
		 * LP2 state here
		 */
		if (mdss_dsi_is_panel_on_ulp(pdata))
			mdss_dsi_panel_power_ctrl(pdata, MDSS_PANEL_POWER_LP1);

		rc = mdss_dsi_bus_clk_start(ctrl);
		if (rc) {
			pr_err("%s: Failed to start bus clocks. rc=%d\n",
				__func__, rc);
			goto error_bus_clk_start;
		}

		/*
		 * Phy and controller setup is needed if coming out of idle
		 * power collapse with clamps enabled.
		 */
		if (ctrl->mmss_clamp) {
			ctrl_rev = MIPI_INP(ctrl->ctrl_base);
			if (ctrl_rev == MDSS_DSI_HW_REV_103)
				mdss_dsi_20nm_phy_init(pdata);
			else
				mdss_dsi_phy_init(pdata);

			mdss_dsi_ctrl_setup(ctrl);
		}

		if (ctrl->ulps) {
			/*
			 * ULPS Entry Request. This is needed if the lanes were
			 * in ULPS prior to power collapse, since after
			 * power collapse and reset, the DSI controller resets
			 * back to idle state and not ULPS. This ulps entry
			 * request will transition the state of the DSI
			 * controller to ULPS which will match the state of the
			 * DSI phy. This needs to be done prior to disabling
			 * the DSI clamps.
			 *
			 * Also, reset the ulps flag so that ulps_config
			 * function would reconfigure the controller state to
			 * ULPS.
			 */
			ctrl->ulps = false;
			rc = mdss_dsi_ulps_config(ctrl, 1);
			if (rc) {
				pr_err("%s: Failed to enter ULPS. rc=%d\n",
					__func__, rc);
				goto error_ulps;
			}
		}

		rc = mdss_dsi_clamp_ctrl(ctrl, 0);
		if (rc) {
			pr_err("%s: Failed to disable dsi clamps. rc=%d\n",
				__func__, rc);
			goto error_ulps;
		}
	} else {
		/* Enable DSI clamps only if entering idle power collapse */
		if (pdata->panel_info.blank_state != MDSS_PANEL_BLANK_BLANK) {
			rc = mdss_dsi_clamp_ctrl(ctrl, 1);
			if (rc)
				pr_err("%s: Failed to enable dsi clamps. rc=%d\n",
					__func__, rc);
		} else {
			/*
			* Make sure that controller is not in ULPS state when
			* the DSI link is not active.
			*/
			rc = mdss_dsi_ulps_config(ctrl, 0);
			if (rc)
				pr_err("%s: failed to disable ulps. rc=%d\n",
					__func__, rc);
		}

		/*
		 * disable bus clocks irrespective of whether dsi phy was
		 * successfully clamped or not
		 */
		mdss_dsi_bus_clk_stop(ctrl);

		/* disable mdss gdsc only if dsi phy was successfully clamped*/
		if (rc) {
			pr_debug("%s: leaving mdss gdsc on\n", __func__);
		} else {
			pr_debug("%s: Disable MDP FS\n", __func__);
			rc = msm_dss_enable_vreg(
				ctrl->power_data[DSI_CORE_PM].vreg_config,
				ctrl->power_data[DSI_CORE_PM].num_vreg, 0);
			if (rc) {
				pr_warn("%s: failed to disable vregs for %s\n",
					__func__,
					__mdss_dsi_pm_name(DSI_CORE_PM));
				rc = 0;
			} else {
				ctrl->core_power = false;
			}
		}

		/*
		 * temp workaround until framework issues pertaining to LP2
		 * power state transitions are fixed. For now, we internally
		 * transition to LP2 state whenever core power is turned off
		 * in LP1 state
		 */
		if (mdss_dsi_is_panel_on_lp(pdata))
			mdss_dsi_panel_power_ctrl(pdata, MDSS_PANEL_POWER_LP2);
	}
	return rc;

error_ulps:
	mdss_dsi_bus_clk_stop(ctrl);
error_bus_clk_start:
	if (msm_dss_enable_vreg(ctrl->power_data[DSI_CORE_PM].vreg_config,
		ctrl->power_data[DSI_CORE_PM].num_vreg, 0))
		pr_warn("%s: failed to disable vregs for %s\n",
			__func__, __mdss_dsi_pm_name(DSI_CORE_PM));
	else
		ctrl->core_power = false;
error:
	return rc;
}