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

Commit 02e76a99 authored by Terence Hampson's avatar Terence Hampson
Browse files

msm: mdp3: implement asynchronous frame events



Asynchronous frame events from MDP allows commit thread to be unblocked
once the frame contents have been flushed to MDP HW and new frame can be
programmed while waiting for DMA transfer to complete.

Change-Id: If2ecc3cedfd92e20b2e98b0ac4f6f3608ce78579
Signed-off-by: default avatarTerence Hampson <thampson@codeaurora.org>
parent ec25030c
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@
#include "mdss_fb.h"

#define MDP_VSYNC_CLK_RATE	19200000
#define KOFF_TIMEOUT msecs_to_jiffies(84)

enum  {
	MDP3_CLK_AHB,
+89 −8
Original line number Diff line number Diff line
@@ -91,6 +91,36 @@ static int mdp3_bufq_count(struct mdp3_buffer_queue *bufq)
	return bufq->count;
}

void mdp3_ctrl_notifier_register(struct mdp3_session_data *ses,
	struct notifier_block *notifier)
{
	blocking_notifier_chain_register(&ses->notifier_head, notifier);
}

void mdp3_ctrl_notifier_unregister(struct mdp3_session_data *ses,
	struct notifier_block *notifier)
{
	blocking_notifier_chain_unregister(&ses->notifier_head, notifier);
}

int mdp3_ctrl_notify(struct mdp3_session_data *ses, int event)
{
	return blocking_notifier_call_chain(&ses->notifier_head, event, ses);
}

static void mdp3_dispatch_dma_done(struct work_struct *work)
{
	struct mdp3_session_data *session;

	pr_debug("%s\n", __func__);
	session = container_of(work, struct mdp3_session_data,
				dma_done_work);
	if (!session)
		return;

	mdp3_ctrl_notify(session, MDP_NOTIFY_FRAME_DONE);
}

static void mdp3_dispatch_clk_off(struct work_struct *work)
{
	struct mdp3_session_data *session;
@@ -121,6 +151,12 @@ void vsync_notify_handler(void *arg)
	sysfs_notify_dirent(session->vsync_event_sd);
}

void dma_done_notify_handler(void *arg)
{
	struct mdp3_session_data *session = (struct mdp3_session_data *)arg;
	schedule_work(&session->dma_done_work);
}

void vsync_count_down(void *arg)
{
	struct mdp3_session_data *session = (struct mdp3_session_data *)arg;
@@ -140,8 +176,8 @@ void mdp3_ctrl_reset_countdown(struct mdp3_session_data *session,
static int mdp3_ctrl_vsync_enable(struct msm_fb_data_type *mfd, int enable)
{
	struct mdp3_session_data *mdp3_session;
	struct mdp3_vsync_notification vsync_client;
	struct mdp3_vsync_notification *arg = NULL;
	struct mdp3_notification vsync_client;
	struct mdp3_notification *arg = NULL;

	pr_debug("mdp3_ctrl_vsync_enable =%d\n", enable);
	mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1;
@@ -464,6 +500,7 @@ static int mdp3_ctrl_dma_init(struct msm_fb_data_type *mfd,
	int frame_rate = mfd->panel_info->mipi.frame_rate;
	int vbp, vfp, vspw;
	int vtotal, vporch;
	struct mdp3_notification dma_done_callback;

	vbp = panel_info->lcdc.v_back_porch;
	vfp = panel_info->lcdc.v_front_porch;
@@ -499,6 +536,13 @@ static int mdp3_ctrl_dma_init(struct msm_fb_data_type *mfd,
		rc = dma->dma_config(dma, &sourceConfig, &outputConfig);
	else
		rc = -EINVAL;

	if (outputConfig.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) {
		dma_done_callback.handler = dma_done_notify_handler;
		dma_done_callback.arg = mfd->mdp.private1;
		dma->dma_done_notifier(dma, &dma_done_callback);
	}

	return rc;
}

@@ -527,6 +571,8 @@ static int mdp3_ctrl_on(struct msm_fb_data_type *mfd)
	}

	mdp3_batfet_ctrl(true);
	mdp3_ctrl_notifier_register(mdp3_session,
		&mdp3_session->mfd->mdp_sync_pt_data.notifier);

	rc = mdp3_iommu_enable(MDP3_CLIENT_DMA_P);
	if (rc) {
@@ -658,6 +704,8 @@ static int mdp3_ctrl_off(struct msm_fb_data_type *mfd)
	if (rc)
		pr_err("fail to dettach MDP DMA SMMU\n");

	mdp3_ctrl_notifier_unregister(mdp3_session,
		&mdp3_session->mfd->mdp_sync_pt_data.notifier);
	mdp3_batfet_ctrl(false);
	mdp3_session->vsync_enabled = 0;
	atomic_set(&mdp3_session->vsync_countdown, 0);
@@ -677,7 +725,7 @@ static int mdp3_ctrl_reset_cmd(struct msm_fb_data_type *mfd)
	struct mdp3_session_data *mdp3_session;
	struct mdp3_dma *mdp3_dma;
	struct mdss_panel_data *panel;
	struct mdp3_vsync_notification vsync_client;
	struct mdp3_notification vsync_client;

	pr_debug("mdp3_ctrl_reset_cmd\n");
	mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1;
@@ -728,7 +776,7 @@ static int mdp3_ctrl_reset(struct msm_fb_data_type *mfd)
	struct mdp3_session_data *mdp3_session;
	struct mdp3_dma *mdp3_dma;
	struct mdss_panel_data *panel;
	struct mdp3_vsync_notification vsync_client;
	struct mdp3_notification vsync_client;

	pr_debug("mdp3_ctrl_reset\n");
	mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1;
@@ -971,13 +1019,27 @@ static int mdp3_ctrl_display_commit_kickoff(struct msm_fb_data_type *mfd,
		return -EPERM;
	}

	mdp3_ctrl_notify(mdp3_session, MDP_NOTIFY_FRAME_BEGIN);
	data = mdp3_bufq_pop(&mdp3_session->bufq_in);
	if (data) {
		mdp3_ctrl_reset_countdown(mdp3_session, mfd);
		mdp3_ctrl_clk_enable(mfd, 1);
		mdp3_session->dma->update(mdp3_session->dma,
		rc = mdp3_session->dma->update(mdp3_session->dma,
			(void *)(int)data->addr,
			mdp3_session->intf);
		/* This is for the previous frame */
		if (rc < 0) {
			mdp3_ctrl_notify(mdp3_session,
				MDP_NOTIFY_FRAME_TIMEOUT);
		} else {
			if (mdp3_ctrl_get_intf_type(mfd) ==
						MDP3_DMA_OUTPUT_SEL_DSI_VIDEO) {
				mdp3_ctrl_notify(mdp3_session,
					MDP_NOTIFY_FRAME_DONE);
			}
		}

		mdp3_ctrl_notify(mdp3_session, MDP_NOTIFY_FRAME_FLUSHED);
		mdp3_bufq_push(&mdp3_session->bufq_out, data);
	}

@@ -1001,7 +1063,7 @@ static int mdp3_ctrl_display_commit_kickoff(struct msm_fb_data_type *mfd,

	mdss_fb_update_notify_update(mfd);

	return rc;
	return 0;
}

static void mdp3_ctrl_pan_display(struct msm_fb_data_type *mfd)
@@ -1011,6 +1073,7 @@ static void mdp3_ctrl_pan_display(struct msm_fb_data_type *mfd)
	u32 offset;
	int bpp;
	struct mdss_panel_info *panel_info;
	int rc;

	pr_debug("mdp3_ctrl_pan_display\n");
	if (!mfd || !mfd->mdp.private1)
@@ -1049,10 +1112,23 @@ static void mdp3_ctrl_pan_display(struct msm_fb_data_type *mfd)

	if (mfd->fbi->screen_base) {
		mdp3_ctrl_reset_countdown(mdp3_session, mfd);
		mdp3_ctrl_notify(mdp3_session, MDP_NOTIFY_FRAME_BEGIN);
		mdp3_ctrl_clk_enable(mfd, 1);
		mdp3_session->dma->update(mdp3_session->dma,
		rc = mdp3_session->dma->update(mdp3_session->dma,
				(void *)(int)(mfd->iova + offset),
				mdp3_session->intf);
		/* This is for the previous frame */
		if (rc < 0) {
			mdp3_ctrl_notify(mdp3_session,
				MDP_NOTIFY_FRAME_TIMEOUT);
		} else {
			if (mdp3_ctrl_get_intf_type(mfd) ==
						MDP3_DMA_OUTPUT_SEL_DSI_VIDEO) {
				mdp3_ctrl_notify(mdp3_session,
					MDP_NOTIFY_FRAME_DONE);
			}
		}
		mdp3_ctrl_notify(mdp3_session, MDP_NOTIFY_FRAME_FLUSHED);
	} else {
		pr_debug("mdp3_ctrl_pan_display no memory, stop interface");
		mdp3_clk_enable(1, 0);
@@ -1683,6 +1759,7 @@ int mdp3_ctrl_init(struct msm_fb_data_type *mfd)
	memset(mdp3_session, 0, sizeof(struct mdp3_session_data));
	mutex_init(&mdp3_session->lock);
	INIT_WORK(&mdp3_session->clk_off_work, mdp3_dispatch_clk_off);
	INIT_WORK(&mdp3_session->dma_done_work, mdp3_dispatch_dma_done);
	atomic_set(&mdp3_session->vsync_countdown, 0);
	mutex_init(&mdp3_session->histo_lock);
	mdp3_session->dma = mdp3_get_dma_pipe(MDP3_DMA_CAP_ALL);
@@ -1718,6 +1795,7 @@ int mdp3_ctrl_init(struct msm_fb_data_type *mfd)
	mdp3_bufq_init(&mdp3_session->bufq_out);
	mdp3_session->histo_status = 0;
	mdp3_session->lut_sel = 0;
	BLOCKING_INIT_NOTIFIER_HEAD(&mdp3_session->notifier_head);

	init_timer(&mdp3_session->vsync_timer);
	mdp3_session->vsync_timer.function = mdp3_vsync_timer_func;
@@ -1746,8 +1824,11 @@ int mdp3_ctrl_init(struct msm_fb_data_type *mfd)
	kobject_uevent(&dev->kobj, KOBJ_ADD);
	pr_debug("vsync kobject_uevent(KOBJ_ADD)\n");

	if (mdp3_get_cont_spash_en())
	if (mdp3_get_cont_spash_en()) {
		mdp3_session->clk_on = 1;
		mdp3_ctrl_notifier_register(mdp3_session,
			&mdp3_session->mfd->mdp_sync_pt_data.notifier);
	}

	if (splash_mismatch) {
		pr_err("splash memory mismatch, stop splash\n");
+2 −0
Original line number Diff line number Diff line
@@ -49,6 +49,7 @@ struct mdp3_session_data {
	struct mdp3_buffer_queue bufq_in;
	struct mdp3_buffer_queue bufq_out;
	struct work_struct clk_off_work;
	struct work_struct dma_done_work;
	int histo_status;
	struct mutex histo_lock;
	int lut_sel;
@@ -56,6 +57,7 @@ struct mdp3_session_data {
	bool vsync_before_commit;
	bool first_commit;
	int clk_on;
	struct blocking_notifier_head notifier_head;

	int vsync_enabled;
	atomic_t vsync_countdown; /* Used to count down  */
+40 −7
Original line number Diff line number Diff line
@@ -27,7 +27,7 @@
static void mdp3_vsync_intr_handler(int type, void *arg)
{
	struct mdp3_dma *dma = (struct mdp3_dma *)arg;
	struct mdp3_vsync_notification vsync_client;
	struct mdp3_notification vsync_client;
	unsigned int wait_for_next_vs;

	pr_debug("mdp3_vsync_intr_handler\n");
@@ -49,10 +49,16 @@ static void mdp3_vsync_intr_handler(int type, void *arg)
static void mdp3_dma_done_intr_handler(int type, void *arg)
{
	struct mdp3_dma *dma = (struct mdp3_dma *)arg;
	struct mdp3_notification dma_client;

	pr_debug("mdp3_dma_done_intr_handler\n");
	spin_lock(&dma->dma_lock);
	dma_client = dma->dma_notifier_client;
	complete(&dma->dma_comp);
	spin_unlock(&dma->dma_lock);
	mdp3_irq_disable_nosync(type);
	if (dma_client.handler)
		dma_client.handler(dma_client.arg);
}

static void mdp3_hist_done_intr_handler(int type, void *arg)
@@ -195,7 +201,7 @@ static int mdp3_dma_callback_setup(struct mdp3_dma *dma)
}

static void mdp3_dma_vsync_enable(struct mdp3_dma *dma,
				struct mdp3_vsync_notification *vsync_client)
				struct mdp3_notification *vsync_client)
{
	unsigned long flag;
	int updated = 0;
@@ -226,6 +232,21 @@ static void mdp3_dma_vsync_enable(struct mdp3_dma *dma,
	}
}

static void mdp3_dma_done_notifier(struct mdp3_dma *dma,
				struct mdp3_notification *dma_client)
{
	unsigned long flag;

	spin_lock_irqsave(&dma->dma_lock, flag);
	if (dma_client) {
		dma->dma_notifier_client = *dma_client;
	} else {
		dma->dma_notifier_client.handler = NULL;
		dma->dma_notifier_client.arg = NULL;
	}
	spin_unlock_irqrestore(&dma->dma_lock, flag);
}

static void mdp3_dma_clk_auto_gating(struct mdp3_dma *dma, int enable)
{
	u32 cgc;
@@ -552,13 +573,20 @@ static int mdp3_dmap_update(struct mdp3_dma *dma, void *buf,
{
	unsigned long flag;
	int cb_type = MDP3_DMA_CALLBACK_TYPE_VSYNC;
	int rc = 0;

	pr_debug("mdp3_dmap_update\n");

	if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) {
		cb_type = MDP3_DMA_CALLBACK_TYPE_DMA_DONE;
		if (intf->active)
			wait_for_completion_killable(&dma->dma_comp);
		if (intf->active) {
			rc = wait_for_completion_timeout(&dma->dma_comp,
				KOFF_TIMEOUT);
			if (rc <= 0) {
				WARN(1, "cmd kickoff timed out (%d)\n", rc);
				rc = -1;
			}
		}
	}
	spin_lock_irqsave(&dma->dma_lock, flag);
	MDP3_REG_WRITE(MDP3_REG_DMA_P_IBUF_ADDR, (u32)buf);
@@ -581,10 +609,14 @@ static int mdp3_dmap_update(struct mdp3_dma *dma, void *buf,

	mdp3_dma_callback_enable(dma, cb_type);
	pr_debug("mdp3_dmap_update wait for vsync_comp in\n");
	if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_VIDEO)
		wait_for_completion_killable(&dma->vsync_comp);
	if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_VIDEO) {
		rc = wait_for_completion_timeout(&dma->vsync_comp,
			KOFF_TIMEOUT);
		if (rc <= 0)
			rc = -1;
	}
	pr_debug("mdp3_dmap_update wait for vsync_comp out\n");
	return 0;
	return rc;
}

static int mdp3_dmas_update(struct mdp3_dma *dma, void *buf,
@@ -878,6 +910,7 @@ int mdp3_dma_init(struct mdp3_dma *dma)
		dma->get_histo = mdp3_dmap_histo_get;
		dma->histo_op = mdp3_dmap_histo_op;
		dma->vsync_enable = mdp3_dma_vsync_enable;
		dma->dma_done_notifier = mdp3_dma_done_notifier;
		dma->start = mdp3_dma_start;
		dma->stop = mdp3_dma_stop;
		dma->config_stride = mdp3_dma_stride_config;
+8 −3
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
#ifndef MDP3_DMA_H
#define MDP3_DMA_H

#include <linux/notifier.h>
#include <linux/sched.h>

#define MDP_HISTOGRAM_BL_SCALE_MAX 1024
@@ -227,7 +228,7 @@ struct mdp3_dma_histogram_data {
	u32 extra[2];
};

struct mdp3_vsync_notification {
struct mdp3_notification {
	void (*handler)(void *arg);
	void *arg;
};
@@ -245,7 +246,8 @@ struct mdp3_dma {
	struct completion vsync_comp;
	struct completion dma_comp;
	struct completion histo_comp;
	struct mdp3_vsync_notification vsync_client;
	struct mdp3_notification vsync_client;
	struct mdp3_notification dma_notifier_client;

	struct mdp3_dma_output_config output_config;
	struct mdp3_dma_source source_config;
@@ -291,7 +293,10 @@ struct mdp3_dma {
	void (*config_stride)(struct mdp3_dma *dma, int stride);

	void (*vsync_enable)(struct mdp3_dma *dma,
			struct mdp3_vsync_notification *vsync_client);
			struct mdp3_notification *vsync_client);

	void (*dma_done_notifier)(struct mdp3_dma *dma,
			struct mdp3_notification *dma_client);
};

struct mdp3_video_intf_cfg {