blob: b1c80413d795ee5cc49275c69c7842434a7e4845 [file] [log] [blame]
/* Copyright (c) 2012-2018, 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/major.h>
#include <linux/pm_runtime.h>
#include <linux/uaccess.h>
#include <linux/delay.h>
#include <linux/msm_mdp.h>
#include <linux/memblock.h>
#include <linux/file.h>
#include <soc/qcom/event_timer.h>
#include "mdss.h"
#include "mdss_debug.h"
#include "mdss_fb.h"
#include "mdss_mdp.h"
#include "mdss_mdp_wfd.h"
#include "mdss_sync.h"
#define CHECK_LAYER_BOUNDS(offset, size, max_size) \
(((size) > (max_size)) || ((offset) > ((max_size) - (size))))
#define SCALER_ENABLED \
(MDP_LAYER_ENABLE_PIXEL_EXT | MDP_LAYER_ENABLE_QSEED3_SCALE)
enum {
MDSS_MDP_RELEASE_FENCE = 0,
MDSS_MDP_RETIRE_FENCE,
};
enum layer_pipe_q {
LAYER_USES_NEW_PIPE_Q = 0,
LAYER_USES_USED_PIPE_Q,
LAYER_USES_DESTROY_PIPE_Q,
};
enum layer_zorder_used {
LAYER_ZORDER_NONE = 0,
LAYER_ZORDER_LEFT = 1,
LAYER_ZORDER_RIGHT = 2,
LAYER_ZORDER_BOTH = 3,
};
struct mdss_mdp_validate_info_t {
struct mdp_input_layer *layer;
struct mdss_mdp_pipe_multirect_params multirect;
};
/*
* __layer_needs_src_split() - check needs source split configuration
* @layer: input layer
*
* return true if the layer should be used as source split
*/
static bool __layer_needs_src_split(struct mdp_input_layer *layer)
{
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
return (layer->flags & MDP_LAYER_ASYNC) ||
mdss_has_quirk(mdata, MDSS_QUIRK_SRC_SPLIT_ALWAYS);
}
static int __async_update_position_check(struct msm_fb_data_type *mfd,
struct mdss_mdp_pipe *pipe, struct mdp_point *src,
struct mdp_point *dst)
{
struct fb_var_screeninfo *var = &mfd->fbi->var;
u32 xres = var->xres;
u32 yres = var->yres;
if (!pipe->async_update
|| CHECK_LAYER_BOUNDS(src->x, pipe->src.w, pipe->img_width)
|| CHECK_LAYER_BOUNDS(src->y, pipe->src.h, pipe->img_height)
|| CHECK_LAYER_BOUNDS(dst->x, pipe->dst.w, xres)
|| CHECK_LAYER_BOUNDS(dst->y, pipe->dst.h, yres)) {
pr_err("invalid configs: async_update=%d, src:{%d,%d}, dst:{%d,%d}\n",
pipe->async_update, src->x, src->y, dst->x, dst->y);
pr_err("pipe:- src:{%d,%d,%d,%d}, dst:{%d,%d,%d,%d}\n",
pipe->src.x, pipe->src.y, pipe->src.w, pipe->src.h,
pipe->dst.x, pipe->dst.y, pipe->dst.w, pipe->dst.h);
return -EINVAL;
}
return 0;
}
static int __cursor_layer_check(struct msm_fb_data_type *mfd,
struct mdp_input_layer *layer)
{
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
if ((layer->z_order != HW_CURSOR_STAGE(mdata))
|| layer->src_rect.w > mdata->max_cursor_size
|| layer->src_rect.h > mdata->max_cursor_size
|| layer->src_rect.w != layer->dst_rect.w
|| layer->src_rect.h != layer->dst_rect.h
|| !mdata->ncursor_pipes) {
pr_err("Incorrect cursor configs for pipe:%d, cursor_pipes:%d, z_order:%d\n",
layer->pipe_ndx, mdata->ncursor_pipes,
layer->z_order);
pr_err("src:{%d,%d,%d,%d}, dst:{%d,%d,%d,%d}\n",
layer->src_rect.x, layer->src_rect.y,
layer->src_rect.w, layer->src_rect.h,
layer->dst_rect.x, layer->dst_rect.y,
layer->dst_rect.w, layer->dst_rect.h);
return -EINVAL;
}
return 0;
}
static int __layer_xres_check(struct msm_fb_data_type *mfd,
struct mdp_input_layer *layer)
{
u32 xres = 0;
u32 left_lm_w = left_lm_w_from_mfd(mfd);
struct mdss_mdp_ctl *ctl = mfd_to_ctl(mfd);
struct mdss_data_type *mdata = mfd_to_mdata(mfd);
if (layer->dst_rect.x >= left_lm_w) {
if (mdata->has_src_split)
xres = left_lm_w;
else
layer->dst_rect.x -= left_lm_w;
if (ctl->mixer_right) {
xres += ctl->mixer_right->width;
} else {
pr_err("ov cannot be placed on right mixer\n");
return -EPERM;
}
} else {
if (ctl->mixer_left) {
xres = ctl->mixer_left->width;
} else {
pr_err("ov cannot be placed on left mixer\n");
return -EPERM;
}
if (mdata->has_src_split && ctl->mixer_right)
xres += ctl->mixer_right->width;
}
if (CHECK_LAYER_BOUNDS(layer->dst_rect.x, layer->dst_rect.w, xres)) {
pr_err("dst_xres is invalid. dst_x:%d, dst_w:%d, xres:%d\n",
layer->dst_rect.x, layer->dst_rect.w, xres);
return -EINVAL;
}
return 0;
}
static int __layer_param_check(struct msm_fb_data_type *mfd,
struct mdp_input_layer *layer, struct mdss_mdp_format_params *fmt,
enum mdss_mdp_pipe_rect rect_num)
{
u32 yres;
u32 min_src_size, min_dst_size = 1;
int content_secure;
struct mdss_data_type *mdata = mfd_to_mdata(mfd);
struct mdss_mdp_ctl *ctl = mfd_to_ctl(mfd);
u32 src_w, src_h, dst_w, dst_h, width, height;
if (!ctl) {
pr_err("ctl is null\n");
return -EINVAL;
}
if (ctl->mixer_left) {
yres = ctl->mixer_left->height;
} else {
pr_debug("Using fb var screen infor for height\n");
yres = mfd->fbi->var.yres;
}
content_secure = (layer->flags & MDP_LAYER_SECURE_SESSION);
if (!ctl->is_secure && content_secure &&
(mfd->panel.type == WRITEBACK_PANEL)) {
pr_debug("return due to security concerns\n");
return -EPERM;
}
min_src_size = fmt->is_yuv ? 2 : 1;
if (layer->z_order >= (mdata->max_target_zorder + MDSS_MDP_STAGE_0)) {
pr_err("zorder %d out of range\n", layer->z_order);
return -EINVAL;
}
if (!mdss_mdp_pipe_search(mdata, layer->pipe_ndx, rect_num)) {
pr_err("layer pipe is invalid: 0x%x rect:%d\n",
layer->pipe_ndx, rect_num);
return -EINVAL;
}
width = layer->buffer.width;
height = layer->buffer.height;
if (layer->flags & MDP_LAYER_DEINTERLACE) {
width *= 2;
height /= 2;
}
if (layer->buffer.width > MAX_IMG_WIDTH ||
layer->buffer.height > MAX_IMG_HEIGHT ||
layer->src_rect.w < min_src_size ||
layer->src_rect.h < min_src_size ||
CHECK_LAYER_BOUNDS(layer->src_rect.x, layer->src_rect.w, width) ||
CHECK_LAYER_BOUNDS(layer->src_rect.y, layer->src_rect.h, height)) {
pr_err("invalid source image img flag=%d wh=%dx%d rect=%d,%d,%d,%d\n",
layer->flags, width, height,
layer->src_rect.x, layer->src_rect.y,
layer->src_rect.w, layer->src_rect.h);
return -EINVAL;
}
if (layer->dst_rect.w < min_dst_size ||
layer->dst_rect.h < min_dst_size) {
pr_err("invalid destination resolution (%dx%d)",
layer->dst_rect.w, layer->dst_rect.h);
return -EINVAL;
}
if (layer->horz_deci || layer->vert_deci) {
if (!mdata->has_decimation) {
pr_err("No Decimation in MDP V=%x\n", mdata->mdp_rev);
return -EINVAL;
} else if ((layer->horz_deci > MAX_DECIMATION) ||
(layer->vert_deci > MAX_DECIMATION)) {
pr_err("Invalid decimation factors horz=%d vert=%d\n",
layer->horz_deci, layer->vert_deci);
return -EINVAL;
} else if (layer->flags & MDP_LAYER_BWC) {
pr_err("Decimation can't be enabled with BWC\n");
return -EINVAL;
} else if (fmt->fetch_mode != MDSS_MDP_FETCH_LINEAR) {
pr_err("Decimation can't be enabled with MacroTile format\n");
return -EINVAL;
}
}
if (CHECK_LAYER_BOUNDS(layer->dst_rect.y, layer->dst_rect.h, yres)) {
pr_err("invalid vertical destination: y=%d, h=%d, yres=%d\n",
layer->dst_rect.y, layer->dst_rect.h, yres);
return -EOVERFLOW;
}
dst_w = layer->dst_rect.w;
dst_h = layer->dst_rect.h;
src_w = layer->src_rect.w >> layer->horz_deci;
src_h = layer->src_rect.h >> layer->vert_deci;
if (src_w > mdata->max_mixer_width) {
pr_err("invalid source width=%d HDec=%d\n",
layer->src_rect.w, layer->horz_deci);
return -EINVAL;
}
if ((src_w * MAX_UPSCALE_RATIO) < dst_w) {
pr_err("too much upscaling Width %d->%d\n",
layer->src_rect.w, layer->dst_rect.w);
return -E2BIG;
}
if ((src_h * MAX_UPSCALE_RATIO) < dst_h) {
pr_err("too much upscaling. Height %d->%d\n",
layer->src_rect.h, layer->dst_rect.h);
return -E2BIG;
}
if (src_w > (dst_w * MAX_DOWNSCALE_RATIO)) {
pr_err("too much downscaling. Width %d->%d H Dec=%d\n",
src_w, layer->dst_rect.w, layer->horz_deci);
return -E2BIG;
}
if (src_h > (dst_h * MAX_DOWNSCALE_RATIO)) {
pr_err("too much downscaling. Height %d->%d V Dec=%d\n",
src_h, layer->dst_rect.h, layer->vert_deci);
return -E2BIG;
}
if (layer->flags & MDP_LAYER_BWC) {
if ((layer->buffer.width != layer->src_rect.w) ||
(layer->buffer.height != layer->src_rect.h)) {
pr_err("BWC: mismatch of src img=%dx%d rect=%dx%d\n",
layer->buffer.width, layer->buffer.height,
layer->src_rect.w, layer->src_rect.h);
return -EINVAL;
}
if (layer->horz_deci || layer->vert_deci) {
pr_err("Can't enable BWC decode && decimate\n");
return -EINVAL;
}
}
if ((layer->flags & MDP_LAYER_DEINTERLACE) &&
!(layer->flags & SCALER_ENABLED)) {
if (layer->flags & MDP_SOURCE_ROTATED_90) {
if ((layer->src_rect.w % 4) != 0) {
pr_err("interlaced rect not h/4\n");
return -EINVAL;
}
} else if ((layer->src_rect.h % 4) != 0) {
pr_err("interlaced rect not h/4\n");
return -EINVAL;
}
}
if (fmt->is_yuv) {
if ((layer->src_rect.x & 0x1) || (layer->src_rect.y & 0x1) ||
(layer->src_rect.w & 0x1) || (layer->src_rect.h & 0x1)) {
pr_err("invalid odd src resolution or coordinates\n");
return -EINVAL;
}
}
return 0;
}
/* compare all reconfiguration parameter validation in this API */
static int __validate_layer_reconfig(struct mdp_input_layer *layer,
struct mdss_mdp_pipe *pipe)
{
int status = 0;
struct mdss_mdp_format_params *src_fmt;
/*
* csc registers are not double buffered. It is not permitted
* to change them on staged pipe with YUV layer.
*/
if (pipe->csc_coeff_set != layer->color_space) {
src_fmt = mdss_mdp_get_format_params(layer->buffer.format);
if (pipe->src_fmt->is_yuv && src_fmt && src_fmt->is_yuv) {
status = -EPERM;
pr_err("csc change is not permitted on used pipe\n");
}
}
return status;
}
static int __validate_single_layer(struct msm_fb_data_type *mfd,
struct mdss_mdp_validate_info_t *layer_info, u32 mixer_mux)
{
u32 bwc_enabled;
int ret;
bool is_vig_needed = false;
struct mdss_mdp_format_params *fmt;
struct mdss_mdp_mixer *mixer = NULL;
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
struct mdss_data_type *mdata = mfd_to_mdata(mfd);
struct mdp_input_layer *layer = layer_info->layer;
int ptype = get_pipe_type_from_ndx(layer->pipe_ndx);
if (ptype == MDSS_MDP_PIPE_TYPE_INVALID) {
pr_err("Invalid pipe ndx=%d\n", layer->pipe_ndx);
return -EINVAL;
}
if ((layer->dst_rect.w > mdata->max_mixer_width) ||
(layer->dst_rect.h > MAX_DST_H)) {
pr_err("exceeded max mixer supported resolution %dx%d\n",
layer->dst_rect.w, layer->dst_rect.h);
ret = -EINVAL;
goto exit_fail;
}
pr_debug("ctl=%u mux=%d z_order=%d flags=0x%x dst_x:%d\n",
mdp5_data->ctl->num, mixer_mux, layer->z_order,
layer->flags, layer->dst_rect.x);
fmt = mdss_mdp_get_format_params(layer->buffer.format);
if (!fmt) {
pr_err("invalid layer format %d\n", layer->buffer.format);
ret = -EINVAL;
goto exit_fail;
}
bwc_enabled = layer->flags & MDP_LAYER_BWC;
if (bwc_enabled) {
if (!mdp5_data->mdata->has_bwc) {
pr_err("layer uses bwc format but MDP does not support it\n");
ret = -EINVAL;
goto exit_fail;
}
layer->buffer.format =
mdss_mdp_get_rotator_dst_format(
layer->buffer.format, false, bwc_enabled);
fmt = mdss_mdp_get_format_params(layer->buffer.format);
if (!fmt) {
pr_err("invalid layer format %d\n",
layer->buffer.format);
ret = -EINVAL;
goto exit_fail;
}
}
if (ptype == MDSS_MDP_PIPE_TYPE_CURSOR) {
ret = __cursor_layer_check(mfd, layer);
if (ret)
goto exit_fail;
}
ret = __layer_xres_check(mfd, layer);
if (ret)
goto exit_fail;
ret = __layer_param_check(mfd, layer, fmt, layer_info->multirect.num);
if (ret)
goto exit_fail;
mixer = mdss_mdp_mixer_get(mdp5_data->ctl, mixer_mux);
if (!mixer) {
pr_err("unable to get %s mixer\n",
(mixer_mux == MDSS_MDP_MIXER_MUX_RIGHT) ?
"right" : "left");
ret = -EPERM;
goto exit_fail;
}
if (fmt->is_yuv || (mdata->has_non_scalar_rgb &&
((layer->src_rect.w != layer->dst_rect.w) ||
(layer->src_rect.h != layer->dst_rect.h))))
is_vig_needed = true;
if (is_vig_needed && ptype != MDSS_MDP_PIPE_TYPE_VIG) {
pr_err("pipe is non-scalar ndx=%x\n", layer->pipe_ndx);
ret = -EINVAL;
goto exit_fail;
}
if (((ptype == MDSS_MDP_PIPE_TYPE_DMA) ||
(ptype == MDSS_MDP_PIPE_TYPE_CURSOR)) &&
(layer->dst_rect.h != layer->src_rect.h ||
layer->dst_rect.w != layer->src_rect.w)) {
pr_err("no scaling supported on dma/cursor pipe, pipe num:%d\n",
layer->pipe_ndx);
return -EINVAL;
}
exit_fail:
return ret;
}
static int __configure_pipe_params(struct msm_fb_data_type *mfd,
struct mdss_mdp_validate_info_t *vinfo, struct mdss_mdp_pipe *pipe,
struct mdss_mdp_pipe *left_blend_pipe, bool is_single_layer,
u32 mixer_mux)
{
int ret = 0;
u32 left_lm_w = left_lm_w_from_mfd(mfd);
u32 flags;
bool is_right_blend = false;
struct mdss_mdp_mixer *mixer = NULL;
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
struct mdss_data_type *mdata = mfd_to_mdata(mfd);
struct mdp_input_layer *layer = vinfo->layer;
mixer = mdss_mdp_mixer_get(mdp5_data->ctl, mixer_mux);
pipe->src_fmt = mdss_mdp_get_format_params(layer->buffer.format);
if (!pipe->src_fmt || !mixer) {
pr_err("invalid layer format:%d or mixer:%pK\n",
layer->buffer.format, pipe->mixer_left);
ret = -EINVAL;
goto end;
}
pipe->comp_ratio = layer->buffer.comp_ratio;
if (mfd->panel_orientation)
layer->flags ^= mfd->panel_orientation;
pipe->mixer_left = mixer;
pipe->mfd = mfd;
pipe->play_cnt = 0;
pipe->flags = 0;
if (layer->flags & MDP_LAYER_FLIP_LR)
pipe->flags = MDP_FLIP_LR;
if (layer->flags & MDP_LAYER_FLIP_UD)
pipe->flags |= MDP_FLIP_UD;
if (layer->flags & MDP_LAYER_SECURE_SESSION)
pipe->flags |= MDP_SECURE_OVERLAY_SESSION;
if (layer->flags & MDP_LAYER_SECURE_DISPLAY_SESSION)
pipe->flags |= MDP_SECURE_DISPLAY_OVERLAY_SESSION;
if (layer->flags & MDP_LAYER_SOLID_FILL)
pipe->flags |= MDP_SOLID_FILL;
if (layer->flags & MDP_LAYER_DEINTERLACE)
pipe->flags |= MDP_DEINTERLACE;
if (layer->flags & MDP_LAYER_BWC)
pipe->flags |= MDP_BWC_EN;
if (layer->flags & MDP_LAYER_PP)
pipe->flags |= MDP_OVERLAY_PP_CFG_EN;
pipe->is_fg = layer->flags & MDP_LAYER_FORGROUND;
pipe->img_width = layer->buffer.width & 0x3fff;
pipe->img_height = layer->buffer.height & 0x3fff;
pipe->src.x = layer->src_rect.x;
pipe->src.y = layer->src_rect.y;
pipe->src.w = layer->src_rect.w;
pipe->src.h = layer->src_rect.h;
pipe->dst.x = layer->dst_rect.x;
pipe->dst.y = layer->dst_rect.y;
pipe->dst.w = layer->dst_rect.w;
pipe->dst.h = layer->dst_rect.h;
pipe->horz_deci = layer->horz_deci;
pipe->vert_deci = layer->vert_deci;
pipe->bg_color = layer->bg_color;
pipe->alpha = layer->alpha;
pipe->transp = layer->transp_mask;
pipe->blend_op = layer->blend_op;
pipe->is_handed_off = false;
pipe->async_update = (layer->flags & MDP_LAYER_ASYNC) ? true : false;
pipe->csc_coeff_set = layer->color_space;
if (mixer->ctl) {
pipe->dst.x += mixer->ctl->border_x_off;
pipe->dst.y += mixer->ctl->border_y_off;
pr_debug("border{%d,%d}\n", mixer->ctl->border_x_off,
mixer->ctl->border_y_off);
}
pr_debug("src{%d,%d,%d,%d}, dst{%d,%d,%d,%d}\n",
pipe->src.x, pipe->src.y, pipe->src.w, pipe->src.h,
pipe->dst.x, pipe->dst.y, pipe->dst.w, pipe->dst.h);
if (layer->flags & SCALER_ENABLED)
memcpy(&pipe->scaler, layer->scale,
sizeof(struct mdp_scale_data_v2));
pipe->scaler.enable = (layer->flags & SCALER_ENABLED);
flags = pipe->flags;
if (is_single_layer)
flags |= PERF_CALC_PIPE_SINGLE_LAYER;
/*
* async update is allowed only in video mode panels with single LM
* or dual LM with src_split enabled.
*/
if (pipe->async_update && ((is_split_lm(mfd) && !mdata->has_src_split)
|| (!mdp5_data->ctl->is_video_mode))) {
pr_err("async update allowed only in video mode panel with src_split\n");
ret = -EINVAL;
goto end;
}
/*
* unstage the pipe if it's current z_order does not match with new
* z_order because client may only call the validate.
*/
if (pipe->mixer_stage != layer->z_order)
mdss_mdp_mixer_pipe_unstage(pipe, pipe->mixer_left);
/*
* check if overlay span across two mixers and if source split is
* available. If yes, enable src_split_req flag so that during mixer
* staging, same pipe will be stagged on both layer mixers.
*/
if (mdata->has_src_split) {
is_right_blend = pipe->is_right_blend;
if (left_blend_pipe) {
if (pipe->priority <= left_blend_pipe->priority) {
pr_err("priority limitation. left:%d right%d\n",
left_blend_pipe->priority,
pipe->priority);
ret = -EPERM;
goto end;
} else {
pr_debug("pipe%d is a right_pipe\n", pipe->num);
is_right_blend = true;
}
} else if (pipe->is_right_blend) {
/*
* pipe used to be right blend. So need to update mixer
* configuration to remove it as a right blend.
*/
mdss_mdp_mixer_pipe_unstage(pipe, pipe->mixer_left);
mdss_mdp_mixer_pipe_unstage(pipe, pipe->mixer_right);
is_right_blend = false;
}
if (is_split_lm(mfd) && __layer_needs_src_split(layer)) {
pipe->src_split_req = true;
} else if ((mixer_mux == MDSS_MDP_MIXER_MUX_LEFT) &&
((layer->dst_rect.x + layer->dst_rect.w) > mixer->width)) {
if (layer->dst_rect.x >= mixer->width) {
pr_err("%pS: err dst_x can't lie in right half",
__builtin_return_address(0));
pr_cont(" flags:0x%x dst x:%d w:%d lm_w:%d\n",
layer->flags, layer->dst_rect.x,
layer->dst_rect.w, mixer->width);
ret = -EINVAL;
goto end;
} else {
pipe->src_split_req = true;
}
} else {
if (pipe->src_split_req) {
mdss_mdp_mixer_pipe_unstage(pipe,
pipe->mixer_right);
pipe->mixer_right = NULL;
}
pipe->src_split_req = false;
}
pipe->is_right_blend = is_right_blend;
}
pipe->multirect.mode = vinfo->multirect.mode;
pipe->mixer_stage = layer->z_order;
if (mfd->panel_orientation & MDP_FLIP_LR)
pipe->dst.x = pipe->mixer_left->width - pipe->dst.x -
pipe->dst.w;
if (mfd->panel_orientation & MDP_FLIP_UD)
pipe->dst.y = pipe->mixer_left->height - pipe->dst.y -
pipe->dst.h;
memcpy(&pipe->layer, layer, sizeof(struct mdp_input_layer));
mdss_mdp_overlay_set_chroma_sample(pipe);
if (pipe->blend_op == BLEND_OP_NOT_DEFINED)
pipe->blend_op = pipe->src_fmt->alpha_enable ?
BLEND_OP_PREMULTIPLIED : BLEND_OP_OPAQUE;
if (pipe->src_fmt->is_yuv && !(pipe->flags & MDP_SOURCE_ROTATED_90) &&
!pipe->scaler.enable) {
pipe->overfetch_disable = OVERFETCH_DISABLE_BOTTOM;
if (pipe->dst.x >= left_lm_w)
pipe->overfetch_disable |= OVERFETCH_DISABLE_RIGHT;
pr_debug("overfetch flags=%x\n", pipe->overfetch_disable);
} else {
pipe->overfetch_disable = 0;
}
/*
* When scaling is enabled src crop and image
* width and height is modified by user
*/
if ((pipe->flags & MDP_DEINTERLACE) && !pipe->scaler.enable) {
if (pipe->flags & MDP_SOURCE_ROTATED_90) {
pipe->src.x = DIV_ROUND_UP(pipe->src.x, 2);
pipe->src.x &= ~1;
pipe->src.w /= 2;
pipe->img_width /= 2;
} else {
pipe->src.h /= 2;
pipe->src.y = DIV_ROUND_UP(pipe->src.y, 2);
pipe->src.y &= ~1;
}
}
ret = mdss_mdp_overlay_setup_scaling(pipe);
if (ret) {
pr_err("scaling setup failed %d\n", ret);
goto end;
}
if (layer->flags & MDP_LAYER_PP) {
memcpy(&pipe->pp_cfg, layer->pp_info,
sizeof(struct mdp_overlay_pp_params));
ret = mdss_mdp_pp_sspp_config(pipe);
if (ret) {
pr_err("pp setup failed %d\n", ret);
goto end;
}
}
if (pipe->type == MDSS_MDP_PIPE_TYPE_CURSOR)
goto end;
ret = mdp_pipe_tune_perf(pipe, flags);
if (ret) {
pr_err("unable to satisfy performance. ret=%d\n", ret);
goto end;
}
ret = mdss_mdp_smp_reserve(pipe);
if (ret) {
pr_err("mdss_mdp_smp_reserve failed. pnum:%d ret=%d\n",
pipe->num, ret);
goto end;
}
end:
return ret;
}
static struct mdss_fence *__create_fence(struct msm_fb_data_type *mfd,
struct msm_sync_pt_data *sync_pt_data, u32 fence_type,
int *fence_fd, int value)
{
struct mdss_overlay_private *mdp5_data;
struct mdss_mdp_ctl *ctl;
struct mdss_fence *sync_fence = NULL;
char fence_name[32];
mdp5_data = mfd_to_mdp5_data(mfd);
ctl = mdp5_data->ctl;
if (!ctl->ops.add_vsync_handler) {
pr_err("fb%d vsync pending first update\n", mfd->index);
return ERR_PTR(-EOPNOTSUPP);
}
if (!mdss_mdp_ctl_is_power_on(ctl)) {
pr_err("fb%d ctl power on failed\n", mfd->index);
return ERR_PTR(-EPERM);
}
if (fence_type == MDSS_MDP_RETIRE_FENCE)
snprintf(fence_name, sizeof(fence_name), "fb%d_retire",
mfd->index);
else
snprintf(fence_name, sizeof(fence_name), "fb%d_release",
mfd->index);
if ((fence_type == MDSS_MDP_RETIRE_FENCE) &&
(mfd->panel.type == MIPI_CMD_PANEL)) {
if (mdp5_data->vsync_timeline) {
value = 1 + mdp5_data->retire_cnt++;
sync_fence = mdss_fb_sync_get_fence(
mdp5_data->vsync_timeline, fence_name,
value);
} else {
return ERR_PTR(-EPERM);
}
} else {
if (fence_type == MDSS_MDP_RETIRE_FENCE)
sync_fence = mdss_fb_sync_get_fence(
sync_pt_data->timeline_retire,
fence_name, value);
else
sync_fence = mdss_fb_sync_get_fence(
sync_pt_data->timeline,
fence_name, value);
}
if (IS_ERR_OR_NULL(sync_fence)) {
pr_err("%s: unable to retrieve release fence\n", fence_name);
goto end;
}
/* get fence fd */
*fence_fd = mdss_get_sync_fence_fd(sync_fence);
if (*fence_fd < 0) {
pr_err("%s: get_unused_fd_flags failed error:0x%x\n",
fence_name, *fence_fd);
mdss_put_sync_fence(sync_fence);
sync_fence = NULL;
goto end;
}
pr_debug("%s:val=%d\n", mdss_get_sync_fence_name(sync_fence), value);
end:
return sync_fence;
}
/*
* __handle_buffer_fences() - copy sync fences and return release/retire
* fence to caller.
*
* This function copies all input sync fences to acquire fence array and
* returns release/retire fences to caller. It acts like buff_sync ioctl.
*/
static int __handle_buffer_fences(struct msm_fb_data_type *mfd,
struct mdp_layer_commit_v1 *commit, struct mdp_input_layer *layer_list)
{
struct mdss_fence *fence, *release_fence, *retire_fence;
struct msm_sync_pt_data *sync_pt_data = NULL;
struct mdp_input_layer *layer;
int value;
u32 acq_fen_count, i, ret = 0;
u32 layer_count = commit->input_layer_cnt;
sync_pt_data = &mfd->mdp_sync_pt_data;
if (!sync_pt_data) {
pr_err("sync point data are NULL\n");
return -EINVAL;
}
i = mdss_fb_wait_for_fence(sync_pt_data);
if (i > 0)
pr_warn("%s: waited on %d active fences\n",
sync_pt_data->fence_name, i);
mutex_lock(&sync_pt_data->sync_mutex);
for (i = 0, acq_fen_count = 0; i < layer_count; i++) {
layer = &layer_list[i];
if (layer->buffer.fence < 0)
continue;
fence = mdss_get_fd_sync_fence(layer->buffer.fence);
if (!fence) {
pr_err("%s: sync fence get failed! fd=%d\n",
sync_pt_data->fence_name, layer->buffer.fence);
ret = -EINVAL;
break;
}
sync_pt_data->acq_fen[acq_fen_count++] = fence;
}
sync_pt_data->acq_fen_cnt = acq_fen_count;
if (ret)
goto sync_fence_err;
value = sync_pt_data->threshold +
atomic_read(&sync_pt_data->commit_cnt);
release_fence = __create_fence(mfd, sync_pt_data,
MDSS_MDP_RELEASE_FENCE, &commit->release_fence, value);
if (IS_ERR_OR_NULL(release_fence)) {
pr_err("unable to retrieve release fence\n");
ret = PTR_ERR(release_fence);
goto release_fence_err;
}
retire_fence = __create_fence(mfd, sync_pt_data,
MDSS_MDP_RETIRE_FENCE, &commit->retire_fence, value);
if (IS_ERR_OR_NULL(retire_fence)) {
pr_err("unable to retrieve retire fence\n");
ret = PTR_ERR(retire_fence);
goto retire_fence_err;
}
mutex_unlock(&sync_pt_data->sync_mutex);
return ret;
retire_fence_err:
put_unused_fd(commit->release_fence);
mdss_put_sync_fence(release_fence);
release_fence_err:
commit->retire_fence = -1;
commit->release_fence = -1;
sync_fence_err:
for (i = 0; i < sync_pt_data->acq_fen_cnt; i++)
mdss_put_sync_fence(sync_pt_data->acq_fen[i]);
sync_pt_data->acq_fen_cnt = 0;
mutex_unlock(&sync_pt_data->sync_mutex);
return ret;
}
/*
* __map_layer_buffer() - map input layer buffer
*
* This function maps input layer buffer. It supports only single layer
* buffer mapping right now. This is case for all formats including UBWC.
*/
static struct mdss_mdp_data *__map_layer_buffer(struct msm_fb_data_type *mfd,
struct mdss_mdp_pipe *pipe,
struct mdss_mdp_validate_info_t *validate_info_list,
u32 layer_count)
{
struct mdss_mdp_data *src_data;
struct mdp_input_layer *layer = NULL;
struct mdp_layer_buffer *buffer;
struct msmfb_data image;
int i, ret;
u32 flags;
struct mdss_mdp_validate_info_t *vitem;
for (i = 0; i < layer_count; i++) {
vitem = &validate_info_list[i];
layer = vitem->layer;
if ((layer->pipe_ndx == pipe->ndx) &&
(vitem->multirect.num == pipe->multirect.num))
break;
}
if (i == layer_count) {
pr_err("layer count index is out of bound\n");
src_data = ERR_PTR(-EINVAL);
goto end;
}
buffer = &layer->buffer;
if (pipe->flags & MDP_SOLID_FILL) {
pr_err("Unexpected buffer queue to a solid fill pipe\n");
src_data = ERR_PTR(-EINVAL);
goto end;
}
flags = (pipe->flags & (MDP_SECURE_OVERLAY_SESSION |
MDP_SECURE_DISPLAY_OVERLAY_SESSION));
if (buffer->planes[0].fd < 0) {
pr_err("invalid file descriptor for layer buffer\n");
src_data = ERR_PTR(-EINVAL);
goto end;
}
src_data = mdss_mdp_overlay_buf_alloc(mfd, pipe);
if (!src_data) {
pr_err("unable to allocate source buffer\n");
src_data = ERR_PTR(-ENOMEM);
goto end;
}
memset(&image, 0, sizeof(image));
image.memory_id = buffer->planes[0].fd;
image.offset = buffer->planes[0].offset;
ret = mdss_mdp_data_get_and_validate_size(src_data, &image, 1,
flags, &mfd->pdev->dev, false, DMA_TO_DEVICE,
buffer);
if (ret)
goto end_buf_free;
src_data->num_planes = 1;
return src_data;
end_buf_free:
mdss_mdp_overlay_buf_free(mfd, src_data);
src_data = ERR_PTR(ret);
end:
return src_data;
}
static inline bool __compare_layer_config(struct mdp_input_layer *validate,
struct mdss_mdp_pipe *pipe)
{
struct mdp_input_layer *layer = &pipe->layer;
bool status = true;
status = !memcmp(&validate->src_rect, &layer->src_rect,
sizeof(validate->src_rect)) &&
!memcmp(&validate->dst_rect, &layer->dst_rect,
sizeof(validate->dst_rect)) &&
validate->flags == layer->flags &&
validate->horz_deci == layer->horz_deci &&
validate->vert_deci == layer->vert_deci &&
validate->alpha == layer->alpha &&
validate->color_space == layer->color_space &&
validate->z_order == (layer->z_order - MDSS_MDP_STAGE_0) &&
validate->transp_mask == layer->transp_mask &&
validate->bg_color == layer->bg_color &&
validate->blend_op == layer->blend_op &&
validate->buffer.width == layer->buffer.width &&
validate->buffer.height == layer->buffer.height &&
validate->buffer.format == layer->buffer.format;
if (status && (validate->flags & SCALER_ENABLED))
status = !memcmp(validate->scale, &pipe->scaler,
sizeof(pipe->scaler));
return status;
}
/*
* __find_layer_in_validate_q() - Search layer in validation queue
*
* This functions helps to skip validation for layers where only buffer is
* changing. For ex: video playback case. In order to skip validation, it
* compares all input layer params except buffer handle, offset, fences.
*/
static struct mdss_mdp_pipe *__find_layer_in_validate_q(
struct mdss_mdp_validate_info_t *vinfo,
struct mdss_overlay_private *mdp5_data)
{
bool found = false;
struct mdss_mdp_pipe *pipe;
struct mdp_input_layer *layer = vinfo->layer;
mutex_lock(&mdp5_data->list_lock);
list_for_each_entry(pipe, &mdp5_data->pipes_used, list) {
if ((pipe->ndx == layer->pipe_ndx) &&
(pipe->multirect.num == vinfo->multirect.num)) {
if (__compare_layer_config(layer, pipe))
found = true;
break;
}
}
mutex_unlock(&mdp5_data->list_lock);
return found ? pipe : NULL;
}
static bool __find_pipe_in_list(struct list_head *head,
int pipe_ndx, struct mdss_mdp_pipe **out_pipe,
enum mdss_mdp_pipe_rect rect_num)
{
struct mdss_mdp_pipe *pipe;
list_for_each_entry(pipe, head, list) {
if ((pipe_ndx == pipe->ndx) &&
(rect_num == pipe->multirect.num)) {
*out_pipe = pipe;
return true;
}
}
return false;
}
/*
* Search pipe from destroy and cleanup list to avoid validation failure.
* It is caller responsibility to hold the list lock before calling this API.
*/
static struct mdss_mdp_pipe *__find_and_move_cleanup_pipe(
struct mdss_overlay_private *mdp5_data, u32 pipe_ndx,
enum mdss_mdp_pipe_rect rect_num)
{
struct mdss_mdp_pipe *pipe = NULL;
if (__find_pipe_in_list(&mdp5_data->pipes_destroy,
pipe_ndx, &pipe, rect_num)) {
pr_debug("reuse destroy pipe id:%d ndx:%d rect:%d\n",
pipe->num, pipe_ndx, rect_num);
list_move(&pipe->list, &mdp5_data->pipes_used);
} else if (__find_pipe_in_list(&mdp5_data->pipes_cleanup,
pipe_ndx, &pipe, rect_num)) {
pr_debug("reuse cleanup pipe id:%d ndx:%d rect:%d\n",
pipe->num, pipe_ndx, rect_num);
mdss_mdp_mixer_pipe_unstage(pipe, pipe->mixer_left);
mdss_mdp_mixer_pipe_unstage(pipe, pipe->mixer_right);
pipe->mixer_stage = MDSS_MDP_STAGE_UNUSED;
list_move(&pipe->list, &mdp5_data->pipes_used);
}
return pipe;
}
/*
* __assign_pipe_for_layer() - get a pipe for layer
*
* This function first searches the pipe from used list, cleanup list and
* destroy list. On successful search, it returns the same pipe for current
* layer. It also un-stage the pipe from current mixer for used, cleanup,
* destroy pipes if they switches the mixer. On failure search, it returns
* the null pipe.
*/
static struct mdss_mdp_pipe *__assign_pipe_for_layer(
struct msm_fb_data_type *mfd,
struct mdss_mdp_mixer *mixer, u32 pipe_ndx,
enum layer_pipe_q *pipe_q_type,
enum mdss_mdp_pipe_rect rect_num)
{
struct mdss_mdp_pipe *pipe = NULL;
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
struct mdss_data_type *mdata = mfd_to_mdata(mfd);
mutex_lock(&mdp5_data->list_lock);
__find_pipe_in_list(&mdp5_data->pipes_used, pipe_ndx, &pipe, rect_num);
if (IS_ERR_OR_NULL(pipe)) {
pipe = __find_and_move_cleanup_pipe(mdp5_data,
pipe_ndx, rect_num);
if (IS_ERR_OR_NULL(pipe))
*pipe_q_type = LAYER_USES_NEW_PIPE_Q;
else
*pipe_q_type = LAYER_USES_DESTROY_PIPE_Q;
} else {
*pipe_q_type = LAYER_USES_USED_PIPE_Q;
}
mutex_unlock(&mdp5_data->list_lock);
/* found the pipe from used, destroy or cleanup list */
if (!IS_ERR_OR_NULL(pipe)) {
if (pipe->mixer_left != mixer) {
if (!mixer->ctl || (mixer->ctl->mfd != mfd)) {
pr_err("Can't switch mixer %d->%d pnum %d!\n",
pipe->mixer_left->num, mixer->num,
pipe->num);
pipe = ERR_PTR(-EINVAL);
goto end;
}
pr_debug("switching pipe%d mixer %d->%d\n",
pipe->num,
pipe->mixer_left ? pipe->mixer_left->num : -1,
mixer->num);
mdss_mdp_mixer_pipe_unstage(pipe, pipe->mixer_left);
pipe->mixer_left = mixer;
}
goto end;
}
pipe = mdss_mdp_pipe_assign(mdata, mixer, pipe_ndx, rect_num);
if (IS_ERR_OR_NULL(pipe)) {
pr_err("error reserving pipe. pipe_ndx=0x%x rect_num=%d mfd ndx=%d\n",
pipe_ndx, rect_num, mfd->index);
goto end;
}
mutex_lock(&mdp5_data->list_lock);
list_add(&pipe->list, &mdp5_data->pipes_used);
mutex_unlock(&mdp5_data->list_lock);
end:
if (!IS_ERR_OR_NULL(pipe)) {
pipe->dirty = false;
pipe->params_changed++;
}
return pipe;
}
/*
* __is_sd_state_valid() - validate secure display state
*
* This function checks if the current state of secrure display is valid,
* based on the new settings.
* For command mode panels, the sd state would be invalid if a non secure pipe
* comes and one of the below condition is met:
* 1) Secure Display is enabled for current client, and there is other
secure client.
* 2) Secure Display is disabled for current client, and there is other
secure client.
* 3) Secure pipes are already staged for the current client.
* For other panels, the sd state would be invalid if a non secure pipe comes
* and one of the below condition is met:
* 1) Secure Display is enabled for current or other client.
* 2) Secure pipes are already staged for the current client.
*
*/
static inline bool __is_sd_state_valid(uint32_t sd_pipes, uint32_t nonsd_pipes,
int panel_type, u32 sd_enabled)
{
if (panel_type == MIPI_CMD_PANEL) {
if ((((mdss_get_sd_client_cnt() > 1) && sd_enabled) ||
(mdss_get_sd_client_cnt() && !sd_enabled) ||
sd_pipes)
&& nonsd_pipes)
return false;
} else {
if ((sd_pipes || mdss_get_sd_client_cnt()) && nonsd_pipes)
return false;
}
return true;
}
/*
* __validate_secure_display() - validate secure display
*
* This function travers through used pipe list and checks if any pipe
* is with secure display enabled flag. It fails if client tries to stage
* unsecure content with secure display session.
*
*/
static int __validate_secure_display(struct mdss_overlay_private *mdp5_data)
{
struct mdss_mdp_pipe *pipe, *tmp;
uint32_t sd_pipes = 0, nonsd_pipes = 0;
int panel_type = mdp5_data->ctl->panel_data->panel_info.type;
int ret = 0;
mutex_lock(&mdp5_data->list_lock);
list_for_each_entry_safe(pipe, tmp, &mdp5_data->pipes_used, list) {
if (pipe->flags & MDP_SECURE_DISPLAY_OVERLAY_SESSION)
sd_pipes++;
else
nonsd_pipes++;
}
mutex_unlock(&mdp5_data->list_lock);
pr_debug("pipe count:: secure display:%d non-secure:%d\n",
sd_pipes, nonsd_pipes);
mdp5_data->sd_transition_state = SD_TRANSITION_NONE;
if (!__is_sd_state_valid(sd_pipes, nonsd_pipes, panel_type,
mdp5_data->sd_enabled)) {
pr_err("non-secure layer validation request during secure display session\n");
pr_err(" secure client cnt:%d secure pipe cnt:%d non-secure pipe cnt:%d\n",
mdss_get_sd_client_cnt(), sd_pipes, nonsd_pipes);
ret = -EINVAL;
} else if (!mdp5_data->sd_enabled && sd_pipes) {
mdp5_data->sd_transition_state =
SD_TRANSITION_NON_SECURE_TO_SECURE;
} else if (mdp5_data->sd_enabled && !sd_pipes) {
mdp5_data->sd_transition_state =
SD_TRANSITION_SECURE_TO_NON_SECURE;
}
return ret;
}
/*
* __handle_free_list() - updates free pipe list
*
* This function travers through used pipe list and checks if any pipe
* is not staged in current validation cycle. It moves the pipe to cleanup
* list if no layer is attached for that pipe.
*
* This should be called after validation is successful for current cycle.
* Moving pipes before can affects staged pipe for previous cycle.
*/
static void __handle_free_list(struct mdss_overlay_private *mdp5_data,
struct mdss_mdp_validate_info_t *validate_info_list, u32 layer_count)
{
int i;
struct mdp_input_layer *layer;
struct mdss_mdp_validate_info_t *vinfo;
struct mdss_mdp_pipe *pipe, *tmp;
mutex_lock(&mdp5_data->list_lock);
list_for_each_entry_safe(pipe, tmp, &mdp5_data->pipes_used, list) {
for (i = 0; i < layer_count; i++) {
vinfo = &validate_info_list[i];
layer = vinfo->layer;
if ((pipe->ndx == layer->pipe_ndx) &&
(pipe->multirect.num == vinfo->multirect.num))
break;
}
/*
* if validate cycle is not attaching any layer for this
* pipe then move it to cleanup list. It does overlay_unset
* task.
*/
if (i == layer_count)
list_move(&pipe->list, &mdp5_data->pipes_cleanup);
}
mutex_unlock(&mdp5_data->list_lock);
}
static bool __multirect_validate_flip(struct mdp_input_layer **layers,
size_t count)
{
/* not supporting more than 2 layers */
if (count != 2)
return false;
/* flip related validation */
if ((layers[0]->flags & MDP_LAYER_FLIP_LR) ||
(layers[1]->flags & MDP_LAYER_FLIP_LR)) {
pr_err("multirect and HFLIP is not allowed. input layer flags=0x%x paired layer flags=0x%x\n",
layers[0]->flags, layers[1]->flags);
return false;
}
if ((layers[0]->flags & MDP_LAYER_FLIP_UD) !=
(layers[1]->flags & MDP_LAYER_FLIP_UD)) {
pr_err("multirect VLFIP mismatch is not allowed\n");
return false;
}
return true;
}
static bool __multirect_validate_format(struct mdp_input_layer **layers,
size_t count)
{
struct mdss_mdp_format_params *rec0_fmt, *rec1_fmt;
bool is_ubwc;
/* not supporting more than 2 layers */
if (count != 2)
return false;
/* format related validation */
rec0_fmt = mdss_mdp_get_format_params(layers[0]->buffer.format);
if (!rec0_fmt) {
pr_err("invalid input layer format %d\n",
layers[0]->buffer.format);
return false;
}
rec1_fmt = mdss_mdp_get_format_params(layers[1]->buffer.format);
if (!rec1_fmt) {
pr_err("invalid paired layer format %d\n",
layers[1]->buffer.format);
return false;
}
if (rec0_fmt->is_yuv || rec1_fmt->is_yuv) {
pr_err("multirect on YUV format is not supported. input=%d paired=%d\n",
rec0_fmt->is_yuv, rec1_fmt->is_yuv);
return false;
}
if (rec0_fmt->fetch_mode != rec1_fmt->fetch_mode) {
pr_err("multirect fetch_mode mismatch is not allowed. input=%d paired=%d\n",
rec0_fmt->fetch_mode, rec1_fmt->fetch_mode);
return false;
}
is_ubwc = mdss_mdp_is_ubwc_format(rec0_fmt);
if (is_ubwc && (rec0_fmt != rec1_fmt)) {
pr_err("multirect UBWC format mismatch is not allowed\n");
return false;
} else if (rec0_fmt->bpp != rec1_fmt->bpp) {
pr_err("multirect linear format bpp mismatch is not allowed. input=%d paired=%d\n",
rec0_fmt->bpp, rec1_fmt->bpp);
return false;
} else if (rec0_fmt->unpack_dx_format != rec1_fmt->unpack_dx_format) {
pr_err("multirect linear format 10bit vs 8bit mismatch is not allowed. input=%d paired=%d\n",
rec0_fmt->unpack_dx_format, rec1_fmt->unpack_dx_format);
return false;
}
if ((layers[0]->flags & MDP_LAYER_SOLID_FILL) !=
(layers[1]->flags & MDP_LAYER_SOLID_FILL)) {
pr_err("solid fill mismatch between multirect layers\n");
return false;
}
return true;
}
static bool __multirect_validate_rects(struct mdp_input_layer **layers,
size_t count)
{
struct mdss_rect dst[MDSS_MDP_PIPE_MAX_RECTS];
int i;
/* not supporting more than 2 layers */
if (count != 2)
return false;
for (i = 0; i < count; i++) {
if ((layers[i]->src_rect.w != layers[i]->dst_rect.w) ||
(layers[i]->src_rect.h != layers[i]->dst_rect.h)) {
pr_err("multirect layers cannot have scaling: src: %dx%d dst: %dx%d\n",
layers[i]->src_rect.w, layers[i]->src_rect.h,
layers[i]->dst_rect.w, layers[i]->dst_rect.h);
return false;
}
dst[i] = (struct mdss_rect) {layers[i]->dst_rect.x,
layers[i]->dst_rect.y,
layers[i]->dst_rect.w,
layers[i]->dst_rect.h};
}
/* resolution related validation */
if (mdss_rect_overlap_check(&dst[0], &dst[1])) {
pr_err("multirect dst overlap is not allowed. input: %d,%d,%d,%d paired %d,%d,%d,%d\n",
dst[0].x, dst[0].y, dst[0].w, dst[0].y,
dst[1].x, dst[1].y, dst[1].w, dst[1].y);
return false;
}
return true;
}
static bool __multirect_validate_properties(struct mdp_input_layer **layers,
size_t count)
{
/* not supporting more than 2 layers */
if (count != 2)
return false;
if ((layers[0]->flags & MDP_LAYER_ASYNC) ||
(layers[1]->flags & MDP_LAYER_ASYNC)) {
pr_err("ASYNC update is not allowed with multirect\n");
return false;
}
if (layers[0]->z_order == layers[1]->z_order) {
pr_err("multirect layers cannot have same z_order=%d\n",
layers[0]->z_order);
return false;
}
return true;
}
static bool (*__multirect_validators[])(struct mdp_input_layer **layers,
size_t count) = {
__multirect_validate_flip,
__multirect_validate_format,
__multirect_validate_rects,
__multirect_validate_properties,
};
static inline int __multirect_layer_flags_to_mode(u32 flags)
{
int mode;
if (flags & MDP_LAYER_MULTIRECT_ENABLE) {
if (flags & MDP_LAYER_MULTIRECT_PARALLEL_MODE)
mode = MDSS_MDP_PIPE_MULTIRECT_PARALLEL;
else
mode = MDSS_MDP_PIPE_MULTIRECT_SERIAL;
} else {
if (flags & MDP_LAYER_MULTIRECT_PARALLEL_MODE) {
pr_err("Invalid parallel mode flag set without multirect enabled\n");
return -EINVAL;
}
mode = MDSS_MDP_PIPE_MULTIRECT_NONE;
}
return mode;
}
static int __multirect_validate_mode(struct msm_fb_data_type *mfd,
struct mdp_input_layer **layers,
size_t count)
{
struct mdss_data_type *mdata = mfd_to_mdata(mfd);
struct mdss_mdp_format_params *rec0_fmt;
bool is_ubwc;
int i, mode;
struct mdp_rect *dst[MDSS_MDP_PIPE_MAX_RECTS];
/* not supporting more than 2 layers */
if (count != 2)
return false;
for (i = 0; i < count; i++)
dst[i] = &layers[i]->dst_rect;
mode = __multirect_layer_flags_to_mode(layers[0]->flags);
/* format related validation */
rec0_fmt = mdss_mdp_get_format_params(layers[0]->buffer.format);
if (!rec0_fmt) {
pr_err("invalid input layer format %d\n",
layers[0]->buffer.format);
return false;
}
is_ubwc = mdss_mdp_is_ubwc_format(rec0_fmt);
if (mode == MDSS_MDP_PIPE_MULTIRECT_SERIAL) {
int threshold, yoffset;
if (dst[0]->y < dst[1]->y)
yoffset = dst[1]->y - (dst[0]->y + dst[0]->h);
else if (dst[1]->y < dst[0]->y)
yoffset = dst[0]->y - (dst[1]->y + dst[1]->h);
else
yoffset = 0;
/*
* time multiplexed is possible only if the y position of layers
* is not overlapping and there is sufficient time to buffer
* 2 lines/tiles. Otherwise use parallel fetch mode
*/
threshold = 2;
if (is_ubwc) {
struct mdss_mdp_format_params_ubwc *uf;
/* in ubwc all layers would need to be same format */
uf = (struct mdss_mdp_format_params_ubwc *)rec0_fmt;
threshold *= uf->micro.tile_height;
}
if (yoffset < threshold) {
pr_err("Unable to operate in serial fetch mode with yoffset=%d dst[0]=%d,%d dst[1]=%d,%d\n",
yoffset, dst[0]->y, dst[0]->h,
dst[1]->y, dst[1]->h);
return -EINVAL;
}
} else if (mode == MDSS_MDP_PIPE_MULTIRECT_PARALLEL) {
u32 left_lm_w, rec0_mixer, rec1_mixer;
/*
* For UBWC, 5 lines worth of buffering is needed in to meet
* the performance which requires 2560w*4bpp*5lines = 50KB,
* where 2560 is max width. Now let's say pixel ram is fixed to
* 50KB then in UBWC parellel fetch, maximum width of each
* rectangle would be 2560/2 = 1280.
*
* For Linear, this restriction is avoided because maximum
* buffering of 2 lines is enough which yields to
* 2560w*4bpp*2lines=20KB. Based on this, we can have 2 max
* width rectangles in parrellel fetch mode.
*/
if (is_ubwc &&
((dst[0]->w > (mdata->max_mixer_width / 2)) ||
(dst[1]->w > (mdata->max_mixer_width / 2)))) {
pr_err("in UBWC multirect parallel mode, max dst_w cannot be greater than %d. rec0_w=%d rec1_w=%d\n",
mdata->max_mixer_width / 2,
dst[0]->w, dst[1]->w);
return -EINVAL;
}
left_lm_w = left_lm_w_from_mfd(mfd);
if (dst[0]->x < left_lm_w) {
if (dst[0]->w > (left_lm_w - dst[0]->x)) {
pr_err("multirect parallel mode, rec0 dst (%d,%d) cannot cross lm boundary (%d)\n",
dst[0]->x, dst[0]->w, left_lm_w);
return -EINVAL;
}
rec0_mixer = MDSS_MDP_MIXER_MUX_LEFT;
} else {
rec0_mixer = MDSS_MDP_MIXER_MUX_RIGHT;
}
if (dst[1]->x < left_lm_w) {
if (dst[0]->w > (left_lm_w - dst[0]->x)) {
pr_err("multirect parallel mode, rec1 dst (%d,%d) cannot cross lm boundary (%d)\n",
dst[1]->x, dst[1]->w, left_lm_w);
return -EINVAL;
}
rec1_mixer = MDSS_MDP_MIXER_MUX_LEFT;
} else {
rec1_mixer = MDSS_MDP_MIXER_MUX_RIGHT;
}
if (rec0_mixer != rec1_mixer) {
pr_err("multirect parallel mode mixer mismatch. rec0_mix=%d rec1_mix=%d\n",
rec0_mixer, rec1_mixer);
return -EINVAL;
}
} else {
pr_err("Invalid multirect mode %d\n", mode);
}
pr_debug("layer->pndx:%d mode=%d\n", layers[0]->pipe_ndx, mode);
return 0;
}
static int __update_multirect_info(struct msm_fb_data_type *mfd,
struct mdss_mdp_validate_info_t *validate_info_list,
struct mdp_input_layer *layer_list, int ndx, int layer_cnt)
{
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
struct mdss_mdp_validate_info_t *vinfo[MDSS_MDP_PIPE_MAX_RECTS];
int i, ptype, max_rects, mode;
int cnt = 1;
mode = __multirect_layer_flags_to_mode(layer_list[ndx].flags);
if (IS_ERR_VALUE((unsigned long)mode))
return mode;
pr_debug("layer #%d pipe_ndx=%d multirect mode=%d\n",
ndx, layer_list[ndx].pipe_ndx, mode);
vinfo[0] = &validate_info_list[ndx];
vinfo[0]->layer = &layer_list[ndx];
vinfo[0]->multirect.mode = mode;
vinfo[0]->multirect.num = MDSS_MDP_PIPE_RECT0;
vinfo[0]->multirect.next = NULL;
/* nothing to be done if multirect is disabled */
if (mode == MDSS_MDP_PIPE_MULTIRECT_NONE)
return cnt;
ptype = get_pipe_type_from_ndx(layer_list[ndx].pipe_ndx);
if (ptype == MDSS_MDP_PIPE_TYPE_INVALID) {
pr_err("invalid pipe ndx %d\n", layer_list[ndx].pipe_ndx);
return -EINVAL;
}
max_rects = mdata->rects_per_sspp[ptype] ? : 1;
for (i = ndx + 1; i < layer_cnt; i++) {
if (layer_list[ndx].pipe_ndx == layer_list[i].pipe_ndx) {
if (cnt >= max_rects) {
pr_err("more than %d layers of type %d with same pipe_ndx=%d indexes=%d %d\n",
max_rects, ptype,
layer_list[ndx].pipe_ndx, ndx, i);
return -EINVAL;
}
mode = __multirect_layer_flags_to_mode(
layer_list[i].flags);
if (IS_ERR_VALUE((unsigned long)mode))
return mode;
if (mode != vinfo[0]->multirect.mode) {
pr_err("unable to set different multirect modes for pipe_ndx=%d (%d %d)\n",
layer_list[ndx].pipe_ndx, ndx, i);
return -EINVAL;
}
pr_debug("found matching pair for pipe_ndx=%d (%d %d)\n",
layer_list[i].pipe_ndx, ndx, i);
vinfo[cnt] = &validate_info_list[i];
vinfo[cnt]->multirect.num = cnt;
vinfo[cnt]->multirect.next = vinfo[0]->layer;
vinfo[cnt]->multirect.mode = mode;
vinfo[cnt]->layer = &layer_list[i];
vinfo[cnt - 1]->multirect.next = vinfo[cnt]->layer;
cnt++;
}
}
if (cnt == 1) {
pr_err("multirect mode enabled but unable to find extra rects for pipe_ndx=%x\n",
layer_list[ndx].pipe_ndx);
return -EINVAL;
}
return cnt;
}
static int __validate_multirect(struct msm_fb_data_type *mfd,
struct mdss_mdp_validate_info_t *validate_info_list,
struct mdp_input_layer *layer_list, int ndx, int layer_cnt)
{
struct mdp_input_layer *layers[MDSS_MDP_PIPE_MAX_RECTS] = { 0 };
int i, cnt, rc;
cnt = __update_multirect_info(mfd, validate_info_list,
layer_list, ndx, layer_cnt);
if (IS_ERR_VALUE((unsigned long)cnt))
return cnt;
if (cnt <= 1) {
/* nothing to validate in single rect mode */
return 0;
} else if (cnt > 2) {
pr_err("unsupported multirect configuration, multirect cnt=%d\n",
cnt);
return -EINVAL;
}
layers[0] = validate_info_list[ndx].layer;
layers[1] = validate_info_list[ndx].multirect.next;
for (i = 0; i < ARRAY_SIZE(__multirect_validators); i++) {
if (!__multirect_validators[i](layers, cnt))
return -EINVAL;
}
rc = __multirect_validate_mode(mfd, layers, cnt);
if (IS_ERR_VALUE((unsigned long)rc))
return rc;
return 0;
}
/*
* __validate_layers() - validate input layers
* @mfd: Framebuffer data structure for display
* @commit: Commit version-1 structure for display
*
* This function validates all input layers present in layer_list. In case
* of failure, it updates the "error_code" for failed layer. It is possible
* to find failed layer from layer_list based on "error_code".
*/
static int __validate_layers(struct msm_fb_data_type *mfd,
struct file *file, struct mdp_layer_commit_v1 *commit)
{
int ret, i = 0;
int rec_ndx[MDSS_MDP_PIPE_MAX_RECTS] = { 0 };
int rec_release_ndx[MDSS_MDP_PIPE_MAX_RECTS] = { 0 };
int rec_destroy_ndx[MDSS_MDP_PIPE_MAX_RECTS] = { 0 };
u32 left_lm_layers = 0, right_lm_layers = 0;
u32 left_cnt = 0, right_cnt = 0;
u32 left_lm_w = left_lm_w_from_mfd(mfd);
u32 mixer_mux, dst_x;
int layer_count = commit->input_layer_cnt;
struct mdss_mdp_pipe *pipe = NULL, *tmp, *left_blend_pipe;
struct mdss_mdp_pipe *right_plist[MAX_PIPES_PER_LM] = {0};
struct mdss_mdp_pipe *left_plist[MAX_PIPES_PER_LM] = {0};
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
struct mdss_mdp_mixer *mixer = NULL;
struct mdp_input_layer *layer, *prev_layer, *layer_list;
struct mdss_mdp_validate_info_t *validate_info_list = NULL;
bool is_single_layer = false, force_validate;
enum layer_pipe_q pipe_q_type;
enum layer_zorder_used zorder_used[MDSS_MDP_MAX_STAGE] = {0};
enum mdss_mdp_pipe_rect rect_num;
ret = mutex_lock_interruptible(&mdp5_data->ov_lock);
if (ret)
return ret;
if (!layer_count)
goto validate_skip;
layer_list = commit->input_layers;
validate_info_list = kcalloc(layer_count, sizeof(*validate_info_list),
GFP_KERNEL);
if (!validate_info_list) {
ret = -ENOMEM;
goto end;
}
for (i = 0; i < layer_count; i++) {
if (layer_list[i].dst_rect.x >= left_lm_w)
right_lm_layers++;
else
left_lm_layers++;
if (right_lm_layers >= MAX_PIPES_PER_LM ||
left_lm_layers >= MAX_PIPES_PER_LM) {
pr_err("too many pipes stagged mixer left: %d mixer right:%d\n",
left_lm_layers, right_lm_layers);
ret = -EINVAL;
goto end;
}
if (!validate_info_list[i].layer) {
ret = __validate_multirect(mfd, validate_info_list,
layer_list, i, layer_count);
if (ret) {
pr_err("error validating multirect config. ret=%d i=%d\n",
ret, i);
goto end;
}
}
rect_num = validate_info_list[i].multirect.num;
WARN_ON(rect_num >= MDSS_MDP_PIPE_MAX_RECTS);
if (rec_ndx[rect_num] & layer_list[i].pipe_ndx) {
pr_err("duplicate layer found pipe_ndx=%d rect=%d (0x%x)\n",
layer_list[i].pipe_ndx, rect_num,
rec_ndx[rect_num]);
ret = -EINVAL;
goto end;
}
rec_ndx[rect_num] |= layer_list[i].pipe_ndx;
}
/*
* Force all layers to go through full validation after
* dynamic resolution switch, immaterial of the configs in
* the layer.
*/
mutex_lock(&mfd->switch_lock);
force_validate = (mfd->switch_state != MDSS_MDP_NO_UPDATE_REQUESTED);
mutex_unlock(&mfd->switch_lock);
for (i = 0; i < layer_count; i++) {
enum layer_zorder_used z = LAYER_ZORDER_NONE;
layer = &layer_list[i];
dst_x = layer->dst_rect.x;
left_blend_pipe = NULL;
prev_layer = (i > 0) ? &layer_list[i - 1] : NULL;
/*
* check if current layer is at same z_order as
* previous one, and fail if any or both are async layers,
* as async layers should have unique z_order.
*
* If it has same z_order and qualifies as a right blend,
* pass a pointer to the pipe representing previous overlay or
* in other terms left blend layer.
*
* Following logic of selecting left_blend has an inherent
* assumption that layer list is sorted on dst_x within a
* same z_order. Otherwise it will fail based on z_order checks.
*/
if (prev_layer && (prev_layer->z_order == layer->z_order)) {
struct mdp_rect *left = &prev_layer->dst_rect;
struct mdp_rect *right = &layer->dst_rect;
if ((layer->flags & MDP_LAYER_ASYNC)
|| (prev_layer->flags & MDP_LAYER_ASYNC)) {
ret = -EINVAL;
layer->error_code = ret;
pr_err("async layer should have unique z_order\n");
goto validate_exit;
}
/*
* check if layer is right blend by checking it's
* directly to the right.
*/
if (((left->x + left->w) == right->x) &&
(left->y == right->y) && (left->h == right->h))
left_blend_pipe = pipe;
/*
* if the layer is right at the left lm boundary and
* src split is not required then right blend is not
* required as it will lie only on the left mixer
*/
if (!__layer_needs_src_split(prev_layer) &&
((left->x + left->w) == left_lm_w))
left_blend_pipe = NULL;
}
if (!is_split_lm(mfd) || __layer_needs_src_split(layer))
z = LAYER_ZORDER_BOTH;
else if (dst_x >= left_lm_w)
z = LAYER_ZORDER_RIGHT;
else if ((dst_x + layer->dst_rect.w) <= left_lm_w)
z = LAYER_ZORDER_LEFT;
else
z = LAYER_ZORDER_BOTH;
if (!left_blend_pipe && (layer->z_order >= MDSS_MDP_MAX_STAGE ||
(z & zorder_used[layer->z_order]))) {
pr_err("invalid z_order=%d or already in use %x\n",
layer->z_order, z);
ret = -EINVAL;
layer->error_code = ret;
goto validate_exit;
} else {
zorder_used[layer->z_order] |= z;
}
if ((layer->dst_rect.x < left_lm_w) ||
__layer_needs_src_split(layer)) {
is_single_layer = (left_lm_layers == 1);
mixer_mux = MDSS_MDP_MIXER_MUX_LEFT;
} else {
is_single_layer = (right_lm_layers == 1);
mixer_mux = MDSS_MDP_MIXER_MUX_RIGHT;
}
/**
* search pipe in current used list to find if parameters
* are same. validation can be skipped if only buffer handle
* is changed.
*/
pipe = (force_validate) ? NULL :
__find_layer_in_validate_q(
&validate_info_list[i], mdp5_data);
if (pipe) {
if (mixer_mux == MDSS_MDP_MIXER_MUX_RIGHT)
right_plist[right_cnt++] = pipe;
else
left_plist[left_cnt++] = pipe;
if (layer->flags & MDP_LAYER_PP) {
memcpy(&pipe->pp_cfg, layer->pp_info,
sizeof(struct mdp_overlay_pp_params));
ret = mdss_mdp_pp_sspp_config(pipe);
if (ret)
pr_err("pp setup failed %d\n", ret);
else
pipe->params_changed++;
}
pipe->dirty = false;
continue;
}
mixer = mdss_mdp_mixer_get(mdp5_data->ctl, mixer_mux);
if (!mixer) {
pr_err("unable to get %s mixer\n",
(mixer_mux == MDSS_MDP_MIXER_MUX_RIGHT) ?
"right" : "left");
ret = -EINVAL;
layer->error_code = ret;
goto validate_exit;
}
layer->z_order += MDSS_MDP_STAGE_0;
ret = __validate_single_layer(mfd, &validate_info_list[i],
mixer_mux);
if (ret) {
pr_err("layer:%d validation failed ret=%d\n", i, ret);
layer->error_code = ret;
goto validate_exit;
}
rect_num = validate_info_list[i].multirect.num;
pipe = __assign_pipe_for_layer(mfd, mixer, layer->pipe_ndx,
&pipe_q_type, rect_num);
if (IS_ERR_OR_NULL(pipe)) {
pr_err("error assigning pipe id=0x%x rc:%ld\n",
layer->pipe_ndx, PTR_ERR(pipe));
ret = PTR_ERR(pipe);
layer->error_code = ret;
goto validate_exit;
}
if (pipe_q_type == LAYER_USES_NEW_PIPE_Q)
rec_release_ndx[rect_num] |= pipe->ndx;
if (pipe_q_type == LAYER_USES_DESTROY_PIPE_Q)
rec_destroy_ndx[rect_num] |= pipe->ndx;
ret = mdss_mdp_pipe_map(pipe);
if (IS_ERR_VALUE((unsigned long)ret)) {
pr_err("Unable to map used pipe%d ndx=%x\n",
pipe->num, pipe->ndx);
layer->error_code = ret;
goto validate_exit;
}
if (pipe_q_type == LAYER_USES_USED_PIPE_Q) {
/*
* reconfig is allowed on new/destroy pipes. Only used
* pipe needs this extra validation.
*/
ret = __validate_layer_reconfig(layer, pipe);
if (ret) {
pr_err("layer reconfig validation failed=%d\n",
ret);
mdss_mdp_pipe_unmap(pipe);
layer->error_code = ret;
goto validate_exit;
}
}
ret = __configure_pipe_params(mfd, &validate_info_list[i], pipe,
left_blend_pipe, is_single_layer, mixer_mux);
if (ret) {
pr_err("configure pipe param failed: pipe index= %d\n",
pipe->ndx);
mdss_mdp_pipe_unmap(pipe);
layer->error_code = ret;
goto validate_exit;
}
mdss_mdp_pipe_unmap(pipe);
/* keep the original copy of dst_x */
pipe->layer.dst_rect.x = layer->dst_rect.x = dst_x;
if (mixer_mux == MDSS_MDP_MIXER_MUX_RIGHT)
right_plist[right_cnt++] = pipe;
else
left_plist[left_cnt++] = pipe;
pr_debug("id:0x%x flags:0x%x dst_x:%d\n",
layer->pipe_ndx, layer->flags, layer->dst_rect.x);
layer->z_order -= MDSS_MDP_STAGE_0;
}
ret = mdss_mdp_perf_bw_check(mdp5_data->ctl, left_plist, left_cnt,
right_plist, right_cnt);
if (ret) {
pr_err("bw validation check failed: %d\n", ret);
goto validate_exit;
}
validate_skip:
__handle_free_list(mdp5_data, validate_info_list, layer_count);
ret = __validate_secure_display(mdp5_data);
validate_exit:
pr_debug("err=%d total_layer:%d left:%d right:%d rec0_rel_ndx=0x%x rec1_rel_ndx=0x%x rec0_destroy_ndx=0x%x rec1_destroy_ndx=0x%x processed=%d\n",
ret, layer_count, left_lm_layers, right_lm_layers,
rec_release_ndx[0], rec_release_ndx[1],
rec_destroy_ndx[0], rec_destroy_ndx[1], i);
MDSS_XLOG(rec_ndx[0], rec_ndx[1], layer_count,
left_lm_layers, right_lm_layers,
rec_release_ndx[0], rec_release_ndx[1],
rec_destroy_ndx[0], rec_destroy_ndx[1], ret);
mutex_lock(&mdp5_data->list_lock);
list_for_each_entry_safe(pipe, tmp, &mdp5_data->pipes_used, list) {
if (IS_ERR_VALUE((unsigned long)ret)) {
if (((pipe->ndx & rec_release_ndx[0]) &&
(pipe->multirect.num == 0)) ||
((pipe->ndx & rec_release_ndx[1]) &&
(pipe->multirect.num == 1))) {
mdss_mdp_smp_unreserve(pipe);
pipe->params_changed = 0;
pipe->dirty = true;
if (!list_empty(&pipe->list))
list_del_init(&pipe->list);
mdss_mdp_pipe_destroy(pipe);
} else if (((pipe->ndx & rec_destroy_ndx[0]) &&
(pipe->multirect.num == 0)) ||
((pipe->ndx & rec_destroy_ndx[1]) &&
(pipe->multirect.num == 1))) {
/*
* cleanup/destroy list pipes should move back
* to destroy list. Next/current kickoff cycle
* will release the pipe because validate also
* acquires ov_lock.
*/
list_move(&pipe->list,
&mdp5_data->pipes_destroy);
}
} else {
pipe->file = file;
pr_debug("file pointer attached with pipe is %pK\n",
file);
}
}
mutex_unlock(&mdp5_data->list_lock);
end:
kfree(validate_info_list);
mutex_unlock(&mdp5_data->ov_lock);
pr_debug("fb%d validated layers =%d\n", mfd->index, i);
return ret;
}
/*
* __parse_frc_info() - parse frc info from userspace
* @mdp5_data: mdss data per FB device
* @input_frc: frc info from user space
*
* This function fills the FRC info of current device which will be used
* during following kickoff.
*/
static void __parse_frc_info(struct mdss_overlay_private *mdp5_data,
struct mdp_frc_info *input_frc)
{
struct mdss_mdp_ctl *ctl = mdp5_data->ctl;
struct mdss_mdp_frc_fsm *frc_fsm = mdp5_data->frc_fsm;
if (input_frc->flags & MDP_VIDEO_FRC_ENABLE) {
struct mdss_mdp_frc_info *frc_info = &frc_fsm->frc_info;
if (!frc_fsm->enable) {
/* init frc_fsm when first entry */
mdss_mdp_frc_fsm_init_state(frc_fsm);
/* keep vsync on when FRC is enabled */
ctl->ops.add_vsync_handler(ctl,
&ctl->frc_vsync_handler);
}
frc_info->cur_frc.frame_cnt = input_frc->frame_cnt;
frc_info->cur_frc.timestamp = input_frc->timestamp;
} else if (frc_fsm->enable) {
/* remove vsync handler when FRC is disabled */
ctl->ops.remove_vsync_handler(ctl, &ctl->frc_vsync_handler);
}
frc_fsm->enable = input_frc->flags & MDP_VIDEO_FRC_ENABLE;
pr_debug("frc_enable=%d\n", frc_fsm->enable);
}
/*
* mdss_mdp_layer_pre_commit() - pre commit validation for input layers
* @mfd: Framebuffer data structure for display
* @commit: Commit version-1 structure for display
*
* This function checks if layers present in commit request are already
* validated or not. If there is mismatch in validate and commit layers
* then it validate all input layers again. On successful validation, it
* maps the input layer buffer and creates release/retire fences.
*
* This function is called from client context and can return the error.
*/
int mdss_mdp_layer_pre_commit(struct msm_fb_data_type *mfd,
struct file *file, struct mdp_layer_commit_v1 *commit)
{
int ret, i;
int layer_count = commit->input_layer_cnt;
bool validate_failed = false;
struct mdss_mdp_pipe *pipe, *tmp;
struct mdp_input_layer *layer_list;
struct mdss_overlay_private *mdp5_data;
struct mdss_mdp_data *src_data[MDSS_MDP_MAX_SSPP];
struct mdss_mdp_validate_info_t *validate_info_list;
mdp5_data = mfd_to_mdp5_data(mfd);
if (!mdp5_data || !mdp5_data->ctl)
return -EINVAL;
layer_list = commit->input_layers;
/* handle null commit */
if (!layer_count) {
__handle_free_list(mdp5_data, NULL, layer_count);
/* Check for secure state transition. */
return __validate_secure_display(mdp5_data);
}
validate_info_list = kcalloc(layer_count, sizeof(*validate_info_list),
GFP_KERNEL);
if (!validate_info_list)
return -ENOMEM;
for (i = 0; i < layer_count; i++) {
if (!validate_info_list[i].layer) {
ret = __update_multirect_info(mfd, validate_info_list,
layer_list, i, layer_count);
if (IS_ERR_VALUE((unsigned long)ret)) {
pr_err("error updating multirect config. ret=%d i=%d\n",
ret, i);
goto end;
}
}
}
for (i = 0; i < layer_count; i++) {
pipe = __find_layer_in_validate_q(&validate_info_list[i],
mdp5_data);
if (!pipe) {
validate_failed = true;
break;
}
}
if (validate_failed) {
ret = __validate_layers(mfd, file, commit);
if (ret) {
pr_err("__validate_layers failed. rc=%d\n", ret);
goto end;
}
} else {
/*
* move unassigned pipes to cleanup list since commit
* supports validate+commit operation.
*/
__handle_free_list(mdp5_data, validate_info_list, layer_count);
}
i = 0;
mutex_lock(&mdp5_data->list_lock);
list_for_each_entry_safe(pipe, tmp, &mdp5_data->pipes_used, list) {
if (pipe->flags & MDP_SOLID_FILL) {
src_data[i] = NULL;
continue;
}
src_data[i] = __map_layer_buffer(mfd, pipe, validate_info_list,
layer_count);
if (IS_ERR_OR_NULL(src_data[i++])) {
i--;
mutex_unlock(&mdp5_data->list_lock);
ret = PTR_ERR(src_data[i]);
goto map_err;
}
}
mutex_unlock(&mdp5_data->list_lock);
ret = mdss_mdp_overlay_start(mfd);
if (ret) {
pr_err("unable to start overlay %d (%d)\n", mfd->index, ret);
goto map_err;
}
if (commit->frc_info)
__parse_frc_info(mdp5_data, commit->frc_info);
ret = __handle_buffer_fences(mfd, commit, layer_list);
map_err:
if (ret) {
mutex_lock(&mdp5_data->list_lock);
for (i--; i >= 0; i--)
if (src_data[i])
mdss_mdp_overlay_buf_free(mfd, src_data[i]);
mutex_unlock(&mdp5_data->list_lock);
}
end:
kfree(validate_info_list);
return ret;
}
/*
* mdss_mdp_layer_atomic_validate() - validate input layers
* @mfd: Framebuffer data structure for display
* @commit: Commit version-1 structure for display
*
* This function validates only input layers received from client. It
* does perform any validation for mdp_output_layer defined for writeback
* display.
*/
int mdss_mdp_layer_atomic_validate(struct msm_fb_data_type *mfd,
struct file *file, struct mdp_layer_commit_v1 *commit)
{
struct mdss_overlay_private *mdp5_data;
if (!mfd || !commit) {
pr_err("invalid input params\n");
return -EINVAL;
}
mdp5_data = mfd_to_mdp5_data(mfd);
if (!mdp5_data || !mdp5_data->ctl) {
pr_err("invalid input params\n");
return -ENODEV;
}
if (mdss_fb_is_power_off(mfd)) {
pr_err("display interface is in off state fb:%d\n",
mfd->index);
return -EPERM;
}
return __validate_layers(mfd, file, commit);
}
int mdss_mdp_layer_pre_commit_wfd(struct msm_fb_data_type *mfd,
struct file *file, struct mdp_layer_commit_v1 *commit)
{
int rc, count;
struct mdss_overlay_private *mdp5_data;
struct mdss_mdp_wfd *wfd = NULL;
struct mdp_output_layer *output_layer = NULL;
struct mdss_mdp_wfd_data *data = NULL;
struct mdss_fence *fence = NULL;
struct msm_sync_pt_data *sync_pt_data = NULL;
if (!mfd || !commit)
return -EINVAL;
mdp5_data = mfd_to_mdp5_data(mfd);
if (!mdp5_data || !mdp5_data->ctl || !mdp5_data->wfd) {
pr_err("invalid wfd state\n");
return -ENODEV;
}
if (commit->output_layer) {
wfd = mdp5_data->wfd;
output_layer = commit->output_layer;
if (output_layer->buffer.plane_count > MAX_PLANES) {
pr_err("Output buffer plane_count exceeds MAX_PLANES limit:%d\n",
output_layer->buffer.plane_count);
return -EINVAL;
}
data = mdss_mdp_wfd_add_data(wfd, output_layer);
if (IS_ERR_OR_NULL(data))
return PTR_ERR(data);
if (output_layer->buffer.fence >= 0) {
fence = mdss_get_fd_sync_fence(
output_layer->buffer.fence);
if (!fence) {
pr_err("fail to get output buffer fence\n");
rc = -EINVAL;
goto fence_get_err;
}
}
} else {
wfd = mdp5_data->wfd;
if (!wfd->ctl || !wfd->ctl->wb) {
pr_err("wfd commit with null out layer and no validate\n");
return -EINVAL;
}
}
rc = mdss_mdp_layer_pre_commit(mfd, file, commit);
if (rc) {
pr_err("fail to import input layer buffers. rc=%d\n", rc);
goto input_layer_err;
}
if (fence) {
sync_pt_data = &mfd->mdp_sync_pt_data;
mutex_lock(&sync_pt_data->sync_mutex);
count = sync_pt_data->acq_fen_cnt;
if (count >= MDP_MAX_FENCE_FD) {
pr_err("Reached maximum possible value for fence count\n");
mutex_unlock(&sync_pt_data->sync_mutex);
rc = -EINVAL;
goto input_layer_err;
}
sync_pt_data->acq_fen[count] = fence;
sync_pt_data->acq_fen_cnt++;
mutex_unlock(&sync_pt_data->sync_mutex);
}
return rc;
input_layer_err:
if (fence)
mdss_put_sync_fence(fence);
fence_get_err:
if (data)
mdss_mdp_wfd_remove_data(wfd, data);
return rc;
}
int mdss_mdp_layer_atomic_validate_wfd(struct msm_fb_data_type *mfd,
struct file *file, struct mdp_layer_commit_v1 *commit)
{
int rc = 0;
struct mdss_overlay_private *mdp5_data;
struct mdss_mdp_wfd *wfd;
struct mdp_output_layer *output_layer;
if (!mfd || !commit)
return -EINVAL;
mdp5_data = mfd_to_mdp5_data(mfd);
if (!mdp5_data || !mdp5_data->ctl || !mdp5_data->wfd) {
pr_err("invalid wfd state\n");
return -ENODEV;
}
if (!commit->output_layer) {
pr_err("no output layer defined\n");
return -EINVAL;
}
wfd = mdp5_data->wfd;
output_layer = commit->output_layer;
rc = mdss_mdp_wfd_validate(wfd, output_layer);
if (rc) {
pr_err("fail to validate the output layer = %d\n", rc);
goto validate_failed;
}
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON);
rc = mdss_mdp_wfd_setup(wfd, output_layer);
if (rc) {
pr_err("fail to prepare wfd = %d\n", rc);
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF);
goto validate_failed;
}
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF);
rc = mdss_mdp_layer_atomic_validate(mfd, file, commit);
if (rc) {
pr_err("fail to validate the input layers = %d\n", rc);
goto validate_failed;
}
validate_failed:
return rc;
}
int mdss_mdp_async_position_update(struct msm_fb_data_type *mfd,
struct mdp_position_update *update_pos)
{
int i, rc = 0;
struct mdss_mdp_pipe *pipe = NULL;
struct mdp_async_layer *layer;
struct mdss_rect dst, src;
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
u32 flush_bits = 0, inputndx = 0;
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON);
for (i = 0; i < update_pos->input_layer_cnt; i++) {
layer = &update_pos->input_layers[i];
mutex_lock(&mdp5_data->list_lock);
__find_pipe_in_list(&mdp5_data->pipes_used, layer->pipe_ndx,
&pipe, MDSS_MDP_PIPE_RECT0);
mutex_unlock(&mdp5_data->list_lock);
if (!pipe) {
pr_err("invalid pipe ndx=0x%x for async update\n",
layer->pipe_ndx);
rc = -ENODEV;
layer->error_code = rc;
goto done;
}
rc = __async_update_position_check(mfd, pipe, &layer->src,
&layer->dst);
if (rc) {
layer->error_code = rc;
goto done;
}
src = (struct mdss_rect) {layer->src.x, layer->src.y,
pipe->src.w, pipe->src.h};
dst = (struct mdss_rect) {layer->dst.x, layer->dst.y,
pipe->src.w, pipe->src.h};
pr_debug("src:{%d,%d,%d,%d}, dst:{%d,%d,%d,%d}\n",
src.x, src.y, src.w, src.h,
dst.x, dst.y, dst.w, dst.h);
mdss_mdp_pipe_position_update(pipe, &src, &dst);
flush_bits |= mdss_mdp_get_pipe_flush_bits(pipe);
inputndx |= layer->pipe_ndx;
}
mdss_mdp_async_ctl_flush(mfd, flush_bits);
done:
MDSS_XLOG(inputndx, update_pos->input_layer_cnt, flush_bits, rc);
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF);
return rc;
}