diff options
| author | Aravind Venkateswaran <aravindh@codeaurora.org> | 2013-10-22 12:54:08 -0700 |
|---|---|---|
| committer | David Keitel <dkeitel@codeaurora.org> | 2016-03-23 20:22:29 -0700 |
| commit | 7201699f7e40a3fd6ab650eea9fdfb2dd7253025 (patch) | |
| tree | 9df3bdec1067bd6e71b0e1f851c4afab4ec611c3 /drivers | |
| parent | 14a869256c2e9c73ac6f0859c6c71b1a0af2eff1 (diff) | |
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: Aravind Venkateswaran <aravindh@codeaurora.org>
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_mdp.h | 14 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_mdp_ctl.c | 78 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c | 2 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_mdp_intf_video.c | 60 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_mdp_overlay.c | 309 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_mdp_pipe.c | 160 |
6 files changed, 549 insertions, 74 deletions
diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h index f168a2c32653..5bec49f3c3af 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.h +++ b/drivers/video/fbdev/msm/mdss_mdp.h @@ -329,6 +329,7 @@ struct mdss_mdp_pipe { atomic_t ref_cnt; u32 play_cnt; int pid; + bool is_handed_off; u32 flags; u32 bwc_mode; @@ -398,6 +399,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; @@ -486,9 +488,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); @@ -501,6 +505,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); @@ -555,6 +561,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); diff --git a/drivers/video/fbdev/msm/mdss_mdp_ctl.c b/drivers/video/fbdev/msm/mdss_mdp_ctl.c index 94fb1654b0ee..f47c401fd41a 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c +++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.c @@ -616,14 +616,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; } @@ -1090,9 +1090,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; - for (i = 0; i < nmixers; i++) - mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_LAYER(i), 0); + 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); @@ -1981,3 +1984,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; +} diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c index 61902dcb4e78..8ce21f8d541d 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c +++ b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c @@ -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; diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c index 57827d64d08c..6c64b43f9e00 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c +++ b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c @@ -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,43 +599,48 @@ int mdss_mdp_video_reconfigure_splash_done(struct mdss_mdp_ctl *ctl) pdata->panel_info.cont_splash_enabled = 0; - 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__); - return ret; - } + 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__); + 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)); - if (data) { - mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_LAYER(i), - MDSS_MDP_LM_BORDER_COLOR); - flush |= (0x40 << i); + /* 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)); + if (data) { + mdss_mdp_ctl_write(ctl, + MDSS_MDP_REG_CTL_LAYER(i), + MDSS_MDP_LM_BORDER_COLOR); + flush |= (0x40 << i); + } } - } - mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_FLUSH, flush); + mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_FLUSH, flush); + + off = MDSS_MDP_REG_INTF_OFFSET(ctl->intf_num); - off = MDSS_MDP_REG_INTF_OFFSET(ctl->intf_num); + if (mdss_mdp_rev >= MDSS_MDP_HW_REV_102) + mdss_v2_intf_off = 0xEC00; - if (mdss_mdp_rev >= MDSS_MDP_HW_REV_102) - mdss_v2_intf_off = 0xEC00; + MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_TIMING_ENGINE_EN - + mdss_v2_intf_off, 0); + /* wait for 1 VSYNC for the pipe to be unstaged */ + msleep(20); - MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_TIMING_ENGINE_EN - - mdss_v2_intf_off, 0); - /* 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; } diff --git a/drivers/video/fbdev/msm/mdss_mdp_overlay.c b/drivers/video/fbdev/msm/mdss_mdp_overlay.c index bcf942ad4f69..6dce3d279615 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_overlay.c +++ b/drivers/video/fbdev/msm/mdss_mdp_overlay.c @@ -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()) { - mdss_iommu_attach(mdss_res); + 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); } @@ -2295,10 +2393,66 @@ static int mdss_mdp_overlay_ioctl_handler(struct msm_fb_data_type *mfd, return ret; } +/** + * __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 = 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); + 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); + rc = PTR_ERR(ctl); + goto error; + } + ctl->vsync_handler.vsync_handler = + mdss_mdp_overlay_handle_vsync; + ctl->vsync_handler.cmd_post_flush = false; + + if (mfd->split_display && pdata->next) { + /* enable split display */ + rc = mdss_mdp_ctl_split_display_setup(ctl, pdata->next); + if (rc) { + mdss_mdp_ctl_destroy(ctl); + 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; @@ -2310,33 +2464,9 @@ static int mdss_mdp_overlay_on(struct msm_fb_data_type *mfd) return -EINVAL; if (!mdp5_data->ctl) { - struct mdss_mdp_ctl *ctl; - struct mdss_panel_data *pdata; - - pdata = dev_get_platdata(&mfd->pdev->dev); - if (!pdata) { - pr_err("no panel connected for fb%d\n", mfd->index); - return -ENODEV; - } - - 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); + ctl = __mdss_mdp_overlay_ctl_init(mfd); + if (IS_ERR_OR_NULL(ctl)) return PTR_ERR(ctl); - } - ctl->vsync_handler.vsync_handler = - mdss_mdp_overlay_handle_vsync; - ctl->vsync_handler.cmd_post_flush = false; - - if (mfd->split_display && pdata->next) { - /* enable split display */ - rc = mdss_mdp_ctl_split_display_setup(ctl, pdata->next); - if (rc) { - mdss_mdp_ctl_destroy(ctl); - return rc; - } - } mdp5_data->ctl = ctl; } @@ -2435,6 +2565,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; @@ -2517,6 +2737,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); diff --git a/drivers/video/fbdev/msm/mdss_mdp_pipe.c b/drivers/video/fbdev/msm/mdss_mdp_pipe.c index 12b5f7110528..bb2bfc4ae948 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_pipe.c +++ b/drivers/video/fbdev/msm/mdss_mdp_pipe.c @@ -29,6 +29,8 @@ static DEFINE_MUTEX(mdss_mdp_sspp_lock); static DEFINE_MUTEX(mdss_mdp_smp_lock); static int mdss_mdp_pipe_free(struct mdss_mdp_pipe *pipe); +static struct mdss_mdp_pipe *mdss_mdp_pipe_search_by_client_id( + struct mdss_data_type *mdata, int client_id); static inline void mdss_mdp_pipe_write(struct mdss_mdp_pipe *pipe, u32 reg, u32 val) @@ -310,6 +312,80 @@ int mdss_mdp_smp_setup(struct mdss_data_type *mdata, u32 cnt, u32 size) return 0; } +/** + * mdss_mdp_smp_handoff() - Handoff SMP MMBs in use by staged pipes + * @mdata: pointer to the global mdss data structure. + * + * Iterate through the list of all SMP MMBs and check to see if any + * of them are assigned to a pipe being marked as being handed-off. + * If so, update the corresponding software allocation map to reflect + * this. + * + * This function would typically be called during MDP probe for the case + * when certain pipes might be programmed in the bootloader to display + * the splash screen. + */ +int mdss_mdp_smp_handoff(struct mdss_data_type *mdata) +{ + int rc = 0; + int i, client_id, prev_id = 0; + u32 off, s, data; + struct mdss_mdp_pipe *pipe = NULL; + + /* + * figure out what SMP MMBs are allocated for each of the pipes + * that need to be handed off. + */ + for (i = 0; i < SMP_MB_CNT; i++) { + off = (i / 3) * 4; + s = (i % 3) * 8; + data = MDSS_MDP_REG_READ(MDSS_MDP_REG_SMP_ALLOC_W0 + off); + client_id = (data >> s) & 0xFF; + if (test_bit(i, mdata->mmb_alloc_map)) { + /* + * Certain pipes may have a dedicated set of + * SMP MMBs statically allocated to them. In + * such cases, we do not need to do anything + * here. + */ + pr_debug("smp mmb %d already assigned to pipe %d (client_id %d)" + , i, pipe->num, client_id); + continue; + } + + if (client_id) { + if (client_id != prev_id) { + pipe = mdss_mdp_pipe_search_by_client_id(mdata, + client_id); + prev_id = client_id; + } + + if (!pipe) { + pr_warn("Invalid client id %d for SMP MMB %d\n", + client_id, i); + continue; + } + + if (!pipe->is_handed_off) { + pr_warn("SMP MMB %d assigned to a pipe not marked for handoff (client id %d)" + , i, client_id); + continue; + } + + /* + * Assume that the source format only has + * one plane + */ + pr_debug("Assigning smp mmb %d to pipe %d (client_id %d)\n" + , i, pipe->num, client_id); + set_bit(i, pipe->smp_map[0].allocated); + set_bit(i, mdata->mmb_alloc_map); + } + } + + return rc; +} + void mdss_mdp_pipe_unmap(struct mdss_mdp_pipe *pipe) { int tmp; @@ -455,6 +531,29 @@ error: return pipe; } +static struct mdss_mdp_pipe *mdss_mdp_pipe_search_by_client_id( + struct mdss_data_type *mdata, int client_id) +{ + u32 i; + + for (i = 0; i < mdata->nrgb_pipes; i++) { + if (mdata->rgb_pipes[i].ftch_id == client_id) + return &mdata->rgb_pipes[i]; + } + + for (i = 0; i < mdata->nvig_pipes; i++) { + if (mdata->vig_pipes[i].ftch_id == client_id) + return &mdata->vig_pipes[i]; + } + + for (i = 0; i < mdata->ndma_pipes; i++) { + if (mdata->dma_pipes[i].ftch_id == client_id) + return &mdata->dma_pipes[i]; + } + + return NULL; +} + struct mdss_mdp_pipe *mdss_mdp_pipe_search(struct mdss_data_type *mdata, u32 ndx) { @@ -509,6 +608,67 @@ int mdss_mdp_pipe_destroy(struct mdss_mdp_pipe *pipe) } +/** + * mdss_mdp_pipe_handoff() - Handoff staged pipes during bootup + * @pipe: pointer to the pipe to be handed-off + * + * Populate the software structures for the pipe based on the current + * configuration of the hardware pipe by the reading the appropriate MDP + * registers. + * + * This function would typically be called during MDP probe for the case + * when certain pipes might be programmed in the bootloader to display + * the splash screen. + */ +int mdss_mdp_pipe_handoff(struct mdss_mdp_pipe *pipe) +{ + int rc = 0; + u32 src_fmt, reg = 0, bpp = 0; + + /* + * todo: for now, only reading pipe src and dest size details + * from the registers. This is needed for appropriately + * calculating perf metrics for the handed off pipes. + * We may need to parse some more details at a later date. + */ + reg = mdss_mdp_pipe_read(pipe, MDSS_MDP_REG_SSPP_SRC_SIZE); + pipe->src.h = reg >> 16; + pipe->src.w = reg & 0xFFFF; + reg = mdss_mdp_pipe_read(pipe, MDSS_MDP_REG_SSPP_OUT_SIZE); + pipe->dst.h = reg >> 16; + pipe->dst.w = reg & 0xFFFF; + + /* Assume that the source format is RGB */ + reg = mdss_mdp_pipe_read(pipe, MDSS_MDP_REG_SSPP_SRC_FORMAT); + bpp = ((reg >> 9) & 0x3) + 1; + switch (bpp) { + case 4: + src_fmt = MDP_RGBA_8888; + break; + case 3: + src_fmt = MDP_RGB_888; + break; + case 2: + src_fmt = MDP_RGB_565; + break; + default: + pr_err("Invalid bpp=%d found\n", bpp); + rc = -EINVAL; + goto error; + } + pipe->src_fmt = mdss_mdp_get_format_params(src_fmt); + + pr_debug("Pipe settings: src.h=%d src.w=%d dst.h=%d dst.w=%d bpp=%d\n" + , pipe->src.h, pipe->src.w, pipe->dst.h, pipe->dst.w, + pipe->src_fmt->bpp); + + pipe->is_handed_off = true; + atomic_inc(&pipe->ref_cnt); + +error: + return rc; +} + void mdss_mdp_crop_rect(struct mdss_mdp_img_rect *src_rect, struct mdss_mdp_img_rect *dst_rect, const struct mdss_mdp_img_rect *sci_rect) |
