123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620 |
- /* Copyright (c) 2013-2015, 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
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
- #define pr_fmt(fmt) "%s: " fmt, __func__
- #include <linux/errno.h>
- #include <linux/kernel.h>
- #include <linux/kthread.h>
- #include <linux/memblock.h>
- #include <linux/bootmem.h>
- #include <linux/iommu.h>
- #include <linux/fb.h>
- #include "mdss_fb.h"
- #include "mdss_mdp.h"
- #include "splash.h"
- #include "mdss_mdp_splash_logo.h"
- #define INVALID_PIPE_INDEX 0xFFFF
- #define MAX_FRAME_DONE_COUNT_WAIT 2
- static int mdss_mdp_splash_alloc_memory(struct msm_fb_data_type *mfd,
- uint32_t size)
- {
- int rc;
- struct msm_fb_splash_info *sinfo;
- unsigned long buf_size = size;
- struct mdss_data_type *mdata;
- if (!mfd || !size)
- return -EINVAL;
- mdata = mfd_to_mdata(mfd);
- sinfo = &mfd->splash_info;
- if (!mdata || !mdata->iclient || sinfo->splash_buffer)
- return -EINVAL;
- sinfo->ion_handle = ion_alloc(mdata->iclient, size, SZ_4K,
- ION_HEAP(ION_SYSTEM_HEAP_ID), 0);
- if (IS_ERR_OR_NULL(sinfo->ion_handle)) {
- pr_err("ion memory allocation failed\n");
- rc = PTR_RET(sinfo->ion_handle);
- goto end;
- }
- rc = ion_map_iommu(mdata->iclient, sinfo->ion_handle,
- mdss_get_iommu_domain(MDSS_IOMMU_DOMAIN_UNSECURE),
- 0, SZ_4K, 0, (unsigned long *)&sinfo->iova,
- (unsigned long *)&buf_size, 0, 0);
- if (rc) {
- pr_err("ion memory map failed\n");
- goto imap_err;
- }
- sinfo->splash_buffer = ion_map_kernel(mdata->iclient,
- sinfo->ion_handle);
- if (IS_ERR_OR_NULL(sinfo->splash_buffer)) {
- pr_err("ion kernel memory mapping failed\n");
- rc = IS_ERR(sinfo->splash_buffer);
- goto kmap_err;
- }
- return rc;
- kmap_err:
- ion_unmap_iommu(mdata->iclient, sinfo->ion_handle,
- mdss_get_iommu_domain(MDSS_IOMMU_DOMAIN_UNSECURE), 0);
- imap_err:
- ion_free(mdata->iclient, sinfo->ion_handle);
- end:
- return rc;
- }
- static void mdss_mdp_splash_free_memory(struct msm_fb_data_type *mfd)
- {
- struct msm_fb_splash_info *sinfo;
- struct mdss_data_type *mdata;
- if (!mfd)
- return;
- sinfo = &mfd->splash_info;
- mdata = mfd_to_mdata(mfd);
- if (!mdata || !mdata->iclient || !sinfo->ion_handle)
- return;
- ion_unmap_kernel(mdata->iclient, sinfo->ion_handle);
- ion_unmap_iommu(mdata->iclient, sinfo->ion_handle,
- mdss_get_iommu_domain(MDSS_IOMMU_DOMAIN_UNSECURE), 0);
- ion_free(mdata->iclient, sinfo->ion_handle);
- sinfo->splash_buffer = NULL;
- }
- static int mdss_mdp_splash_iommu_attach(struct msm_fb_data_type *mfd)
- {
- struct iommu_domain *domain;
- struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
- int rc, ret;
- /*
- * iommu dynamic attach for following conditions.
- * 1. it is still not attached
- * 2. MDP hardware version supports the feature
- * 3. configuration is with valid splash buffer
- */
- if (is_mdss_iommu_attached() ||
- !mfd->panel_info->cont_splash_enabled ||
- !mdss_mdp_iommu_dyn_attach_supported(mdp5_data->mdata) ||
- !mdp5_data->splash_mem_addr ||
- !mdp5_data->splash_mem_size) {
- pr_debug("dynamic attach is not supported\n");
- return -EPERM;
- }
- domain = msm_get_iommu_domain(mdss_get_iommu_domain(
- MDSS_IOMMU_DOMAIN_UNSECURE));
- if (!domain) {
- pr_debug("mdss iommu domain get failed\n");
- return -EINVAL;
- }
- rc = iommu_map(domain, mdp5_data->splash_mem_addr,
- mdp5_data->splash_mem_addr,
- mdp5_data->splash_mem_size, IOMMU_READ);
- if (rc) {
- pr_debug("iommu memory mapping failed rc=%d\n", rc);
- } else {
- ret = mdss_iommu_ctrl(1);
- if (IS_ERR_VALUE(ret)) {
- pr_err("mdss iommu attach failed\n");
- iommu_unmap(domain, mdp5_data->splash_mem_addr,
- mdp5_data->splash_mem_size);
- } else {
- mfd->splash_info.iommu_dynamic_attached = true;
- }
- }
- return rc;
- }
- static void mdss_mdp_splash_unmap_splash_mem(struct msm_fb_data_type *mfd)
- {
- struct iommu_domain *domain;
- struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
- if (mfd->splash_info.iommu_dynamic_attached) {
- domain = msm_get_iommu_domain(mdss_get_iommu_domain(
- MDSS_IOMMU_DOMAIN_UNSECURE));
- if (!domain) {
- pr_err("mdss iommu domain get failed\n");
- return;
- }
- iommu_unmap(domain, mdp5_data->splash_mem_addr,
- mdp5_data->splash_mem_size);
- mdss_iommu_ctrl(0);
- mfd->splash_info.iommu_dynamic_attached = false;
- }
- }
- void mdss_mdp_release_splash_pipe(struct msm_fb_data_type *mfd)
- {
- struct msm_fb_splash_info *sinfo;
- if (!mfd || !mfd->splash_info.splash_pipe_allocated)
- return;
- sinfo = &mfd->splash_info;
- if (sinfo->pipe_ndx[0] != INVALID_PIPE_INDEX)
- mdss_mdp_overlay_release(mfd, sinfo->pipe_ndx[0]);
- if (sinfo->pipe_ndx[1] != INVALID_PIPE_INDEX)
- mdss_mdp_overlay_release(mfd, sinfo->pipe_ndx[1]);
- sinfo->splash_pipe_allocated = false;
- }
- int mdss_mdp_splash_cleanup(struct msm_fb_data_type *mfd,
- bool use_borderfill)
- {
- struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
- struct mdss_mdp_ctl *ctl = mdp5_data->ctl;
- int rc = 0;
- if (!mfd || !mdp5_data)
- return -EINVAL;
- if (mfd->splash_info.iommu_dynamic_attached ||
- !mfd->panel_info->cont_splash_enabled)
- goto end;
- if (use_borderfill && 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 (mdp5_data->handoff && 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;
- }
- }
- }
- if (rc || mdp5_data->handoff)
- {
- /* 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);
- if (!sec_debug_is_enabled()) {
- if (mdp5_data->splash_mem_addr) {
- /* 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);
- }
- }
- mdss_mdp_footswitch_ctrl_splash(0);
- end:
- return rc;
- }
- static struct mdss_mdp_pipe *mdss_mdp_splash_get_pipe(
- struct msm_fb_data_type *mfd,
- struct mdp_overlay *req)
- {
- struct mdss_mdp_pipe *pipe;
- int ret;
- struct mdss_mdp_data *buf;
- uint32_t image_size = SPLASH_IMAGE_WIDTH * SPLASH_IMAGE_HEIGHT
- * SPLASH_IMAGE_BPP;
- ret = mdss_mdp_overlay_pipe_setup(mfd, req, &pipe);
- if (ret)
- return NULL;
- if (mdss_mdp_pipe_map(pipe)) {
- pr_err("unable to map base pipe\n");
- return NULL;
- }
- buf = &pipe->back_buf;
- buf->p[0].addr = mfd->splash_info.iova;
- buf->p[0].len = image_size;
- buf->num_planes = 1;
- mdss_mdp_pipe_unmap(pipe);
- return pipe;
- }
- static int mdss_mdp_splash_kickoff(struct msm_fb_data_type *mfd,
- struct mdss_mdp_img_rect *src_rect,
- struct mdss_mdp_img_rect *dest_rect)
- {
- struct mdss_mdp_pipe *pipe;
- struct fb_info *fbi;
- struct mdp_overlay req;
- struct mdss_overlay_private *mdp5_data;
- struct mdss_data_type *mdata;
- struct mdss_mdp_mixer *mixer;
- int ret;
- bool use_single_pipe = false;
- struct msm_fb_splash_info *sinfo;
- if (!mfd)
- return -EINVAL;
- fbi = mfd->fbi;
- mdp5_data = mfd_to_mdp5_data(mfd);
- mdata = mfd_to_mdata(mfd);
- sinfo = &mfd->splash_info;
- if (!mdp5_data || !mdp5_data->ctl)
- return -EINVAL;
- if (mutex_lock_interruptible(&mdp5_data->ov_lock))
- return -EINVAL;
- ret = mdss_mdp_overlay_start(mfd);
- if (ret) {
- pr_err("unable to start overlay %d (%d)\n", mfd->index, ret);
- goto end;
- }
- mixer = mdss_mdp_mixer_get(mdp5_data->ctl, MDSS_MDP_MIXER_MUX_LEFT);
- if (!mixer) {
- pr_err("unable to retrieve mixer\n");
- ret = -EINVAL;
- goto end;
- }
- memset(&req, 0, sizeof(struct mdp_overlay));
- /*
- * use single pipe for
- * 1. split display disabled
- * 2. splash image is only on one side of panel
- */
- use_single_pipe =
- !mfd->split_display ||
- (mfd->split_display &&
- ((dest_rect->x + dest_rect->w) < mfd->split_fb_left ||
- dest_rect->x > mfd->split_fb_left));
- req.src.width = src_rect->w;
- if (use_single_pipe)
- req.src_rect.w = src_rect->w;
- else
- req.src_rect.w = min_t(u16, mixer->width, src_rect->w >> 1);
- req.dst_rect.w = req.src_rect.w;
- req.src.height = req.dst_rect.h = req.src_rect.h =
- src_rect->h;
- req.src.format = SPLASH_IMAGE_FORMAT;
- req.id = MSMFB_NEW_REQUEST;
- req.z_order = MDSS_MDP_STAGE_0;
- req.alpha = 0xff;
- req.transp_mask = MDP_TRANSP_NOP;
- req.dst_rect.x = dest_rect->x;
- req.dst_rect.y = dest_rect->y;
- pipe = mdss_mdp_splash_get_pipe(mfd, &req);
- if (!pipe) {
- pr_err("unable to allocate base pipe\n");
- ret = -EINVAL;
- goto end;
- }
- sinfo->pipe_ndx[0] = pipe->ndx;
- if (!use_single_pipe) {
- req.id = MSMFB_NEW_REQUEST;
- req.src_rect.x = src_rect->x + min_t(u16, mixer->width,
- src_rect->w - req.src_rect.w);
- req.dst_rect.x = mixer->width;
- pipe = mdss_mdp_splash_get_pipe(mfd, &req);
- if (!pipe) {
- pr_err("unable to allocate right base pipe\n");
- mdss_mdp_overlay_release(mfd, sinfo->pipe_ndx[0]);
- ret = -EINVAL;
- goto end;
- }
- sinfo->pipe_ndx[1] = pipe->ndx;
- }
- mutex_unlock(&mdp5_data->ov_lock);
- ret = mfd->mdp.kickoff_fnc(mfd, NULL);
- if (ret) {
- pr_err("error in displaying image\n");
- mdss_mdp_overlay_release(mfd, sinfo->pipe_ndx[0] |
- sinfo->pipe_ndx[1]);
- }
- return ret;
- end:
- sinfo->pipe_ndx[0] = INVALID_PIPE_INDEX;
- sinfo->pipe_ndx[1] = INVALID_PIPE_INDEX;
- mutex_unlock(&mdp5_data->ov_lock);
- return ret;
- }
- static int mdss_mdp_display_splash_image(struct msm_fb_data_type *mfd)
- {
- int rc = 0;
- struct fb_info *fbi;
- uint32_t image_len = SPLASH_IMAGE_WIDTH * SPLASH_IMAGE_HEIGHT
- * SPLASH_IMAGE_BPP;
- struct mdss_mdp_img_rect src_rect, dest_rect;
- struct msm_fb_splash_info *sinfo;
- if (!mfd || !mfd->fbi) {
- pr_err("invalid input parameter\n");
- rc = -EINVAL;
- goto end;
- }
- fbi = mfd->fbi;
- sinfo = &mfd->splash_info;
- if (SPLASH_IMAGE_WIDTH > fbi->var.xres ||
- SPLASH_IMAGE_HEIGHT > fbi->var.yres ||
- SPLASH_IMAGE_BPP > (fbi->var.bits_per_pixel >> 3)) {
- pr_err("invalid splash parameter configuration\n");
- rc = -EINVAL;
- goto end;
- }
- sinfo->pipe_ndx[0] = INVALID_PIPE_INDEX;
- sinfo->pipe_ndx[1] = INVALID_PIPE_INDEX;
- src_rect.x = 0;
- src_rect.y = 0;
- dest_rect.w = src_rect.w = SPLASH_IMAGE_WIDTH;
- dest_rect.h = src_rect.h = SPLASH_IMAGE_HEIGHT;
- dest_rect.x = (fbi->var.xres >> 1) - (SPLASH_IMAGE_WIDTH >> 1);
- dest_rect.y = (fbi->var.yres >> 1) - (SPLASH_IMAGE_HEIGHT >> 1);
- rc = mdss_mdp_splash_alloc_memory(mfd, image_len);
- if (rc) {
- pr_err("splash buffer allocation failed\n");
- goto end;
- }
- memcpy(sinfo->splash_buffer, splash_bgr888_image, image_len);
- rc = mdss_mdp_splash_iommu_attach(mfd);
- if (rc)
- pr_debug("iommu dynamic attach failed\n");
- rc = mdss_mdp_splash_kickoff(mfd, &src_rect, &dest_rect);
- if (rc)
- pr_err("splash image display failed\n");
- else
- sinfo->splash_pipe_allocated = true;
- end:
- return rc;
- }
- static int mdss_mdp_splash_ctl_cb(struct notifier_block *self,
- unsigned long event, void *data)
- {
- struct msm_fb_splash_info *sinfo = container_of(self,
- struct msm_fb_splash_info, notifier);
- struct msm_fb_data_type *mfd;
- if (!sinfo)
- goto done;
- mfd = container_of(sinfo, struct msm_fb_data_type, splash_info);
- if (!mfd)
- goto done;
- if (event != MDP_NOTIFY_FRAME_DONE)
- goto done;
- if (!sinfo->frame_done_count) {
- mdss_mdp_splash_unmap_splash_mem(mfd);
- /* wait for 2 frame done events before releasing memory */
- } else if (sinfo->frame_done_count > MAX_FRAME_DONE_COUNT_WAIT &&
- sinfo->splash_thread) {
- complete(&sinfo->frame_done);
- sinfo->splash_thread = NULL;
- }
- /* increase frame done count after pipes are staged from other client */
- if (!sinfo->splash_pipe_allocated)
- sinfo->frame_done_count++;
- done:
- return NOTIFY_OK;
- }
- static int mdss_mdp_splash_thread(void *data)
- {
- struct msm_fb_data_type *mfd = data;
- struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
- int ret = -EINVAL;
- if (!mfd) {
- pr_err("invalid input parameter\n");
- goto end;
- }
- lock_fb_info(mfd->fbi);
- ret = fb_blank(mfd->fbi, FB_BLANK_UNBLANK);
- if (ret) {
- pr_err("can't turn on fb!\n");
- goto end;
- }
- unlock_fb_info(mfd->fbi);
- mutex_lock(&mfd->bl_lock);
- mfd->bl_updated = true;
- mdss_fb_set_backlight(mfd, mfd->panel_info->bl_max >> 1);
- mutex_unlock(&mfd->bl_lock);
- init_completion(&mfd->splash_info.frame_done);
- mfd->splash_info.notifier.notifier_call = mdss_mdp_splash_ctl_cb;
- mdss_mdp_ctl_notifier_register(mdp5_data->ctl,
- &mfd->splash_info.notifier);
- ret = mdss_mdp_display_splash_image(mfd);
- if (ret) {
- /*
- * keep thread alive to release dynamically allocated
- * resources
- */
- pr_err("splash image display failed\n");
- }
- /* wait for second display complete to release splash resources */
- ret = wait_for_completion_killable(&mfd->splash_info.frame_done);
- mdss_mdp_splash_free_memory(mfd);
- mdss_mdp_ctl_notifier_unregister(mdp5_data->ctl,
- &mfd->splash_info.notifier);
- end:
- return ret;
- }
- static __ref int mdss_mdp_splash_parse_dt(struct msm_fb_data_type *mfd)
- {
- struct platform_device *pdev = mfd->pdev;
- struct mdss_overlay_private *mdp5_mdata = mfd_to_mdp5_data(mfd);
- int len = 0, rc = 0;
- u32 offsets[2];
- mfd->splash_info.splash_logo_enabled =
- of_property_read_bool(pdev->dev.of_node,
- "qcom,mdss-fb-splash-logo-enabled");
- of_find_property(pdev->dev.of_node, "qcom,memblock-reserve", &len);
- if (len < 1) {
- pr_debug("mem reservation for splash screen fb not present\n");
- rc = -EINVAL;
- goto error;
- }
- len = len / sizeof(u32);
- rc = of_property_read_u32_array(pdev->dev.of_node,
- "qcom,memblock-reserve", offsets, len);
- if (rc) {
- pr_debug("error reading mem reserve settings for fb\n");
- goto error;
- }
- if (!memblock_is_reserved(offsets[0])) {
- pr_debug("failed to reserve memory for fb splash\n");
- rc = -EINVAL;
- goto error;
- }
- mdp5_mdata->splash_mem_addr = offsets[0];
- mdp5_mdata->splash_mem_size = offsets[1];
- pr_debug("memaddr=%x size=%x\n", mdp5_mdata->splash_mem_addr,
- mdp5_mdata->splash_mem_size);
- error:
- if (!rc && !mfd->panel_info->cont_splash_enabled &&
- mdp5_mdata->splash_mem_addr) {
- pr_debug("mem reservation not reqd if cont splash disabled\n");
- memblock_free(mdp5_mdata->splash_mem_addr,
- mdp5_mdata->splash_mem_size);
- free_bootmem_late(mdp5_mdata->splash_mem_addr,
- mdp5_mdata->splash_mem_size);
- } else if (rc && mfd->panel_info->cont_splash_enabled) {
- pr_err("no rsvd mem found in DT for splash screen\n");
- } else {
- rc = 0;
- }
- return rc;
- }
- int mdss_mdp_splash_init(struct msm_fb_data_type *mfd)
- {
- int rc;
- if (!mfd) {
- rc = -EINVAL;
- goto end;
- }
- rc = mdss_mdp_splash_parse_dt(mfd);
- if (rc) {
- pr_err("splash memory reserve failed\n");
- goto end;
- }
- if (!mfd->splash_info.splash_logo_enabled) {
- rc = -EINVAL;
- goto end;
- }
- mfd->splash_info.splash_thread = kthread_run(mdss_mdp_splash_thread,
- mfd, "mdss_fb_splash");
- if (IS_ERR(mfd->splash_info.splash_thread)) {
- pr_err("unable to start splash thread %d\n", mfd->index);
- mfd->splash_info.splash_thread = NULL;
- }
- end:
- return rc;
- }
|