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

Commit 38f22f3b authored by Kuogee Hsieh's avatar Kuogee Hsieh
Browse files

mdss: display: add command mode mdp fifo underflow recovery



At command mode, mdp underflow can not be recovered but cause
mdp to hang up. Add mdp underflow recovery mechanism  when mdp
underflow detected.

Change-Id: I26fc64005ac247372257aa126f2c8c0076925bb9
Signed-off-by: default avatarKuogee Hsieh <khsieh@codeaurora.org>
parent b16fa45c
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -714,6 +714,7 @@ static int mdss_dsi_event_handler(struct mdss_panel_data *pdata,
		mdss_dsi_clk_req(ctrl_pdata, (int)arg);
		break;
	case MDSS_EVENT_DSI_CMDLIST_KOFF:
		ctrl_pdata->recovery = (struct mdss_panel_recovery *)arg;
		mdss_dsi_cmdlist_commit(ctrl_pdata, 1);
	case MDSS_EVENT_PANEL_UPDATE_FPS:
		if (arg != NULL) {
+6 −1
Original line number Diff line number Diff line
@@ -307,6 +307,10 @@ enum {
	DSI_CTRL_MAX,
};

#define DSI_EV_PLL_UNLOCKED		0x0001
#define DSI_EV_MDP_FIFO_UNDERFLOW	0x0002
#define DSI_EV_MDP_BUSY_RELEASE		0x80000000

struct mdss_dsi_ctrl_pdata {
	int ndx;	/* panel_num */
	int (*on) (struct mdss_panel_data *pdata);
@@ -344,6 +348,7 @@ struct mdss_dsi_ctrl_pdata {
	struct dss_module_power power_data;
	u32 dsi_irq_mask;
	struct mdss_hw *dsi_hw;
	struct mdss_panel_recovery *recovery;

	struct dsi_panel_cmds on_cmds;
	struct dsi_panel_cmds off_cmds;
@@ -386,7 +391,7 @@ void mdss_dsi_cmd_mode_ctrl(int enable);
void mdp4_dsi_cmd_trigger(void);
void mdss_dsi_cmd_mdp_start(struct mdss_dsi_ctrl_pdata *ctrl);
void mdss_dsi_cmd_bta_sw_trigger(struct mdss_panel_data *pdata);
void mdss_dsi_ack_err_status(unsigned char *dsi_base);
void mdss_dsi_ack_err_status(struct mdss_dsi_ctrl_pdata *ctrl);
int mdss_dsi_clk_ctrl(struct mdss_dsi_ctrl_pdata *ctrl, int enable);
void mdss_dsi_clk_req(struct mdss_dsi_ctrl_pdata *ctrl,
				int enable);
+218 −20
Original line number Diff line number Diff line
@@ -19,11 +19,13 @@
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/iopoll.h>
#include <linux/kthread.h>

#include <mach/iommu_domains.h>

#include "mdss.h"
#include "mdss_dsi.h"
#include "mdss_panel.h"

#define VSYNC_PERIOD 17

@@ -44,6 +46,28 @@ struct mdss_hw mdss_dsi1_hw = {
	.irq_handler = mdss_dsi_isr,
};


#define DSI_EVENT_Q_MAX	4

/* event */
struct dsi_event_q {
	struct mdss_dsi_ctrl_pdata *ctrl;
	u32 todo;
};

struct mdss_dsi_event {
	int inited;
	wait_queue_head_t event_q;
	u32 event_pndx;
	u32 event_gndx;
	struct dsi_event_q todo_list[DSI_EVENT_Q_MAX];
	spinlock_t event_lock;
};

static struct mdss_dsi_event dsi_event;

static int dsi_event_thread(void *data);

void mdss_dsi_ctrl_init(struct mdss_dsi_ctrl_pdata *ctrl)
{
	if (ctrl->shared_pdata.broadcast_enable)
@@ -80,6 +104,13 @@ void mdss_dsi_ctrl_init(struct mdss_dsi_ctrl_pdata *ctrl)
	mutex_init(&ctrl->cmd_mutex);
	mdss_dsi_buf_alloc(&ctrl->tx_buf, SZ_4K);
	mdss_dsi_buf_alloc(&ctrl->rx_buf, SZ_4K);


	if (dsi_event.inited == 0) {
		kthread_run(dsi_event_thread, (void *)&dsi_event,
						"mdss_dsi_event");
		dsi_event.inited  = 1;
	}
}

void mdss_dsi_clk_req(struct mdss_dsi_ctrl_pdata *ctrl, int enable)
@@ -94,6 +125,21 @@ void mdss_dsi_clk_req(struct mdss_dsi_ctrl_pdata *ctrl, int enable)
	mdss_dsi_clk_ctrl(ctrl, enable);
}

void mdss_dsi_pll_relock(struct mdss_dsi_ctrl_pdata *ctrl)
{
	int i, cnt;

	cnt = ctrl->clk_cnt;

	/* disable dsi clk */
	for (i = 0; i < cnt; i++)
		mdss_dsi_clk_ctrl(ctrl, 0);

	/* enable dsi clk */
	for (i = 0; i < cnt; i++)
		mdss_dsi_clk_ctrl(ctrl, 1);
}

void mdss_dsi_enable_irq(struct mdss_dsi_ctrl_pdata *ctrl, u32 term)
{
	unsigned long flags;
@@ -942,6 +988,50 @@ void mdss_dsi_sw_reset(struct mdss_panel_data *pdata)
	wmb();
}

void mdss_dsi_sw_reset_restore(struct mdss_dsi_ctrl_pdata *ctrl)
{
	u32 data0, data1;

	data0 = MIPI_INP(ctrl->ctrl_base + 0x0004);
	data1 = data0;
	data1 &= ~0x01;
	MIPI_OUTP(ctrl->ctrl_base + 0x0004, data1);
	/*
	 * dsi controller need to be disabled before
	 * clocks turned on
	 */
	wmb();	/* make sure dsi contoller is disabled */

	/* turn esc, byte, dsi, pclk, sclk, hclk on */
	MIPI_OUTP(ctrl->ctrl_base + 0x11c, 0x23f); /* DSI_CLK_CTRL */
	wmb();	/* make sure clocks enabled */

	/* dsi controller can only be reset while clocks are running */
	MIPI_OUTP(ctrl->ctrl_base + 0x118, 0x01);
	wmb();	/* make sure reset happen */
	MIPI_OUTP(ctrl->ctrl_base + 0x118, 0x00);
	wmb();	/* controller out of reset */
	MIPI_OUTP(ctrl->ctrl_base + 0x0004, data0);
	wmb();	/* make sure dsi controller enabled again */
}

void mdss_dsi_err_intr_ctrl(struct mdss_dsi_ctrl_pdata *ctrl, u32 mask,
					int enable)
{
	u32 intr;

	intr = MIPI_INP(ctrl->ctrl_base + 0x0110);

	if (enable)
		intr |= mask;
	else
		intr &= ~mask;

	pr_debug("%s: intr=%x enable=%d\n", __func__, intr, enable);

	MIPI_OUTP(ctrl->ctrl_base + 0x0110, intr); /* DSI_INTL_CTRL */
}

void mdss_dsi_controller_cfg(int enable,
			     struct mdss_panel_data *pdata)
{
@@ -1072,7 +1162,7 @@ void mdss_dsi_cmd_bta_sw_trigger(struct mdss_panel_data *pdata)
				0, timeout_us))
		pr_info("%s: DSI status=%x failed\n", __func__, status);

	mdss_dsi_ack_err_status((ctrl_pdata->ctrl_base));
	mdss_dsi_ack_err_status(ctrl_pdata);

	pr_debug("%s: BTA done, status = %d\n", __func__, status);
}
@@ -1756,9 +1846,89 @@ int mdss_dsi_cmdlist_put(struct mdss_dsi_ctrl_pdata *ctrl,
	return ret;
}

void mdss_dsi_ack_err_status(unsigned char *base)

static void dsi_send_events(struct mdss_dsi_ctrl_pdata *ctrl, u32 events)
{
	struct dsi_event_q *evq;

	if (!dsi_event.inited)
		return;

	pr_debug("%s: ev=%x\n", __func__, events);

	spin_lock(&dsi_event.event_lock);
	evq = &dsi_event.todo_list[dsi_event.event_pndx++];
	evq->todo = events;
	evq->ctrl = ctrl;
	dsi_event.event_pndx %= DSI_EVENT_Q_MAX;
	wake_up(&dsi_event.event_q);
	spin_unlock(&dsi_event.event_lock);
}

static int dsi_event_thread(void *data)
{
	struct mdss_dsi_event *ev;
	struct dsi_event_q *evq;
	struct mdss_dsi_ctrl_pdata *ctrl;
	unsigned long flag;
	struct sched_param param;
	u32 todo = 0;
	int ret;

	param.sched_priority = 16;
	ret = sched_setscheduler_nocheck(current, SCHED_FIFO, &param);
	if (ret)
		pr_err("%s: set priority failed\n", __func__);

	ev = (struct mdss_dsi_event *)data;
	/* event */
	init_waitqueue_head(&ev->event_q);
	spin_lock_init(&ev->event_lock);

	while (1) {
		wait_event(ev->event_q, (ev->event_pndx != ev->event_gndx));
		spin_lock_irqsave(&ev->event_lock, flag);
		evq = &ev->todo_list[ev->event_gndx++];
		todo = evq->todo;
		ctrl = evq->ctrl;
		evq->todo = 0;
		ev->event_gndx %= DSI_EVENT_Q_MAX;
		spin_unlock_irqrestore(&ev->event_lock, flag);

		pr_debug("%s: ev=%x\n", __func__, todo);

		if (todo & DSI_EV_PLL_UNLOCKED)
			mdss_dsi_pll_relock(ctrl);

		if (todo & DSI_EV_MDP_FIFO_UNDERFLOW) {
			if (ctrl->recovery) {
				mdss_dsi_sw_reset_restore(ctrl);
				ctrl->recovery->fxn(ctrl->recovery->data);
			}
		}

		if (todo & DSI_EV_MDP_BUSY_RELEASE) {
			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);

			/* enable dsi error interrupt */
			mdss_dsi_err_intr_ctrl(ctrl, DSI_INTR_ERROR_MASK, 1);
		}

	}

	return 0;
}

void mdss_dsi_ack_err_status(struct mdss_dsi_ctrl_pdata *ctrl)
{
	u32 status;
	unsigned char *base;

	base = ctrl->ctrl_base;

	status = MIPI_INP(base + 0x0068);/* DSI_ACK_ERR_STATUS */

@@ -1768,20 +1938,27 @@ void mdss_dsi_ack_err_status(unsigned char *base)
	}
}

void mdss_dsi_timeout_status(unsigned char *base)
void mdss_dsi_timeout_status(struct mdss_dsi_ctrl_pdata *ctrl)
{
	u32 status;
	unsigned char *base;

	base = ctrl->ctrl_base;

	status = MIPI_INP(base + 0x00c0);/* DSI_TIMEOUT_STATUS */

	if (status & 0x0111) {
		MIPI_OUTP(base + 0x00c0, status);
		pr_err("%s: status=%x\n", __func__, status);
	}
}

void mdss_dsi_dln0_phy_err(unsigned char *base)
void mdss_dsi_dln0_phy_err(struct mdss_dsi_ctrl_pdata *ctrl)
{
	u32 status;
	unsigned char *base;

	base = ctrl->ctrl_base;

	status = MIPI_INP(base + 0x00b4);/* DSI_DLN0_PHY_ERR */

@@ -1791,44 +1968,70 @@ void mdss_dsi_dln0_phy_err(unsigned char *base)
	}
}

void mdss_dsi_fifo_status(unsigned char *base)
void mdss_dsi_fifo_status(struct mdss_dsi_ctrl_pdata *ctrl)
{
	u32 status;
	unsigned char *base;

	base = ctrl->ctrl_base;

	status = MIPI_INP(base + 0x000c);/* DSI_FIFO_STATUS */

	if (status & 0x44444489) {
	/* fifo underflow, overflow */
	if (status & 0xcccc4489) {
		MIPI_OUTP(base + 0x000c, status);
		pr_err("%s: status=%x\n", __func__, status);
		if (status & 0x0080)  /* CMD_DMA_FIFO_UNDERFLOW */
			dsi_send_events(ctrl, DSI_EV_MDP_FIFO_UNDERFLOW);
	}
}

void mdss_dsi_status(unsigned char *base)
void mdss_dsi_status(struct mdss_dsi_ctrl_pdata *ctrl)
{
	u32 status;
	unsigned char *base;

	base = ctrl->ctrl_base;

	status = MIPI_INP(base + 0x0008);/* DSI_STATUS */

	if (status & 0x80000000) {
	if (status & 0x80000000) { /* INTERLEAVE_OP_CONTENTION */
		MIPI_OUTP(base + 0x0008, status);
		pr_err("%s: status=%x\n", __func__, status);
	}
}

void mdss_dsi_error(struct mdss_dsi_ctrl_pdata *ctrl)
void mdss_dsi_clk_status(struct mdss_dsi_ctrl_pdata *ctrl)
{
	u32 status;
	unsigned char *base;

	base = ctrl->ctrl_base;
	status = MIPI_INP(base + 0x0120);/* DSI_CLK_STATUS */

	/* DSI_ERR_INT_MASK0 */
	mdss_dsi_ack_err_status(base);	/* mask0, 0x01f */
	mdss_dsi_timeout_status(base);	/* mask0, 0x0e0 */
	mdss_dsi_fifo_status(base);		/* mask0, 0x133d00 */
	mdss_dsi_status(base);		/* mask0, 0xc0100 */
	mdss_dsi_dln0_phy_err(base);	/* mask0, 0x3e00000 */
	if (status & 0x10000) { /* DSI_CLK_PLL_UNLOCKED */
		MIPI_OUTP(base + 0x0120, status);
		dsi_send_events(ctrl, DSI_EV_PLL_UNLOCKED);
		pr_err("%s: status=%x\n", __func__, status);
	}
}

void mdss_dsi_error(struct mdss_dsi_ctrl_pdata *ctrl)
{

	/* disable dsi error interrupt */
	mdss_dsi_err_intr_ctrl(ctrl, DSI_INTR_ERROR_MASK, 0);

	/* DSI_ERR_INT_MASK0 */
	mdss_dsi_clk_status(ctrl);	/* Mask0, 0x10000000 */
	mdss_dsi_fifo_status(ctrl);	/* mask0, 0x133d00 */
	mdss_dsi_ack_err_status(ctrl);	/* mask0, 0x01f */
	mdss_dsi_timeout_status(ctrl);	/* mask0, 0x0e0 */
	mdss_dsi_status(ctrl);		/* mask0, 0xc0100 */
	mdss_dsi_dln0_phy_err(ctrl);	/* mask0, 0x3e00000 */

	dsi_send_events(ctrl, DSI_EV_MDP_BUSY_RELEASE);
}

irqreturn_t mdss_dsi_isr(int irq, void *ptr)
{
@@ -1856,12 +2059,7 @@ irqreturn_t mdss_dsi_isr(int irq, void *ptr)

	if (isr & DSI_INTR_ERROR) {
		pr_err("%s: isr=%x %x", __func__, isr, (int)DSI_INTR_ERROR);
		spin_lock(&ctrl->mdp_lock);
		ctrl->mdp_busy = false;
		mdss_dsi_disable_irq_nosync(ctrl, DSI_MDP_TERM);
		complete(&ctrl->mdp_comp);
		mdss_dsi_error(ctrl);
		spin_unlock(&ctrl->mdp_lock);
	}

	if (isr & DSI_INTR_VIDEO_DONE) {
+1 −0
Original line number Diff line number Diff line
@@ -607,4 +607,5 @@ int mdss_mdp_wb_get_format(struct msm_fb_data_type *mfd,
#define mfd_to_wb(mfd) (((struct mdss_overlay_private *)\
				(mfd->mdp.private1))->wb)

int  mdss_mdp_ctl_reset(struct mdss_mdp_ctl *ctl);
#endif /* MDSS_MDP_H */
+36 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>

#include "mdss_fb.h"
#include "mdss_mdp.h"
@@ -1210,6 +1211,41 @@ int mdss_mdp_ctl_stop(struct mdss_mdp_ctl *ctl)
	return ret;
}

/*
 * mdss_mdp_ctl_reset() - reset mdp ctl path.
 * @ctl: mdp controller.
 * this function called when underflow happen,
 * it will reset mdp ctl path and poll for its completion
 *
 * Note: called within atomic context.
 */
int mdss_mdp_ctl_reset(struct mdss_mdp_ctl *ctl)
{
	u32 status = 1;
	int cnt = 20;

	mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_SW_RESET, 1);

	/*
	 * it takes around 30us to have mdp finish resetting its ctl path
	 * poll every 50us so that reset should be completed at 1st poll
	 */

	do {
		udelay(50);
		status = mdss_mdp_ctl_read(ctl, MDSS_MDP_REG_CTL_SW_RESET);
		status &= 0x01;
		pr_debug("status=%x\n", status);
		cnt--;
		if (cnt == 0) {
			pr_err("timeout\n");
			return -EAGAIN;
		}
	} while (status);

	return 0;
}

static int mdss_mdp_mixer_setup(struct mdss_mdp_ctl *ctl,
				struct mdss_mdp_mixer *mixer)
{
Loading