Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bfd805f9 authored by Xiaoming Zhou's avatar Xiaoming Zhou
Browse files

msm: mdss: fix the dsi dma transaction time out on 8x10



The dsi dma data is sent during the blanking period for
the dsi video mode panel.  If the dma data are triggered
at the end of blank period, it can be in conflict with
the next video data, causing dsi controller to freeze.
This change is to trigger the dma data during the active
region, so it has enough time to be sent during the next
blank region.

Change-Id: I416bb9f82304063b802aa7f5ff09008dfdf0e6bc
Signed-off-by: default avatarXiaoming Zhou <zhoux@codeaurora.org>
parent 9e79afb8
Loading
Loading
Loading
Loading
+283 −167
Original line number Diff line number Diff line
@@ -31,6 +31,10 @@
#define DSI_POLL_TIMEOUT_US 16000
#define DSI_ESC_CLK_RATE 19200000
#define DSI_DMA_CMD_TIMEOUT_MS 200
#define VSYNC_PERIOD 17
#define DSI_MAX_PKT_SIZE 10
#define DSI_SHORT_PKT_DATA_SIZE 2
#define DSI_MAX_BYTES_TO_READ 16

struct dsi_host_v2_private {
	int irq_no;
@@ -131,7 +135,7 @@ void msm_dsi_error(unsigned char *ctrl_base)
	msm_dsi_dln0_phy_err(ctrl_base);
}

void msm_dsi_set_irq_mask(struct mdss_dsi_ctrl_pdata *ctrl, u32 mask)
static void msm_dsi_set_irq_mask(struct mdss_dsi_ctrl_pdata *ctrl, u32 mask)
{
	u32 intr_ctrl;
	intr_ctrl = MIPI_INP(dsi_host_private->dsi_base + DSI_INT_CTRL);
@@ -139,7 +143,7 @@ void msm_dsi_set_irq_mask(struct mdss_dsi_ctrl_pdata *ctrl, u32 mask)
	MIPI_OUTP(dsi_host_private->dsi_base + DSI_INT_CTRL, intr_ctrl);
}

void msm_dsi_clear_irq_mask(struct mdss_dsi_ctrl_pdata *ctrl, u32 mask)
static void msm_dsi_clear_irq_mask(struct mdss_dsi_ctrl_pdata *ctrl, u32 mask)
{
	u32 intr_ctrl;
	intr_ctrl = MIPI_INP(dsi_host_private->dsi_base + DSI_INT_CTRL);
@@ -147,7 +151,7 @@ void msm_dsi_clear_irq_mask(struct mdss_dsi_ctrl_pdata *ctrl, u32 mask)
	MIPI_OUTP(dsi_host_private->dsi_base + DSI_INT_CTRL, intr_ctrl);
}

void msm_dsi_set_irq(struct mdss_dsi_ctrl_pdata *ctrl, u32 mask)
static void msm_dsi_set_irq(struct mdss_dsi_ctrl_pdata *ctrl, u32 mask)
{
	unsigned long flags;

@@ -167,7 +171,7 @@ void msm_dsi_set_irq(struct mdss_dsi_ctrl_pdata *ctrl, u32 mask)
	spin_unlock_irqrestore(&ctrl->irq_lock, flags);
}

void msm_dsi_clear_irq(struct mdss_dsi_ctrl_pdata *ctrl, u32 mask)
static void msm_dsi_clear_irq(struct mdss_dsi_ctrl_pdata *ctrl, u32 mask)
{
	unsigned long flags;

@@ -232,6 +236,73 @@ int msm_dsi_irq_init(struct device *dev, int irq_no,
	return 0;
}

static void msm_dsi_get_cmd_engine(struct mdss_dsi_ctrl_pdata *ctrl)
{
	unsigned char *ctrl_base = dsi_host_private->dsi_base;
	u32 dsi_ctrl;

	if (ctrl->panel_mode == DSI_VIDEO_MODE) {
		dsi_ctrl = MIPI_INP(ctrl_base + DSI_CTRL);
		MIPI_OUTP(ctrl_base + DSI_CTRL, dsi_ctrl | 0x04);
	}
}

static void msm_dsi_release_cmd_engine(struct mdss_dsi_ctrl_pdata *ctrl)
{
	unsigned char *ctrl_base = dsi_host_private->dsi_base;
	u32 dsi_ctrl;
	if (ctrl->panel_mode == DSI_VIDEO_MODE) {
		dsi_ctrl = MIPI_INP(ctrl_base + DSI_CTRL);
		dsi_ctrl &= ~0x04;
		MIPI_OUTP(ctrl_base + DSI_CTRL, dsi_ctrl);
	}
}

static int msm_dsi_wait4video_done(struct mdss_dsi_ctrl_pdata *ctrl)
{
	int rc;
	unsigned long flag;

	spin_lock_irqsave(&ctrl->mdp_lock, flag);
	INIT_COMPLETION(ctrl->video_comp);
	msm_dsi_set_irq(ctrl, DSI_INTR_VIDEO_DONE_MASK);
	spin_unlock_irqrestore(&ctrl->mdp_lock, flag);

	rc = wait_for_completion_timeout(&ctrl->video_comp,
				msecs_to_jiffies(VSYNC_PERIOD * 4));

	if (rc == 0) {
		pr_err("DSI wait 4 video done time out\n");
		rc = -ETIME;
	} else if (!IS_ERR_VALUE(rc)) {
		rc = 0;
	}

	msm_dsi_clear_irq(ctrl, DSI_INTR_VIDEO_DONE_MASK);

	return rc;
}

static int msm_dsi_wait4video_eng_busy(struct mdss_dsi_ctrl_pdata *ctrl)
{
	int rc = 0;
	u32 dsi_status;
	unsigned char *ctrl_base = dsi_host_private->dsi_base;

	if (ctrl->panel_mode == DSI_CMD_MODE)
		return rc;

	dsi_status = MIPI_INP(ctrl_base + DSI_STATUS);
	if (dsi_status & 0x08) {
		pr_debug("dsi command in video mode wait for active region\n");
		rc = msm_dsi_wait4video_done(ctrl);
		/* delay 4-5 ms to skip BLLP */
		if (!rc)
			usleep_range(4000, 5000);
	}
	return rc;
}

void msm_dsi_host_init(struct mipi_panel_info *pinfo)
{
	u32 dsi_ctrl, data;
@@ -458,6 +529,7 @@ int msm_dsi_cmd_dma_tx(struct mdss_dsi_ctrl_pdata *ctrl,
	int len, rc;
	unsigned long size, addr;
	unsigned char *ctrl_base = dsi_host_private->dsi_base;
	unsigned long flag;

	len = ALIGN(tp->len, 4);
	size = ALIGN(tp->len, SZ_4K);
@@ -471,7 +543,12 @@ int msm_dsi_cmd_dma_tx(struct mdss_dsi_ctrl_pdata *ctrl,

	addr = tp->dmap;

	msm_dsi_get_cmd_engine(ctrl);

	spin_lock_irqsave(&ctrl->mdp_lock, flag);
	INIT_COMPLETION(ctrl->dma_comp);
	msm_dsi_set_irq(ctrl, DSI_INTR_CMD_DMA_DONE_MASK);
	spin_unlock_irqrestore(&ctrl->mdp_lock, flag);

	MIPI_OUTP(ctrl_base + DSI_DMA_CMD_OFFSET, addr);
	MIPI_OUTP(ctrl_base + DSI_DMA_CMD_LENGTH, len);
@@ -492,10 +569,16 @@ int msm_dsi_cmd_dma_tx(struct mdss_dsi_ctrl_pdata *ctrl,
	dma_unmap_single(&dsi_host_private->dis_dev, tp->dmap, size,
			DMA_TO_DEVICE);
	tp->dmap = 0;

	msm_dsi_clear_irq(ctrl, DSI_INTR_CMD_DMA_DONE_MASK);

	msm_dsi_release_cmd_engine(ctrl);

	return rc;
}

int msm_dsi_cmd_dma_rx(struct dsi_buf *rp, int rlen)
int msm_dsi_cmd_dma_rx(struct mdss_dsi_ctrl_pdata *ctrl,
			struct dsi_buf *rp, int rlen)
{
	u32 *lp, data;
	int i, off, cnt;
@@ -521,223 +604,252 @@ int msm_dsi_cmd_dma_rx(struct dsi_buf *rp, int rlen)
		rp->len += sizeof(*lp);
	}

	return 0;
	return rlen;
}

int msm_dsi_cmds_tx(struct mdss_dsi_ctrl_pdata *ctrl,
static int msm_dsi_cmds_tx(struct mdss_dsi_ctrl_pdata *ctrl,
			struct dsi_cmd_desc *cmds, int cnt)
{
	struct dsi_buf *tp;
	struct dsi_cmd_desc *cm;
	u32 dsi_ctrl, data;
	int i, video_mode, rc = 0;
	unsigned char *ctrl_base = dsi_host_private->dsi_base;

	/* turn on cmd mode
	* for video mode, do not send cmds more than
	* one pixel line, since it only transmit it
	* during BLLP.
	*/
	dsi_ctrl = MIPI_INP(ctrl_base + DSI_CTRL);
	video_mode = dsi_ctrl & 0x02; /* VIDEO_MODE_EN */
	if (video_mode) {
		data = dsi_ctrl | 0x04; /* CMD_MODE_EN */
		MIPI_OUTP(ctrl_base + DSI_CTRL, data);
	}
	struct dsi_ctrl_hdr *dchdr;
	int len;
	int rc = 0;

	msm_dsi_set_irq(ctrl, DSI_INTR_CMD_DMA_DONE_MASK);

	tp = &ctrl->tx_buf;
	cm = cmds;
	for (i = 0; i < cnt; i++) {
	mdss_dsi_buf_init(tp);
		rc = mdss_dsi_cmd_dma_add(tp, cm);
		if (!rc) {
			pr_err("%s: dsi_cmd_dma_add fail\n", __func__);
	cm = cmds;
	len = 0;
	while (cnt--) {
		dchdr = &cm->dchdr;
		mdss_dsi_buf_reserve(tp, len);
		len = mdss_dsi_cmd_dma_add(tp, cm);
		if (!len) {
			pr_err("%s: failed to add cmd = 0x%x\n",
				__func__,  cm->payload[0]);
			rc = -EINVAL;
			break;
			goto dsi_cmds_tx_err;
		}

		if (dchdr->last) {
			tp->data = tp->start; /* begin of buf */
			rc = msm_dsi_wait4video_eng_busy(ctrl);
			if (rc) {
				pr_err("%s: wait4video_eng failed\n", __func__);
				goto dsi_cmds_tx_err;

			}

			rc = msm_dsi_cmd_dma_tx(ctrl, tp);
		if (IS_ERR_VALUE(rc)) {
			pr_err("%s: failed to call cmd_dma_tx\n", __func__);
			break;
			if (IS_ERR_VALUE(len)) {
				pr_err("%s: failed to call cmd_dma_tx for cmd = 0x%x\n",
					__func__,  cmds->payload[0]);
				goto dsi_cmds_tx_err;
			}

			if (dchdr->wait)
				usleep(dchdr->wait * 1000);

			mdss_dsi_buf_init(tp);
			len = 0;
		}
		if (cm->dchdr.wait)
			msleep(cm->dchdr.wait);
		cm++;
	}

	msm_dsi_clear_irq(ctrl, DSI_INTR_CMD_DMA_DONE_MASK);
dsi_cmds_tx_err:
	return rc;
}

static int msm_dsi_parse_rx_response(struct dsi_buf *rp)
{
	int rc = 0;
	unsigned char cmd;

	cmd = rp->data[0];
	switch (cmd) {
	case DTYPE_ACK_ERR_RESP:
		pr_debug("%s: rx ACK_ERR_PACLAGE\n", __func__);
		rc = -EINVAL;
		break;
	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:
		rc = -EINVAL;
		pr_warn("%s: Unknown cmd received\n", __func__);
		break;
	}

	if (video_mode)
		MIPI_OUTP(ctrl_base + DSI_CTRL, dsi_ctrl);
	return rc;
}

/* MDSS_DSI_MRPS, Maximum Return Packet Size */
/* MIPI_DSI_MRPS, Maximum Return Packet Size */
static char max_pktsize[2] = {0x00, 0x00}; /* LSB tx first, 10 bytes */

static struct dsi_cmd_desc pkt_size_cmd[] = {
	{{DTYPE_MAX_PKTSIZE, 1, 0, 0, 0,
		sizeof(max_pktsize)}, max_pktsize}
static struct dsi_cmd_desc pkt_size_cmd = {
	{DTYPE_MAX_PKTSIZE, 1, 0, 0, 0, sizeof(max_pktsize)},
	max_pktsize,
};

/*
 * DSI panel reply with  MAX_RETURN_PACKET_SIZE bytes of data
 * plus DCS header, ECC and CRC for DCS long read response
 * mdss_dsi_controller only have 4x32 bits register ( 16 bytes) to
 * hold data per transaction.
 * MDSS_DSI_LEN equal to 8
 * len should be either 4 or 8
 * any return data more than MDSS_DSI_LEN need to be break down
 * to multiple transactions.
 *
 * ov_mutex need to be acquired before call this function.
 */
int msm_dsi_cmds_rx(struct mdss_dsi_ctrl_pdata *ctrl,
			struct dsi_cmd_desc *cmds, int rlen)
static int msm_dsi_set_max_packet_size(struct mdss_dsi_ctrl_pdata *ctrl,
						int size)
{
	struct dsi_buf *tp, *rp;
	int cnt, len, diff, pkt_size, rc = 0;
	char cmd;
	unsigned char *ctrl_base = dsi_host_private->dsi_base;
	u32 dsi_ctrl, data;
	int video_mode;
	struct mdss_panel_data *pdata = &ctrl->panel_data;

	/* turn on cmd mode for video mode */
	dsi_ctrl = MIPI_INP(ctrl_base + DSI_CTRL);
	video_mode = dsi_ctrl & 0x02; /* VIDEO_MODE_EN */
	if (video_mode) {
		data = dsi_ctrl | 0x04; /* CMD_MODE_EN */
		MIPI_OUTP(ctrl_base + DSI_CTRL, data);
	}
	struct dsi_buf *tp;
	int rc;

	if (pdata->panel_info.mipi.no_max_pkt_size)
		rlen = ALIGN(rlen, 4); /* Only support rlen = 4*n */
	tp = &ctrl->tx_buf;
	mdss_dsi_buf_init(tp);
	max_pktsize[0] = size;

	len = rlen;
	diff = 0;
	rc = mdss_dsi_cmd_dma_add(tp, &pkt_size_cmd);
	if (!rc) {
		pr_err("%s: failed to add max_pkt_size\n", __func__);
		return -EINVAL;
	}

	if (len <= 2) {
		cnt = 4;	/* short read */
	} else {
		if (len > MDSS_DSI_LEN)
			len = MDSS_DSI_LEN;	/* 8 bytes at most */
	rc = msm_dsi_wait4video_eng_busy(ctrl);
	if (rc) {
		pr_err("%s: failed to wait4video_eng\n", __func__);
		return rc;
	}

		len = ALIGN(len, 4); /* len 4 bytes align */
		diff = len - rlen;
		/*
		 * add extra 2 bytes to len 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.
		 * after all, len should be either 6 or 10.
		 */
		len += 2;
		cnt = len + 6; /* 4 bytes header + 2 bytes crc */
	rc = msm_dsi_cmd_dma_tx(ctrl, tp);
	if (IS_ERR_VALUE(rc)) {
		pr_err("%s: failed to tx max_pkt_size\n", __func__);
		return rc;
	}
	pr_debug("%s: max_pkt_size=%d sent\n", __func__, size);
	return rc;
}

	msm_dsi_set_irq(ctrl, DSI_INTR_CMD_DMA_DONE_MASK);
/* read data length is less than or equal to 10 bytes*/
static int msm_dsi_cmds_rx_1(struct mdss_dsi_ctrl_pdata *ctrl,
				struct dsi_cmd_desc *cmds, int rlen)
{
	int rc;
	struct dsi_buf *tp, *rp;

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

	if (!pdata->panel_info.mipi.no_max_pkt_size) {
		/* packet size need to be set at every read */
		pkt_size = len;
		max_pktsize[0] = pkt_size;
	mdss_dsi_buf_init(rp);
	mdss_dsi_buf_init(tp);
		rc = mdss_dsi_cmd_dma_add(tp, pkt_size_cmd);

	rc = mdss_dsi_cmd_dma_add(tp, cmds);
	if (!rc) {
		pr_err("%s: dsi_cmd_dma_add failed\n", __func__);
			rp->len = 0;
			goto msm_dsi_cmds_rx_err;
		rc = -EINVAL;
		goto dsi_cmds_rx_1_error;
	}

	rc = msm_dsi_wait4video_eng_busy(ctrl);
	if (rc) {
		pr_err("%s: wait4video_eng failed\n", __func__);
		goto dsi_cmds_rx_1_error;
	}

	rc = msm_dsi_cmd_dma_tx(ctrl, tp);
	if (IS_ERR_VALUE(rc)) {
		pr_err("%s: msm_dsi_cmd_dma_tx failed\n", __func__);
			rp->len = 0;
			goto msm_dsi_cmds_rx_err;
		goto dsi_cmds_rx_1_error;
	}
		pr_debug("%s: Max packet size sent\n", __func__);

	if (rlen <= DSI_SHORT_PKT_DATA_SIZE) {
		msm_dsi_cmd_dma_rx(ctrl, rp, rlen);
	} else {
		msm_dsi_cmd_dma_rx(ctrl, rp, rlen + DSI_HOST_HDR_SIZE);
		rp->len = rlen + DSI_HOST_HDR_SIZE;
	}
	rc = msm_dsi_parse_rx_response(rp);

dsi_cmds_rx_1_error:
	if (rc)
		rp->len = 0;

	return rc;
}

/* read data length is more than 10 bytes, which requires multiple DSI read*/
static int msm_dsi_cmds_rx_2(struct mdss_dsi_ctrl_pdata *ctrl,
				struct dsi_cmd_desc *cmds, int rlen)
{
	int rc;
	struct dsi_buf *tp, *rp;
	int pkt_size, data_bytes, total;

	tp = &ctrl->tx_buf;
	rp = &ctrl->rx_buf;
	mdss_dsi_buf_init(rp);
	pkt_size = DSI_MAX_PKT_SIZE;
	data_bytes = MDSS_DSI_LEN;
	total = 0;

	while (true) {
		rc = msm_dsi_set_max_packet_size(ctrl, pkt_size);
		if (rc)
			break;

		mdss_dsi_buf_init(tp);
		rc = mdss_dsi_cmd_dma_add(tp, cmds);
		if (!rc) {
			pr_err("%s: dsi_cmd_dma_add failed\n", __func__);
		rp->len = 0;
		goto msm_dsi_cmds_rx_err;
			rc = -EINVAL;
			break;
	}
		rc = msm_dsi_wait4video_eng_busy(ctrl);
		if (rc) {
			pr_err("%s: wait4video_eng failed\n", __func__);
			break;
		}

	/* transmit read comamnd to client */
		rc = msm_dsi_cmd_dma_tx(ctrl, tp);
		if (IS_ERR_VALUE(rc)) {
			pr_err("%s: msm_dsi_cmd_dma_tx failed\n", __func__);
		rp->len = 0;
		goto msm_dsi_cmds_rx_err;
	}
	/*
	 * once cmd_dma_done interrupt received,
	 * return data from client is ready and stored
	 * at RDBK_DATA register already
	 */
	mdss_dsi_buf_init(rp);
	if (pdata->panel_info.mipi.no_max_pkt_size) {
		/*
		 * expect rlen = n * 4
		 * short alignement for start addr
		 */
		rp->data += 2;
			break;
		}

	msm_dsi_cmd_dma_rx(rp, cnt);
		msm_dsi_cmd_dma_rx(ctrl, rp, DSI_MAX_BYTES_TO_READ);

	if (pdata->panel_info.mipi.no_max_pkt_size) {
		/*
		 * remove extra 2 bytes from previous
		 * rx transaction at shift register
		 * which was inserted during copy
		 * shift registers to rx buffer
		 * rx payload start from long alignment addr
		 */
		rp->data += 2;
		rp->data += DSI_MAX_BYTES_TO_READ - DSI_HOST_HDR_SIZE;
		total += data_bytes;
		if (total >= rlen)
			break;

		data_bytes = DSI_MAX_BYTES_TO_READ - DSI_HOST_HDR_SIZE;
		pkt_size += data_bytes;
	}

	cmd = rp->data[0];
	switch (cmd) {
	case DTYPE_ACK_ERR_RESP:
		pr_debug("%s: rx ACK_ERR_PACLAGE\n", __func__);
		rp->len = 0;
		break;
	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);
		rp->len -= 2; /* extra 2 bytes added */
		rp->len -= diff; /* align bytes */
		break;
	default:
		pr_warn("%s: Unknown cmd received\n", __func__);
	if (!rc) {
		rp->data = rp->start;
		rp->len = rlen + DSI_HOST_HDR_SIZE;
		rc = msm_dsi_parse_rx_response(rp);
	}

	if (rc)
		rp->len = 0;
		break;

	return rc;
}

	if (video_mode)
		MIPI_OUTP(ctrl_base + DSI_CTRL,
					dsi_ctrl); /* restore */
int msm_dsi_cmds_rx(struct mdss_dsi_ctrl_pdata *ctrl,
			struct dsi_cmd_desc *cmds, int rlen)
{
	int rc;
	if (rlen <= DSI_MAX_PKT_SIZE)
		rc = msm_dsi_cmds_rx_1(ctrl, cmds, rlen);
	else
		rc = msm_dsi_cmds_rx_2(ctrl, cmds, rlen);

msm_dsi_cmds_rx_err:
	msm_dsi_clear_irq(ctrl, DSI_INTR_CMD_DMA_DONE_MASK);
	return rp->len;
	return rc;
}

void msm_dsi_cmdlist_tx(struct mdss_dsi_ctrl_pdata *ctrl,
@@ -780,11 +892,15 @@ void msm_dsi_cmdlist_commit(struct mdss_dsi_ctrl_pdata *ctrl, int from_mdp)
		return;
	}

	dsi_set_tx_power_mode(0);

	if (req->flags & CMD_REQ_RX)
		msm_dsi_cmdlist_rx(ctrl, req);
	else
		msm_dsi_cmdlist_tx(ctrl, req);

	dsi_set_tx_power_mode(1);

	mutex_unlock(&ctrl->cmd_mutex);
}