static bool mdss_dsi_is_ulps_req_valid(struct mdss_dsi_ctrl_pdata *ctrl,
		int enable)
{
	struct mdss_panel_data *pdata = &ctrl->panel_data;
	struct mdss_panel_info *pinfo = &pdata->panel_info;

	pr_debug("%s: checking ulps req validity for ctrl%d\n",
		__func__, ctrl->ndx);

	if (!mdss_dsi_ulps_feature_enabled(pdata)) {
		pr_debug("%s: ULPS feature is not enabled\n", __func__);
		return false;
	}

	/*
	 * No need to enter ULPS when transitioning from splash screen to
	 * boot animation since it is expected that the clocks would be turned
	 * right back on.
	 */
	if (enable && pinfo->cont_splash_enabled) {
		pr_debug("%s: skip ULPS config with splash screen enabled\n",
			__func__);
		return false;
	}

	/*
	 * No need to enable ULPS if panel is not yet initialized.
	 * However, this should be allowed in following usecases:
	 *   1. If ULPS during suspend feature is enabled, where we
	 *      configure the lanes in ULPS after turning off the panel.
	 *   2. When coming out of idle PC with clamps enabled, where we
	 *      transition the controller HW state back to ULPS prior to
	 *      disabling ULPS.
	 */
	if (enable && !ctrl->mmss_clamp &&
		!(ctrl->ctrl_state & CTRL_STATE_PANEL_INIT)) {
		pr_debug("%s: panel not yet initialized\n", __func__);
		return false;
	}

	return true;
}
/**
 * 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
 *
 * This function executes the necessary programming sequence to enter/exit
 * DSI Ultra-Low Power State (ULPS). This function assumes that the link and
 * bus clocks are already on.
 */
static int mdss_dsi_ulps_config(struct mdss_dsi_ctrl_pdata *ctrl,
	int enable)
{
	int ret = 0;
	struct mdss_panel_data *pdata = NULL;
	struct mdss_panel_info *pinfo;
	struct mipi_panel_info *mipi;
	u32 lane_status = 0;
	u32 active_lanes = 0;

	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;
	}
	pinfo = &pdata->panel_info;
	mipi = &pinfo->mipi;

	if (!mdss_dsi_ulps_feature_enabled(pdata) &&
		!pinfo->ulps_suspend_enabled) {
		pr_debug("%s: ULPS feature not supported. enable=%d\n",
			__func__, enable);
		return -ENOTSUPP;
	}

	/*
	 * No need to enter ULPS when transitioning from splash screen to
	 * boot animation since it is expected that the clocks would be turned
	 * right back on.
	 */
	if (pinfo->cont_splash_enabled) {
		pr_debug("%s: skip ULPS config with splash screen enabled\n",
			__func__);
		return 0;
	}

	/* clock lane will always be programmed for ulps */
	active_lanes = BIT(4);
	/*
	 * make a note of all active data lanes for which ulps entry/exit
	 * is needed
	 */
	if (mipi->data_lane0)
		active_lanes |= BIT(0);
	if (mipi->data_lane1)
		active_lanes |= BIT(1);
	if (mipi->data_lane2)
		active_lanes |= BIT(2);
	if (mipi->data_lane3)
		active_lanes |= BIT(3);

	pr_debug("%s: configuring ulps (%s) for ctrl%d, active lanes=0x%08x\n",
		__func__, (enable ? "on" : "off"), ctrl->ndx,
		active_lanes);

	if (enable && !ctrl->ulps) {
		/*
		 * 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(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;
			goto error;
		}

		ctrl->ulps = true;
	} else if (!enable && ctrl->ulps) {
		/*
		 * Clear out any phy errors prior to exiting ULPS
		 * This fixes certain instances where phy does not exit
		 * ULPS cleanly.
		 */
		mdss_dsi_dln0_phy_err(ctrl);

		/*
		 * ULPS Exit Request
		 * Hardware requirement is to wait for at least 1ms
		 */
		MIPI_OUTP(ctrl->ctrl_base + 0x0AC, active_lanes << 8);
		usleep(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);

		/*
		 * Wait for a short duration before enabling
		 * data transmission
		 */
		usleep(100);

		lane_status = MIPI_INP(ctrl->ctrl_base + 0xA8);
		ctrl->ulps = false;
	} else {
		pr_debug("%s: No change requested: %s -> %s\n", __func__,
			ctrl->ulps ? "enabled" : "disabled",
			enable ? "enabled" : "disabled");
	}

	pr_debug("%s: DSI lane status = 0x%08x. Ulps %s\n", __func__,
		lane_status, enable ? "enabled" : "disabled");

error:
	return ret;
}
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;
}
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;
}
int mdss_dsi_ulps_config(struct mdss_dsi_ctrl_pdata *ctrl_pdata, int enable)
{
	int ret = 0;
	struct mdss_panel_data *pdata = NULL;
	struct mdss_panel_info *pinfo;
	struct mipi_panel_info *mipi;
	u32 lane_status = 0, regval;
	u32 active_lanes = 0, clamp_reg;

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

	if (&ctrl_pdata->mmss_misc_io == NULL) {
		pr_err("%s: mmss_misc_io is NULL. ULPS not valid\n", __func__);
		return -EINVAL;
	}

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

	if (!mdss_dsi_ulps_feature_enabled(pdata)) {
		pr_debug("%s: ULPS feature not supported. enable=%d\n",
			__func__, enable);
		return -ENOTSUPP;
	}

	/*
	 * No need to enter ULPS when transitioning from splash screen to
	 * boot animation since it is expected that the clocks would be turned
	 * right back on.
	 */
	if (pinfo->cont_splash_enabled) {
		pr_debug("%s: skip ULPS config with splash screen enabled\n",
			__func__);
		return 0;
	}

	/* clock lane will always be programmed for ulps and will be clamped */
	active_lanes = BIT(4);
	clamp_reg = BIT(8) | BIT(9);
	/*
	 * make a note of all active data lanes for which ulps entry/exit
	 * as well as DSI clamps are needed
	 */
	if (mipi->data_lane0) {
		active_lanes |= BIT(0);
		clamp_reg |= (BIT(0) | BIT(1));
	}
	if (mipi->data_lane1) {
		active_lanes |= BIT(1);
		clamp_reg |= (BIT(2) | BIT(3));
	}
	if (mipi->data_lane2) {
		active_lanes |= BIT(2);
		clamp_reg |= (BIT(4) | BIT(5));
	}
	if (mipi->data_lane3) {
		active_lanes |= BIT(3);
		clamp_reg |= (BIT(6) | BIT(7));
	}

	pr_debug("%s: configuring ulps (%s) for ctrl%d, active lanes=0x%08x\n",
		__func__, (enable ? "on" : "off"), ctrl_pdata->ndx,
		active_lanes);

	if (enable && !ctrl_pdata->ulps) {
		/*
		 * ULPS Entry Request.
		 * Wait for a short duration to ensure that the lanes
		 * enter ULP state.
		 */
		MIPI_OUTP(ctrl_pdata->ctrl_base + 0x0AC, active_lanes);
		usleep(100);

		/* Check to make sure that all active data lanes are in ULPS */
		lane_status = MIPI_INP(ctrl_pdata->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_pdata->ndx, lane_status);
			ret = -EINVAL;
			goto error;
		}

		/* Enable MMSS DSI Clamps */
		if (ctrl_pdata->ndx == DSI_CTRL_0) {
			regval = MIPI_INP(ctrl_pdata->mmss_misc_io.base + 0x14);
			MIPI_OUTP(ctrl_pdata->mmss_misc_io.base + 0x14,
				regval | clamp_reg);
			MIPI_OUTP(ctrl_pdata->mmss_misc_io.base + 0x14,
				regval | (clamp_reg | BIT(15)));
		} else if (ctrl_pdata->ndx == DSI_CTRL_1) {
			regval = MIPI_INP(ctrl_pdata->mmss_misc_io.base + 0x14);
			MIPI_OUTP(ctrl_pdata->mmss_misc_io.base + 0x14,
				regval | (clamp_reg << 16));
			MIPI_OUTP(ctrl_pdata->mmss_misc_io.base + 0x14,
				regval | ((clamp_reg << 16) | BIT(31)));
		}

		wmb();

		/*
		 * This register write ensures that DSI PHY will not be
		 * reset when mdss ahb clock reset is asserted while coming
		 * out of power collapse
		 */
		MIPI_OUTP(ctrl_pdata->mmss_misc_io.base + 0x108, 0x1);
		ctrl_pdata->ulps = true;
	} else if (ctrl_pdata->ulps) {
		MIPI_OUTP(ctrl_pdata->mmss_misc_io.base + 0x108, 0x0);
		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);

		/*
		 * ULPS Entry Request. This is needed because, after power
		 * collapse and reset, the DSI controller resets back to
		 * idle state and not ULPS.
		 * Wait for a short duration to ensure that the lanes
		 * enter ULP state.
		 */
		MIPI_OUTP(ctrl_pdata->ctrl_base + 0x0AC, active_lanes);
		usleep(100);

		/* Disable MMSS DSI Clamps */
		if (ctrl_pdata->ndx == DSI_CTRL_0) {
			regval = MIPI_INP(ctrl_pdata->mmss_misc_io.base + 0x14);
			MIPI_OUTP(ctrl_pdata->mmss_misc_io.base + 0x14,
				regval & ~(clamp_reg | BIT(15)));
		} else if (ctrl_pdata->ndx == DSI_CTRL_1) {
			regval = MIPI_INP(ctrl_pdata->mmss_misc_io.base + 0x14);
			MIPI_OUTP(ctrl_pdata->mmss_misc_io.base + 0x14,
				regval & ~((clamp_reg << 16) | BIT(31)));
		}


		/*
		 * ULPS Exit Request
		 * Hardware requirement is to wait for at least 1ms
		 */
		MIPI_OUTP(ctrl_pdata->ctrl_base + 0x0AC, active_lanes << 8);
		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;
}
int mdss_dsi_ulps_config(struct mdss_dsi_ctrl_pdata *ctrl_pdata, int enable)
{
	int ret = 0;
	struct mdss_panel_data *pdata = NULL;
	struct mdss_panel_info *pinfo;
	struct mipi_panel_info *mipi;
	u32 lane_status = 0, regval;
	u32 active_lanes = 0, clamp_reg;

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

	if (&ctrl_pdata->mmss_misc_io == NULL) {
		pr_err("%s: mmss_misc_io is NULL. ULPS not valid\n", __func__);
		return -EINVAL;
	}

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

	if (!mdss_dsi_ulps_feature_enabled(pdata)) {
		pr_debug("%s: ULPS feature not supported. enable=%d\n",
			__func__, enable);
		return -ENOTSUPP;
	}

	if (pinfo->cont_splash_enabled) {
		pr_debug("%s: skip ULPS config with splash screen enabled\n",
			__func__);
		return 0;
	}

	
	active_lanes = BIT(4);
	clamp_reg = BIT(8) | BIT(9);
	if (mipi->data_lane0) {
		active_lanes |= BIT(0);
		clamp_reg |= (BIT(0) | BIT(1));
	}
	if (mipi->data_lane1) {
		active_lanes |= BIT(1);
		clamp_reg |= (BIT(2) | BIT(3));
	}
	if (mipi->data_lane2) {
		active_lanes |= BIT(2);
		clamp_reg |= (BIT(4) | BIT(5));
	}
	if (mipi->data_lane3) {
		active_lanes |= BIT(3);
		clamp_reg |= (BIT(6) | BIT(7));
	}

	pr_debug("%s: configuring ulps (%s) for ctrl%d, active lanes=0x%08x\n",
		__func__, (enable ? "on" : "off"), ctrl_pdata->ndx,
		active_lanes);

	if (enable && !ctrl_pdata->ulps) {
		MIPI_OUTP(ctrl_pdata->ctrl_base + 0x0AC, active_lanes);
		usleep(100);

		
		lane_status = MIPI_INP(ctrl_pdata->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_pdata->ndx, lane_status);
			ret = -EINVAL;
			goto error;
		}

		
		if (ctrl_pdata->ndx == DSI_CTRL_0) {
			regval = MIPI_INP(ctrl_pdata->mmss_misc_io.base + 0x14);
			MIPI_OUTP(ctrl_pdata->mmss_misc_io.base + 0x14,
				regval | clamp_reg);
			MIPI_OUTP(ctrl_pdata->mmss_misc_io.base + 0x14,
				regval | (clamp_reg | BIT(15)));
		} else if (ctrl_pdata->ndx == DSI_CTRL_1) {
			regval = MIPI_INP(ctrl_pdata->mmss_misc_io.base + 0x14);
			MIPI_OUTP(ctrl_pdata->mmss_misc_io.base + 0x14,
				regval | (clamp_reg << 16));
			MIPI_OUTP(ctrl_pdata->mmss_misc_io.base + 0x14,
				regval | ((clamp_reg << 16) | BIT(31)));
		}

		wmb();

		MIPI_OUTP(ctrl_pdata->mmss_misc_io.base + 0x108, 0x1);
		ctrl_pdata->ulps = true;
	} else if (ctrl_pdata->ulps) {
		MIPI_OUTP(ctrl_pdata->mmss_misc_io.base + 0x108, 0x0);
		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);

		MIPI_OUTP(ctrl_pdata->ctrl_base + 0x0AC, active_lanes);
		usleep(100);

		
		if (ctrl_pdata->ndx == DSI_CTRL_0) {
			regval = MIPI_INP(ctrl_pdata->mmss_misc_io.base + 0x14);
			MIPI_OUTP(ctrl_pdata->mmss_misc_io.base + 0x14,
				regval & ~(clamp_reg | BIT(15)));
		} else if (ctrl_pdata->ndx == DSI_CTRL_1) {
			regval = MIPI_INP(ctrl_pdata->mmss_misc_io.base + 0x14);
			MIPI_OUTP(ctrl_pdata->mmss_misc_io.base + 0x14,
				regval & ~((clamp_reg << 16) | BIT(31)));
		}


		MIPI_OUTP(ctrl_pdata->ctrl_base + 0x0AC, active_lanes << 8);
		usleep(1000);
		MIPI_OUTP(ctrl_pdata->ctrl_base + 0x0AC, 0x0);

		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_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;
}