Пример #1
0
static int mdss_dsi_ulps_config(struct mdss_dsi_ctrl_pdata *ctrl,
	int enable)
{
	int rc;
	struct mdss_dsi_ctrl_pdata *mctrl = NULL;

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

	if (mdss_dsi_is_slave_ctrl(ctrl)) {
		mctrl = mdss_dsi_get_master_ctrl();
		if (!mctrl) {
			pr_err("%s: Unable to get master control\n", __func__);
			return -EINVAL;
		}
	}

	if (mctrl) {
		pr_debug("%s: configuring ulps (%s) for master ctrl%d\n",
			__func__, (enable ? "on" : "off"), ctrl->ndx);
		rc = mdss_dsi_ulps_config_sub(mctrl, enable, 0);
		if (rc)
			return rc;
	}

	pr_debug("%s: configuring ulps (%s) for ctrl%d\n",
		__func__, (enable ? "on" : "off"), ctrl->ndx);
	return mdss_dsi_ulps_config_sub(ctrl, enable, 0);
}
/*
 * mdss_dsi_cmds_tx:
 * thread context only
 */
int mdss_dsi_cmds_tx(struct mdss_dsi_ctrl_pdata *ctrl,
		struct dsi_cmd_desc *cmds, int cnt)
{
	int ret = 0;
	bool ctrl_restore = false, mctrl_restore = false;
	struct mdss_dsi_ctrl_pdata *mctrl = NULL;

	/*
	 * In broadcast mode, the configuration for master controller
	 * would be done when the slave controller is configured
	 */
	if (mdss_dsi_is_master_ctrl(ctrl)) {
		pr_debug("%s: Broadcast mode enabled. skipping config for ctrl%d\n",
			__func__, ctrl->ndx);
		return 0;
	}

	/*
	 * Turn on cmd mode in order to transmit the commands.
	 * For video mode, do not send cmds more than one pixel line,
	 * since it only transmit it during BLLP.
	 * Ensure that for slave controller, master is also configured
	 */
	if (mdss_dsi_is_slave_ctrl(ctrl)) {
		mctrl = mdss_dsi_get_master_ctrl();
		if (!mctrl)
			pr_warn("%s: Unable to get master control\n",
				__func__);
		else
			mctrl_restore = __mdss_dsi_cmd_mode_config(mctrl, 1);
	}

	ctrl_restore = __mdss_dsi_cmd_mode_config(ctrl, 1);

	ret = mdss_dsi_cmds2buf_tx(ctrl, cmds, cnt);
	if (IS_ERR_VALUE(ret)) {
		pr_err("%s: failed to call\n", __func__);
		cnt = -EINVAL;
	}

	if (mctrl_restore)
		__mdss_dsi_cmd_mode_config(mctrl, 0);

	if (ctrl_restore)
		__mdss_dsi_cmd_mode_config(ctrl, 0);

	return cnt;
}
/*
 * mdss_dsi_cmds_tx:
 * thread context only
 */
int mdss_dsi_cmds_tx(struct mdss_dsi_ctrl_pdata *ctrl,
		struct dsi_cmd_desc *cmds, int cnt)
{
	int ret = 0;
	bool ctrl_restore = false, mctrl_restore = false;
	struct mdss_dsi_ctrl_pdata *mctrl = NULL;

	/*
	 * In broadcast mode, the configuration for master controller
	 * would be done when the slave controller is configured
	 */
	if (mdss_dsi_is_master_ctrl(ctrl)) {
		pr_debug("%s: Broadcast mode enabled. skipping config for ctrl%d\n",
			__func__, ctrl->ndx);
		return 0;
	}

	/*
	 * Turn on cmd mode in order to transmit the commands.
	 * For video mode, do not send cmds more than one pixel line,
	 * since it only transmit it during BLLP.
	 * Ensure that for slave controller, master is also configured
	 */
	if (mdss_dsi_is_slave_ctrl(ctrl)) {
		mctrl = mdss_dsi_get_master_ctrl();
		if (!mctrl)
			pr_warn("%s: Unable to get master control\n",
				__func__);
#ifdef CONFIG_MACH_LGE
		else{
			mdelay(5);
			mctrl_restore = __mdss_dsi_cmd_mode_config(mctrl, 1);
		}
#else
		else
			mctrl_restore = __mdss_dsi_cmd_mode_config(mctrl, 1);
#endif
	}
void mdss_dsi_op_mode_config(int mode,
			     struct mdss_panel_data *pdata)
{
	u32 dsi_ctrl, intr_ctrl;
	struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;
	struct mdss_dsi_ctrl_pdata *mctrl = NULL;

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

	ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata,
				panel_data);

	/*
	 * In broadcast mode, the configuration for master controller
	 * would be done when the slave controller is configured
	 */
	if (mdss_dsi_is_master_ctrl(ctrl_pdata)) {
		pr_debug("%s: Broadcast mode enabled. skipping config for ctrl%d\n",
			__func__, ctrl_pdata->ndx);
		return;
	}

	dsi_ctrl = MIPI_INP((ctrl_pdata->ctrl_base) + 0x0004);
	/*If Video enabled, Keep Video and Cmd mode ON */
	if (dsi_ctrl & 0x02)
		dsi_ctrl &= ~0x05;
	else
		dsi_ctrl &= ~0x07;

	if (mode == DSI_VIDEO_MODE) {
		dsi_ctrl |= 0x03;
		intr_ctrl = DSI_INTR_CMD_DMA_DONE_MASK | DSI_INTR_BTA_DONE_MASK;
	} else {		/* command mode */
		dsi_ctrl |= 0x05;
		if (pdata->panel_info.type == MIPI_VIDEO_PANEL)
			dsi_ctrl |= 0x02;

		intr_ctrl = DSI_INTR_CMD_DMA_DONE_MASK | DSI_INTR_ERROR_MASK |
			DSI_INTR_CMD_MDP_DONE_MASK | DSI_INTR_BTA_DONE_MASK;
	}

	/* Ensure that for slave controller, master is also configured */
	if (mdss_dsi_is_slave_ctrl(ctrl_pdata)) {
		mctrl = mdss_dsi_get_master_ctrl();
		if (mctrl) {
			pr_debug("%s: configuring ctrl%d\n", __func__,
				mctrl->ndx);
			MIPI_OUTP(mctrl->ctrl_base + 0x0110, intr_ctrl);
			MIPI_OUTP(mctrl->ctrl_base + 0x0004, dsi_ctrl);
		} else {
			pr_warn("%s: Unable to get master control\n",
				__func__);
		}
	}

	pr_debug("%s: configuring ctrl%d\n", __func__, ctrl_pdata->ndx);
	MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0110, intr_ctrl);
	MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0004, dsi_ctrl);
	wmb();
}
/*
 * mdss_dsi_cmds_rx() - dcs read from panel
 * @ctrl: dsi controller
 * @cmds: read command descriptor
 * @len: number of bytes to read back
 *
 * controller have 4 registers can hold 16 bytes of rxed data
 * dcs packet: 4 bytes header + payload + 2 bytes crc
 * 2 padding bytes add to payload to have payload length is mutipled by 4
 * 1st read: 4 bytes header + 8 bytes payload + 2 padding + 2 crc
 * 2nd read: 12 bytes payload + 2 padding + 2 crc
 * 3rd read: 12 bytes payload + 2 padding + 2 crc
 *
 */
int mdss_dsi_cmds_rx(struct mdss_dsi_ctrl_pdata *ctrl,
			struct dsi_cmd_desc *cmds, int rlen)
{
	int data_byte, rx_byte, dlen, end;
	int short_response, diff, pkt_size, ret = 0;
	struct dsi_buf *tp, *rp;
	char cmd;
	bool ctrl_restore = false, mctrl_restore = false;
	struct mdss_dsi_ctrl_pdata *mctrl = NULL;

	/*
	 * In broadcast mode, the configuration for master controller
	 * would be done when the slave controller is configured
	 */
	if (mdss_dsi_is_master_ctrl(ctrl)) {
		pr_debug("%s: Broadcast mode enabled. skipping config for ctrl%d\n",
			__func__, ctrl->ndx);
		return 0;
	}

	/*
	 * Turn on cmd mode in order to transmit the commands.
	 * For video mode, do not send cmds more than one pixel line,
	 * since it only transmit it during BLLP.
	 * Ensure that for slave controller, master is also configured
	 */
	if (mdss_dsi_is_slave_ctrl(ctrl)) {
		mctrl = mdss_dsi_get_master_ctrl();
		if (!mctrl)
			pr_warn("%s: Unable to get master control\n",
				__func__);
		else
			mctrl_restore = __mdss_dsi_cmd_mode_config(mctrl, 1);
	}

	ctrl_restore = __mdss_dsi_cmd_mode_config(ctrl, 1);

	if (rlen <= 2) {
		short_response = 1;
		rx_byte = 4;
	} else {
		short_response = 0;
		data_byte = 8;	/* first read */
		/*
		 * add extra 2 padding bytes to have overall
		 * packet size is multipe by 4. This also make
		 * sure 4 bytes dcs headerlocates within a
		 * 32 bits register after shift in.
		 */
		pkt_size = data_byte + 2;
		rx_byte = data_byte + 8; /* 4 header + 2 crc  + 2 padding*/
	}


	tp = &ctrl->tx_buf;
	rp = &ctrl->rx_buf;

	end = 0;
	mdss_dsi_buf_init(rp);
	while (!end) {
		pr_debug("%s:  rlen=%d pkt_size=%d rx_byte=%d\n",
				__func__, rlen, pkt_size, rx_byte);
		 if (!short_response) {
			max_pktsize[0] = pkt_size;
			mdss_dsi_buf_init(tp);
			ret = mdss_dsi_cmd_dma_add(tp, &pkt_size_cmd);
			if (!ret) {
				pr_err("%s: failed to add max_pkt_size\n",
					__func__);
				rp->len = 0;
				goto end;
			}

			mdss_dsi_wait4video_eng_busy(ctrl);

			mdss_dsi_enable_irq(ctrl, DSI_CMD_TERM);
			ret = mdss_dsi_cmd_dma_tx(ctrl, tp);
			if (IS_ERR_VALUE(ret)) {
				mdss_dsi_disable_irq(ctrl, DSI_CMD_TERM);
				pr_err("%s: failed to tx max_pkt_size\n",
					__func__);
				rp->len = 0;
				goto end;
			}
			pr_debug("%s: max_pkt_size=%d sent\n",
						__func__, pkt_size);
		}

		mdss_dsi_buf_init(tp);
		ret = mdss_dsi_cmd_dma_add(tp, cmds);
		if (!ret) {
			pr_err("%s: failed to add cmd = 0x%x\n",
				__func__,  cmds->payload[0]);
			rp->len = 0;
			goto end;
		}

		mdss_dsi_wait4video_eng_busy(ctrl);	/* video mode only */
		mdss_dsi_enable_irq(ctrl, DSI_CMD_TERM);
		/* transmit read comamnd to client */
		ret = mdss_dsi_cmd_dma_tx(ctrl, tp);
		if (IS_ERR_VALUE(ret)) {
			mdss_dsi_disable_irq(ctrl, DSI_CMD_TERM);
			pr_err("%s: failed to tx cmd = 0x%x\n",
				__func__,  cmds->payload[0]);
			rp->len = 0;
			goto end;
		}
		/*
		 * once cmd_dma_done interrupt received,
		 * return data from client is ready and stored
		 * at RDBK_DATA register already
		 * since rx fifo is 16 bytes, dcs header is kept at first loop,
		 * after that dcs header lost during shift into registers
		 */
		dlen = mdss_dsi_cmd_dma_rx(ctrl, rp, rx_byte);

		if (short_response)
			break;

		if (rlen <= data_byte) {
			diff = data_byte - rlen;
			end = 1;
		} else {
			diff = 0;
			rlen -= data_byte;
		}

		dlen -= 2; /* 2 padding bytes */
		dlen -= 2; /* 2 crc */
		dlen -= diff;
		rp->data += dlen;	/* next start position */
		rp->len += dlen;
		data_byte = 12;	/* NOT first read */
		pkt_size += data_byte;
		pr_debug("%s: rp data=%x len=%d dlen=%d diff=%d\n",
			 __func__, (int) (unsigned long) rp->data,
			 rp->len, dlen, diff);
	}

	rp->data = rp->start;	/* move back to start position */
	cmd = rp->data[0];
	switch (cmd) {
	case DTYPE_ACK_ERR_RESP:
		pr_debug("%s: rx ACK_ERR_PACLAGE\n", __func__);
		rp->len = 0;
	case DTYPE_GEN_READ1_RESP:
	case DTYPE_DCS_READ1_RESP:
		mdss_dsi_short_read1_resp(rp);
		break;
	case DTYPE_GEN_READ2_RESP:
	case DTYPE_DCS_READ2_RESP:
		mdss_dsi_short_read2_resp(rp);
		break;
	case DTYPE_GEN_LREAD_RESP:
	case DTYPE_DCS_LREAD_RESP:
		mdss_dsi_long_read_resp(rp);
		break;
	default:
		pr_warning("%s:Invalid response cmd\n", __func__);
		rp->len = 0;
	}
end:
	if (mctrl_restore)
		__mdss_dsi_cmd_mode_config(mctrl, 0);

	if (ctrl_restore)
		__mdss_dsi_cmd_mode_config(ctrl, 0);

	return rp->len;
}
irqreturn_t mdss_dsi_isr(int irq, void *ptr)
{
	u32 isr;
	struct mdss_dsi_ctrl_pdata *ctrl =
			(struct mdss_dsi_ctrl_pdata *)ptr;
	struct mdss_dsi_ctrl_pdata *mctrl = NULL;

	if (!ctrl->ctrl_base) {
		pr_err("%s:%d DSI base adr no Initialized",
						__func__, __LINE__);
		return IRQ_HANDLED;
	}

	isr = MIPI_INP(ctrl->ctrl_base + 0x0110);/* DSI_INTR_CTRL */
	MIPI_OUTP(ctrl->ctrl_base + 0x0110, isr);

	if (mdss_dsi_is_slave_ctrl(ctrl)) {
		mctrl = mdss_dsi_get_master_ctrl();
		if (mctrl) {
			u32 isr0;
			isr0 = MIPI_INP(mctrl->ctrl_base + 0x0110);
			if (isr0 & DSI_INTR_CMD_DMA_DONE)
				MIPI_OUTP(mctrl->ctrl_base + 0x0110,
					DSI_INTR_CMD_DMA_DONE);
		} else {
			pr_warn("%s: Unable to get master control\n",
				__func__);
		}
	}

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

	if (isr & DSI_INTR_ERROR) {
		MDSS_XLOG(ctrl->ndx, ctrl->mdp_busy, isr, 0x97);
		pr_err("%s: ndx=%d isr=%x\n", __func__, ctrl->ndx, isr);
		mdss_dsi_error(ctrl);
	}

	if (isr & DSI_INTR_VIDEO_DONE) {
		spin_lock(&ctrl->mdp_lock);
		mdss_dsi_disable_irq_nosync(ctrl, DSI_VIDEO_TERM);
		complete(&ctrl->video_comp);
		spin_unlock(&ctrl->mdp_lock);
	}

	if (isr & DSI_INTR_CMD_DMA_DONE) {
		MDSS_XLOG(ctrl->ndx, ctrl->mdp_busy, isr, 0x98);
		spin_lock(&ctrl->mdp_lock);
		mdss_dsi_disable_irq_nosync(ctrl, DSI_CMD_TERM);
		complete(&ctrl->dma_comp);
		spin_unlock(&ctrl->mdp_lock);
	}

	if (isr & DSI_INTR_CMD_MDP_DONE) {
		MDSS_XLOG(ctrl->ndx, ctrl->mdp_busy, isr, 0x99);
		spin_lock(&ctrl->mdp_lock);
		ctrl->mdp_busy = false;
		mdss_dsi_disable_irq_nosync(ctrl, DSI_MDP_TERM);
		complete(&ctrl->mdp_comp);
		spin_unlock(&ctrl->mdp_lock);
	}

	if (isr & DSI_INTR_BTA_DONE) {
		spin_lock(&ctrl->mdp_lock);
		mdss_dsi_disable_irq_nosync(ctrl, DSI_BTA_TERM);
		complete(&ctrl->bta_comp);
		spin_unlock(&ctrl->mdp_lock);
	}

	return IRQ_HANDLED;
}
static int mdss_dsi_cmd_dma_tx(struct mdss_dsi_ctrl_pdata *ctrl,
					struct dsi_buf *tp)
{
	int len, ret = 0;
	int domain = MDSS_IOMMU_DOMAIN_UNSECURE;
	char *bp;
	unsigned long size;
	dma_addr_t addr;
	struct mdss_dsi_ctrl_pdata *mctrl = NULL;
#ifdef DEBUG_CMD
	int i;
	bp = tp->data;

	pr_info("%s: ", __func__);
	for (i = 0; i < tp->len; i++)
		printk("%x ", *bp++);

	pr_info("\n");
#endif
	bp = tp->data;

	len = ALIGN(tp->len, 4);
	size = ALIGN(tp->len, SZ_4K);
	

	if (is_mdss_iommu_attached()) {
		ret = msm_iommu_map_contig_buffer(tp->dmap,
					mdss_get_iommu_domain(domain), 0,
					size, SZ_4K, 0, &(addr));
		if (IS_ERR_VALUE(ret)) {
			pr_err("unable to map dma memory to iommu(%d)\n", ret);
			return -ENOMEM;
		}
	} else {
		addr = tp->dmap;
	}

	INIT_COMPLETION(ctrl->dma_comp);

	/* Ensure that for slave controller, master is also configured */
	if (mdss_dsi_is_slave_ctrl(ctrl)) {
		mctrl = mdss_dsi_get_master_ctrl();
		if (mctrl) {
			MIPI_OUTP(mctrl->ctrl_base + 0x048, addr);
			MIPI_OUTP(mctrl->ctrl_base + 0x04c, len);
		} else {
			pr_warn("%s: Unable to get master control\n",
				__func__);
		}
	}

	MIPI_OUTP((ctrl->ctrl_base) + 0x048, addr);
	MIPI_OUTP((ctrl->ctrl_base) + 0x04c, len);
	wmb();

	/* Trigger on master controller as well */
	if (mctrl)
		MIPI_OUTP(mctrl->ctrl_base + 0x090, 0x01);

	MIPI_OUTP((ctrl->ctrl_base) + 0x090, 0x01);
	wmb();

	ret = wait_for_completion_timeout(&ctrl->dma_comp,
				msecs_to_jiffies(DMA_TX_TIMEOUT));
	if (ret == 0)
		ret = -ETIMEDOUT;
	else
		ret = tp->len;

	if (is_mdss_iommu_attached())
		msm_iommu_unmap_contig_buffer(addr,
			mdss_get_iommu_domain(domain), 0, size);

	return ret;
}
int mdss_dsi_clk_ctrl(struct mdss_dsi_ctrl_pdata *ctrl,
	u8 clk_type, int enable)
{
	int rc = 0;
	int link_changed = 0, bus_changed = 0;
	int m_link_changed = 0, m_bus_changed = 0;
	struct mdss_dsi_ctrl_pdata *mctrl = NULL;

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

	/*
	 * In broadcast mode, we need to enable clocks for the
	 * master controller as well when enabling clocks for the
	 * slave controller
	 */
	if (mdss_dsi_is_slave_ctrl(ctrl)) {
		mctrl = mdss_dsi_get_master_ctrl();
		if (!mctrl)
			pr_warn("%s: Unable to get master control\n", __func__);
	}

	pr_debug("%s++: ndx=%d clk_type=%d bus_clk_cnt=%d link_clk_cnt=%d\n",
		__func__, ctrl->ndx, clk_type, ctrl->bus_clk_cnt,
		ctrl->link_clk_cnt);
	pr_debug("%s++: mctrl=%s m_bus_clk_cnt=%d m_link_clk_cnt=%d, enable=%d\n",
		__func__, mctrl ? "yes" : "no", mctrl ? mctrl->bus_clk_cnt : -1,
		mctrl ? mctrl->link_clk_cnt : -1, enable);

	mutex_lock(&dsi_clk_lock);

	if (clk_type & DSI_BUS_CLKS) {
		bus_changed = __mdss_dsi_update_clk_cnt(&ctrl->bus_clk_cnt,
			enable);
		if (bus_changed && mctrl)
			m_bus_changed = __mdss_dsi_update_clk_cnt(
				&mctrl->bus_clk_cnt, enable);
	}

	if (clk_type & DSI_LINK_CLKS) {
		link_changed = __mdss_dsi_update_clk_cnt(&ctrl->link_clk_cnt,
			enable);
		if (link_changed && mctrl)
			m_link_changed = __mdss_dsi_update_clk_cnt(
				&mctrl->link_clk_cnt, enable);
	}

	if (!link_changed && !bus_changed)
		goto no_error; /* clk cnts updated, nothing else needed */

	/*
	 * If updating link clock, need to make sure that the bus
	 * clocks are enabled
	 */
	if (link_changed && (!bus_changed && !ctrl->bus_clk_cnt)) {
		pr_err("%s: Trying to enable link clks w/o enabling bus clks for ctrl%d",
			__func__, mctrl->ndx);
		goto error_mctrl_start;
	}

	if (m_link_changed && (!m_bus_changed && !mctrl->bus_clk_cnt)) {
		pr_err("%s: Trying to enable link clks w/o enabling bus clks for ctrl%d",
			__func__, ctrl->ndx);
		goto error_mctrl_start;
	}

	if (enable && (m_bus_changed || m_link_changed)) {
		rc = mdss_dsi_clk_ctrl_sub(mctrl, clk_type, enable);
		if (rc) {
			pr_err("Failed to start mctrl clocks. rc=%d\n", rc);
			goto error_mctrl_start;
		}
	}

	if (!enable && (m_bus_changed || m_link_changed)) {
		rc = mdss_dsi_clk_ctrl_sub(mctrl, clk_type, enable);
		if (rc) {
			pr_err("Failed to stop mctrl clocks. rc=%d\n", rc);
			goto error_mctrl_stop;
		}
	}
	rc = mdss_dsi_clk_ctrl_sub(ctrl, clk_type, enable);
	if (rc) {
		pr_err("Failed to %s ctrl clocks. rc=%d\n",
			(enable ? "start" : "stop"), rc);
		goto error_ctrl;
	}

	goto no_error;

error_mctrl_stop:
	mdss_dsi_clk_ctrl_sub(ctrl, clk_type, enable ? 0 : 1);
error_ctrl:
	if (enable && (m_bus_changed || m_link_changed))
		mdss_dsi_clk_ctrl_sub(mctrl, clk_type, 0);
error_mctrl_start:
	if (clk_type & DSI_BUS_CLKS) {
		if (mctrl)
			__mdss_dsi_update_clk_cnt(&mctrl->bus_clk_cnt,
				enable ? 0 : 1);
		__mdss_dsi_update_clk_cnt(&ctrl->bus_clk_cnt, enable ? 0 : 1);
	}
	if (clk_type & DSI_LINK_CLKS) {
		if (mctrl)
			__mdss_dsi_update_clk_cnt(&mctrl->link_clk_cnt,
				enable ? 0 : 1);
		__mdss_dsi_update_clk_cnt(&ctrl->link_clk_cnt, enable ? 0 : 1);
	}

no_error:
	mutex_unlock(&dsi_clk_lock);
	pr_debug("%s--: ndx=%d clk_type=%d bus_clk_cnt=%d link_clk_cnt=%d changed=%d\n",
		__func__, ctrl->ndx, clk_type, ctrl->bus_clk_cnt,
		ctrl->link_clk_cnt, link_changed && bus_changed);
	pr_debug("%s--: mctrl=%s m_bus_clk_cnt=%d m_link_clk_cnt=%d, m_changed=%d, enable=%d\n",
		__func__, mctrl ? "yes" : "no", mctrl ? mctrl->bus_clk_cnt : -1,
		mctrl ? mctrl->link_clk_cnt : -1,
		m_link_changed && m_bus_changed, enable);

	return rc;
}
int mdss_dsi_clk_ctrl(struct mdss_dsi_ctrl_pdata *ctrl,
	u8 clk_type, int enable)
{
	int rc = 0;
	int changed = 0, m_changed = 0;
	struct mdss_dsi_ctrl_pdata *mctrl = NULL;

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

	/*
	 * In broadcast mode, we need to enable clocks for the
	 * master controller as well when enabling clocks for the
	 * slave controller
	 */
	if (mdss_dsi_is_slave_ctrl(ctrl)) {
		mctrl = mdss_dsi_get_master_ctrl();
		if (!mctrl)
			pr_warn("%s: Unable to get master control\n", __func__);
	}

	pr_debug("%s++: ndx=%d clk_type=%d bus_clk_cnt=%d link_clk_cnt=%d",
		__func__, ctrl->ndx, clk_type, ctrl->bus_clk_cnt,
		ctrl->link_clk_cnt);
	pr_debug("%s++: mctrl=%s m_bus_clk_cnt=%d m_link_clk_cnt=%d\n, enable=%d\n",
		__func__, mctrl ? "yes" : "no", mctrl ? mctrl->bus_clk_cnt : -1,
		mctrl ? mctrl->link_clk_cnt : -1, enable);

	mutex_lock(&dsi_clk_lock);
	if (clk_type & DSI_BUS_CLKS) {
		changed = __mdss_dsi_update_clk_cnt(&ctrl->bus_clk_cnt,
			enable);
		if (changed && mctrl)
			m_changed = __mdss_dsi_update_clk_cnt(
				&mctrl->bus_clk_cnt, enable);
	}

	if (clk_type & DSI_LINK_CLKS) {
		changed += __mdss_dsi_update_clk_cnt(&ctrl->link_clk_cnt,
			enable);
		if (changed && mctrl)
			m_changed += __mdss_dsi_update_clk_cnt(
				&mctrl->link_clk_cnt, enable);
	}
#if defined (CONFIG_FB_MSM_MDSS_DSI_DBG)
	xlog(__func__,ctrl->ndx, enable,changed,m_changed,ctrl->bus_clk_cnt,mctrl?mctrl->bus_clk_cnt:0xbbb);
#endif
	if (changed) {
		if (enable && m_changed) {
			rc = mdss_dsi_clk_ctrl_sub(mctrl, clk_type, enable);
			if (rc) {
				pr_err("Failed to start mctrl clocks. rc=%d\n",
					rc);
				goto error_mctrl_start;
			}
		}

		rc = mdss_dsi_clk_ctrl_sub(ctrl, clk_type, enable);
		if (rc) {
			pr_err("Failed to %s ctrl clocks. rc=%d\n",
				(enable ? "start" : "stop"), rc);
			goto error_ctrl;
		}

		if (!enable && m_changed) {
			rc = mdss_dsi_clk_ctrl_sub(mctrl, clk_type, enable);
			if (rc) {
				pr_err("Failed to stop mctrl clocks. rc=%d\n",
					rc);
				goto error_mctrl_stop;
			}
		}
	}
	goto no_error;

error_mctrl_stop:
	mdss_dsi_clk_ctrl_sub(ctrl, clk_type, enable ? 0 : 1);
error_ctrl:
	if (enable && m_changed)
		mdss_dsi_clk_ctrl_sub(mctrl, clk_type, 0);
error_mctrl_start:
	if (clk_type & DSI_BUS_CLKS) {
		if (mctrl)
			__mdss_dsi_update_clk_cnt(&mctrl->bus_clk_cnt,
				enable ? 0 : 1);
		__mdss_dsi_update_clk_cnt(&ctrl->bus_clk_cnt, enable ? 0 : 1);
	}
	if (clk_type & DSI_LINK_CLKS) {
		if (mctrl)
			__mdss_dsi_update_clk_cnt(&mctrl->link_clk_cnt,
				enable ? 0 : 1);
		__mdss_dsi_update_clk_cnt(&ctrl->link_clk_cnt, enable ? 0 : 1);
	}

no_error:
	mutex_unlock(&dsi_clk_lock);
	pr_debug("%s++: ndx=%d clk_type=%d bus_clk_cnt=%d link_clk_cnt=%d changed=%d",
		__func__, ctrl->ndx, clk_type, ctrl->bus_clk_cnt,
		ctrl->link_clk_cnt, changed);
	pr_debug("%s++: mctrl=%s m_bus_clk_cnt=%d m_link_clk_cnt=%d\n, m_changed=%d, enable=%d\n",
		__func__, mctrl ? "yes" : "no", mctrl ? mctrl->bus_clk_cnt : -1,
		mctrl ? mctrl->link_clk_cnt : -1, m_changed, enable);

	return rc;
}