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

Commit 499a7d7d authored by Aravind Venkateswaran's avatar Aravind Venkateswaran
Browse files

msm: mdss: Implement overlay handoff for continuous splash screen



In order to support a smooth and seemless transition from the splash
screen to the boot animation, MDP needs to handoff all the
hardware ctl, mixers and pipes that were staged by the bootloader.
Implement this functionality by populating the MDP software structure
to reflect the state of the hardware at probe time. This removes the
the need to turn off the MDP timing generator when transitioning to
the boot animation and reduces this delay to just one frame.

Change-Id: I607e7b8af3e477ad960497548e8617ceb9cfbdfd
CRs-Fixed: 546447
Signed-off-by: default avatarAravind Venkateswaran <aravindh@codeaurora.org>
parent 14a5d3a3
Loading
Loading
Loading
Loading
+11 −3
Original line number Diff line number Diff line
@@ -330,6 +330,7 @@ struct mdss_mdp_pipe {
	atomic_t ref_cnt;
	u32 play_cnt;
	int pid;
	bool is_handed_off;

	u32 flags;
	u32 bwc_mode;
@@ -399,6 +400,7 @@ struct mdss_overlay_private {
	int free_list_size;
	int ad_state;

	bool handoff;
	u32 splash_mem_addr;
	u32 splash_mem_size;
	u32 sd_enabled;
@@ -477,9 +479,11 @@ int mdss_mdp_overlay_kickoff(struct msm_fb_data_type *mfd,

struct mdss_mdp_ctl *mdss_mdp_ctl_init(struct mdss_panel_data *pdata,
					struct msm_fb_data_type *mfd);
int mdss_mdp_video_reconfigure_splash_done(struct mdss_mdp_ctl *ctl);
int mdss_mdp_cmd_reconfigure_splash_done(struct mdss_mdp_ctl *ctl);
int mdss_mdp_ctl_splash_finish(struct mdss_mdp_ctl *ctl);
int mdss_mdp_video_reconfigure_splash_done(struct mdss_mdp_ctl *ctl,
		bool handoff);
int mdss_mdp_cmd_reconfigure_splash_done(struct mdss_mdp_ctl *ctl,
		bool handoff);
int mdss_mdp_ctl_splash_finish(struct mdss_mdp_ctl *ctl, bool handoff);
int mdss_mdp_ctl_setup(struct mdss_mdp_ctl *ctl);
int mdss_mdp_ctl_split_display_setup(struct mdss_mdp_ctl *ctl,
		struct mdss_panel_data *pdata);
@@ -492,6 +496,8 @@ int mdss_mdp_perf_calc_pipe(struct mdss_mdp_pipe *pipe,

int mdss_mdp_scan_pipes(void);

int mdss_mdp_mixer_handoff(struct mdss_mdp_ctl *ctl, u32 num,
	struct mdss_mdp_pipe *pipe);
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);
@@ -546,6 +552,8 @@ int mdss_mdp_ad_addr_setup(struct mdss_data_type *mdata, u32 *ad_off);
int mdss_mdp_calib_mode(struct msm_fb_data_type *mfd,
				struct mdss_calib_cfg *cfg);

int mdss_mdp_pipe_handoff(struct mdss_mdp_pipe *pipe);
int mdss_mdp_smp_handoff(struct mdss_data_type *mdata);
struct mdss_mdp_pipe *mdss_mdp_pipe_alloc(struct mdss_mdp_mixer *mixer,
					  u32 type);
struct mdss_mdp_pipe *mdss_mdp_pipe_get(struct mdss_data_type *mdata, u32 ndx);
+72 −6
Original line number Diff line number Diff line
@@ -610,14 +610,14 @@ int mdss_mdp_wb_mixer_destroy(struct mdss_mdp_mixer *mixer)
	return 0;
}

int mdss_mdp_ctl_splash_finish(struct mdss_mdp_ctl *ctl)
int mdss_mdp_ctl_splash_finish(struct mdss_mdp_ctl *ctl, bool handoff)
{
	switch (ctl->panel_data->panel_info.type) {
	case MIPI_VIDEO_PANEL:
	case EDP_PANEL:
		return mdss_mdp_video_reconfigure_splash_done(ctl);
		return mdss_mdp_video_reconfigure_splash_done(ctl, handoff);
	case MIPI_CMD_PANEL:
		return mdss_mdp_cmd_reconfigure_splash_done(ctl);
		return mdss_mdp_cmd_reconfigure_splash_done(ctl, handoff);
	default:
		return 0;
	}
@@ -1084,9 +1084,12 @@ static int mdss_mdp_ctl_start_sub(struct mdss_mdp_ctl *ctl)

	pr_debug("ctl_num=%d\n", ctl->num);

	nmixers = MDSS_MDP_INTF_MAX_LAYERMIXER + MDSS_MDP_WB_MAX_LAYERMIXER;
	if (!ctl->panel_data->panel_info.cont_splash_enabled) {
		nmixers = MDSS_MDP_INTF_MAX_LAYERMIXER +
			MDSS_MDP_WB_MAX_LAYERMIXER;
		for (i = 0; i < nmixers; i++)
			mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_LAYER(i), 0);
	}

	mixer = ctl->mixer_left;
	mdss_mdp_pp_resume(ctl, mixer->num);
@@ -1976,3 +1979,66 @@ static inline int __mdss_mdp_ctl_get_mixer_off(struct mdss_mdp_mixer *mixer)
				MDSS_MDP_INTF_LAYERMIXER3);
	}
}

static int __mdss_mdp_mixer_handoff_helper(struct mdss_mdp_mixer *mixer,
	struct mdss_mdp_pipe *pipe)
{
	int rc = 0;

	if (!mixer) {
		rc = -EINVAL;
		goto error;
	}

	if (mixer->stage_pipe[MDSS_MDP_STAGE_UNUSED] != NULL) {
		pr_err("More than one pipe staged on mixer num %d\n",
			mixer->num);
		rc = -EINVAL;
		goto error;
	}

	pr_debug("Staging pipe num %d on mixer num %d\n",
		pipe->num, mixer->num);
	mixer->stage_pipe[MDSS_MDP_STAGE_UNUSED] = pipe;
	pipe->mixer = mixer;
	pipe->mixer_stage = MDSS_MDP_STAGE_UNUSED;

error:
	return rc;
}

/**
 * mdss_mdp_mixer_handoff() - Stages a given pipe on the appropriate mixer
 * @ctl:  pointer to the control structure associated with the overlay device.
 * @num:  the mixer number on which the pipe needs to be staged.
 * @pipe: pointer to the pipe to be staged.
 *
 * Function stages a given pipe on either the left mixer or the right mixer
 * for the control structre based on the mixer number. If the input mixer
 * number does not match either of the mixers then an error is returned.
 * This function is called during overlay handoff when certain pipes are
 * already staged by the bootloader.
 */
int mdss_mdp_mixer_handoff(struct mdss_mdp_ctl *ctl, u32 num,
	struct mdss_mdp_pipe *pipe)
{
	int rc = 0;
	struct mdss_mdp_mixer *mx_left = ctl->mixer_left;
	struct mdss_mdp_mixer *mx_right = ctl->mixer_right;

	/*
	 * For performance calculations, stage the handed off pipe
	 * as MDSS_MDP_STAGE_UNUSED
	 */
	if (mx_left && (mx_left->num == num)) {
		rc = __mdss_mdp_mixer_handoff_helper(mx_left, pipe);
	} else if (mx_right && (mx_right->num == num)) {
		rc = __mdss_mdp_mixer_handoff_helper(mx_right, pipe);
	} else {
		pr_err("pipe num %d staged on unallocated mixer num %d\n",
			pipe->num, num);
		rc = -EINVAL;
	}

	return rc;
}
+1 −1
Original line number Diff line number Diff line
@@ -380,7 +380,7 @@ static int mdss_mdp_cmd_remove_vsync_handler(struct mdss_mdp_ctl *ctl,
	return 0;
}

int mdss_mdp_cmd_reconfigure_splash_done(struct mdss_mdp_ctl *ctl)
int mdss_mdp_cmd_reconfigure_splash_done(struct mdss_mdp_ctl *ctl, bool handoff)
{
	struct mdss_panel_data *pdata;
	int ret = 0;
+33 −27
Original line number Diff line number Diff line
@@ -583,7 +583,8 @@ static int mdss_mdp_video_display(struct mdss_mdp_ctl *ctl, void *arg)
	return 0;
}

int mdss_mdp_video_reconfigure_splash_done(struct mdss_mdp_ctl *ctl)
int mdss_mdp_video_reconfigure_splash_done(struct mdss_mdp_ctl *ctl,
	bool handoff)
{
	struct mdss_panel_data *pdata;
	int i, ret = 0, off;
@@ -598,20 +599,23 @@ int mdss_mdp_video_reconfigure_splash_done(struct mdss_mdp_ctl *ctl)

	pdata->panel_info.cont_splash_enabled = 0;

	if (!handoff) {
		ret = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_CONT_SPLASH_BEGIN,
					      NULL);
		if (ret) {
		pr_err("%s: Failed to handle 'CONT_SPLASH_BEGIN' event\n",
					__func__);
			pr_err("%s: Failed to handle 'CONT_SPLASH_BEGIN' event\n"
				, __func__);
			return ret;
		}

		/* clear up mixer0 and mixer1 */
		flush = 0;
		for (i = 0; i < 2; i++) {
		data = mdss_mdp_ctl_read(ctl, MDSS_MDP_REG_CTL_LAYER(i));
			data = mdss_mdp_ctl_read(ctl,
				MDSS_MDP_REG_CTL_LAYER(i));
			if (data) {
			mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_LAYER(i),
				mdss_mdp_ctl_write(ctl,
					MDSS_MDP_REG_CTL_LAYER(i),
					MDSS_MDP_LM_BORDER_COLOR);
				flush |= (0x40 << i);
			}
@@ -628,13 +632,15 @@ int mdss_mdp_video_reconfigure_splash_done(struct mdss_mdp_ctl *ctl)
		/* wait for 1 VSYNC for the pipe to be unstaged */
		msleep(20);

		ret = mdss_mdp_ctl_intf_event(ctl,
			MDSS_EVENT_CONT_SPLASH_FINISH, NULL);
	}

	/* Give back the reserved memory to the system */
	memblock_free(mdp5_data->splash_mem_addr, mdp5_data->splash_mem_size);
	free_bootmem_late(mdp5_data->splash_mem_addr,
				 mdp5_data->splash_mem_size);

	ret = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_CONT_SPLASH_FINISH,
			NULL);
	mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
	return ret;
}
+272 −37
Original line number Diff line number Diff line
@@ -810,12 +810,60 @@ static void mdss_mdp_overlay_cleanup(struct msm_fb_data_type *mfd)
		mdss_mdp_pipe_destroy(pipe);
}

static void __mdss_mdp_handoff_cleanup_pipes(struct msm_fb_data_type *mfd,
	u32 type)
{
	u32 i, npipes;
	struct mdss_mdp_pipe *pipes;
	struct mdss_mdp_pipe *pipe;
	struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
	struct mdss_data_type *mdata = mfd_to_mdata(mfd);

	switch (type) {
	case MDSS_MDP_PIPE_TYPE_VIG:
		pipes = mdata->vig_pipes;
		npipes = mdata->nvig_pipes;
		break;
	case MDSS_MDP_PIPE_TYPE_RGB:
		pipes = mdata->rgb_pipes;
		npipes = mdata->nrgb_pipes;
		break;
	case MDSS_MDP_PIPE_TYPE_DMA:
		pipes = mdata->dma_pipes;
		npipes = mdata->ndma_pipes;
		break;
	default:
		return;
	}

	for (i = 0; i < npipes; i++) {
		pipe = &pipes[i];
		if (pipe->is_handed_off) {
			pr_debug("Unmapping handed off pipe %d\n", pipe->num);
			list_add(&pipe->cleanup_list,
				&mdp5_data->pipes_cleanup);
			mdss_mdp_mixer_pipe_unstage(pipe);
			pipe->is_handed_off = false;
		}
	}
}

/**
 * mdss_mdp_overlay_start() - Programs the MDP control data path to hardware
 * @mfd: Msm frame buffer structure associated with fb device.
 *
 * Program the MDP hardware with the control settings for the framebuffer
 * device. In addition to this, this function also handles the transition
 * from the the splash screen to the android boot animation when the
 * continuous splash screen feature is enabled.
 */
static int mdss_mdp_overlay_start(struct msm_fb_data_type *mfd)
{
	int rc;
	struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
	struct mdss_mdp_ctl *ctl = mdp5_data->ctl;

	if (mdp5_data->ctl->power_on) {
	if (ctl->power_on) {
		if (!mdp5_data->mdata->batfet)
			mdss_mdp_batfet_ctrl(mdp5_data->mdata, true);
		return 0;
@@ -829,24 +877,74 @@ static int mdss_mdp_overlay_start(struct msm_fb_data_type *mfd)
		return rc;
	}

	if (mfd->panel_info->cont_splash_enabled) {
		mdss_mdp_ctl_splash_finish(mdp5_data->ctl);
		mdss_mdp_footswitch_ctrl_splash(0);
	}

	/*
	 * We need to do hw init before any hw programming.
	 * Also, hw init involves programming the VBIF registers which
	 * should be done only after attaching IOMMU which in turn would call
	 * in to TZ to restore security configs on the VBIF registers.
	 * This is not needed when continuous splash screen is enabled since
	 * we would have called in to TZ to restore security configs from LK.
	 */
	if (!is_mdss_iommu_attached()) {
		if (!mfd->panel_info->cont_splash_enabled)
			mdss_iommu_attach(mdss_res);
		mdss_hw_init(mdss_res);
	}

	rc = mdss_mdp_ctl_start(mdp5_data->ctl);
	rc = mdss_mdp_ctl_start(ctl);
	if (rc == 0) {
		atomic_inc(&ov_active_panels);
	} else {
		pr_err("overlay start failed.\n");
		mdss_mdp_ctl_destroy(mdp5_data->ctl);
		mdp5_data->ctl = NULL;
		pr_err("mdp ctl start failed.\n");
		goto error;
	}

	if (mfd->panel_info->cont_splash_enabled) {
		if (mdp5_data->handoff) {
			/*
			 * Set up border-fill on the handed off pipes.
			 * This is needed to ensure that there are no memory
			 * accesses prior to attaching iommu during continuous
			 * splash screen case. However, for command mode
			 * displays, this is not necessary since the panels can
			 * refresh from their internal memory if no data is sent
			 * out on the dsi lanes.
			 */
			if (ctl && ctl->is_video_mode) {
				rc = mdss_mdp_display_commit(ctl, NULL);
				if (!IS_ERR_VALUE(rc)) {
					mdss_mdp_display_wait4comp(ctl);
				} else {
					/*
					 * Since border-fill setup failed, we
					 * need to ensure that we turn off the
					 * MDP timing generator before attaching
					 * iommu
					 */
					pr_err("failed to set BF at handoff\n");
					mdp5_data->handoff = false;
					rc = 0;
				}
			}

			/* Add all the handed off pipes to the cleanup list */
			__mdss_mdp_handoff_cleanup_pipes(mfd,
				MDSS_MDP_PIPE_TYPE_RGB);
			__mdss_mdp_handoff_cleanup_pipes(mfd,
				MDSS_MDP_PIPE_TYPE_VIG);
			__mdss_mdp_handoff_cleanup_pipes(mfd,
				MDSS_MDP_PIPE_TYPE_DMA);
		}
		mdss_mdp_ctl_splash_finish(ctl, mdp5_data->handoff);
		mdss_mdp_footswitch_ctrl_splash(0);
		if (!is_mdss_iommu_attached())
			mdss_iommu_attach(mdss_res);
	}

error:
	if (rc) {
		mdss_mdp_ctl_destroy(ctl);
		mdp5_data->ctl = NULL;
		pm_runtime_put(&mfd->pdev->dev);
	}

@@ -2289,35 +2387,39 @@ static int mdss_mdp_overlay_ioctl_handler(struct msm_fb_data_type *mfd,
	return ret;
}

static int mdss_mdp_overlay_on(struct msm_fb_data_type *mfd)
/**
 * __mdss_mdp_overlay_ctl_init - Helper function to intialize control structure
 * @mfd: msm frame buffer data structure associated with the fb device.
 *
 * Helper function that allocates and initializes the mdp control structure
 * for a frame buffer device. Whenver applicable, this function will also setup
 * the control for the split display path as well.
 *
 * Return: pointer to the newly allocated control structure.
 */
static struct mdss_mdp_ctl *__mdss_mdp_overlay_ctl_init(
	struct msm_fb_data_type *mfd)
{
	int rc;
	struct mdss_overlay_private *mdp5_data;
	if (!mfd)
		return -ENODEV;

	if (mfd->key != MFD_KEY)
		return -EINVAL;

	mdp5_data = mfd_to_mdp5_data(mfd);
	if (!mdp5_data)
		return -EINVAL;

	if (!mdp5_data->ctl) {
	int rc = 0;
	struct mdss_mdp_ctl *ctl;
	struct mdss_panel_data *pdata;

	if (!mfd)
		return ERR_PTR(-EINVAL);

	pdata = dev_get_platdata(&mfd->pdev->dev);
	if (!pdata) {
		pr_err("no panel connected for fb%d\n", mfd->index);
			return -ENODEV;
		rc = -ENODEV;
		goto error;
	}

	ctl = mdss_mdp_ctl_init(pdata, mfd);
	if (IS_ERR_OR_NULL(ctl)) {
		pr_err("Unable to initialize ctl for fb%d\n",
			mfd->index);
			return PTR_ERR(ctl);
		rc = PTR_ERR(ctl);
		goto error;
	}
	ctl->vsync_handler.vsync_handler =
					mdss_mdp_overlay_handle_vsync;
@@ -2328,9 +2430,37 @@ static int mdss_mdp_overlay_on(struct msm_fb_data_type *mfd)
		rc = mdss_mdp_ctl_split_display_setup(ctl, pdata->next);
		if (rc) {
			mdss_mdp_ctl_destroy(ctl);
				return rc;
			goto error;
		}
	}

error:
	if (rc)
		return ERR_PTR(rc);
	else
		return ctl;
}

static int mdss_mdp_overlay_on(struct msm_fb_data_type *mfd)
{
	int rc;
	struct mdss_overlay_private *mdp5_data;
	struct mdss_mdp_ctl *ctl = NULL;

	if (!mfd)
		return -ENODEV;

	if (mfd->key != MFD_KEY)
		return -EINVAL;

	mdp5_data = mfd_to_mdp5_data(mfd);
	if (!mdp5_data)
		return -EINVAL;

	if (!mdp5_data->ctl) {
		ctl = __mdss_mdp_overlay_ctl_init(mfd);
		if (IS_ERR_OR_NULL(ctl))
			return PTR_ERR(ctl);
		mdp5_data->ctl = ctl;
	}

@@ -2429,6 +2559,96 @@ int mdss_panel_register_done(struct mdss_panel_data *pdata)
	return 0;
}

/**
 * mdss_mdp_overlay_handoff() - Read MDP registers to handoff an active ctl path
 * @mfd: Msm frame buffer structure associated with the fb device.
 *
 * This function populates the MDP software structures with the current state of
 * the MDP hardware to handoff any active control path for the framebuffer
 * device. This is needed to identify any ctl, mixers and pipes being set up by
 * the bootloader to display the splash screen when the continuous splash screen
 * feature is enabled in kernel.
 */
static int mdss_mdp_overlay_handoff(struct msm_fb_data_type *mfd)
{
	int rc = 0;
	struct mdss_data_type *mdata = mfd_to_mdata(mfd);
	struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
	int i, j;
	u32 reg;
	struct mdss_mdp_pipe *pipe = NULL;
	struct mdss_mdp_ctl *ctl = NULL;

	if (!mdp5_data->ctl) {
		ctl = __mdss_mdp_overlay_ctl_init(mfd);
		if (IS_ERR_OR_NULL(ctl)) {
			rc = PTR_ERR(ctl);
			goto error;
		}
		mdp5_data->ctl = ctl;
	}

	rc = mdss_mdp_ctl_setup(ctl);
	if (rc)
		goto error;

	ctl->clk_rate = mdss_mdp_get_clk_rate(MDSS_CLK_MDP_SRC);
	pr_debug("Set the ctl clock rate to %d Hz\n", ctl->clk_rate);

	for (i = 0; i < mdata->nmixers_intf; i++) {
		reg = mdss_mdp_ctl_read(ctl, MDSS_MDP_REG_CTL_LAYER(i));
		pr_debug("for lm%d reg = 0x%09x\n", i, reg);
		for (j = MDSS_MDP_SSPP_VIG0; j < MDSS_MDP_MAX_SSPP; j++) {
			u32 cfg = j * 3;
			if ((j == MDSS_MDP_SSPP_VIG3) ||
				(j == MDSS_MDP_SSPP_RGB3)) {
				/* Add 2 to account for Cursor & Border bits */
				cfg += 2;
			}
			if (reg & (0x7 << cfg)) {
				pr_debug("Pipe %d staged\n", j);
				pipe = mdss_mdp_pipe_search(mdata, BIT(j));
				if (!pipe) {
					pr_warn("Invalid pipe %d staged\n", j);
					continue;
				}

				rc = mdss_mdp_pipe_handoff(pipe);
				if (rc) {
					pr_err("Failed to handoff pipe num %d\n"
						, pipe->num);
					goto error;
				}

				rc = mdss_mdp_mixer_handoff(ctl, i, pipe);
				if (rc) {
					pr_err("failed to handoff mixer num %d\n"
						, i);
					goto error;
				}
			}
		}
	}

	rc = mdss_mdp_smp_handoff(mdata);
	if (rc)
		pr_err("Failed to handoff smps\n");

	mdp5_data->handoff = true;

error:
	if (rc && ctl) {
		__mdss_mdp_handoff_cleanup_pipes(mfd, MDSS_MDP_PIPE_TYPE_RGB);
		__mdss_mdp_handoff_cleanup_pipes(mfd, MDSS_MDP_PIPE_TYPE_VIG);
		__mdss_mdp_handoff_cleanup_pipes(mfd, MDSS_MDP_PIPE_TYPE_DMA);
		mdss_mdp_ctl_destroy(ctl);
		mdp5_data->ctl = NULL;
		mdp5_data->handoff = false;
	}

	return rc;
}

int mdss_mdp_overlay_init(struct msm_fb_data_type *mfd)
{
	struct device *dev = mfd->fbi->dev;
@@ -2511,6 +2731,21 @@ int mdss_mdp_overlay_init(struct msm_fb_data_type *mfd)
	if (!mdp5_data->cpu_pm_hdl)
		pr_warn("%s: unable to add event timer\n", __func__);

	if (mfd->panel_info->cont_splash_enabled) {
		rc = mdss_mdp_overlay_handoff(mfd);
		if (rc) {
			/*
			 * Even though handoff failed, it is not fatal.
			 * MDP can continue, just that we would have a longer
			 * delay in transitioning from splash screen to boot
			 * animation
			 */
			pr_warn("Overlay handoff failed for fb%d. rc=%d\n",
				mfd->index, rc);
			rc = 0;
		}
	}

	return rc;
init_fail:
	kfree(mdp5_data);
Loading