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

Commit ae82d88c authored by Aravind Venkateswaran's avatar Aravind Venkateswaran
Browse files

msm: mdss: refactor MDSS idle power collapse logic



MDSS GDSC can be turned off, or power-collased for command mode panels
in static screen use-cases. In the current implementation, the power
collapse is triggered by a worker thread in the interface driver
whenever it enters the idle state. However, MDP driver already
registers pm_runtime callbacks for idle and suspend notifications and
also maintains a count of active overlay sessions. It is much simpler
and cleaner to trigger idle-power collapse based on that. This also
avoids unnecessary synchronization issues pertaining to the power
collapse worker thread.

Change-Id: I198e6d7685e5ef6eed7cd06a77fa76435940b805
Signed-off-by: default avatarAravind Venkateswaran <aravindh@codeaurora.org>
parent 83cb3d64
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -240,6 +240,7 @@ struct mdss_data_type {
	bool traffic_shaper_en;
	int iommu_ref_cnt;
	u32 latency_buff_per;
	atomic_t active_intf_cnt;

	u64 ab[MDSS_MAX_HW_BLK];
	u64 ib[MDSS_MAX_HW_BLK];
+69 −43
Original line number Diff line number Diff line
@@ -87,6 +87,7 @@ static DEFINE_SPINLOCK(mdp_lock);
static DEFINE_MUTEX(mdp_clk_lock);
static DEFINE_MUTEX(bus_bw_lock);
static DEFINE_MUTEX(mdp_iommu_lock);
static DEFINE_MUTEX(mdp_fs_idle_pc_lock);

static struct mdss_panel_intf pan_types[] = {
	{"dsi", MDSS_PANEL_INTF_DSI},
@@ -774,6 +775,41 @@ int mdss_iommu_ctrl(int enable)
		return mdata->iommu_ref_cnt;
}

/**
 * mdss_mdp_idle_pc_restore() - Restore MDSS settings when exiting idle pc
 *
 * MDSS GDSC can be voted off during idle-screen usecase for MIPI DSI command
 * mode displays, referred to as MDSS idle power collapse. Upon subsequent
 * frame update, MDSS GDSC needs to turned back on and hw state needs to be
 * restored.
 */
static int mdss_mdp_idle_pc_restore(void)
{
	struct mdss_data_type *mdata = mdss_mdp_get_mdata();
	int rc = 0;

	mutex_lock(&mdp_fs_idle_pc_lock);
	if (!mdata->idle_pc) {
		pr_debug("no idle pc, no need to restore\n");
		goto end;
	}

	pr_debug("called from %pS\n", __builtin_return_address(0));
	rc = mdss_iommu_ctrl(1);
	if (IS_ERR_VALUE(rc)) {
		pr_err("mdss iommu attach failed rc=%d\n", rc);
		goto end;
	}
	mdss_hw_init(mdata);
	mdss_iommu_ctrl(0);
	mdss_mdp_ctl_restore();
	mdata->idle_pc = false;

end:
	mutex_unlock(&mdp_fs_idle_pc_lock);
	return rc;
}

/**
 * mdss_bus_bandwidth_ctrl() -- place bus bandwidth request
 * @enable:	value of enable or disable
@@ -814,6 +850,7 @@ void mdss_bus_bandwidth_ctrl(int enable)
			pm_runtime_put(&mdata->pdev->dev);
		} else {
			pm_runtime_get_sync(&mdata->pdev->dev);
			mdss_mdp_idle_pc_restore();
			msm_bus_scale_client_update_request(
				mdata->bus_hdl, mdata->curr_bw_uc_idx);
		}
@@ -849,10 +886,10 @@ void mdss_mdp_clk_ctrl(int enable)
			__func__, mdp_clk_cnt, changed, enable);

	if (changed) {
		mdata->clk_ena = enable;
		if (enable)
			pm_runtime_get_sync(&mdata->pdev->dev);

		mdata->clk_ena = enable;
		mdss_mdp_clk_update(MDSS_CLK_AHB, enable);
		mdss_mdp_clk_update(MDSS_CLK_AXI, enable);
		mdss_mdp_clk_update(MDSS_CLK_MDP_CORE, enable);
@@ -1452,6 +1489,7 @@ static int mdss_mdp_probe(struct platform_device *pdev)
	mdss_res = mdata;
	mutex_init(&mdata->reg_lock);
	atomic_set(&mdata->sd_client_count, 0);
	atomic_set(&mdata->active_intf_cnt, 0);

	rc = msm_dss_ioremap_byname(pdev, &mdata->mdss_io, "mdp_phys");
	if (rc) {
@@ -2909,19 +2947,30 @@ vreg_set_voltage_fail:
	return rc;
}

/**
 * mdss_mdp_footswitch_ctrl() - Disable/enable MDSS GDSC and CX/Batfet rails
 * @mdata: MDP private data
 * @on: 1 to turn on footswitch, 0 to turn off footswitch
 *
 * When no active references to the MDP device node and it's child nodes are
 * held, MDSS GDSC can be turned off. However, any any panels are still
 * active (but likely in an idle state), the vote for the CX and the batfet
 * rails should not be released.
 */
static void mdss_mdp_footswitch_ctrl(struct mdss_data_type *mdata, int on)
{
	int ret;
	int active_cnt = 0;

	if (!mdata->fs)
		return;

	if (on) {
		pr_debug("Enable MDP FS\n");
		if (!mdata->fs_ena) {
			pr_debug("Enable MDP FS\n");
			ret = regulator_enable(mdata->fs);
			if (ret)
				pr_err("Footswitch failed to enable\n");
				pr_warn("Footswitch failed to enable\n");
			if (!mdata->idle_pc) {
				mdss_mdp_cx_ctrl(mdata, true);
				mdss_mdp_batfet_ctrl(mdata, true);
@@ -2929,52 +2978,27 @@ static void mdss_mdp_footswitch_ctrl(struct mdss_data_type *mdata, int on)
		}
		mdata->fs_ena = true;
	} else {
		pr_debug("Disable MDP FS\n");
		if (mdata->fs_ena) {
			regulator_disable(mdata->fs);
			if (!mdata->idle_pc) {
			pr_debug("Disable MDP FS\n");
			active_cnt = atomic_read(&mdata->active_intf_cnt);
			if (active_cnt != 0) {
				/*
				 * Turning off GDSC while overlays are still
				 * active.
				 */
				mdata->idle_pc = true;
				pr_debug("idle pc. active overlays=%d\n",
					active_cnt);
			} else {
				mdss_mdp_cx_ctrl(mdata, false);
				mdss_mdp_batfet_ctrl(mdata, false);
			}
			regulator_disable(mdata->fs);
		}
		mdata->fs_ena = false;
	}
}

/**
 * mdss_mdp_footswitch_ctrl_idle_pc() - MDSS GDSC control with idle power collapse
 * @on: 1 to turn on footswitch, 0 to turn off footswitch
 * @dev: framebuffer device node
 *
 * MDSS GDSC can be voted off during idle-screen usecase for MIPI DSI command
 * mode displays. Upon subsequent frame update, MDSS GDSC needs to turned back
 * on and hw state needs to be restored. It returns error if footswitch control
 * API fails.
 */
int mdss_mdp_footswitch_ctrl_idle_pc(int on, struct device *dev)
{
	struct mdss_data_type *mdata = mdss_mdp_get_mdata();
	int rc = 0;

	pr_debug("called on=%d\n", on);
	if (on) {
		pm_runtime_get_sync(dev);
		rc = mdss_iommu_ctrl(1);
		if (IS_ERR_VALUE(rc)) {
			pr_err("mdss iommu attach failed rc=%d\n", rc);
			return rc;
		}
		mdss_hw_init(mdata);
		mdss_iommu_ctrl(0);
		mdata->idle_pc = false;
	} else {
		mdata->idle_pc = true;
		pm_runtime_put_sync(dev);
	}

	return 0;
}

int mdss_mdp_secure_display_ctrl(unsigned int enable)
{
	struct sd_ctrl_req {
@@ -3089,7 +3113,8 @@ static int mdss_mdp_runtime_resume(struct device *dev)
	if (!mdata)
		return -ENODEV;

	dev_dbg(dev, "pm_runtime: resuming...\n");
	dev_dbg(dev, "pm_runtime: resuming. active overlay cnt=%d\n",
		atomic_read(&mdata->active_intf_cnt));

	/* do not resume panels when coming out of idle power collapse */
	if (!mdata->idle_pc)
@@ -3116,17 +3141,18 @@ static int mdss_mdp_runtime_suspend(struct device *dev)
	bool device_on = false;
	if (!mdata)
		return -ENODEV;
	dev_dbg(dev, "pm_runtime: suspending...\n");
	dev_dbg(dev, "pm_runtime: suspending. active overlay cnt=%d\n",
		atomic_read(&mdata->active_intf_cnt));

	if (mdata->clk_ena) {
		pr_err("MDP suspend failed\n");
		return -EBUSY;
	}

	mdss_mdp_footswitch_ctrl(mdata, false);
	/* do not suspend panels when going in to idle power collapse */
	if (!mdata->idle_pc)
		device_for_each_child(dev, &device_on, mdss_fb_suspres_panel);
	mdss_mdp_footswitch_ctrl(mdata, false);

	return 0;
}
+2 −2
Original line number Diff line number Diff line
@@ -230,6 +230,7 @@ struct mdss_mdp_ctl {
					struct mdss_mdp_vsync_handler *);
	int (*config_fps_fnc) (struct mdss_mdp_ctl *ctl,
				struct mdss_mdp_ctl *sctl, int new_fps);
	int (*restore_fnc) (struct mdss_mdp_ctl *ctl);

	struct blocking_notifier_head notifier_head;

@@ -889,7 +890,6 @@ int mdss_mdp_wb_get_format(struct msm_fb_data_type *mfd,
int mdss_mdp_pipe_program_pixel_extn(struct mdss_mdp_pipe *pipe);
int mdss_mdp_wb_set_secure(struct msm_fb_data_type *mfd, int enable);
int mdss_mdp_wb_get_secure(struct msm_fb_data_type *mfd, uint8_t *enable);
void mdss_mdp_ctl_restore(struct mdss_mdp_ctl *ctl);
int mdss_mdp_footswitch_ctrl_idle_pc(int on, struct device *dev);
void mdss_mdp_ctl_restore(void);
int  mdss_mdp_ctl_reset(struct mdss_mdp_ctl *ctl);
#endif /* MDSS_MDP_H */
+22 −9
Original line number Diff line number Diff line
@@ -2097,23 +2097,36 @@ static void mdss_mdp_ctl_restore_sub(struct mdss_mdp_ctl *ctl)

/*
 * mdss_mdp_ctl_restore() - restore mdp ctl path
 * @ctl: mdp controller.
 *
 * This function is called whenever MDP comes out of a power collapse as
 * a result of a screen update when DSI ULPS mode is enabled. It restores
 * the MDP controller's software state to the hardware registers.
 * a result of a screen update. It restores the MDP controller's software
 * state to the hardware registers.
 */
void mdss_mdp_ctl_restore(struct mdss_mdp_ctl *ctl)
void mdss_mdp_ctl_restore(void)
{
	struct mdss_mdp_ctl *ctl = NULL;
	struct mdss_mdp_ctl *sctl;
	struct mdss_data_type *mdata = mdss_mdp_get_mdata();
	u32 cnum;

	sctl = mdss_mdp_get_split_ctl(ctl);
	mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON);
	for (cnum = MDSS_MDP_CTL0; cnum < mdata->nctl; cnum++) {
		ctl = mdata->ctl_off + cnum;
		if (!ctl->power_on)
			continue;

		pr_debug("restoring ctl%d, intf_type=%d\n", cnum,
			ctl->intf_type);
		ctl->play_cnt = 0;
		sctl = mdss_mdp_get_split_ctl(ctl);
		mdss_mdp_ctl_restore_sub(ctl);
		if (sctl) {
			mdss_mdp_ctl_restore_sub(sctl);
			mdss_mdp_ctl_split_display_enable(1, ctl, sctl);
		}
		if (ctl->restore_fnc)
			ctl->restore_fnc(ctl);
	}
	mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF);
}

+14 −47
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@
 */

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

#include "mdss_mdp.h"
#include "mdss_panel.h"
@@ -45,12 +46,10 @@ struct mdss_mdp_cmd_ctx {
	spinlock_t clk_lock;
	spinlock_t koff_lock;
	struct work_struct clk_work;
	struct delayed_work pc_work;
	struct work_struct pp_done_work;
	atomic_t pp_done_cnt;
	struct mdss_panel_recovery recovery;
	struct mdss_mdp_cmd_ctx *sync_ctx; /* for partial update */
	bool idle_pc;
};

struct mdss_mdp_cmd_ctx mdss_mdp_cmd_ctx_list[MAX_SESSIONS];
@@ -217,31 +216,15 @@ static inline void mdss_mdp_cmd_clk_on(struct mdss_mdp_cmd_ctx *ctx)
						ctx->rdptr_enabled);
	if (!ctx->clk_enabled) {
		mdss_bus_bandwidth_ctrl(true);

		ctx->clk_enabled = 1;
		if (cancel_delayed_work_sync(&ctx->pc_work))
			pr_debug("deleted pending power collapse work\n");

		rc = mdss_iommu_ctrl(1);
		if (IS_ERR_VALUE(rc))
			pr_err("IOMMU attach failed\n");

		if (ctx->idle_pc) {
			mdss_mdp_footswitch_ctrl_idle_pc(1,
				&ctx->ctl->mfd->pdev->dev);
			mdss_mdp_ctl_restore(ctx->ctl);
			mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON);

			if (mdss_mdp_cmd_tearcheck_setup(ctx->ctl, true))
				pr_warn("tearcheck setup failed\n");
			ctx->idle_pc = false;
		} else {
		mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON);
		}

		mdss_mdp_ctl_intf_event
			(ctx->ctl, MDSS_EVENT_PANEL_CLK_CTRL, (void *)1);

		mdss_mdp_hist_intr_setup(&mdata->hist_intr, MDSS_IRQ_RESUME);
	}
	spin_lock_irqsave(&ctx->clk_lock, flags);
@@ -277,9 +260,6 @@ static inline void mdss_mdp_cmd_clk_off(struct mdss_mdp_cmd_ctx *ctx)
		mdss_iommu_ctrl(0);
		mdss_bus_bandwidth_ctrl(false);
		mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF);
		if ((ctx->panel_on) && (mdata->idle_pc_enabled))
			schedule_delayed_work(&ctx->pc_work,
				POWER_COLLAPSE_TIME);
	}
	mutex_unlock(&ctx->clk_mtx);
}
@@ -427,27 +407,6 @@ static void clk_ctrl_work(struct work_struct *work)
	mdss_mdp_cmd_clk_off(ctx);
}

static void __mdss_mdp_cmd_pc_work(struct work_struct *work)
{
	struct delayed_work *dw = to_delayed_work(work);
	struct mdss_mdp_cmd_ctx *ctx =
		container_of(dw, struct mdss_mdp_cmd_ctx, pc_work);

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

	if (!ctx->panel_on) {
		pr_err("Panel is off. skipping power collapse\n");
		return;
	}

	ctx->idle_pc = true;
	ctx->ctl->play_cnt = 0;
	mdss_mdp_footswitch_ctrl_idle_pc(0, &ctx->ctl->mfd->pdev->dev);
}

static int mdss_mdp_cmd_add_vsync_handler(struct mdss_mdp_ctl *ctl,
		struct mdss_mdp_vsync_handler *handle)
{
@@ -788,6 +747,17 @@ int mdss_mdp_cmd_kickoff(struct mdss_mdp_ctl *ctl, void *arg)
	return 0;
}

int mdss_mdp_cmd_restore(struct mdss_mdp_ctl *ctl)
{
	pr_debug("%s: called for ctl%d\n", __func__, ctl->num);
	mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON);
	if (mdss_mdp_cmd_tearcheck_setup(ctl, true))
		pr_warn("%s: tearcheck setup failed\n", __func__);
	mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF);

	return 0;
}

int mdss_mdp_cmd_intfs_stop(struct mdss_mdp_ctl *ctl, int session)
{
	struct mdss_mdp_cmd_ctx *ctx;
@@ -845,9 +815,6 @@ int mdss_mdp_cmd_intfs_stop(struct mdss_mdp_ctl *ctl, int session)
	if (cancel_work_sync(&ctx->clk_work))
		pr_debug("no pending clk work\n");

	if (cancel_delayed_work_sync(&ctx->pc_work))
		pr_debug("deleted pending power collapse work\n");

	mdss_mdp_ctl_intf_event(ctl,
			MDSS_EVENT_REGISTER_RECOVERY_HANDLER,
			NULL);
@@ -960,7 +927,6 @@ static int mdss_mdp_cmd_intfs_setup(struct mdss_mdp_ctl *ctl,
	spin_lock_init(&ctx->koff_lock);
	mutex_init(&ctx->clk_mtx);
	INIT_WORK(&ctx->clk_work, clk_ctrl_work);
	INIT_DELAYED_WORK(&ctx->pc_work, __mdss_mdp_cmd_pc_work);
	INIT_WORK(&ctx->pp_done_work, pingpong_done_work);
	atomic_set(&ctx->pp_done_cnt, 0);
	INIT_LIST_HEAD(&ctx->vsync_handlers);
@@ -1007,6 +973,7 @@ int mdss_mdp_cmd_start(struct mdss_mdp_ctl *ctl)
	ctl->add_vsync_handler = mdss_mdp_cmd_add_vsync_handler;
	ctl->remove_vsync_handler = mdss_mdp_cmd_remove_vsync_handler;
	ctl->read_line_cnt_fnc = mdss_mdp_cmd_line_count;
	ctl->restore_fnc = mdss_mdp_cmd_restore;
	pr_debug("%s:-\n", __func__);

	return 0;
Loading