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

Commit bfd73843 authored by Huaibin Yang's avatar Huaibin Yang
Browse files

msm: mdss: release mdp bandwidth voting for command mode when idle



There is no bandwidth usage for command mode panel if no new commit
comes, so mdp bandwidth request can be zero as soon as data transfer
to panel is done. This could bring power savings for use cases of
short transfer time relatively to long idle time (duty cycle) such as
partial update, and slow fps. When video mode panel is present, this
bandwidth cannot be released since the timing of this release is
unpredictable.

Change-Id: Ia67143aa421b7b8d8b1f359244dd397dab369b4a
Signed-off-by: default avatarHuaibin Yang <huaibiny@codeaurora.org>
parent b3c68106
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -177,6 +177,7 @@ struct mdss_mdp_ctl {
	int force_screen_state;
	struct mdss_mdp_perf_params cur_perf;
	struct mdss_mdp_perf_params new_perf;
	int perf_status;

	struct mdss_data_type *mdata;
	struct msm_fb_data_type *mfd;
@@ -184,6 +185,7 @@ struct mdss_mdp_ctl {
	struct mdss_mdp_mixer *mixer_right;
	struct mutex lock;
	struct mutex *shared_lock;
	spinlock_t spin_lock;

	struct mdss_panel_data *panel_data;
	struct mdss_mdp_vsync_handler vsync_handler;
@@ -557,6 +559,11 @@ int mdss_mdp_scan_pipes(void);

int mdss_mdp_mixer_handoff(struct mdss_mdp_ctl *ctl, u32 num,
	struct mdss_mdp_pipe *pipe);

void mdss_mdp_ctl_perf_taken(struct mdss_mdp_ctl *ctl);
void mdss_mdp_ctl_perf_done(struct mdss_mdp_ctl *ctl);
void mdss_mdp_ctl_perf_release_bw(struct mdss_mdp_ctl *ctl);

struct mdss_mdp_mixer *mdss_mdp_wb_mixer_alloc(int rotator);
int mdss_mdp_wb_mixer_destroy(struct mdss_mdp_mixer *mixer);
struct mdss_mdp_mixer *mdss_mdp_mixer_get(struct mdss_mdp_ctl *ctl, int mux);
+154 −21
Original line number Diff line number Diff line
@@ -570,6 +570,156 @@ static void mdss_mdp_perf_calc_ctl(struct mdss_mdp_ctl *ctl,
		 perf->bw_overlap, perf->bw_prefill, perf->prefill_bytes);
}

static bool mdss_mdp_ctl_perf_bw_released(struct mdss_mdp_ctl *ctl)
{
	unsigned long flags;
	bool released = false;

	if (!ctl || !ctl->panel_data ||
		(ctl->panel_data->panel_info.type != MIPI_CMD_PANEL))
		return released;

	spin_lock_irqsave(&ctl->spin_lock, flags);
	if (ctl->perf_status == 0) {
		released = true;
		ctl->perf_status++;
	} else if (ctl->perf_status <= 2) {
		ctl->perf_status++;
	} else {
		pr_err("pervious commit was not done\n");
	}

	pr_debug("perf_status=%d\n", ctl->perf_status);
	spin_unlock_irqrestore(&ctl->spin_lock, flags);

	return released;
}

/**
 * @mdss_mdp_ctl_perf_taken() - indicates a committed buffer is taken
 *                              by h/w
 * @ctl - pointer to ctl data structure
 *
 * A committed buffer to be displayed is taken at a vsync or reader
 * pointer interrupt by h/w. This function must be called in vsync
 * interrupt context to indicate the buf status is changed.
 */
void mdss_mdp_ctl_perf_taken(struct mdss_mdp_ctl *ctl)
{
	if (!ctl || !ctl->panel_data ||
		(ctl->panel_data->panel_info.type != MIPI_CMD_PANEL))
		return;

	spin_lock(&ctl->spin_lock);
	if (ctl->perf_status)
		ctl->perf_status++;
	pr_debug("perf_status=%d\n", ctl->perf_status);
	spin_unlock(&ctl->spin_lock);
}

/**
 * @mdss_mdp_ctl_perf_done() - indicates a committed buffer is
 *                             displayed, so resources such as
 *                             bandwidth that are associated to this
 *                             buffer can be released.
 * @ctl - pointer to a ctl
 *
 * When pingping done interrupt is trigged, mdp finishes displaying a
 * buffer which was committed by user and taken by h/w and calling
 * this function to clear those two states. This function must be
 * called in pinppong done interrupt context.
 */
void mdss_mdp_ctl_perf_done(struct mdss_mdp_ctl *ctl)
{
	if (!ctl || !ctl->panel_data ||
		(ctl->panel_data->panel_info.type != MIPI_CMD_PANEL))
		return;

	spin_lock(&ctl->spin_lock);
	if (ctl->perf_status) {
		ctl->perf_status--;
		if (ctl->perf_status)
			ctl->perf_status--;
	}
	pr_debug("perf_status=%d\n", ctl->perf_status);
	spin_unlock(&ctl->spin_lock);
}

static inline void mdss_mdp_ctl_perf_update_bus(struct mdss_mdp_ctl *ctl)
{
	u64 bw_sum_of_intfs = 0;
	u64 bus_ab_quota, bus_ib_quota;
	struct mdss_data_type *mdata;
	int i;

	if (!ctl || !ctl->mdata)
		return;

	mdata = ctl->mdata;
	for (i = 0; i < mdata->nctl; i++) {
		struct mdss_mdp_ctl *ctl;
		ctl = mdata->ctl_off + i;
		if (ctl->power_on) {
			bw_sum_of_intfs += ctl->cur_perf.bw_ctl;
			pr_debug("c=%d bw=%llu\n", ctl->num,
				ctl->cur_perf.bw_ctl);
		}
	}
	bus_ib_quota = bw_sum_of_intfs;
	bus_ab_quota = AB_FUDGE_FACTOR(bw_sum_of_intfs);
	mdss_mdp_bus_scale_set_quota(bus_ab_quota, bus_ib_quota);
	pr_debug("ab=%llu ib=%llu\n", bus_ab_quota, bus_ib_quota);
}

/**
 * @mdss_mdp_ctl_perf_release_bw() - request zero bandwidth
 * @ctl - pointer to a ctl
 *
 * Function checks a state variable for the ctl, if all pending commit
 * requests are done, meanning no more bandwidth is needed, release
 * bandwidth request.
 */
void mdss_mdp_ctl_perf_release_bw(struct mdss_mdp_ctl *ctl)
{
	unsigned long flags;
	int need_release = 0;
	struct mdss_data_type *mdata;
	int i;

	/* only do this for command panel */
	if (!ctl || !ctl->mdata || !ctl->panel_data ||
		(ctl->panel_data->panel_info.type != MIPI_CMD_PANEL))
		return;

	mutex_lock(&mdss_mdp_ctl_lock);
	mdata = ctl->mdata;
	/*
	 * If video interface present, cmd panel bandwidth cannot be
	 * released.
	 */
	for (i = 0; i < mdata->nctl; i++) {
		struct mdss_mdp_ctl *ctl = mdata->ctl_off + i;

		if (ctl->power_on && ctl->is_video_mode) {
			mutex_unlock(&mdss_mdp_ctl_lock);
			return;
		}
	}

	spin_lock_irqsave(&ctl->spin_lock, flags);
	if (!ctl->perf_status)
		need_release = 1;
	pr_debug("need release=%d\n", need_release);
	spin_unlock_irqrestore(&ctl->spin_lock, flags);

	if (need_release) {
		ctl->cur_perf.bw_ctl = 0;
		ctl->new_perf.bw_ctl = 0;
		mdss_mdp_ctl_perf_update_bus(ctl);
	}
	mutex_unlock(&mdss_mdp_ctl_lock);
}

static void mdss_mdp_ctl_perf_update(struct mdss_mdp_ctl *ctl,
		int params_changed)
{
@@ -587,9 +737,8 @@ static void mdss_mdp_ctl_perf_update(struct mdss_mdp_ctl *ctl,
	new = &ctl->new_perf;

	if (ctl->power_on) {
		if (params_changed)
		if (params_changed || mdss_mdp_ctl_perf_bw_released(ctl))
			mdss_mdp_perf_calc_ctl(ctl, new);

		/*
		 * if params have just changed delay the update until
		 * later once the hw configuration has been flushed to
@@ -617,25 +766,8 @@ static void mdss_mdp_ctl_perf_update(struct mdss_mdp_ctl *ctl,
		update_clk = 1;
	}

	if (update_bus) {
		u64 bw_sum_of_intfs = 0;
		u64 bus_ab_quota = 0, bus_ib_quota = 0;
		int i;

		for (i = 0; i < mdata->nctl; i++) {
			struct mdss_mdp_ctl *ctl;
			ctl = mdata->ctl_off + i;
			if (ctl->power_on) {
				bw_sum_of_intfs += ctl->cur_perf.bw_ctl;
				pr_debug("c=%d bw=%llu\n", ctl->num,
					 ctl->cur_perf.bw_ctl);
			}
		}
		bus_ib_quota = bw_sum_of_intfs;
		bus_ab_quota = AB_FUDGE_FACTOR(bw_sum_of_intfs);
		mdss_mdp_bus_scale_set_quota(bus_ab_quota, bus_ib_quota);
		pr_debug("ab=%llu ib=%llu\n", bus_ab_quota, bus_ib_quota);
	}
	if (update_bus)
		mdss_mdp_ctl_perf_update_bus(ctl);

	if (update_clk) {
		u32 clk_rate = 0;
@@ -672,6 +804,7 @@ static struct mdss_mdp_ctl *mdss_mdp_ctl_alloc(struct mdss_data_type *mdata,
			ctl->ref_cnt++;
			ctl->mdata = mdata;
			mutex_init(&ctl->lock);
			spin_lock_init(&ctl->spin_lock);
			BLOCKING_INIT_NOTIFIER_HEAD(&ctl->notifier_head);
			pr_debug("alloc ctl_num=%d\n", ctl->num);
			break;
+9 −2
Original line number Diff line number Diff line
/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -227,6 +227,8 @@ static void mdss_mdp_cmd_readptr_done(void *arg)
		return;
	}

	mdss_mdp_ctl_perf_taken(ctl);

	vsync_time = ktime_get();
	ctl->vsync_cnt++;

@@ -288,6 +290,8 @@ static void mdss_mdp_cmd_pingpong_done(void *arg)
		return;
	}

	mdss_mdp_ctl_perf_done(ctl);

	spin_lock(&ctx->clk_lock);
	list_for_each_entry(tmp, &ctx->vsync_handlers, list) {
		if (tmp->enabled && tmp->cmd_post_flush)
@@ -320,9 +324,12 @@ static void pingpong_done_work(struct work_struct *work)
	struct mdss_mdp_cmd_ctx *ctx =
		container_of(work, typeof(*ctx), pp_done_work);

	if (ctx->ctl)
	if (ctx->ctl) {
		while (atomic_add_unless(&ctx->pp_done_cnt, -1, 0))
			mdss_mdp_ctl_notify(ctx->ctl, MDP_NOTIFY_FRAME_DONE);

		mdss_mdp_ctl_perf_release_bw(ctx->ctl);
	}
}

static void clk_ctrl_work(struct work_struct *work)