738 lines
19 KiB
C
738 lines
19 KiB
C
|
/* 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/of_address.h>
|
||
|
#include <linux/fb.h>
|
||
|
#include <linux/dma-buf.h>
|
||
|
|
||
|
#include "mdss_fb.h"
|
||
|
#include "mdss_mdp.h"
|
||
|
#include "splash.h"
|
||
|
#include "mdss_mdp_splash_logo.h"
|
||
|
#include "mdss_smmu.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;
|
||
|
struct ion_handle *handle;
|
||
|
|
||
|
if (!mfd || !size)
|
||
|
return -EINVAL;
|
||
|
|
||
|
mdata = mfd_to_mdata(mfd);
|
||
|
sinfo = &mfd->splash_info;
|
||
|
|
||
|
if (!mdata || !mdata->iclient || sinfo->splash_buffer)
|
||
|
return -EINVAL;
|
||
|
|
||
|
handle = ion_alloc(mdata->iclient, size, SZ_4K,
|
||
|
ION_HEAP(ION_SYSTEM_HEAP_ID), 0);
|
||
|
if (IS_ERR_OR_NULL(handle)) {
|
||
|
pr_err("ion memory allocation failed\n");
|
||
|
rc = PTR_RET(handle);
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
sinfo->size = size;
|
||
|
sinfo->dma_buf = ion_share_dma_buf(mdata->iclient, handle);
|
||
|
if (IS_ERR(sinfo->dma_buf)) {
|
||
|
rc = PTR_ERR(sinfo->dma_buf);
|
||
|
goto imap_err;
|
||
|
}
|
||
|
|
||
|
sinfo->attachment = mdss_smmu_dma_buf_attach(sinfo->dma_buf,
|
||
|
&mfd->pdev->dev, MDSS_IOMMU_DOMAIN_UNSECURE);
|
||
|
if (IS_ERR(sinfo->attachment)) {
|
||
|
rc = PTR_ERR(sinfo->attachment);
|
||
|
goto err_put;
|
||
|
}
|
||
|
|
||
|
sinfo->table = dma_buf_map_attachment(sinfo->attachment,
|
||
|
DMA_BIDIRECTIONAL);
|
||
|
if (IS_ERR(sinfo->table)) {
|
||
|
rc = PTR_ERR(sinfo->table);
|
||
|
goto err_detach;
|
||
|
}
|
||
|
|
||
|
rc = mdss_smmu_map_dma_buf(sinfo->dma_buf, sinfo->table,
|
||
|
MDSS_IOMMU_DOMAIN_UNSECURE, &sinfo->iova,
|
||
|
&buf_size, DMA_BIDIRECTIONAL);
|
||
|
if (rc) {
|
||
|
pr_err("mdss smmu map dma buf failed!\n");
|
||
|
goto err_unmap;
|
||
|
}
|
||
|
sinfo->size = buf_size;
|
||
|
|
||
|
dma_buf_begin_cpu_access(sinfo->dma_buf, 0, size, DMA_BIDIRECTIONAL);
|
||
|
sinfo->splash_buffer = dma_buf_kmap(sinfo->dma_buf, 0);
|
||
|
if (IS_ERR(sinfo->splash_buffer)) {
|
||
|
pr_err("ion kernel memory mapping failed\n");
|
||
|
rc = IS_ERR(sinfo->splash_buffer);
|
||
|
goto kmap_err;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* dma_buf has the reference
|
||
|
*/
|
||
|
ion_free(mdata->iclient, handle);
|
||
|
|
||
|
return rc;
|
||
|
kmap_err:
|
||
|
mdss_smmu_unmap_dma_buf(sinfo->table, MDSS_IOMMU_DOMAIN_UNSECURE,
|
||
|
DMA_BIDIRECTIONAL, sinfo->dma_buf);
|
||
|
err_unmap:
|
||
|
dma_buf_unmap_attachment(sinfo->attachment, sinfo->table,
|
||
|
DMA_BIDIRECTIONAL);
|
||
|
err_detach:
|
||
|
dma_buf_detach(sinfo->dma_buf, sinfo->attachment);
|
||
|
err_put:
|
||
|
dma_buf_put(sinfo->dma_buf);
|
||
|
imap_err:
|
||
|
ion_free(mdata->iclient, 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->dma_buf)
|
||
|
return;
|
||
|
|
||
|
dma_buf_end_cpu_access(sinfo->dma_buf, 0, sinfo->size,
|
||
|
DMA_BIDIRECTIONAL);
|
||
|
dma_buf_kunmap(sinfo->dma_buf, 0, sinfo->splash_buffer);
|
||
|
|
||
|
mdss_smmu_unmap_dma_buf(sinfo->table, MDSS_IOMMU_DOMAIN_UNSECURE, 0,
|
||
|
sinfo->dma_buf);
|
||
|
dma_buf_unmap_attachment(sinfo->attachment, sinfo->table,
|
||
|
DMA_BIDIRECTIONAL);
|
||
|
dma_buf_detach(sinfo->dma_buf, sinfo->attachment);
|
||
|
dma_buf_put(sinfo->dma_buf);
|
||
|
|
||
|
sinfo->splash_buffer = NULL;
|
||
|
}
|
||
|
|
||
|
static int mdss_mdp_splash_iommu_attach(struct msm_fb_data_type *mfd)
|
||
|
{
|
||
|
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
|
||
|
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
|
||
|
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 (mdata->mdss_util->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;
|
||
|
}
|
||
|
|
||
|
rc = mdss_smmu_map(MDSS_IOMMU_DOMAIN_UNSECURE,
|
||
|
mdp5_data->splash_mem_addr,
|
||
|
mdp5_data->splash_mem_addr,
|
||
|
mdp5_data->splash_mem_size,
|
||
|
IOMMU_READ | IOMMU_NOEXEC);
|
||
|
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");
|
||
|
mdss_smmu_unmap(MDSS_IOMMU_DOMAIN_UNSECURE,
|
||
|
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 mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
|
||
|
|
||
|
if (mfd->splash_info.iommu_dynamic_attached) {
|
||
|
|
||
|
mdss_smmu_unmap(MDSS_IOMMU_DOMAIN_UNSECURE,
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* In order to free reseved memory from bootup we are not
|
||
|
* able to call the __init free functions, as we could be
|
||
|
* passed the init boot sequence. As a reult we need to
|
||
|
* free this memory ourselves using the
|
||
|
* free_reeserved_page() function.
|
||
|
*/
|
||
|
void mdss_free_bootmem(u32 mem_addr, u32 size)
|
||
|
{
|
||
|
unsigned long pfn_start, pfn_end, pfn_idx;
|
||
|
pfn_start = mem_addr >> PAGE_SHIFT;
|
||
|
pfn_end = (mem_addr + size) >> PAGE_SHIFT;
|
||
|
for (pfn_idx = pfn_start; pfn_idx < pfn_end; pfn_idx++)
|
||
|
free_reserved_page(pfn_to_page(pfn_idx));
|
||
|
}
|
||
|
|
||
|
int mdss_mdp_splash_cleanup(struct msm_fb_data_type *mfd,
|
||
|
bool use_borderfill)
|
||
|
{
|
||
|
struct mdss_overlay_private *mdp5_data;
|
||
|
struct mdss_mdp_ctl *ctl;
|
||
|
int rc = 0;
|
||
|
|
||
|
if (!mfd)
|
||
|
return -EINVAL;
|
||
|
|
||
|
mdp5_data = mfd_to_mdp5_data(mfd);
|
||
|
if (!mdp5_data)
|
||
|
return -EINVAL;
|
||
|
|
||
|
ctl = mdp5_data->ctl;
|
||
|
if (!ctl)
|
||
|
return -EINVAL;
|
||
|
|
||
|
if (!mfd->panel_info->cont_splash_enabled ||
|
||
|
(mfd->splash_info.iommu_dynamic_attached && !use_borderfill)) {
|
||
|
if (mfd->splash_info.iommu_dynamic_attached &&
|
||
|
use_borderfill) {
|
||
|
mdss_mdp_splash_unmap_splash_mem(mfd);
|
||
|
memblock_free(mdp5_data->splash_mem_addr,
|
||
|
mdp5_data->splash_mem_size);
|
||
|
mdss_free_bootmem(mdp5_data->splash_mem_addr,
|
||
|
mdp5_data->splash_mem_size);
|
||
|
}
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
/* 1-to-1 mapping */
|
||
|
mdss_mdp_splash_iommu_attach(mfd);
|
||
|
|
||
|
if (use_borderfill && mdp5_data->handoff &&
|
||
|
!mfd->splash_info.iommu_dynamic_attached) {
|
||
|
/*
|
||
|
* 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, 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 (mdp5_data->splash_mem_addr &&
|
||
|
!mfd->splash_info.iommu_dynamic_attached) {
|
||
|
/* Give back the reserved memory to the system */
|
||
|
memblock_free(mdp5_data->splash_mem_addr,
|
||
|
mdp5_data->splash_mem_size);
|
||
|
mdss_free_bootmem(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;
|
||
|
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
|
||
|
uint32_t image_size = SPLASH_IMAGE_WIDTH * SPLASH_IMAGE_HEIGHT
|
||
|
* SPLASH_IMAGE_BPP;
|
||
|
|
||
|
ret = mdss_mdp_overlay_pipe_setup(mfd, req, &pipe, NULL, true);
|
||
|
if (ret)
|
||
|
return NULL;
|
||
|
|
||
|
if (mdss_mdp_pipe_map(pipe)) {
|
||
|
pr_err("unable to map base pipe\n");
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
mutex_lock(&mdp5_data->list_lock);
|
||
|
buf = mdss_mdp_overlay_buf_alloc(mfd, pipe);
|
||
|
if (!buf) {
|
||
|
pr_err("unable to allocate memory for splash buffer\n");
|
||
|
mdss_mdp_pipe_unmap(pipe);
|
||
|
mutex_unlock(&mdp5_data->list_lock);
|
||
|
return NULL;
|
||
|
}
|
||
|
mutex_unlock(&mdp5_data->list_lock);
|
||
|
|
||
|
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_rect *src_rect,
|
||
|
struct mdss_rect *dest_rect)
|
||
|
{
|
||
|
struct mdss_mdp_pipe *pipe;
|
||
|
struct fb_info *fbi;
|
||
|
struct mdp_overlay *req = NULL;
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
req = kzalloc(sizeof(struct mdp_overlay), GFP_KERNEL);
|
||
|
if (!req)
|
||
|
return -ENOMEM;
|
||
|
|
||
|
/*
|
||
|
* use single pipe for
|
||
|
* 1. split display disabled
|
||
|
* 2. splash image is only on one side of panel
|
||
|
* 3. source split is enabled and splash image is within line
|
||
|
* buffer boundry
|
||
|
*/
|
||
|
use_single_pipe =
|
||
|
!is_split_lm(mfd) ||
|
||
|
(is_split_lm(mfd) &&
|
||
|
((dest_rect->x + dest_rect->w) < mfd->split_fb_left ||
|
||
|
dest_rect->x > mfd->split_fb_left)) ||
|
||
|
(mdata->has_src_split &&
|
||
|
src_rect->w < min_t(u16, mixer->width,
|
||
|
mdss_mdp_line_buffer_width()) &&
|
||
|
dest_rect->w < min_t(u16, mixer->width,
|
||
|
mdss_mdp_line_buffer_width()));
|
||
|
|
||
|
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]);
|
||
|
}
|
||
|
|
||
|
kfree(req);
|
||
|
return ret;
|
||
|
end:
|
||
|
kfree(req);
|
||
|
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_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);
|
||
|
mdss_mdp_splash_cleanup(mfd, false);
|
||
|
/* 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;
|
||
|
int ret = -EINVAL;
|
||
|
|
||
|
if (!mfd) {
|
||
|
pr_err("invalid input parameter\n");
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
mdp5_data = mfd_to_mdp5_data(mfd);
|
||
|
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->allow_bl_update = 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];
|
||
|
struct device_node *pnode, *child_node;
|
||
|
|
||
|
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) {
|
||
|
len = len / sizeof(u32);
|
||
|
|
||
|
rc = of_property_read_u32_array(pdev->dev.of_node,
|
||
|
"qcom,memblock-reserve", offsets, len);
|
||
|
if (rc) {
|
||
|
pr_err("error reading mem reserve settings for fb\n");
|
||
|
goto error;
|
||
|
}
|
||
|
} else {
|
||
|
child_node = of_get_child_by_name(pdev->dev.of_node,
|
||
|
"qcom,cont-splash-memory");
|
||
|
if (!child_node) {
|
||
|
pr_err("splash mem child node is not present\n");
|
||
|
rc = -EINVAL;
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
pnode = of_parse_phandle(child_node, "linux,contiguous-region",
|
||
|
0);
|
||
|
if (pnode != NULL) {
|
||
|
const u32 *addr;
|
||
|
u64 size;
|
||
|
addr = of_get_address(pnode, 0, &size, NULL);
|
||
|
if (!addr) {
|
||
|
pr_err("failed to parse the splash memory address\n");
|
||
|
of_node_put(pnode);
|
||
|
rc = -EINVAL;
|
||
|
goto error;
|
||
|
}
|
||
|
offsets[0] = (u32) of_read_ulong(addr, 2);
|
||
|
offsets[1] = (u32) size;
|
||
|
of_node_put(pnode);
|
||
|
} else {
|
||
|
pr_err("mem reservation for splash screen fb not present\n");
|
||
|
rc = -EINVAL;
|
||
|
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);
|
||
|
mdss_free_bootmem(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;
|
||
|
}
|