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

Commit bfcc8574 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "msm: mdss: add support for LP2 low power state"

parents 91c7709d 399096d5
Loading
Loading
Loading
Loading
+8 −7
Original line number Diff line number Diff line
@@ -184,9 +184,9 @@ error:
	return ret;
}

static int mdss_dsi_panel_power_doze(struct mdss_panel_data *pdata, int enable)
static int mdss_dsi_panel_power_lp(struct mdss_panel_data *pdata, int enable)
{
	/* Panel power control when entering/exiting doze mode */
	/* Panel power control when entering/exiting lp mode */
	return 0;
}

@@ -223,12 +223,13 @@ static int mdss_dsi_panel_power_ctrl(struct mdss_panel_data *pdata,
		break;
	case MDSS_PANEL_POWER_ON:
		if (mdss_dsi_is_panel_on_lp(pdata))
			ret = mdss_dsi_panel_power_doze(pdata, false);
			ret = mdss_dsi_panel_power_lp(pdata, false);
		else
			ret = mdss_dsi_panel_power_on(pdata);
		break;
	case MDSS_PANEL_POWER_DOZE:
		ret = mdss_dsi_panel_power_doze(pdata, true);
	case MDSS_PANEL_POWER_LP1:
	case MDSS_PANEL_POWER_LP2:
		ret = mdss_dsi_panel_power_lp(pdata, true);
		break;
	default:
		pr_err("%s: unknown panel power state requested (%d)\n",
@@ -474,7 +475,7 @@ static int mdss_dsi_off(struct mdss_panel_data *pdata, int power_state)
		goto end;
	}

	if (power_state != MDSS_PANEL_POWER_OFF) {
	if (mdss_panel_is_power_on(power_state)) {
		pr_debug("%s: dsi_off with panel always on\n", __func__);
		goto panel_power_ctrl;
	}
@@ -747,7 +748,7 @@ static int mdss_dsi_blank(struct mdss_panel_data *pdata, int power_state)

	mdss_dsi_clk_ctrl(ctrl_pdata, DSI_ALL_CLKS, 1);

	if (power_state == MDSS_PANEL_POWER_DOZE) {
	if (mdss_panel_is_power_on_lp(power_state)) {
		pr_debug("%s: low power state requested\n", __func__);
		if (ctrl_pdata->low_power_config)
			ret = ctrl_pdata->low_power_config(pdata, true);
+129 −73
Original line number Diff line number Diff line
@@ -65,6 +65,10 @@
#endif

#define MAX_FBI_LIST 32

#define BLANK_FLAG_LP	FB_BLANK_VSYNC_SUSPEND
#define BLANK_FLAG_ULP	FB_BLANK_NORMAL

static struct fb_info *fbi_list[MAX_FBI_LIST];
static int fbi_list_index;

@@ -853,10 +857,11 @@ static int mdss_fb_suspend_sub(struct msm_fb_data_type *mfd)
		/*
		 * Ideally, display should have either been blanked by now, or
		 * should have transitioned to a low power state. If not, then
		 * as a fall back option, blank the display.
		 * as a fall back option, enter ulp state to leave the display
		 * on, but turn off all interface clocks.
		 */
		if (mdss_fb_is_power_on_interactive(mfd)) {
			ret = mdss_fb_blank_sub(FB_BLANK_POWERDOWN, mfd->fbi,
		if (mdss_fb_is_power_on(mfd)) {
			ret = mdss_fb_blank_sub(BLANK_FLAG_ULP, mfd->fbi,
					mfd->suspend.op_enable);
			if (ret) {
				pr_err("can't turn off display!\n");
@@ -892,13 +897,16 @@ static int mdss_fb_resume_sub(struct msm_fb_data_type *mfd)
	mfd->op_enable = mfd->suspend.op_enable;

	/*
	 * If the fb was explicitly blanked during suspend, then unblank it
	 * during resume.
	 * If the fb was explicitly blanked or transitioned to ulp during
	 * suspend, then undo it during resume with the appropriate unblank
	 * flag. If fb was in ulp state when entering suspend, then nothing
	 * needs to be done.
	 */
	if (mdss_panel_is_power_on(mfd->suspend.panel_power_state)) {
	if (mdss_panel_is_power_on(mfd->suspend.panel_power_state) &&
		!mdss_panel_is_power_on_ulp(mfd->suspend.panel_power_state)) {
		int unblank_flag = mdss_panel_is_power_on_interactive(
			mfd->suspend.panel_power_state) ? FB_BLANK_UNBLANK :
			FB_BLANK_VSYNC_SUSPEND;
			BLANK_FLAG_LP;

		ret = mdss_fb_blank_sub(unblank_flag, mfd->fbi, mfd->op_enable);
		if (ret)
@@ -1179,7 +1187,61 @@ static void mdss_panel_validate_debugfs_info(struct msm_fb_data_type *mfd)
	}
}

static int mdss_fb_unblank_sub(struct msm_fb_data_type *mfd)
static int mdss_fb_blank_blank(struct msm_fb_data_type *mfd,
	int req_power_state)
{
	int ret = 0;
	int cur_power_state;

	if (!mfd)
		return -EINVAL;

	if (!mdss_fb_is_power_on(mfd) || !mfd->mdp.off_fnc)
		return 0;

	cur_power_state = mfd->panel_power_state;

	pr_debug("Transitioning from %d --> %d\n", cur_power_state,
		req_power_state);

	if (cur_power_state == req_power_state) {
		pr_debug("No change in power state\n");
		return 0;
	}

	mutex_lock(&mfd->update.lock);
	mfd->update.type = NOTIFY_TYPE_SUSPEND;
	mfd->update.is_suspend = 1;
	mutex_unlock(&mfd->update.lock);
	complete(&mfd->update.comp);
	del_timer(&mfd->no_update.timer);
	mfd->no_update.value = NOTIFY_TYPE_SUSPEND;
	complete(&mfd->no_update.comp);

	mfd->op_enable = false;
	mutex_lock(&mfd->bl_lock);
	if (mdss_panel_is_power_off(req_power_state)) {
		/* Stop Display thread */
		if (mfd->disp_thread)
			mdss_fb_stop_disp_thread(mfd);
		mdss_fb_set_backlight(mfd, 0);
		mfd->bl_updated = 0;
	}
	mfd->panel_power_state = req_power_state;
	mutex_unlock(&mfd->bl_lock);

	ret = mfd->mdp.off_fnc(mfd);
	if (ret)
		mfd->panel_power_state = cur_power_state;
	else if (mdss_panel_is_power_off(req_power_state))
		mdss_fb_release_fences(mfd);
	mfd->op_enable = true;
	complete(&mfd->power_off_comp);

	return ret;
}

static int mdss_fb_blank_unblank(struct msm_fb_data_type *mfd)
{
	int ret = 0;
	int cur_power_state;
@@ -1198,16 +1260,23 @@ static int mdss_fb_unblank_sub(struct msm_fb_data_type *mfd)
	}

	cur_power_state = mfd->panel_power_state;
	if (!mdss_panel_is_power_on_interactive(cur_power_state) &&
		mfd->mdp.on_fnc) {
	pr_debug("Transitioning from %d --> %d\n", cur_power_state,
		MDSS_PANEL_POWER_ON);

	if (mdss_panel_is_power_on_interactive(cur_power_state)) {
		pr_debug("No change in power state\n");
		return 0;
	}

	if (mfd->mdp.on_fnc) {
		ret = mfd->mdp.on_fnc(mfd);
		if (ret == 0) {
			mfd->panel_power_state = MDSS_PANEL_POWER_ON;
			mfd->panel_info->panel_dead = false;
		} else if (mfd->disp_thread) {
		if (ret) {
			mdss_fb_stop_disp_thread(mfd);
			goto error;
		}

		mfd->panel_power_state = MDSS_PANEL_POWER_ON;
		mfd->panel_info->panel_dead = false;
		mutex_lock(&mfd->update.lock);
		mfd->update.type = NOTIFY_TYPE_UPDATE;
		mfd->update.is_suspend = 0;
@@ -1252,82 +1321,67 @@ static int mdss_fb_blank_sub(int blank_mode, struct fb_info *info,
	cur_power_state = mfd->panel_power_state;

	/*
	 * If doze mode is requested for video mode panels, treat
	 * the request as full unblank as there are no low power mode
	 * settings for video mode panels.
	 * Low power (lp) and ultra low pwoer (ulp) modes are currently only
	 * supported for command mode panels. For all other panel, treat lp
	 * mode as full unblank and ulp mode as full blank.
	 */
	if ((FB_BLANK_VSYNC_SUSPEND == blank_mode) &&
		(mfd->panel_info->type != MIPI_CMD_PANEL)) {
		pr_debug("Doze mode only valid for cmd mode panels\n");

		if (mdss_panel_is_power_on(cur_power_state))
	if (mfd->panel_info->type != MIPI_CMD_PANEL) {
		if (BLANK_FLAG_LP == blank_mode) {
			pr_debug("lp mode only valid for cmd mode panels\n");
			if (mdss_fb_is_power_on_interactive(mfd))
				return 0;
			else
				blank_mode = FB_BLANK_UNBLANK;
		} else if (BLANK_FLAG_ULP == blank_mode) {
			pr_debug("ulp mode valid for cmd mode panels\n");
			if (mdss_fb_is_power_off(mfd))
				return 0;
			else
				blank_mode = FB_BLANK_POWERDOWN;
		}
	}

	switch (blank_mode) {
	case FB_BLANK_UNBLANK:
		pr_debug("unblank called. cur pwr state=%d\n", cur_power_state);
		ret = mdss_fb_unblank_sub(mfd);
		ret = mdss_fb_blank_unblank(mfd);
		break;
	case BLANK_FLAG_ULP:
		req_power_state = MDSS_PANEL_POWER_LP2;
		pr_debug("ultra low power mode requested\n");
		if (mdss_fb_is_power_off(mfd)) {
			pr_debug("Unsupp transition: off --> ulp\n");
			return 0;
		}

	case FB_BLANK_VSYNC_SUSPEND:
		req_power_state = MDSS_PANEL_POWER_DOZE;
		pr_debug("Doze power mode requested\n");
		ret = mdss_fb_blank_blank(mfd, req_power_state);
		break;
	case BLANK_FLAG_LP:
		req_power_state = MDSS_PANEL_POWER_LP1;
		pr_debug(" power mode requested\n");

		/*
		 * If doze mode is requested when panel is already off,
		 * then first unblank the panel before entering doze mode
		 * If low power mode is requested when panel is already off,
		 * then first unblank the panel before entering low power mode
		 */
		if (mdss_fb_is_power_off(mfd) && mfd->mdp.on_fnc) {
			pr_debug("off --> doze. switch to on first\n");
			ret = mdss_fb_unblank_sub(mfd);
			pr_debug("off --> lp. switch to on first\n");
			ret = mdss_fb_blank_unblank(mfd);
			if (ret)
				break;
		}

		/* Enter doze mode only if unblank succeeded */
		if (ret)
		ret = mdss_fb_blank_blank(mfd, req_power_state);
		break;
	case FB_BLANK_HSYNC_SUSPEND:
	case FB_BLANK_NORMAL:
	case FB_BLANK_POWERDOWN:
	default:
		pr_debug("blank powerdown called. cur mode=%d, req mode=%d\n",
			cur_power_state, req_power_state);
		if (mdss_fb_is_power_on(mfd) && mfd->mdp.off_fnc) {
			cur_power_state = mfd->panel_power_state;

			mutex_lock(&mfd->update.lock);
			mfd->update.type = NOTIFY_TYPE_SUSPEND;
			mfd->update.is_suspend = 1;
			mutex_unlock(&mfd->update.lock);
			complete(&mfd->update.comp);
			del_timer(&mfd->no_update.timer);
			mfd->no_update.value = NOTIFY_TYPE_SUSPEND;
			complete(&mfd->no_update.comp);

			mfd->op_enable = false;
			mutex_lock(&mfd->bl_lock);
			if (mdss_panel_is_power_off(req_power_state)) {
				/* Stop Display thread */
				if (mfd->disp_thread)
					mdss_fb_stop_disp_thread(mfd);
				mdss_fb_set_backlight(mfd, 0);
				mfd->bl_updated = 0;
			}
			mfd->panel_power_state = req_power_state;
			mutex_unlock(&mfd->bl_lock);

			ret = mfd->mdp.off_fnc(mfd);
			if (ret)
				mfd->panel_power_state = cur_power_state;
			else if (mdss_panel_is_power_off(req_power_state))
				mdss_fb_release_fences(mfd);
			mfd->op_enable = true;
			complete(&mfd->power_off_comp);
		}
		req_power_state = MDSS_PANEL_POWER_OFF;
		pr_debug("blank powerdown called\n");
		ret = mdss_fb_blank_blank(mfd, req_power_state);
		break;
	}

	/* Notify listeners */
	sysfs_notify(&mfd->fbi->dev->kobj, NULL, "show_blank_event");

@@ -1343,8 +1397,10 @@ static int mdss_fb_blank(int blank_mode, struct fb_info *info)
	if (mfd->op_enable == 0) {
		if (blank_mode == FB_BLANK_UNBLANK)
			mfd->suspend.panel_power_state = MDSS_PANEL_POWER_ON;
		else if (blank_mode == FB_BLANK_VSYNC_SUSPEND)
			mfd->suspend.panel_power_state = MDSS_PANEL_POWER_DOZE;
		else if (blank_mode == BLANK_FLAG_ULP)
			mfd->suspend.panel_power_state = MDSS_PANEL_POWER_LP2;
		else if (blank_mode == BLANK_FLAG_LP)
			mfd->suspend.panel_power_state = MDSS_PANEL_POWER_LP1;
		else
			mfd->suspend.panel_power_state = MDSS_PANEL_POWER_OFF;
		return 0;
+2 −1
Original line number Diff line number Diff line
@@ -2550,11 +2550,12 @@ int mdss_mdp_ctl_stop(struct mdss_mdp_ctl *ctl, int power_state)
		mdss_mdp_ctl_write(ctl, off, 0);
	}

	ctl->power_state = power_state;
	ctl->play_cnt = 0;
	mdss_mdp_ctl_perf_update(ctl, 0);

end:
	if (!ret)
		ctl->power_state = power_state;
	mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF);

	mutex_unlock(&ctl->lock);
+87 −28
Original line number Diff line number Diff line
@@ -731,6 +731,8 @@ static int mdss_mdp_cmd_panel_on(struct mdss_mdp_ctl *ctl,
		mdss_mdp_ctl_intf_event(ctl,
			MDSS_EVENT_REGISTER_RECOVERY_HANDLER,
			(void *)&ctx->intf_recovery);

		ctx->intf_stopped = 0;
	} else {
		pr_err("%s: Panel already on\n", __func__);
	}
@@ -878,7 +880,6 @@ int mdss_mdp_cmd_intfs_stop(struct mdss_mdp_ctl *ctl, int session,
		pr_err("invalid ctx session: %d\n", session);
		return -ENODEV;
	}
	ctx->ref_cnt--;

	/* intf stopped,  no more kickoff */
	ctx->intf_stopped = 1;
@@ -919,6 +920,11 @@ int mdss_mdp_cmd_intfs_stop(struct mdss_mdp_ctl *ctl, int session,
	flush_work(&ctx->pp_done_work);
	mdss_mdp_cmd_tearcheck_setup(ctl, false);

	if (mdss_panel_is_power_on(panel_power_state)) {
		pr_debug("%s: intf stopped with panel on\n", __func__);
		goto end;
	}

	mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_PING_PONG_RD_PTR,
		ctx->pp_num, NULL, NULL);
	mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_PING_PONG_COMP,
@@ -926,6 +932,7 @@ int mdss_mdp_cmd_intfs_stop(struct mdss_mdp_ctl *ctl, int session,

	memset(ctx, 0, sizeof(*ctx));

end:
	pr_debug("%s:-\n", __func__);

	return 0;
@@ -944,17 +951,6 @@ static int mdss_mdp_cmd_stop_sub(struct mdss_mdp_ctl *ctl,
		return -ENODEV;
	}

	/* if power state already updated, skip this */
	if (ctx->panel_power_state == panel_power_state)
		return 0;

	/*
	 * If the panel will be left on, then we do not need to turn off
	 * interface clocks since we may continue to get display updates.
	 */
	if (mdss_panel_is_power_on(panel_power_state))
		return 0;

	list_for_each_entry_safe(handle, tmp, &ctx->vsync_handlers, list)
		mdss_mdp_cmd_remove_vsync_handler(ctl, handle);
	MDSS_XLOG(ctl->num, atomic_read(&ctx->koff_cnt), ctx->clk_enabled,
@@ -969,19 +965,78 @@ int mdss_mdp_cmd_stop(struct mdss_mdp_ctl *ctl, int panel_power_state)
{
	struct mdss_mdp_cmd_ctx *ctx = ctl->priv_data;
	struct mdss_mdp_ctl *sctl = mdss_mdp_get_split_ctl(ctl);
	int ret;
	bool panel_off = false;
	bool turn_off_clocks = false;
	bool send_panel_events = false;
	int ret = 0;

	if (!ctx) {
		pr_err("invalid ctx\n");
		return -ENODEV;
	}

	if (ctx->panel_power_state != panel_power_state) {
	if (ctx->panel_power_state == panel_power_state)
		return 0;

	if (__mdss_mdp_cmd_is_panel_power_off(ctx)) {
		pr_debug("%s: panel already off\n", __func__);
		return 0;
	}

	if (ctx->panel_power_state == panel_power_state) {
		pr_debug("%s: no transition needed %d --> %d\n", __func__,
			ctx->panel_power_state, panel_power_state);
		return 0;
	}

	pr_debug("%s: transition from %d --> %d\n", __func__,
		ctx->panel_power_state, panel_power_state);

	if (__mdss_mdp_cmd_is_panel_power_on_interactive(ctx)) {
		if (mdss_panel_is_power_on_lp(panel_power_state)) {
			/*
			 * If we are transitioning from interactive to low
			 * power, then we need to send events to the interface
			 * so that the panel can be configured in low power
			 * mode.
			 */
			send_panel_events = true;
			if (mdss_panel_is_power_on_ulp(panel_power_state))
				turn_off_clocks = true;
		} else if (mdss_panel_is_power_off(panel_power_state)) {
			send_panel_events = true;
			turn_off_clocks = true;
			panel_off = true;
		}
	} else {
		if (mdss_panel_is_power_on_ulp(panel_power_state)) {
			/*
			 * If we are transitioning from low power to ultra low
			 * power mode, no more display updates are expected.
			 * Turn off the interface clocks.
			 */
			pr_debug("%s: turn off clocks\n", __func__);
			turn_off_clocks = true;
		} else {
			/*
			 * Transition from ultra low power to low power does
			 * not require any special handling. The clocks would
			 * get turned on when the first update comes.
			 */
			pr_debug("%s: nothing to be done.\n", __func__);
			return 0;
		}
	}

	if (!turn_off_clocks)
		goto panel_events;

	pr_debug("%s: turn off interface clocks\n", __func__);
	ret = mdss_mdp_cmd_stop_sub(ctl, panel_power_state);
	if (IS_ERR_VALUE(ret)) {
		pr_err("%s: unable to stop interface: %d\n",
				__func__, ret);
			return ret;
		goto end;
	}

	if (sctl) {
@@ -989,10 +1044,13 @@ int mdss_mdp_cmd_stop(struct mdss_mdp_ctl *ctl, int panel_power_state)
		if (IS_ERR_VALUE(ret)) {
			pr_err("%s: unable to stop slave intf: %d\n",
					__func__, ret);
				return ret;
			goto end;
		}
	}

panel_events:
	if ((ctl->num == 0) && send_panel_events) {
		pr_debug("%s: send panel events\n", __func__);
		ret = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_BLANK,
				(void *) (long int) panel_power_state);
		WARN(ret, "intf %d unblank error (%d)\n", ctl->intf_num, ret);
@@ -1004,11 +1062,12 @@ int mdss_mdp_cmd_stop(struct mdss_mdp_ctl *ctl, int panel_power_state)

	ctx->panel_power_state = panel_power_state;

	if (mdss_panel_is_power_on(panel_power_state)) {
	if (!panel_off) {
		pr_debug("%s: cmd_stop with panel always on\n", __func__);
		goto end;
	}

	pr_debug("%s: turn off panel\n", __func__);
	ctl->priv_data = NULL;
	ctl->stop_fnc = NULL;
	ctl->display_fnc = NULL;
@@ -1021,7 +1080,7 @@ end:
				ctx->rdptr_enabled, XLOG_FUNC_EXIT);
	pr_debug("%s:-\n", __func__);

	return 0;
	return ret;
}

static int mdss_mdp_cmd_intfs_setup(struct mdss_mdp_ctl *ctl,
+17 −3
Original line number Diff line number Diff line
@@ -91,7 +91,8 @@ enum {
enum {
	MDSS_PANEL_POWER_OFF = 0,
	MDSS_PANEL_POWER_ON,
	MDSS_PANEL_POWER_DOZE,
	MDSS_PANEL_POWER_LP1,
	MDSS_PANEL_POWER_LP2,
};

enum {
@@ -625,8 +626,8 @@ static inline bool mdss_panel_is_power_on(int panel_power_state)
 * @panel_power_state: enum identifying the power state to be checked
 *
 * This function returns true if the panel is in an intermediate low power
 * state where it is still on but not fully interactive. It may still accept
 * commands and display updates but would be operating in a low power mode.
 * state where it is still on but not fully interactive. It may or may not
 * accept any commands and display updates.
 */
static inline bool mdss_panel_is_power_on_lp(int panel_power_state)
{
@@ -634,6 +635,19 @@ static inline bool mdss_panel_is_power_on_lp(int panel_power_state)
		!mdss_panel_is_power_on_interactive(panel_power_state);
}

/**
 * mdss_panel_is_panel_power_on_ulp: - checks if panel is in ultra low power mode
 * @pdata: pointer to the panel struct associated to the panel
 * @panel_power_state: enum identifying the power state to be checked
 *
 * This function returns true if the panel is in a ultra low power
 * state where it is still on but cannot recieve any display updates.
 */
static inline bool mdss_panel_is_power_on_ulp(int panel_power_state)
{
	return panel_power_state == MDSS_PANEL_POWER_LP2;
}

/**
 * mdss_panel_intf_type: - checks if a given intf type is primary
 * @intf_val: panel interface type of the individual controller