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

Commit 3a8db755 authored by Ingrid Gallardo's avatar Ingrid Gallardo
Browse files

msm: mdss: delay dma commands for split-dsi cmd mode panels



For split-dsi command mode panels, when burst mode
is enabled, driver needs to make sure that dsi commands
are not sent at the beginning of the frame in order
to avoid dsi fifo underflows.
Prevent this condition by making sure that commands
are delayed after the rd pointer interrupt is received.

Change-Id: I5f78efc88dc08da06e24c101ad1e8ebe693db7c7
Signed-off-by: default avatarIngrid Gallardo <ingridg@codeaurora.org>
parent 53d1dd96
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -181,6 +181,7 @@ static ssize_t panel_debug_base_reg_write(struct file *file,
	if (mdata->debug_inf.debug_enable_clock)
		mdata->debug_inf.debug_enable_clock(1);

	if (ctrl_pdata->ctrl_state & CTRL_STATE_PANEL_INIT)
		mdss_dsi_cmdlist_put(ctrl_pdata, &cmdreq);

	if (mdata->debug_inf.debug_enable_clock)
+15 −1
Original line number Diff line number Diff line
@@ -1489,7 +1489,6 @@ static int mdss_dsi_unblank(struct mdss_panel_data *pdata)
			}
			ATRACE_END("dsi_panel_on");
		}
		ctrl_pdata->ctrl_state |= CTRL_STATE_PANEL_INIT;
	}

	if ((pdata->panel_info.type == MIPI_CMD_PANEL) &&
@@ -1499,6 +1498,8 @@ static int mdss_dsi_unblank(struct mdss_panel_data *pdata)
			enable_irq(gpio_to_irq(ctrl_pdata->disp_te_gpio));
	}

	ctrl_pdata->ctrl_state |= CTRL_STATE_PANEL_INIT;

error:
	mdss_dsi_clk_ctrl(ctrl_pdata, ctrl_pdata->dsi_clk_handle,
			  MDSS_DSI_ALL_CLKS, MDSS_DSI_CLK_OFF);
@@ -2349,6 +2350,15 @@ int mdss_dsi_register_recovery_handler(struct mdss_dsi_ctrl_pdata *ctrl,
	return 0;
}

static int mdss_dsi_register_mdp_callback(struct mdss_dsi_ctrl_pdata *ctrl,
	struct mdss_intf_recovery *mdp_callback)
{
	mutex_lock(&ctrl->mutex);
	ctrl->mdp_callback = mdp_callback;
	mutex_unlock(&ctrl->mutex);
	return 0;
}

static struct device_node *mdss_dsi_get_fb_node_cb(struct platform_device *pdev)
{
	struct device_node *fb_node;
@@ -2501,6 +2511,10 @@ static int mdss_dsi_event_handler(struct mdss_panel_data *pdata,
		rc = mdss_dsi_register_recovery_handler(ctrl_pdata,
			(struct mdss_intf_recovery *)arg);
		break;
	case MDSS_EVENT_REGISTER_MDP_CALLBACK:
		rc = mdss_dsi_register_mdp_callback(ctrl_pdata,
			(struct mdss_intf_recovery *)arg);
		break;
	case MDSS_EVENT_DSI_DYNAMIC_SWITCH:
		mode = (u32)(unsigned long) arg;
		mdss_dsi_switch_mode(pdata, mode);
+1 −0
Original line number Diff line number Diff line
@@ -456,6 +456,7 @@ struct mdss_dsi_ctrl_pdata {
	u32 dsi_irq_mask;
	struct mdss_hw *dsi_hw;
	struct mdss_intf_recovery *recovery;
	struct mdss_intf_recovery *mdp_callback;

	struct dsi_panel_cmds on_cmds;
	struct dsi_panel_cmds post_dms_on_cmds;
+54 −0
Original line number Diff line number Diff line
@@ -2476,6 +2476,49 @@ int mdss_dsi_cmdlist_rx(struct mdss_dsi_ctrl_pdata *ctrl,
	return len;
}

static inline bool mdss_dsi_delay_cmd(struct mdss_dsi_ctrl_pdata *ctrl,
	bool from_mdp)
{
	unsigned long flags;
	bool mdp_busy = false;
	bool need_wait = false;

	if (!ctrl->mdp_callback)
		goto exit;

	/* delay only for split dsi, cmd mode and burst mode enabled cases */
	if (!mdss_dsi_is_hw_config_split(ctrl->shared_data) ||
	    !(ctrl->panel_mode == DSI_CMD_MODE) ||
	    !ctrl->burst_mode_enabled)
		goto exit;

	/* delay only if cmd is not from mdp and panel has been initialized */
	if (from_mdp || !(ctrl->ctrl_state & CTRL_STATE_PANEL_INIT))
		goto exit;

	/* if broadcast enabled, apply delay only if this is the ctrl trigger */
	if (mdss_dsi_sync_wait_enable(ctrl) &&
	   !mdss_dsi_sync_wait_trigger(ctrl))
		goto exit;

	spin_lock_irqsave(&ctrl->mdp_lock, flags);
	if (ctrl->mdp_busy == true)
		mdp_busy = true;
	spin_unlock_irqrestore(&ctrl->mdp_lock, flags);

	/*
	 * apply delay only if:
	 *  mdp_busy bool is set - kickoff is being scheduled by sw
	 *  MDP_BUSY bit  is not set - transfer is not on-going in hw yet
	 */
	if (mdp_busy && !(MIPI_INP(ctrl->ctrl_base + 0x008) & BIT(2)))
		need_wait = true;

exit:
	MDSS_XLOG(need_wait, from_mdp, mdp_busy);
	return need_wait;
}

int mdss_dsi_cmdlist_commit(struct mdss_dsi_ctrl_pdata *ctrl, int from_mdp)
{
	struct dcs_cmd_req *req;
@@ -2581,6 +2624,17 @@ int mdss_dsi_cmdlist_commit(struct mdss_dsi_ctrl_pdata *ctrl, int from_mdp)
	mdss_dsi_clk_ctrl(ctrl, ctrl->dsi_clk_handle, MDSS_DSI_ALL_CLKS,
			  MDSS_DSI_CLK_ON);

	/*
	 * In ping pong split cases, check if we need to apply a
	 * delay for any commands that are not coming from
	 * mdp path
	 */
	mutex_lock(&ctrl->mutex);
	if (mdss_dsi_delay_cmd(ctrl, from_mdp))
		ctrl->mdp_callback->fxn(ctrl->mdp_callback->data,
			MDP_INTF_CALLBACK_DSI_WAIT);
	mutex_unlock(&ctrl->mutex);

	if (req->flags & CMD_REQ_HS_MODE)
		mdss_dsi_set_tx_power_mode(0, &ctrl->panel_data);

+120 −0
Original line number Diff line number Diff line
@@ -13,6 +13,8 @@

#include <linux/kernel.h>
#include <linux/pm_runtime.h>
#include <linux/iopoll.h>
#include <linux/delay.h>

#include "mdss_mdp.h"
#include "mdss_panel.h"
@@ -57,6 +59,8 @@ struct mdss_mdp_cmd_ctx {

	u8 ref_cnt;
	struct completion stop_comp;
	atomic_t rdptr_cnt;
	wait_queue_head_t rdptr_waitq;
	struct completion pp_done;
	wait_queue_head_t pp_waitq;
	struct list_head vsync_handlers;
@@ -88,6 +92,7 @@ struct mdss_mdp_cmd_ctx {
	int vsync_irq_cnt;

	struct mdss_intf_recovery intf_recovery;
	struct mdss_intf_recovery intf_mdp_callback;
	struct mdss_mdp_cmd_ctx *sync_ctx; /* for partial update */
	u32 pp_timeout_report_cnt;
	bool pingpong_split_slave;
@@ -101,6 +106,7 @@ static inline void mdss_mdp_cmd_clk_off(struct mdss_mdp_cmd_ctx *ctx);
static int mdss_mdp_cmd_wait4pingpong(struct mdss_mdp_ctl *ctl, void *arg);
static int mdss_mdp_disable_autorefresh(struct mdss_mdp_ctl *ctl,
	struct mdss_mdp_ctl *sctl);
static int mdss_mdp_setup_vsync(struct mdss_mdp_cmd_ctx *ctx, bool enable);

static bool __mdss_mdp_cmd_is_aux_pp_needed(struct mdss_data_type *mdata,
	struct mdss_mdp_ctl *mctl)
@@ -958,6 +964,17 @@ static void mdss_mdp_cmd_readptr_done(void *arg)
	MDSS_XLOG(ctl->num, atomic_read(&ctx->koff_cnt));
	complete_all(&ctx->rdptr_done);

	/* If caller is waiting for the read pointer, notify. */
	if (atomic_read(&ctx->rdptr_cnt)) {
		if (atomic_add_unless(&ctx->rdptr_cnt, -1, 0)) {
			MDSS_XLOG(atomic_read(&ctx->rdptr_cnt));
			if (atomic_read(&ctx->rdptr_cnt))
				pr_warn("%s: too many rdptrs=%d!\n",
				  __func__, atomic_read(&ctx->rdptr_cnt));
		}
		wake_up_all(&ctx->rdptr_waitq);
	}

	spin_lock(&ctx->clk_lock);
	list_for_each_entry(tmp, &ctx->vsync_handlers, list) {
		if (tmp->enabled && !tmp->cmd_post_flush)
@@ -966,6 +983,82 @@ static void mdss_mdp_cmd_readptr_done(void *arg)
	spin_unlock(&ctx->clk_lock);
}

static int mdss_mdp_cmd_wait4readptr(struct mdss_mdp_cmd_ctx *ctx)
{
	int rc = 0;

	rc = wait_event_timeout(ctx->rdptr_waitq,
			atomic_read(&ctx->rdptr_cnt) == 0,
			KOFF_TIMEOUT);
	if (rc <= 0) {
		if (atomic_read(&ctx->rdptr_cnt))
			pr_err("timed out waiting for rdptr irq\n");
		else
			rc = 1;
	}
	return rc;
}

static void mdss_mdp_cmd_intf_callback(void *data, int event)
{
	struct mdss_mdp_cmd_ctx *ctx = data;
	struct mdss_mdp_pp_tear_check *te = NULL;
	u32 timeout_us = 3000, val = 0;
	struct mdss_mdp_mixer *mixer;

	if (!data) {
		pr_err("%s: invalid ctx\n", __func__);
		return;
	}

	if (!ctx->ctl)
		return;

	switch (event) {
	case MDP_INTF_CALLBACK_DSI_WAIT:
		pr_debug("%s: wait for frame cnt:%d event:%d\n",
			__func__, atomic_read(&ctx->rdptr_cnt), event);

		/*
		 * if we are going to suspended or pp split is not enabled,
		 * just return
		 */
		if (ctx->intf_stopped || !is_pingpong_split(ctx->ctl->mfd))
			return;
		atomic_inc(&ctx->rdptr_cnt);

		/* enable clks and rd_ptr interrupt */
		mdss_mdp_setup_vsync(ctx, true);

		mixer = mdss_mdp_mixer_get(ctx->ctl, MDSS_MDP_MIXER_MUX_LEFT);
		if (!mixer) {
			pr_err("%s: null mixer\n", __func__);
			return;
		}

		/* wait for read pointer */
		MDSS_XLOG(atomic_read(&ctx->rdptr_cnt));
		pr_debug("%s: wait for frame cnt:%d\n",
			__func__, atomic_read(&ctx->rdptr_cnt));
		mdss_mdp_cmd_wait4readptr(ctx);

		/* wait for 3ms to make sure we are within the frame */
		te = &ctx->ctl->panel_data->panel_info.te;
		readl_poll_timeout(mixer->pingpong_base +
			MDSS_MDP_REG_PP_INT_COUNT_VAL, val,
			(val & 0xffff) > (te->start_pos +
			te->sync_threshold_start), 10, timeout_us);

		/* disable rd_ptr interrupt */
		mdss_mdp_setup_vsync(ctx, false);

		break;
	default:
		pr_debug("%s: unhandled event=%d\n", __func__, event);
		break;
	}
}

static void mdss_mdp_cmd_intf_recovery(void *data, int event)
{
	struct mdss_mdp_cmd_ctx *ctx = data;
@@ -1756,6 +1849,11 @@ static int mdss_mdp_cmd_panel_on(struct mdss_mdp_ctl *ctl,
			(void *)&ctx->intf_recovery,
			CTL_INTF_EVENT_FLAG_DEFAULT);

		mdss_mdp_ctl_intf_event(ctl,
			MDSS_EVENT_REGISTER_MDP_CALLBACK,
			(void *)&ctx->intf_mdp_callback,
			CTL_INTF_EVENT_FLAG_DEFAULT);

		ctx->intf_stopped = 0;
		if (sctx)
			sctx->intf_stopped = 0;
@@ -2437,6 +2535,14 @@ int mdss_mdp_cmd_ctx_stop(struct mdss_mdp_ctl *ctl,
	/* intf stopped,  no more kickoff */
	ctx->intf_stopped = 1;

	/* Make sure any rd ptr for dsi callback is done before disable vsync */
	if (is_pingpong_split(ctl->mfd)) {
		pr_debug("%s will wait for rd ptr:%d\n", __func__,
			atomic_read(&ctx->rdptr_cnt));
		MDSS_XLOG(atomic_read(&ctx->rdptr_cnt));
		mdss_mdp_cmd_wait4readptr(ctx);
	}

	/*
	 * if any vsyncs are still enabled, loop until the refcount
	 * goes to zero, so the rd ptr interrupt is disabled.
@@ -2454,6 +2560,10 @@ int mdss_mdp_cmd_ctx_stop(struct mdss_mdp_ctl *ctl,
		mdss_mdp_ctl_intf_event(ctl,
			MDSS_EVENT_REGISTER_RECOVERY_HANDLER,
			NULL, CTL_INTF_EVENT_FLAG_DEFAULT);

		mdss_mdp_ctl_intf_event(ctl,
			MDSS_EVENT_REGISTER_MDP_CALLBACK,
			NULL, CTL_INTF_EVENT_FLAG_DEFAULT);
	}

	/* shut down the MDP/DSI resources if still enabled */
@@ -2604,6 +2714,12 @@ int mdss_mdp_cmd_stop(struct mdss_mdp_ctl *ctl, int panel_power_state)
				MDSS_EVENT_REGISTER_RECOVERY_HANDLER,
				(void *)&ctx->intf_recovery,
				CTL_INTF_EVENT_FLAG_DEFAULT);

			mdss_mdp_ctl_intf_event(ctl,
				MDSS_EVENT_REGISTER_MDP_CALLBACK,
				(void *)&ctx->intf_mdp_callback,
				CTL_INTF_EVENT_FLAG_DEFAULT);

			ctx->intf_stopped = 0;
			if (sctx)
				sctx->intf_stopped = 0;
@@ -2758,6 +2874,7 @@ static int mdss_mdp_cmd_ctx_setup(struct mdss_mdp_ctl *ctl,
	ctx->pingpong_split_slave = pingpong_split_slave;
	ctx->pp_timeout_report_cnt = 0;
	init_waitqueue_head(&ctx->pp_waitq);
	init_waitqueue_head(&ctx->rdptr_waitq);
	init_completion(&ctx->stop_comp);
	init_completion(&ctx->autorefresh_ppdone);
	init_completion(&ctx->rdptr_done);
@@ -2780,6 +2897,9 @@ static int mdss_mdp_cmd_ctx_setup(struct mdss_mdp_ctl *ctl,
	ctx->intf_recovery.fxn = mdss_mdp_cmd_intf_recovery;
	ctx->intf_recovery.data = ctx;

	ctx->intf_mdp_callback.fxn = mdss_mdp_cmd_intf_callback;
	ctx->intf_mdp_callback.data = ctx;

	ctx->intf_stopped = 0;

	pr_debug("%s: ctx=%p num=%d aux=%d\n", __func__, ctx,
Loading