Hi Daoyuan, sorry for having been so long to come back to this! I know you have another version prepared, but here are a few extra things that may need attention. On Thu, May 16, 2019 at 12:25 PM Daoyuan Huang <daoyuan.huang@xxxxxxxxxxxx> wrote: > > From: daoyuan huang <daoyuan.huang@xxxxxxxxxxxx> > > This patch adds driver for Media Data Path 3 (MDP3). > Each modules' related operation control is sited in mtk-mdp3-comp.c > Each modules' register table is defined in file with "mdp_reg_" > and "mmsys_" prefix > GCE related API, operation control sited in mtk-mdp3-cmdq.c > V4L2 m2m device functions are implemented in mtk-mdp3-m2m.c > Probe, power, suspend/resume, system level functions are defined in > mtk-mdp3-core.c > > Signed-off-by: Ping-Hsun Wu <ping-hsun.wu@xxxxxxxxxxxx> > Signed-off-by: daoyuan huang <daoyuan.huang@xxxxxxxxxxxx> [snip] > diff --git a/drivers/media/platform/mtk-mdp3/mtk-img-ipi.h b/drivers/media/platform/mtk-mdp3/mtk-img-ipi.h > new file mode 100644 > index 000000000000..9fabe7e8b71d > --- /dev/null > +++ b/drivers/media/platform/mtk-mdp3/mtk-img-ipi.h > @@ -0,0 +1,282 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Copyright (c) 2018 MediaTek Inc. > + * Author: Holmes Chiou <holmes.chiou@xxxxxxxxxxxx> > + * Ping-Hsun Wu <ping-hsun.wu@xxxxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License 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. > + */ > + > +#ifndef __MTK_IMG_IPI_H__ > +#define __MTK_IMG_IPI_H__ > + > +#include <linux/types.h> > + > +/* ISP-MDP generic input information */ > + > +#define IMG_MAX_HW_INPUTS 1 > + > +#define IMG_MAX_HW_OUTPUTS 4 > + > +#define IMG_MAX_PLANES 3 > + > +#define IMG_IPI_INIT 1 > +#define IMG_IPI_DEINIT 2 > +#define IMG_IPI_FRAME 3 > +#define IMG_IPI_DEBUG 4 > + > +struct img_addr { > + u64 va; /* Used by Linux OS access */ > + u32 pa; /* Used by CM4 access */ > + u32 iova; /* Used by IOMMU HW access */ > +} __attribute__ ((__packed__)); > + > +struct img_sw_addr { > + u64 va; /* Used by APMCU access */ > + u32 pa; /* Used by CM4 access */ > +} __attribute__ ((__packed__)); > + > +struct img_plane_format { > + u32 size; > + u16 stride; > +} __attribute__ ((__packed__)); > + > +struct img_pix_format { > + u16 width; > + u16 height; > + u32 colorformat; /* enum mdp_color */ > + u16 ycbcr_prof; /* enum mdp_ycbcr_profile */ > + struct img_plane_format plane_fmt[IMG_MAX_PLANES]; > +} __attribute__ ((__packed__)); > + > +struct img_image_buffer { > + struct img_pix_format format; > + u32 iova[IMG_MAX_PLANES]; > + /* enum mdp_buffer_usage, FD or advanced ISP usages */ > + u32 usage; > +} __attribute__ ((__packed__)); > + > +#define IMG_SUBPIXEL_SHIFT 20 > + > +struct img_crop { > + s16 left; > + s16 top; One tab too much here? Looks like there are similar indentation issues elsewhere as well, please confirm. > + u16 width; > + u16 height; > + u32 left_subpix; > + u32 top_subpix; > + u32 width_subpix; > + u32 height_subpix; > +} __attribute__ ((__packed__)); > + > +#define IMG_CTRL_FLAG_HFLIP BIT(0) > +#define IMG_CTRL_FLAG_DITHER BIT(1) > +#define IMG_CTRL_FLAG_SHARPNESS BIT(4) > +#define IMG_CTRL_FLAG_HDR BIT(5) > +#define IMG_CTRL_FLAG_DRE BIT(6) > + > +struct img_input { > + struct img_image_buffer buffer; > + u16 flags; /* HDR, DRE, dither */ > +} __attribute__ ((__packed__)); > + > +struct img_output { > + struct img_image_buffer buffer; > + struct img_crop crop; > + s16 rotation; > + u16 flags; /* H-flip, sharpness, dither */ > +} __attribute__ ((__packed__)); > + > +struct img_ipi_frameparam { > + u32 index; > + u32 frame_no; > + u64 timestamp; > + u8 type; /* enum mdp_stream_type */ > + u8 state; > + u8 num_inputs; > + u8 num_outputs; > + u64 drv_data; > + struct img_input inputs[IMG_MAX_HW_INPUTS]; > + struct img_output outputs[IMG_MAX_HW_OUTPUTS]; > + struct img_addr tuning_data; > + struct img_addr subfrm_data; > + struct img_sw_addr config_data; > + struct img_sw_addr self_data; > + /* u8 pq_data[]; */ > +} __attribute__ ((__packed__)); > + > +struct img_ipi_param { > + u8 usage; > + struct img_sw_addr frm_param; > +} __attribute__ ((__packed__)); > + > +struct img_frameparam { > + struct list_head list_entry; > + struct img_ipi_frameparam frameparam; > +}; > + > +/* ISP-MDP generic output information */ > + > +struct img_comp_frame { > + u32 output_disable:1; > + u32 bypass:1; > + u16 in_width; > + u16 in_height; > + u16 out_width; > + u16 out_height; > + struct img_crop crop; > + u16 in_total_width; > + u16 out_total_width; > +} __attribute__ ((__packed__)); > + > +struct img_region { > + s16 left; > + s16 right; > + s16 top; > + s16 bottom; > +} __attribute__ ((__packed__)); > + > +struct img_offset { > + s16 left; > + s16 top; > + u32 left_subpix; > + u32 top_subpix; > +} __attribute__ ((__packed__)); > + > +struct img_comp_subfrm { > + u32 tile_disable:1; > + struct img_region in; > + struct img_region out; > + struct img_offset luma; > + struct img_offset chroma; > + s16 out_vertical; /* Output vertical index */ > + s16 out_horizontal; /* Output horizontal index */ > +} __attribute__ ((__packed__)); > + > +#define IMG_MAX_SUBFRAMES 12 > + > +struct mdp_rdma_subfrm { > + u32 offset[IMG_MAX_PLANES]; > + u32 offset_0_p; > + u32 src; > + u32 clip; > + u32 clip_ofst; > +} __attribute__ ((__packed__)); > + > +struct mdp_rdma_data { > + u32 src_ctrl; > + u32 control; > + u32 iova[IMG_MAX_PLANES]; > + u32 iova_end[IMG_MAX_PLANES]; > + u32 mf_bkgd; > + u32 mf_bkgd_in_pxl; > + u32 sf_bkgd; > + u32 ufo_dec_y; > + u32 ufo_dec_c; > + u32 transform; > + struct mdp_rdma_subfrm subfrms[IMG_MAX_SUBFRAMES]; > +} __attribute__ ((__packed__)); > + > +struct mdp_rsz_subfrm { > + u32 control2; > + u32 src; > + u32 clip; > +} __attribute__ ((__packed__)); > + > +struct mdp_rsz_data { > + u32 coeff_step_x; > + u32 coeff_step_y; > + u32 control1; > + u32 control2; > + struct mdp_rsz_subfrm subfrms[IMG_MAX_SUBFRAMES]; > +} __attribute__ ((__packed__)); > + > +struct mdp_wrot_subfrm { > + u32 offset[IMG_MAX_PLANES]; > + u32 src; > + u32 clip; > + u32 clip_ofst; > + u32 main_buf; > +} __attribute__ ((__packed__)); > + > +struct mdp_wrot_data { > + u32 iova[IMG_MAX_PLANES]; > + u32 control; > + u32 stride[IMG_MAX_PLANES]; > + u32 mat_ctrl; > + u32 fifo_test; > + u32 filter; > + struct mdp_wrot_subfrm subfrms[IMG_MAX_SUBFRAMES]; > +} __attribute__ ((__packed__)); > + > +struct mdp_wdma_subfrm { > + u32 offset[IMG_MAX_PLANES]; > + u32 src; > + u32 clip; > + u32 clip_ofst; > +} __attribute__ ((__packed__)); > + > +struct mdp_wdma_data { > + u32 wdma_cfg; > + u32 iova[IMG_MAX_PLANES]; > + u32 w_in_byte; > + u32 uv_stride; > + struct mdp_wdma_subfrm subfrms[IMG_MAX_SUBFRAMES]; > +} __attribute__ ((__packed__)); > + > +struct isp_data { > + u64 dl_flags; /* 1 << (enum mdp_comp_type) */ > + u32 smxi_iova[4]; > + u32 cq_idx; > + u32 cq_iova; > + u32 tpipe_iova[IMG_MAX_SUBFRAMES]; > +} __attribute__ ((__packed__)); > + > +struct img_compparam { > + u16 type; /* enum mdp_comp_type */ > + u16 id; /* enum mdp_comp_id */ > + u32 input; > + u32 outputs[IMG_MAX_HW_OUTPUTS]; > + u32 num_outputs; > + struct img_comp_frame frame; > + struct img_comp_subfrm subfrms[IMG_MAX_SUBFRAMES]; > + u32 num_subfrms; > + union { > + struct mdp_rdma_data rdma; > + struct mdp_rsz_data rsz; > + struct mdp_wrot_data wrot; > + struct mdp_wdma_data wdma; > + /* struct mdp_hdr_data hdr; */ > + struct isp_data isp; > + /* struct wpe_data wpe; */ > + }; > +} __attribute__ ((__packed__)); > + > +#define IMG_MAX_COMPONENTS 20 > + > +struct img_mux { > + u32 reg; > + u32 value; > +}; > + > +struct img_mmsys_ctrl { > + struct img_mux sets[IMG_MAX_COMPONENTS * 2]; > + u32 num_sets; > +}; > + > +struct img_config { > + struct img_compparam components[IMG_MAX_COMPONENTS]; > + u32 num_components; > + struct img_mmsys_ctrl ctrls[IMG_MAX_SUBFRAMES]; > + u32 num_subfrms; > +} __attribute__ ((__packed__)); > + > +#endif /* __MTK_IMG_IPI_H__ */ > + > diff --git a/drivers/media/platform/mtk-mdp3/mtk-mdp3-cmdq.c b/drivers/media/platform/mtk-mdp3/mtk-mdp3-cmdq.c > new file mode 100644 > index 000000000000..562ad8509fc2 > --- /dev/null > +++ b/drivers/media/platform/mtk-mdp3/mtk-mdp3-cmdq.c > @@ -0,0 +1,442 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (c) 2018 MediaTek Inc. > + * Author: Ping-Hsun Wu <ping-hsun.wu@xxxxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License 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. > + */ > + > +#include <linux/platform_device.h> > +#include "mtk-mdp3-cmdq.h" > +#include "mtk-mdp3-comp.h" > +#include "mtk-mdp3-core.h" > + > +#include "mdp-platform.h" > +#include "mmsys_mutex.h" > + > +#define DISP_MUTEX_MDP_FIRST (5) > +#define DISP_MUTEX_MDP_COUNT (5) > + > +#define MDP_PATH_MAX_COMPS IMG_MAX_COMPONENTS > + > +struct mdp_path { > + struct mdp_dev *mdp_dev; > + struct mdp_comp_ctx comps[MDP_PATH_MAX_COMPS]; > + u32 num_comps; > + const struct img_config *config; > + const struct img_ipi_frameparam *param; > + const struct v4l2_rect *composes[IMG_MAX_HW_OUTPUTS]; > + struct v4l2_rect bounds[IMG_MAX_HW_OUTPUTS]; > +}; > + > +#define has_op(ctx, op) \ > + (ctx->comp->ops && ctx->comp->ops->op) > +#define call_op(ctx, op, ...) \ > + (has_op(ctx, op) ? ctx->comp->ops->op(ctx, ##__VA_ARGS__) : 0) > + > +struct mdp_path_subfrm { > + s32 mutex_id; > + u32 mutex_mod; > + s32 sofs[MDP_PATH_MAX_COMPS]; > + u32 num_sofs; > +}; > + > +bool is_output_disable(const struct img_compparam *param, u32 count) This function can be static. > +{ > + return (count < param->num_subfrms) ? > + (param->frame.output_disable || > + param->subfrms[count].tile_disable) : > + true; > +} > + > +int mdp_path_subfrm_require(struct mdp_path_subfrm *subfrm, > + const struct mdp_path *path, struct mdp_cmd *cmd, > + u32 count) This one as well. > +{ > + const struct img_config *config = path->config; > + const struct mdp_comp_ctx *ctx; > + phys_addr_t mm_mutex = path->mdp_dev->mm_mutex.reg_base; > + s32 mutex_id = -1; > + u32 mutex_sof = 0; > + int mdp_color = 0; > + int index; > + u8 subsys_id = path->mdp_dev->mm_mutex.subsys_id; > + > + /* Default value */ > + memset(subfrm, 0, sizeof(*subfrm)); > + > + for (index = 0; index < config->num_components; index++) { > + ctx = &path->comps[index]; > + if (is_output_disable(ctx->param, count)) > + continue; > + switch (ctx->comp->id) { > + /********************************************** > + * Name MSB LSB > + * DISP_MUTEX_MOD 23 0 > + * > + * Specifies which modules are in this mutex. > + * Every bit denotes a module. Bit definition: > + * 2 mdp_rdma0 > + * 4 mdp_rsz0 > + * 5 mdp_rsz1 > + * 6 mdp_tdshp > + * 7 mdp_wrot0 > + * 8 mdp_wdma > + * 13 mdp_color > + * 23 mdp_aal > + * 24 mdp_ccorr > + **********************************************/ > + case MDP_AAL0: > + subfrm->mutex_mod |= 1 << 23; > + break; > + case MDP_CCORR0: > + subfrm->mutex_mod |= 1 << 24; > + break; > + case MDP_COLOR0: > + if (mdp_color) > + subfrm->mutex_mod |= 1 << 13; > + break; > + case MDP_WDMA: > + subfrm->mutex_mod |= 1 << 8; > + subfrm->sofs[subfrm->num_sofs++] = MDP_WDMA; > + break; > + case MDP_WROT0: > + subfrm->mutex_mod |= 1 << 7; > + subfrm->sofs[subfrm->num_sofs++] = MDP_WROT0; > + break; > + case MDP_TDSHP0: > + subfrm->mutex_mod |= 1 << 6; > + subfrm->sofs[subfrm->num_sofs++] = MDP_TDSHP0; > + break; > + case MDP_SCL1: > + subfrm->mutex_mod |= 1 << 5; > + subfrm->sofs[subfrm->num_sofs++] = MDP_SCL1; > + break; > + case MDP_SCL0: > + subfrm->mutex_mod |= 1 << 4; > + subfrm->sofs[subfrm->num_sofs++] = MDP_SCL0; > + break; > + case MDP_RDMA0: > + mutex_id = DISP_MUTEX_MDP_FIRST + 1; > + subfrm->mutex_mod |= 1 << 2; > + subfrm->sofs[subfrm->num_sofs++] = MDP_RDMA0; > + break; > + case MDP_IMGI: > + mutex_id = DISP_MUTEX_MDP_FIRST; > + break; > + case MDP_WPEI: > + mutex_id = DISP_MUTEX_MDP_FIRST + 3; > + break; > + case MDP_WPEI2: > + mutex_id = DISP_MUTEX_MDP_FIRST + 4; > + break; > + default: > + break; > + } > + } > + > + subfrm->mutex_id = mutex_id; > + if (-1 == mutex_id) { > + mdp_err("No mutex assigned"); > + return -EINVAL; > + } > + > + if (subfrm->mutex_mod) { > + /* Set mutex modules */ > + MM_REG_WRITE(cmd, subsys_id, mm_mutex, MM_MUTEX_MOD, > + subfrm->mutex_mod, 0x07FFFFFF); > + MM_REG_WRITE(cmd, subsys_id, mm_mutex, MM_MUTEX_SOF, > + mutex_sof, 0x00000007); > + } > + return 0; > +} > + > +int mdp_path_subfrm_run(const struct mdp_path_subfrm *subfrm, > + const struct mdp_path *path, struct mdp_cmd *cmd) And this one. > +{ > + phys_addr_t mm_mutex = path->mdp_dev->mm_mutex.reg_base; > + s32 mutex_id = subfrm->mutex_id; > + u8 subsys_id = path->mdp_dev->mm_mutex.subsys_id; > + > + if (-1 == mutex_id) { > + mdp_err("Incorrect mutex id"); > + return -EINVAL; > + } > + > + if (subfrm->mutex_mod) { > + int index; > + > + /* Wait WROT SRAM shared to DISP RDMA */ > + /* Clear SOF event for each engine */ > + for (index = 0; index < subfrm->num_sofs; index++) { > + switch (subfrm->sofs[index]) { > + case MDP_RDMA0: > + MM_REG_CLEAR(cmd, RDMA0_SOF); > + break; > + case MDP_TDSHP0: > + MM_REG_CLEAR(cmd, TDSHP0_SOF); > + break; > + case MDP_SCL0: > + MM_REG_CLEAR(cmd, RSZ0_SOF); > + break; > + case MDP_SCL1: > + MM_REG_CLEAR(cmd, RSZ1_SOF); > + break; > + case MDP_WDMA: > + MM_REG_CLEAR(cmd, WDMA0_SOF); > + break; > + case MDP_WROT0: > +#if WROT0_DISP_SRAM_SHARING > + MM_REG_WAIT_NO_CLEAR(cmd, WROT0_SRAM_READY); > +#endif > + MM_REG_CLEAR(cmd, WROT0_SOF); > + break; > + default: > + break; > + } > + } > + > + /* Enable the mutex */ > + MM_REG_WRITE(cmd, subsys_id, mm_mutex, MM_MUTEX_EN, 0x1, > + 0x00000001); > + > + /* Wait SOF events and clear mutex modules (optional) */ > + for (index = 0; index < subfrm->num_sofs; index++) { > + switch (subfrm->sofs[index]) { > + case MDP_RDMA0: > + MM_REG_WAIT(cmd, RDMA0_SOF); > + break; > + case MDP_TDSHP0: > + MM_REG_WAIT(cmd, TDSHP0_SOF); > + break; > + case MDP_SCL0: > + MM_REG_WAIT(cmd, RSZ0_SOF); > + break; > + case MDP_SCL1: > + MM_REG_WAIT(cmd, RSZ1_SOF); > + break; > + case MDP_WDMA: > + MM_REG_WAIT(cmd, WDMA0_SOF); > + break; > + case MDP_WROT0: > + MM_REG_WAIT(cmd, WROT0_SOF); > + break; > + default: > + break; > + } > + } > + } > + return 0; > +} > + > +static int mdp_path_config_subfrm(struct mdp_cmd *cmd, struct mdp_path *path, > + u32 count) > +{ > + struct mdp_path_subfrm subfrm; > + const struct img_config *config = path->config; > + const struct img_mmsys_ctrl *ctrl = &config->ctrls[count]; > + const struct img_mux *set; > + struct mdp_comp_ctx *ctx; > + phys_addr_t mmsys = path->mdp_dev->mmsys.reg_base; > + int index, ret; > + u8 subsys_id = path->mdp_dev->mmsys.subsys_id; > + > + /* Acquire components */ > + ret = mdp_path_subfrm_require(&subfrm, path, cmd, count); > + if (ret) > + return ret; > + /* Enable mux settings */ > + for (index = 0; index < ctrl->num_sets; index++) { > + set = &ctrl->sets[index]; > + MM_REG_WRITE_MASK(cmd, subsys_id, mmsys, set->reg, set->value, > + 0xFFFFFFFF); > + } > + /* Config sub-frame information */ > + for (index = (config->num_components - 1); index >= 0; index--) { > + ctx = &path->comps[index]; > + if (is_output_disable(ctx->param, count)) > + continue; > + ret = call_op(ctx, config_subfrm, cmd, count); > + if (ret) > + return ret; > + } > + /* Run components */ > + ret = mdp_path_subfrm_run(&subfrm, path, cmd); > + if (ret) > + return ret; > + /* Wait components done */ > + for (index = 0; index < config->num_components; index++) { > + ctx = &path->comps[index]; > + if (is_output_disable(ctx->param, count)) > + continue; > + ret = call_op(ctx, wait_comp_event, cmd); > + if (ret) > + return ret; > + } > + /* Advance to the next sub-frame */ > + for (index = 0; index < config->num_components; index++) { > + ctx = &path->comps[index]; > + ret = call_op(ctx, advance_subfrm, cmd, count); > + if (ret) > + return ret; > + } > + /* Disable mux settings */ > + for (index = 0; index < ctrl->num_sets; index++) { > + set = &ctrl->sets[index]; > + MM_REG_WRITE_MASK(cmd, subsys_id, mmsys, set->reg, 0, > + 0xFFFFFFFF); > + } > + return 0; > +} > + > +static int mdp_path_config(struct mdp_dev *mdp, struct mdp_cmd *cmd, > + struct mdp_path *path) > +{ > + const struct img_config *config = path->config; > + struct mdp_comp_ctx *ctx; > + int index, count, ret; > + > + for (index = 0; index < config->num_components; index++) { > + ret = mdp_comp_ctx_init(mdp, &path->comps[index], > + &config->components[index], > + path->param); > + if (ret) > + return ret; > + } > + > + /* Config path frame */ > + /* Reset components */ > + for (index = 0; index < config->num_components; index++) { > + ctx = &path->comps[index]; > + ret = call_op(ctx, init_comp, cmd); > + if (ret) > + return ret; > + } > + /* Config frame mode */ > + for (index = 0; index < config->num_components; index++) { > + const struct v4l2_rect *compose = > + path->composes[ctx->param->outputs[0]]; > + > + ctx = &path->comps[index]; > + ret = call_op(ctx, config_frame, cmd, compose); > + if (ret) > + return ret; > + } > + > + /* Config path sub-frames */ > + for (count = 0; count < config->num_subfrms; count++) { > + ret = mdp_path_config_subfrm(cmd, path, count); > + if (ret) > + return ret; > + } > + /* Post processing information */ > + for (index = 0; index < config->num_components; index++) { > + ctx = &path->comps[index]; > + ret = call_op(ctx, post_process, cmd); > + if (ret) > + return ret; > + } > + return 0; > +} > + > +void mdp_handle_cmdq_callback(struct cmdq_cb_data data) This can be static too. > +{ > + struct mdp_cmdq_cb_param *cb_param; > + > + if (!data.data) { > + mdp_err("%s:no callback data\n", __func__); > + return; > + } > + > + cb_param = (struct mdp_cmdq_cb_param *)data.data; > + if (cb_param->user_cmdq_cb) { > + struct cmdq_cb_data user_cb_data; > + > + user_cb_data.sta = data.sta; > + user_cb_data.data = cb_param->user_cb_data; > + cb_param->user_cmdq_cb(user_cb_data); > + } > + > + cmdq_pkt_destroy(cb_param->pkt); > + kfree(cb_param); > +} > + > +int mdp_cmdq_send(struct mdp_dev *mdp, struct mdp_cmdq_param *param) > +{ > + struct mdp_cmd cmd; > + struct mdp_path path; > + int i, ret; > + > + cmd.pkt = cmdq_pkt_create(mdp->cmdq_clt, SZ_16K); > + if (IS_ERR(cmd.pkt)) > + return PTR_ERR(cmd.pkt); > + cmd.event = &mdp->event[0]; > + > + path.mdp_dev = mdp; > + path.config = param->config; > + path.param = param->param; > + for (i = 0; i < param->param->num_outputs; i++) { > + path.bounds[i].left = 0; > + path.bounds[i].top = 0; > + path.bounds[i].width = > + param->param->outputs[i].buffer.format.width; > + path.bounds[i].height = > + param->param->outputs[i].buffer.format.height; > + path.composes[i] = param->composes[i] ? > + param->composes[i] : &path.bounds[i]; > + } > + ret = mdp_path_config(mdp, &cmd, &path); > + if (ret) > + return ret; > + > + // TODO: engine conflict dispatch > + for (i = 0; i < param->config->num_components; i++) > + mdp_comp_clock_on(&mdp->pdev->dev, path.comps[i].comp); > + > + if (param->wait) { > + ret = cmdq_pkt_flush(cmd.pkt); > + cmdq_pkt_destroy(cmd.pkt); > + for (i = 0; i < param->config->num_components; i++) > + mdp_comp_clock_off(&mdp->pdev->dev, path.comps[i].comp); > + } else { > + struct mdp_cmdq_cb_param *cb_param = > + kzalloc(sizeof(struct mdp_cmdq_cb_param), GFP_KERNEL); > + > + cb_param->user_cmdq_cb = param->cmdq_cb; > + cb_param->user_cb_data = param->cb_data; > + cb_param->pkt = cmd.pkt; > + > + ret = cmdq_pkt_flush_async(cmd.pkt, > + mdp_handle_cmdq_callback, > + (void *)cb_param); > + // TODO: destroy & clock-off after callback > + } > + return ret; > +} > + > +int mdp_cmdq_sendtask(struct platform_device *pdev, struct img_config *config, > + struct img_ipi_frameparam *param, > + struct v4l2_rect *compose, unsigned int wait, > + void (*cmdq_cb)(struct cmdq_cb_data data), void *cb_data) > +{ > + struct mdp_dev *mdp = platform_get_drvdata(pdev); > + struct mdp_cmdq_param task = { > + .config = config, > + .param = param, > + .composes[0] = compose, > + .wait = wait, > + .cmdq_cb = cmdq_cb, > + .cb_data = cb_data, > + }; > + > + return mdp_cmdq_send(mdp, &task); > +} > +EXPORT_SYMBOL_GPL(mdp_cmdq_sendtask); > + > diff --git a/drivers/media/platform/mtk-mdp3/mtk-mdp3-cmdq.h b/drivers/media/platform/mtk-mdp3/mtk-mdp3-cmdq.h > new file mode 100644 > index 000000000000..b61ab1ac4325 > --- /dev/null > +++ b/drivers/media/platform/mtk-mdp3/mtk-mdp3-cmdq.h > @@ -0,0 +1,57 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Copyright (c) 2018 MediaTek Inc. > + * Author: Ping-Hsun Wu <ping-hsun.wu@xxxxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License 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. > + */ > + > +#ifndef __MTK_MDP3_CMDQ_H__ > +#define __MTK_MDP3_CMDQ_H__ > + > +#include <linux/platform_device.h> > +#include <linux/videodev2.h> > +#include <linux/soc/mediatek/mtk-cmdq.h> > +#include "mtk-img-ipi.h" > + > +struct platform_device *mdp_get_plat_device(struct platform_device *pdev); This function is defined in mtk-mdp3-core.c, either its declaration or its definition should be moved? > + > +int mdp_cmdq_sendtask(struct platform_device *pdev, struct img_config *config, > + struct img_ipi_frameparam *param, > + struct v4l2_rect *compose, unsigned int wait, > + void (*cmdq_cb)(struct cmdq_cb_data data), void *cb_data); This function is defined and exported, but I don't see any call to it anywhere? Looks like it can just be removed. > + > +struct mdp_cmd { > + struct cmdq_pkt *pkt; > + s32 *event; > +}; > + > +struct mdp_cmdq_param { > + struct img_config *config; > + struct img_ipi_frameparam *param; > + const struct v4l2_rect *composes[IMG_MAX_HW_OUTPUTS]; > + unsigned int wait; > + > + void (*cmdq_cb)(struct cmdq_cb_data data); > + void *cb_data; > +}; > + > +struct mdp_cmdq_cb_param { > + void (*user_cmdq_cb)(struct cmdq_cb_data data); > + void *user_cb_data; > + struct cmdq_pkt *pkt; > +}; > + > +struct mdp_dev; > + > +int mdp_cmdq_send(struct mdp_dev *mdp, struct mdp_cmdq_param *param); > + > +#endif /* __MTK_MDP3_CMDQ_H__ */ > + > diff --git a/drivers/media/platform/mtk-mdp3/mtk-mdp3-comp.c b/drivers/media/platform/mtk-mdp3/mtk-mdp3-comp.c > new file mode 100644 > index 000000000000..3dc36915a744 [snip] > +static void mdp_comp_deinit(struct device *dev, struct mdp_comp *comp) > +{ > + iounmap(comp->regs); > + /* of_node_put(comp->dev_node); */ > +} > + > +void mdp_component_deinit(struct device *dev, struct mdp_dev *mdp) > +{ > + int i; > + > + mdp_comp_deinit(dev, &mdp->mmsys); > + mdp_comp_deinit(dev, &mdp->mm_mutex); > + for (i = 0; i < ARRAY_SIZE(mdp->comp); i++) { > + if (mdp->comp[i]) { > + mdp_comp_deinit(dev, mdp->comp[i]); > + kfree(mdp->comp[i]); > + } > + } > +} > + > +int mdp_component_init(struct device *dev, struct mdp_dev *mdp) dev can be obtained from mdp->pdev->dev, so there is no need to pass it as argument. > +{ > + struct device_node *node, *parent; > + int i, ret; > + > + for (i = 0; i < ARRAY_SIZE(gce_event_names); i++) { > + s32 event_id; > + > + event_id = cmdq_dev_get_event(dev, gce_event_names[i]); > + mdp->event[i] = (event_id < 0) ? -i : event_id; > + dev_info(dev, "Get event %s id:%d\n", > + gce_event_names[i], mdp->event[i]); > + } > + > + ret = mdp_mm_init(dev, mdp, &mdp->mmsys, "mediatek,mmsys"); Same for mdp_mm_init. > + if (ret) > + goto err_init_mm; > + > + ret = mdp_mm_init(dev, mdp, &mdp->mm_mutex, "mediatek,mm-mutex"); > + if (ret) > + goto err_init_mm; > + > + parent = dev->of_node->parent; > + /* Iterate over sibling MDP function blocks */ > + for_each_child_of_node(parent, node) { > + const struct of_device_id *of_id; > + enum mdp_comp_type type; > + int id; > + struct mdp_comp *comp; > + > + of_id = of_match_node(mdp_comp_dt_ids, node); > + if (!of_id) > + continue; > + > + if (!of_device_is_available(node)) { > + dev_err(dev, "Skipping disabled component %pOF\n", > + node); > + continue; > + } > + > + type = (enum mdp_comp_type)of_id->data; > + id = mdp_comp_get_id(dev, node, type); > + if (id < 0) { > + dev_warn(dev, "Skipping unknown component %pOF\n", > + node); > + continue; > + } > + > + comp = devm_kzalloc(dev, sizeof(*comp), GFP_KERNEL); > + if (!comp) { > + ret = -ENOMEM; > + goto err_init_comps; > + } > + mdp->comp[id] = comp; > + > + ret = mdp_comp_init(dev, mdp, node, comp, id); > + if (ret) > + goto err_init_comps; > + > + dev_info(dev, "%s type:%d alias:%d id:%d base:%#x regs:%p\n", > + of_id->compatible, type, comp->alias_id, id, > + (u32)comp->reg_base, comp->regs); > + } > + return 0; > + > +err_init_comps: > + mdp_component_deinit(dev, mdp); > +err_init_mm: > + return ret; > +} > + > +int mdp_comp_ctx_init(struct mdp_dev *mdp, struct mdp_comp_ctx *ctx, > + const struct img_compparam *param, > + const struct img_ipi_frameparam *frame) > +{ > + int i; > + > + if (param->type < 0 || param->type >= MDP_MAX_COMP_COUNT) { > + mdp_err("Invalid component id %d", param->type); > + return -EINVAL; > + } > + > + ctx->comp = mdp->comp[param->type]; > + if (!ctx->comp) { > + mdp_err("Uninit component id %d", param->type); > + return -EINVAL; > + } > + > + ctx->param = param; > + ctx->input = &frame->inputs[param->input]; > + for (i = 0; i < param->num_outputs; i++) > + ctx->outputs[i] = &frame->outputs[param->outputs[i]]; > + return 0; > +} > + > diff --git a/drivers/media/platform/mtk-mdp3/mtk-mdp3-comp.h b/drivers/media/platform/mtk-mdp3/mtk-mdp3-comp.h > new file mode 100644 > index 000000000000..0c65214ef695 > --- /dev/null > +++ b/drivers/media/platform/mtk-mdp3/mtk-mdp3-comp.h > @@ -0,0 +1,177 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Copyright (c) 2018 MediaTek Inc. > + * Author: Ping-Hsun Wu <ping-hsun.wu@xxxxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License 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. You already have the SPDX identifier, so please don't also copy the GPL boilerplate. The SPDX is supposed to replace it. This applies to a few other files as well. > + */ > + > +#ifndef __MTK_MDP3_COMP_H__ > +#define __MTK_MDP3_COMP_H__ > + > +#include "mtk-mdp3-cmdq.h" > + > +enum mdp_comp_type { > + MDP_COMP_TYPE_INVALID = 0, > + > + MDP_COMP_TYPE_RDMA, > + MDP_COMP_TYPE_RSZ, > + MDP_COMP_TYPE_WROT, > + MDP_COMP_TYPE_WDMA, > + MDP_COMP_TYPE_PATH, > + > + MDP_COMP_TYPE_TDSHP, > + MDP_COMP_TYPE_COLOR, > + MDP_COMP_TYPE_DRE, > + MDP_COMP_TYPE_CCORR, > + MDP_COMP_TYPE_HDR, > + > + MDP_COMP_TYPE_IMGI, > + MDP_COMP_TYPE_WPEI, > + MDP_COMP_TYPE_EXTO, /* External path */ > + MDP_COMP_TYPE_DL_PATH, /* Direct-link path */ > + > + MDP_COMP_TYPE_COUNT /* ALWAYS keep at the end */ > +}; > + > +enum mdp_comp_id { > + MDP_COMP_NONE = -1, /* Invalid engine */ > + > + /* ISP */ > + MDP_COMP_WPEI = 0, > + MDP_COMP_WPEO, /* 1 */ > + MDP_COMP_WPEI2, /* 2 */ > + MDP_COMP_WPEO2, /* 3 */ > + MDP_COMP_ISP_IMGI, /* 4 */ > + MDP_COMP_ISP_IMGO, /* 5 */ > + MDP_COMP_ISP_IMG2O, /* 6 */ > + > + /* IPU */ > + MDP_COMP_IPUI, /* 7 */ > + MDP_COMP_IPUO, /* 8 */ > + > + /* MDP */ > + MDP_COMP_CAMIN, /* 9 */ > + MDP_COMP_CAMIN2, /* 10 */ > + MDP_COMP_RDMA0, /* 11 */ > + MDP_COMP_AAL0, /* 12 */ > + MDP_COMP_CCORR0, /* 13 */ > + MDP_COMP_RSZ0, /* 14 */ > + MDP_COMP_RSZ1, /* 15 */ > + MDP_COMP_TDSHP0, /* 16 */ > + MDP_COMP_COLOR0, /* 17 */ > + MDP_COMP_PATH0_SOUT, /* 18 */ > + MDP_COMP_PATH1_SOUT, /* 19 */ > + MDP_COMP_WROT0, /* 20 */ > + MDP_COMP_WDMA, /* 21 */ > + > + /* Dummy Engine */ > + MDP_COMP_RDMA1, /* 22 */ > + MDP_COMP_RSZ2, /* 23 */ > + MDP_COMP_TDSHP1, /* 24 */ > + MDP_COMP_WROT1, /* 25 */ > + > + MDP_MAX_COMP_COUNT /* ALWAYS keep at the end */ > +}; > + > +enum mdp_comp_event { > + RDMA0_SOF, > + RDMA0_DONE, > + RDMA1_SOF, > + RDMA1_DONE, > + RSZ0_SOF, > + RSZ1_SOF, > + TDSHP0_SOF, > + WROT0_SOF, > + WROT0_DONE, > + WROT1_SOF, > + WROT1_DONE, > + WDMA0_SOF, > + WDMA0_DONE, > + IMG_DL_SOF, > + > + ISP_P2_0_DONE, > + ISP_P2_1_DONE, > + ISP_P2_2_DONE, > + ISP_P2_3_DONE, > + ISP_P2_4_DONE, > + ISP_P2_5_DONE, > + ISP_P2_6_DONE, > + ISP_P2_7_DONE, > + ISP_P2_8_DONE, > + ISP_P2_9_DONE, > + ISP_P2_10_DONE, > + ISP_P2_11_DONE, > + ISP_P2_12_DONE, > + ISP_P2_13_DONE, > + ISP_P2_14_DONE, > + > + WPE_DONE, > + WPE_B_DONE, > + WROT0_SRAM_READY, > + WROT1_SRAM_READY, > + > + MDP_MAX_EVENT_COUNT /* ALWAYS keep at the end */ > +}; > + > +struct mdp_comp_ops; > + > +struct mdp_comp { > + struct mdp_dev *mdp_dev; > + /* struct device_node *dev_node; */ > + void __iomem *regs; > + phys_addr_t reg_base; > + u8 subsys_id; > + struct clk *clks[2]; > + struct device *larb_dev; > + enum mdp_comp_type type; > + enum mdp_comp_id id; > + u32 alias_id; > + const struct mdp_comp_ops *ops; > +}; > + > +struct mdp_comp_ctx { > + struct mdp_comp *comp; > + const struct img_compparam *param; > + const struct img_input *input; > + const struct img_output *outputs[IMG_MAX_HW_OUTPUTS]; > +}; > + > +struct mdp_comp_ops { > + s64 (*get_comp_flag)(const struct mdp_comp_ctx *ctx); > + /* s64 (*query_feature)(void); */ > + int (*init_comp)(struct mdp_comp_ctx *ctx, struct mdp_cmd *cmd); > + int (*config_frame)(struct mdp_comp_ctx *ctx, struct mdp_cmd *cmd, > + const struct v4l2_rect *compose); > + /* int (*config_frame_end)(struct mdp_comp_ctx *ctx, > + * struct mdp_cmd *cmd); > + */ > + int (*config_subfrm)(struct mdp_comp_ctx *ctx, > + struct mdp_cmd *cmd, u32 index); > + int (*wait_comp_event)(struct mdp_comp_ctx *ctx, > + struct mdp_cmd *cmd); > + int (*advance_subfrm)(struct mdp_comp_ctx *ctx, > + struct mdp_cmd *cmd, u32 index); > + int (*post_process)(struct mdp_comp_ctx *ctx, struct mdp_cmd *cmd); > + /* void (*release)(struct mdp_comp_ctx *ctx); */ > +}; > + > +struct mdp_dev; > + > +int mdp_component_init(struct device *dev, struct mdp_dev *mdp); > +void mdp_component_deinit(struct device *dev, struct mdp_dev *mdp); > +void mdp_comp_clock_on(struct device *dev, struct mdp_comp *comp); > +void mdp_comp_clock_off(struct device *dev, struct mdp_comp *comp); > +int mdp_comp_ctx_init(struct mdp_dev *mdp, struct mdp_comp_ctx *ctx, > + const struct img_compparam *param, > + const struct img_ipi_frameparam *frame); > + > +#endif /* __MTK_MDP3_COMP_H__ */ > + > diff --git a/drivers/media/platform/mtk-mdp3/mtk-mdp3-core.c b/drivers/media/platform/mtk-mdp3/mtk-mdp3-core.c > new file mode 100644 > index 000000000000..47b5d87834de > --- /dev/null > +++ b/drivers/media/platform/mtk-mdp3/mtk-mdp3-core.c > @@ -0,0 +1,256 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (c) 2018 MediaTek Inc. > + * Author: Ping-Hsun Wu <ping-hsun.wu@xxxxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License 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. > + */ > + > +#include <linux/module.h> > +#include <linux/of_platform.h> > +#include <linux/platform_device.h> > +#include <linux/pm_runtime.h> > +#include <linux/remoteproc.h> > +#include <linux/platform_data/mtk_scp.h> > +#include <media/videobuf2-dma-contig.h> > +#include "mtk-mdp3-core.h" > +#include "mtk-mdp3-m2m.h" > + > +/* MDP debug log level (0-3). 3 shows all the logs. */ > +int mtk_mdp_debug; > +EXPORT_SYMBOL(mtk_mdp_debug); > +module_param_named(debug, mtk_mdp_debug, int, 0644); > + > +static const struct of_device_id mdp_of_ids[] = { > + { .compatible = "mediatek,mt8183-mdp3", }, > + {}, > +}; > +MODULE_DEVICE_TABLE(of, mdp_of_ids); > + > +struct platform_device *mdp_get_plat_device(struct platform_device *pdev) > +{ > + struct device *dev = &pdev->dev; > + struct device_node *mdp_node; > + struct platform_device *mdp_pdev; > + > + mdp_node = of_parse_phandle(dev->of_node, "mediatek,mdp3", 0); > + if (!mdp_node) { > + dev_err(dev, "can't get mdp node\n"); > + return NULL; > + } > + > + mdp_pdev = of_find_device_by_node(mdp_node); > + if (WARN_ON(!mdp_pdev)) { > + dev_err(dev, "mdp pdev failed\n"); > + of_node_put(mdp_node); > + return NULL; > + } > + > + return mdp_pdev; > +} > +EXPORT_SYMBOL_GPL(mdp_get_plat_device); > + > +int mdp_vpu_get_locked(struct mdp_dev *mdp) > +{ > + int ret = 0; > + > + if (mdp->vpu_count++ == 0) { > + ret = rproc_boot(mdp->rproc_handle); > + if (ret < 0) { > + dev_err(&mdp->pdev->dev, > + "vpu_load_firmware failed %d\n", ret); > + goto err_load_vpu; > + } > + ret = mdp_vpu_register(mdp->vpu_dev); With mdp_vpu_unregister doing nothing, isn't it a problem to register the same callbacks several times? How about moving this into probe? > + if (ret < 0) { > + dev_err(&mdp->pdev->dev, > + "mdp_vpu register failed %d\n", ret); > + goto err_reg_vpu; > + } > + ret = mdp_vpu_dev_init(&mdp->vpu, mdp->vpu_dev, &mdp->vpu_lock); > + if (ret) { > + dev_err(&mdp->pdev->dev, > + "mdp_vpu device init failed %d\n", ret); > + goto err_init_vpu; > + } > + } > + return 0; > + > +err_init_vpu: > + mdp_vpu_unregister(mdp->vpu_dev); > +err_reg_vpu: > +err_load_vpu: > + mdp->vpu_count--; > + return ret; > +} > + > +void mdp_vpu_put_locked(struct mdp_dev *mdp) > +{ > + if (--mdp->vpu_count == 0) { > + mdp_vpu_dev_deinit(&mdp->vpu); > + mdp_vpu_unregister(mdp->vpu_dev); > + } > +} > + > +static int mdp_probe(struct platform_device *pdev) > +{ > + struct device *dev = &pdev->dev; > + struct mdp_dev *mdp; > + phandle rproc_phandle; > + int ret; > + > + mdp = devm_kzalloc(dev, sizeof(*mdp), GFP_KERNEL); > + if (!mdp) > + return -ENOMEM; > + > + mdp->pdev = pdev; > + ret = mdp_component_init(dev, mdp); > + if (ret) { > + dev_err(dev, "Failed to initialize mdp components\n"); > + goto err_init_comp; > + } > + > + mdp->job_wq = create_singlethread_workqueue(MDP_MODULE_NAME); > + if (!mdp->job_wq) { > + dev_err(dev, "Unable to create job workqueue\n"); > + ret = -ENOMEM; > + goto err_create_job_wq; > + } > + > + mdp->vpu_dev = scp_get_pdev(pdev); > + > + if (of_property_read_u32(pdev->dev.of_node, "mediatek,scp", > + &rproc_phandle)) > + dev_err(&pdev->dev, "Could not get scp device\n"); > + else > + dev_info(&pdev->dev, "Find mediatek,scp phandle:%llx\n", > + (unsigned long long)rproc_phandle); > + > + mdp->rproc_handle = rproc_get_by_phandle(rproc_phandle); > + > + dev_info(&pdev->dev, "MDP rproc_handle: %llx", > + (unsigned long long)mdp->rproc_handle); > + > + if (!mdp->rproc_handle) > + dev_err(&pdev->dev, "Could not get MDP's rproc_handle\n"); > + > + /* vpu_wdt_reg_handler(mdp->vpu_dev, mdp_reset_handler, mdp, > + * VPU_RST_MDP); > + */ > + mutex_init(&mdp->vpu_lock); > + mdp->vpu_count = 0; > + mdp->id_count = 0; > + > + mdp->cmdq_clt = cmdq_mbox_create(dev, 0, 1200); > + if (IS_ERR(mdp->cmdq_clt)) > + goto err_mbox_create; > + > + ret = v4l2_device_register(dev, &mdp->v4l2_dev); > + if (ret) { > + dev_err(dev, "Failed to register v4l2 device\n"); > + ret = -EINVAL; > + goto err_v4l2_register; > + } > + > + ret = mdp_m2m_device_register(mdp); > + if (ret) { > + v4l2_err(&mdp->v4l2_dev, "Failed to register m2m device\n"); > + goto err_m2m_register; > + } > + mutex_init(&mdp->m2m_lock); > + > + platform_set_drvdata(pdev, mdp); > + > + vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); > + pm_runtime_enable(dev); > + dev_dbg(dev, "mdp-%d registered successfully\n", pdev->id); > + return 0; > + > +err_m2m_register: > + v4l2_device_unregister(&mdp->v4l2_dev); > +err_v4l2_register: > +err_mbox_create: > + destroy_workqueue(mdp->job_wq); > +err_create_job_wq: > +err_init_comp: > + kfree(mdp); > + > + dev_dbg(dev, "Errno %d\n", ret); > + return ret; > +} > + > +static int mdp_remove(struct platform_device *pdev) > +{ > + struct mdp_dev *mdp = platform_get_drvdata(pdev); > + > + pm_runtime_disable(&pdev->dev); > + vb2_dma_contig_clear_max_seg_size(&pdev->dev); > + mdp_m2m_device_unregister(mdp); > + v4l2_device_unregister(&mdp->v4l2_dev); > + > + flush_workqueue(mdp->job_wq); > + destroy_workqueue(mdp->job_wq); > + > + mdp_component_deinit(&pdev->dev, mdp); > + kfree(mdp); > + > + dev_dbg(&pdev->dev, "%s driver unloaded\n", pdev->name); > + return 0; > +} > + > +static int __maybe_unused mdp_pm_suspend(struct device *dev) > +{ > + // TODO: mdp clock off > + return 0; > +} > + > +static int __maybe_unused mdp_pm_resume(struct device *dev) > +{ > + // TODO: mdp clock on > + return 0; > +} > + > +static int __maybe_unused mdp_suspend(struct device *dev) > +{ > + if (pm_runtime_suspended(dev)) > + return 0; > + > + return mdp_pm_suspend(dev); > +} > + > +static int __maybe_unused mdp_resume(struct device *dev) > +{ > + if (pm_runtime_suspended(dev)) > + return 0; > + > + return mdp_pm_resume(dev); > +} > + > +static const struct dev_pm_ops mdp_pm_ops = { > + SET_SYSTEM_SLEEP_PM_OPS(mdp_suspend, mdp_resume) > + SET_RUNTIME_PM_OPS(mdp_pm_suspend, mdp_pm_resume, NULL) > +}; > + > +static struct platform_driver mdp_driver = { > + .probe = mdp_probe, > + .remove = mdp_remove, > + .driver = { > + .name = MDP_MODULE_NAME, > + .pm = &mdp_pm_ops, > + .of_match_table = mdp_of_ids, > + }, > +}; > + > +module_platform_driver(mdp_driver); > + > +MODULE_AUTHOR("Ping-Hsun Wu <ping-hsun.wu@xxxxxxxxxxxx>"); > +MODULE_DESCRIPTION("Mediatek image processor 3 driver"); > +MODULE_LICENSE("GPL v2"); > + > diff --git a/drivers/media/platform/mtk-mdp3/mtk-mdp3-core.h b/drivers/media/platform/mtk-mdp3/mtk-mdp3-core.h > new file mode 100644 > index 000000000000..5ef3199b55e2 > --- /dev/null > +++ b/drivers/media/platform/mtk-mdp3/mtk-mdp3-core.h > @@ -0,0 +1,88 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Copyright (c) 2018 MediaTek Inc. > + * Author: Ping-Hsun Wu <ping-hsun.wu@xxxxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License 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. > + */ > + > +#ifndef __MTK_MDP3_CORE_H__ > +#define __MTK_MDP3_CORE_H__ > + > +#include <media/v4l2-device.h> > +#include <media/v4l2-mem2mem.h> > +#include "mtk-mdp3-comp.h" > +#include "mtk-mdp3-vpu.h" > + > +#define MDP_MODULE_NAME "mtk-mdp3" > + > +enum mdp_buffer_usage { > + MDP_BUFFER_USAGE_HW_READ, > + MDP_BUFFER_USAGE_MDP, > + MDP_BUFFER_USAGE_MDP2, > + MDP_BUFFER_USAGE_ISP, > + MDP_BUFFER_USAGE_WPE, > +}; > + > +struct mdp_dev { > + struct platform_device *pdev; > + struct mdp_comp mmsys; > + struct mdp_comp mm_mutex; > + struct mdp_comp *comp[MDP_MAX_COMP_COUNT]; > + s32 event[MDP_MAX_EVENT_COUNT]; > + > + struct workqueue_struct *job_wq; > + struct mdp_vpu_dev vpu; > + struct platform_device *vpu_dev; > + struct rproc *rproc_handle; > + /* synchronization protect for image configuration reference count */ > + struct mutex vpu_lock; > + s32 vpu_count; > + u32 id_count; > + struct cmdq_client *cmdq_clt; > + > + struct v4l2_device v4l2_dev; > + struct video_device *m2m_vdev; > + struct v4l2_m2m_dev *m2m_dev; > + /* synchronization protect for m2m device operation */ > + struct mutex m2m_lock; > +}; > + > +int mdp_vpu_get_locked(struct mdp_dev *mdp); > +void mdp_vpu_put_locked(struct mdp_dev *mdp); > + > +extern int mtk_mdp_debug; > + > +#define DEBUG > +#if defined(DEBUG) > + > +#define mdp_dbg(level, fmt, ...)\ > + do {\ > + if (mtk_mdp_debug >= (level))\ > + pr_info("[MTK-MDP3] %d %s:%d: " fmt "\n",\ > + level, __func__, __LINE__, ##__VA_ARGS__);\ > + } while (0) > + > +#define mdp_err(fmt, ...)\ > + pr_err("[MTK-MDP3][ERR] %s:%d: " fmt "\n", __func__, __LINE__,\ > + ##__VA_ARGS__) > + > +#else > + > +#define mdp_dbg(level, fmt, ...) do {} while (0) > +#define mdp_err(fmt, ...) do {} while (0) > + > +#endif > + > +#define mdp_dbg_enter() mdp_dbg(3, "+") > +#define mdp_dbg_leave() mdp_dbg(3, "-") > + > +#endif /* __MTK_MDP3_CORE_H__ */ > + > diff --git a/drivers/media/platform/mtk-mdp3/mtk-mdp3-m2m.c b/drivers/media/platform/mtk-mdp3/mtk-mdp3-m2m.c > new file mode 100644 > index 000000000000..638a854c502c > --- /dev/null > +++ b/drivers/media/platform/mtk-mdp3/mtk-mdp3-m2m.c > @@ -0,0 +1,823 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (c) 2018 MediaTek Inc. > + * Author: Ping-Hsun Wu <ping-hsun.wu@xxxxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License 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. > + */ > + > +#include <linux/platform_device.h> > +#include <media/v4l2-ioctl.h> > +#include <media/v4l2-event.h> > +#include <media/videobuf2-dma-contig.h> > +#include "mtk-mdp3-m2m.h" > + > +static inline struct mdp_m2m_ctx *fh_to_ctx(struct v4l2_fh *fh) > +{ > + return container_of(fh, struct mdp_m2m_ctx, fh); > +} > + > +static inline struct mdp_m2m_ctx *ctrl_to_ctx(struct v4l2_ctrl *ctrl) > +{ > + return container_of(ctrl->handler, struct mdp_m2m_ctx, ctrl_handler); > +} > + > +static inline struct mdp_frame *ctx_get_frame(struct mdp_m2m_ctx *ctx, > + enum v4l2_buf_type type) > +{ > + if (V4L2_TYPE_IS_OUTPUT(type)) > + return &ctx->curr_param->output; > + return &ctx->curr_param->captures[0]; Let's use an else here for symetry. > +} > + > +static void mdp_m2m_ctx_set_state(struct mdp_m2m_ctx *ctx, u32 state) > +{ > + mutex_lock(&ctx->curr_param->lock); > + ctx->curr_param->state |= state; > + mutex_unlock(&ctx->curr_param->lock); > +} > + > +static bool mdp_m2m_ctx_is_state_set(struct mdp_m2m_ctx *ctx, u32 mask) > +{ > + bool ret; > + > + mutex_lock(&ctx->curr_param->lock); > + ret = (ctx->curr_param->state & mask) == mask; > + mutex_unlock(&ctx->curr_param->lock); > + return ret; > +} > + > +static void mdp_m2m_ctx_lock(struct vb2_queue *q) > +{ > + struct mdp_m2m_ctx *ctx = vb2_get_drv_priv(q); > + > + mutex_lock(&ctx->mdp_dev->m2m_lock); > +} > + > +static void mdp_m2m_ctx_unlock(struct vb2_queue *q) > +{ > + struct mdp_m2m_ctx *ctx = vb2_get_drv_priv(q); > + > + mutex_unlock(&ctx->mdp_dev->m2m_lock); > +} > + > +static void mdp_m2m_job_abort(void *priv) > +{ > +} > + > +static void mdp_m2m_process_done(void *priv, int vb_state) > +{ > + struct mdp_m2m_ctx *ctx = priv; > + struct vb2_buffer *src_vb, *dst_vb; > + struct vb2_v4l2_buffer *src_vbuf, *dst_vbuf; > + u32 valid_output_flags = V4L2_BUF_FLAG_TIMECODE | > + V4L2_BUF_FLAG_TSTAMP_SRC_MASK | > + V4L2_BUF_FLAG_KEYFRAME | > + V4L2_BUF_FLAG_PFRAME | V4L2_BUF_FLAG_BFRAME; > + > + src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); Compiler warning: assignment from incompatible pointer. > + src_vbuf = to_vb2_v4l2_buffer(src_vb); > + dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); Here too, as well as in other places of this file where these functions are called. > + dst_vbuf = to_vb2_v4l2_buffer(dst_vb); > + > + src_vbuf->sequence = ctx->frame_count; > + dst_vbuf->sequence = src_vbuf->sequence; > + dst_vbuf->timecode = src_vbuf->timecode; > + dst_vbuf->flags &= ~valid_output_flags; > + dst_vbuf->flags |= src_vbuf->flags & valid_output_flags; > + > + v4l2_m2m_buf_done(src_vbuf, vb_state); > + v4l2_m2m_buf_done(dst_vbuf, vb_state); > + v4l2_m2m_job_finish(ctx->mdp_dev->m2m_dev, ctx->m2m_ctx); > + > + ctx->curr_param->frame_no = ctx->frame_count++; > +} [snip] > diff --git a/drivers/media/platform/mtk-mdp3/mtk-mdp3-m2m.h b/drivers/media/platform/mtk-mdp3/mtk-mdp3-m2m.h > new file mode 100644 > index 000000000000..1f681b48c2ad > --- /dev/null > +++ b/drivers/media/platform/mtk-mdp3/mtk-mdp3-m2m.h > @@ -0,0 +1,52 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Copyright (c) 2018 MediaTek Inc. > + * Author: Ping-Hsun Wu <ping-hsun.wu@xxxxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License 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. > + */ > + > +#ifndef __MTK_MDP3_M2M_H__ > +#define __MTK_MDP3_M2M_H__ > + > +#include <media/v4l2-ctrls.h> > +#include "mtk-mdp3-core.h" > +#include "mtk-mdp3-vpu.h" > +#include "mtk-mdp3-regs.h" > + > +#define MDP_MAX_CTRLS 10 > + > +struct mdp_m2m_ctrls { > + struct v4l2_ctrl *hflip; > + struct v4l2_ctrl *vflip; > + /* struct v4l2_ctrl *sharpness; */ > + struct v4l2_ctrl *rotate; > +}; I see the members of this struct assigned in mdp_m2m_ctrls_create(), but they are then completely untouched. What is their purpose? Looks like we can just remove them. > + > +struct mdp_m2m_ctx { > + u32 id; > + struct mdp_dev *mdp_dev; > + struct v4l2_fh fh; > + struct v4l2_ctrl_handler ctrl_handler; > + struct mdp_m2m_ctrls ctrls; > + struct v4l2_m2m_ctx *m2m_ctx; > + struct mdp_vpu_ctx vpu; > + struct work_struct work; > + u32 frame_count; > + > + struct mdp_frameparam *curr_param; > + struct list_head param_list; > +}; > + > +int mdp_m2m_device_register(struct mdp_dev *mdp); > +void mdp_m2m_device_unregister(struct mdp_dev *mdp); > + > +#endif /* __MTK_MDP3_M2M_H__ */ > + > diff --git a/drivers/media/platform/mtk-mdp3/mtk-mdp3-regs.c b/drivers/media/platform/mtk-mdp3/mtk-mdp3-regs.c > new file mode 100644 > index 000000000000..b623eda06c7d > --- /dev/null > +++ b/drivers/media/platform/mtk-mdp3/mtk-mdp3-regs.c > @@ -0,0 +1,757 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (c) 2018 MediaTek Inc. > + * Author: Ping-Hsun Wu <ping-hsun.wu@xxxxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License 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. > + */ > + > +#include <media/v4l2-common.h> > +#include <media/videobuf2-v4l2.h> > +#include <media/videobuf2-dma-contig.h> > +#include "mtk-mdp3-core.h" > +#include "mtk-mdp3-regs.h" > + > +static const struct mdp_format mdp_formats[] = { > + { > + .pixelformat = V4L2_PIX_FMT_GREY, > + .mdp_color = MDP_COLOR_GREY, > + .depth = { 8 }, > + .row_depth = { 8 }, > + .num_planes = 1, > + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, > + }, { > + .pixelformat = V4L2_PIX_FMT_RGB565X, > + .mdp_color = MDP_COLOR_RGB565, > + .depth = { 16 }, > + .row_depth = { 16 }, > + .num_planes = 1, > + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, > + }, { > + .pixelformat = V4L2_PIX_FMT_RGB565, > + .mdp_color = MDP_COLOR_BGR565, > + .depth = { 16 }, > + .row_depth = { 16 }, > + .num_planes = 1, > + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, > + }, { > + .pixelformat = V4L2_PIX_FMT_RGB24, > + .mdp_color = MDP_COLOR_RGB888, > + .depth = { 24 }, > + .row_depth = { 24 }, > + .num_planes = 1, > + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, > + }, { > + .pixelformat = V4L2_PIX_FMT_BGR24, > + .mdp_color = MDP_COLOR_BGR888, > + .depth = { 24 }, > + .row_depth = { 24 }, > + .num_planes = 1, > + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, > + }, { > + .pixelformat = V4L2_PIX_FMT_ABGR32, > + .mdp_color = MDP_COLOR_BGRA8888, > + .depth = { 32 }, > + .row_depth = { 32 }, > + .num_planes = 1, > + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, > + }, { > + .pixelformat = V4L2_PIX_FMT_ARGB32, > + .mdp_color = MDP_COLOR_ARGB8888, > + .depth = { 32 }, > + .row_depth = { 32 }, > + .num_planes = 1, > + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, > + }, { > + .pixelformat = V4L2_PIX_FMT_UYVY, > + .mdp_color = MDP_COLOR_UYVY, > + .depth = { 16 }, > + .row_depth = { 16 }, > + .num_planes = 1, > + .walign = 1, > + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, > + }, { > + .pixelformat = V4L2_PIX_FMT_VYUY, > + .mdp_color = MDP_COLOR_VYUY, > + .depth = { 16 }, > + .row_depth = { 16 }, > + .num_planes = 1, > + .walign = 1, > + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, > + }, { > + .pixelformat = V4L2_PIX_FMT_YUYV, > + .mdp_color = MDP_COLOR_YUYV, > + .depth = { 16 }, > + .row_depth = { 16 }, > + .num_planes = 1, > + .walign = 1, > + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, > + }, { > + .pixelformat = V4L2_PIX_FMT_YVYU, > + .mdp_color = MDP_COLOR_YVYU, > + .depth = { 16 }, > + .row_depth = { 16 }, > + .num_planes = 1, > + .walign = 1, > + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, > + }, { > + .pixelformat = V4L2_PIX_FMT_YUV420, > + .mdp_color = MDP_COLOR_I420, > + .depth = { 12 }, > + .row_depth = { 8 }, > + .num_planes = 1, > + .walign = 1, > + .halign = 1, > + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, > + }, { > + .pixelformat = V4L2_PIX_FMT_YVU420, > + .mdp_color = MDP_COLOR_YV12, > + .depth = { 12 }, > + .row_depth = { 8 }, > + .num_planes = 1, > + .walign = 1, > + .halign = 1, > + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, > + }, { > + .pixelformat = V4L2_PIX_FMT_NV12, > + .mdp_color = MDP_COLOR_NV12, > + .depth = { 12 }, > + .row_depth = { 8 }, > + .num_planes = 1, > + .walign = 1, > + .halign = 1, > + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, > + }, { > + .pixelformat = V4L2_PIX_FMT_NV21, > + .mdp_color = MDP_COLOR_NV21, > + .depth = { 12 }, > + .row_depth = { 8 }, > + .num_planes = 1, > + .walign = 1, > + .halign = 1, > + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, > + }, { > + .pixelformat = V4L2_PIX_FMT_NV16, > + .mdp_color = MDP_COLOR_NV16, > + .depth = { 16 }, > + .row_depth = { 8 }, > + .num_planes = 1, > + .walign = 1, > + .flags = MDP_FMT_FLAG_OUTPUT, > + }, { > + .pixelformat = V4L2_PIX_FMT_NV61, > + .mdp_color = MDP_COLOR_NV61, > + .depth = { 16 }, > + .row_depth = { 8 }, > + .num_planes = 1, > + .walign = 1, > + .flags = MDP_FMT_FLAG_OUTPUT, > + }, { > + .pixelformat = V4L2_PIX_FMT_NV24, > + .mdp_color = MDP_COLOR_NV24, > + .depth = { 24 }, > + .row_depth = { 8 }, > + .num_planes = 1, > + .flags = MDP_FMT_FLAG_OUTPUT, > + }, { > + .pixelformat = V4L2_PIX_FMT_NV42, > + .mdp_color = MDP_COLOR_NV42, > + .depth = { 24 }, > + .row_depth = { 8 }, > + .num_planes = 1, > + .flags = MDP_FMT_FLAG_OUTPUT, > + }, { > + .pixelformat = V4L2_PIX_FMT_MT21C, > + .mdp_color = MDP_COLOR_420_BLK_UFO, > + .depth = { 8, 4 }, > + .row_depth = { 8, 8 }, > + .num_planes = 2, > + .walign = 4, > + .halign = 5, > + .flags = MDP_FMT_FLAG_OUTPUT, > + }, { > + .pixelformat = V4L2_PIX_FMT_NV12MT, > + .mdp_color = MDP_COLOR_420_BLK, > + .depth = { 8, 4 }, > + .row_depth = { 8, 8 }, > + .num_planes = 2, > + .walign = 4, > + .halign = 5, > + .flags = MDP_FMT_FLAG_OUTPUT, > + }, { > + .pixelformat = V4L2_PIX_FMT_NV12M, > + .mdp_color = MDP_COLOR_NV12, > + .depth = { 8, 4 }, > + .row_depth = { 8, 8 }, > + .num_planes = 2, > + .walign = 1, > + .halign = 1, > + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, > + }, { > + .pixelformat = V4L2_PIX_FMT_NV21M, > + .mdp_color = MDP_COLOR_NV21, > + .depth = { 8, 4 }, > + .row_depth = { 8, 8 }, > + .num_planes = 2, > + .walign = 1, > + .halign = 1, > + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, > + }, { > + .pixelformat = V4L2_PIX_FMT_NV16M, > + .mdp_color = MDP_COLOR_NV16, > + .depth = { 8, 8 }, > + .row_depth = { 8, 8 }, > + .num_planes = 2, > + .walign = 1, > + .flags = MDP_FMT_FLAG_OUTPUT, > + }, { > + .pixelformat = V4L2_PIX_FMT_NV61M, > + .mdp_color = MDP_COLOR_NV61, > + .depth = { 8, 8 }, > + .row_depth = { 8, 8 }, > + .num_planes = 2, > + .walign = 1, > + .flags = MDP_FMT_FLAG_OUTPUT, > + }, { > + .pixelformat = V4L2_PIX_FMT_YUV420M, > + .mdp_color = MDP_COLOR_I420, > + .depth = { 8, 2, 2 }, > + .row_depth = { 8, 4, 4 }, > + .num_planes = 3, > + .walign = 1, > + .halign = 1, > + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, > + }, { > + .pixelformat = V4L2_PIX_FMT_YVU420M, > + .mdp_color = MDP_COLOR_YV12, > + .depth = { 8, 2, 2 }, > + .row_depth = { 8, 4, 4 }, > + .num_planes = 3, > + .walign = 1, > + .halign = 1, > + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, > + } > +}; > + > +static const struct mdp_limit mdp_def_limit = { > + .out_limit = { > + .wmin = 16, > + .hmin = 16, > + .wmax = 8176, > + .hmax = 8176, > + }, > + .cap_limit = { > + .wmin = 2, > + .hmin = 2, > + .wmax = 8176, > + .hmax = 8176, > + }, > + .h_scale_up_max = 32, > + .v_scale_up_max = 32, > + .h_scale_down_max = 20, > + .v_scale_down_max = 128, > +}; > + > +static const struct mdp_format *mdp_find_fmt(u32 pixelformat, u32 type) > +{ > + u32 i, flag; > + > + flag = V4L2_TYPE_IS_OUTPUT(type) ? MDP_FMT_FLAG_OUTPUT : > + MDP_FMT_FLAG_CAPTURE; > + for (i = 0; i < ARRAY_SIZE(mdp_formats); ++i) { > + if (!(mdp_formats[i].flags & flag)) > + continue; > + if (mdp_formats[i].pixelformat == pixelformat) > + return &mdp_formats[i]; > + } > + return NULL; > +} > + > +static const struct mdp_format *mdp_find_fmt_by_index(u32 index, u32 type) > +{ > + u32 i, flag, num = 0; > + > + flag = V4L2_TYPE_IS_OUTPUT(type) ? MDP_FMT_FLAG_OUTPUT : > + MDP_FMT_FLAG_CAPTURE; > + for (i = 0; i < ARRAY_SIZE(mdp_formats); ++i) { > + if (!(mdp_formats[i].flags & flag)) > + continue; > + if (index == num) > + return &mdp_formats[i]; > + num++; > + } > + return NULL; > +} > + > +enum mdp_ycbcr_profile mdp_map_ycbcr_prof_mplane(struct v4l2_format *f, > + u32 mdp_color) > +{ > + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; > + > + if (MDP_COLOR_IS_RGB(mdp_color)) > + return MDP_YCBCR_PROFILE_FULL_BT601; > + > + switch (pix_mp->colorspace) { > + case V4L2_COLORSPACE_JPEG: > + return MDP_YCBCR_PROFILE_JPEG; > + case V4L2_COLORSPACE_REC709: > + case V4L2_COLORSPACE_DCI_P3: > + if (pix_mp->quantization == V4L2_QUANTIZATION_FULL_RANGE) > + return MDP_YCBCR_PROFILE_FULL_BT709; > + return MDP_YCBCR_PROFILE_BT709; > + case V4L2_COLORSPACE_BT2020: > + if (pix_mp->quantization == V4L2_QUANTIZATION_FULL_RANGE) > + return MDP_YCBCR_PROFILE_FULL_BT2020; > + return MDP_YCBCR_PROFILE_BT2020; > + } > + /* V4L2_COLORSPACE_SRGB or else */ > + if (pix_mp->quantization == V4L2_QUANTIZATION_FULL_RANGE) > + return MDP_YCBCR_PROFILE_FULL_BT601; > + return MDP_YCBCR_PROFILE_BT601; > +} > + > +static void mdp_bound_align_image(u32 *w, unsigned int wmin, unsigned int wmax, > + unsigned int walign, > + u32 *h, unsigned int hmin, unsigned int hmax, > + unsigned int halign, unsigned int salign) > +{ > + unsigned int org_w, org_h, wstep, hstep; > + > + org_w = *w; > + org_h = *h; > + v4l_bound_align_image(w, wmin, wmax, walign, h, hmin, hmax, halign, > + salign); > + > + wstep = 1 << walign; > + hstep = 1 << halign; > + if (*w < org_w && (*w + wstep) <= wmax) > + *w += wstep; > + if (*h < org_h && (*h + hstep) <= hmax) > + *h += hstep; > +} > + > +static int mdp_clamp_align(s32 *x, int min, int max, unsigned int align) > +{ > + unsigned int mask; > + > + if (min < 0 || max < 0) > + return -ERANGE; > + > + /* Bits that must be zero to be aligned */ > + mask = ~((1 << align) - 1); > + > + min = 0 ? 0 : ((min + ~mask) & mask); > + max = max & mask; > + if ((unsigned int)min > (unsigned int)max) > + return -ERANGE; > + > + /* Clamp to aligned min and max */ > + *x = clamp(*x, min, max); > + > + /* Round to nearest aligned value */ > + if (align) > + *x = (*x + (1 << (align - 1))) & mask; > + return 0; > +} > + > +int mdp_enum_fmt_mplane(struct v4l2_fmtdesc *f) > +{ > + const struct mdp_format *fmt; > + > + if (!V4L2_TYPE_IS_MULTIPLANAR(f->type)) > + return -EINVAL; > + > + fmt = mdp_find_fmt_by_index(f->index, f->type); > + if (!fmt) > + return -EINVAL; > + > + /* f->description */ > + f->pixelformat = fmt->pixelformat; > + return 0; > +} > + > +const struct mdp_format *mdp_try_fmt_mplane(struct v4l2_format *f, > + struct mdp_frameparam *param, > + u32 ctx_id) > +{ > + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; > + const struct mdp_format *fmt; > + const struct mdp_pix_limit *pix_limit; > + u32 wmin, wmax, hmin, hmax, org_w, org_h; > + unsigned int i; > + > + if (!V4L2_TYPE_IS_MULTIPLANAR(f->type)) > + return NULL; > + > + fmt = mdp_find_fmt(pix_mp->pixelformat, f->type); > + if (!fmt) > + fmt = mdp_find_fmt_by_index(0, f->type); > + if (!fmt) { > + mdp_dbg(0, "[%d] pixelformat %c%c%c%c invalid", ctx_id, > + (pix_mp->pixelformat & 0xff), > + (pix_mp->pixelformat >> 8) & 0xff, > + (pix_mp->pixelformat >> 16) & 0xff, > + (pix_mp->pixelformat >> 24) & 0xff); > + return NULL; > + } > + > + pix_mp->field = V4L2_FIELD_NONE; > + pix_mp->flags = 0; > + pix_mp->pixelformat = fmt->pixelformat; > + if (!V4L2_TYPE_IS_OUTPUT(f->type)) { > + pix_mp->colorspace = param->colorspace; > + pix_mp->xfer_func = param->xfer_func; > + pix_mp->ycbcr_enc = param->ycbcr_enc; > + pix_mp->quantization = param->quant; > + } > + memset(pix_mp->reserved, 0, sizeof(pix_mp->reserved)); > + > + pix_limit = V4L2_TYPE_IS_OUTPUT(f->type) ? ¶m->limit->out_limit : > + ¶m->limit->cap_limit; > + wmin = pix_limit->wmin; > + wmax = pix_limit->wmax; > + hmin = pix_limit->hmin; > + hmax = pix_limit->hmax; > + org_w = pix_mp->width; > + org_h = pix_mp->height; > + > + mdp_bound_align_image(&pix_mp->width, wmin, wmax, fmt->walign, > + &pix_mp->height, hmin, hmax, fmt->halign, > + fmt->salign); > + if (org_w != pix_mp->width || org_h != pix_mp->height) > + mdp_dbg(1, "[%d] size change: %ux%u to %ux%u", ctx_id, > + org_w, org_h, pix_mp->width, pix_mp->height); > + > + if (pix_mp->num_planes && pix_mp->num_planes != fmt->num_planes) > + mdp_dbg(1, "[%d] num of planes change: %u to %u", ctx_id, > + pix_mp->num_planes, fmt->num_planes); > + pix_mp->num_planes = fmt->num_planes; > + > + for (i = 0; i < pix_mp->num_planes; ++i) { > + u32 min_bpl = (pix_mp->width * fmt->row_depth[i]) / 8; > + u32 bpl = pix_mp->plane_fmt[i].bytesperline; > + u32 si; > + > + if (bpl < min_bpl) > + bpl = min_bpl; > + si = (bpl * pix_mp->height * fmt->depth[i]) / fmt->row_depth[i]; > + > + pix_mp->plane_fmt[i].bytesperline = bpl; > + if (pix_mp->plane_fmt[i].sizeimage < si) > + pix_mp->plane_fmt[i].sizeimage = si; > + memset(pix_mp->plane_fmt[i].reserved, 0, > + sizeof(pix_mp->plane_fmt[i].reserved)); > + mdp_dbg(2, "[%d] p%u, bpl:%u (%u), sizeimage:%u (%u)", ctx_id, > + i, bpl, min_bpl, pix_mp->plane_fmt[i].sizeimage, si); > + } > + > + return fmt; > +} > + > +static inline int mdp_clamp_start(s32 *x, int min, int max, unsigned int align, > + u32 flags) Please remove the inline here, the compiler will decide whether this needs to be inlined or not. > +{ > + if (flags & V4L2_SEL_FLAG_GE) > + max = *x; > + if (flags & V4L2_SEL_FLAG_LE) > + min = *x; > + return mdp_clamp_align(x, min, max, align); > +} > + > +static inline int mdp_clamp_end(s32 *x, int min, int max, unsigned int align, > + u32 flags) Same here. > +{ > + if (flags & V4L2_SEL_FLAG_GE) > + min = *x; > + if (flags & V4L2_SEL_FLAG_LE) > + max = *x; > + return mdp_clamp_align(x, min, max, align); > +} [snip] > diff --git a/drivers/media/platform/mtk-mdp3/mtk-mdp3-regs.h b/drivers/media/platform/mtk-mdp3/mtk-mdp3-regs.h > new file mode 100644 > index 000000000000..a2b5c31d3d39 > --- /dev/null > +++ b/drivers/media/platform/mtk-mdp3/mtk-mdp3-regs.h > @@ -0,0 +1,386 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Copyright (c) 2018 MediaTek Inc. > + * Author: Ping-Hsun Wu <ping-hsun.wu@xxxxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License 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. > + */ > + > +#ifndef __MTK_MDP3_REGS_H__ > +#define __MTK_MDP3_REGS_H__ > + > +#include <linux/videodev2.h> > +#include <media/videobuf2-core.h> > +#include "mtk-img-ipi.h" > + > +/* > + * MDP native color code > + * Plane count: 1, 2, 3 > + * H-subsample: 0, 1, 2 > + * V-subsample: 0, 1 > + * Color group: 0-RGB, 1-YUV, 2-raw > + */ > +#define MDP_COLOR(PACKED, LOOSE, VIDEO, PLANE, HF, VF, BITS, GROUP, SWAP, ID)\ > + (((PACKED) << 27) | ((LOOSE) << 26) | ((VIDEO) << 23) |\ > + ((PLANE) << 21) | ((HF) << 19) | ((VF) << 18) | ((BITS) << 8) |\ > + ((GROUP) << 6) | ((SWAP) << 5) | ((ID) << 0)) > + > +#define MDP_COLOR_IS_10BIT_PACKED(c) ((0x08000000 & (c)) >> 27) > +#define MDP_COLOR_IS_10BIT_LOOSE(c) (((0x0c000000 & (c)) >> 26) == 1) > +#define MDP_COLOR_IS_10BIT_TILE(c) (((0x0c000000 & (c)) >> 26) == 3) > +#define MDP_COLOR_IS_UFP(c) ((0x02000000 & (c)) >> 25) > +#define MDP_COLOR_IS_INTERLACED(c) ((0x01000000 & (c)) >> 24) > +#define MDP_COLOR_IS_BLOCK_MODE(c) ((0x00800000 & (c)) >> 23) > +#define MDP_COLOR_GET_PLANE_COUNT(c) ((0x00600000 & (c)) >> 21) > +#define MDP_COLOR_GET_H_SUBSAMPLE(c) ((0x00180000 & (c)) >> 19) > +#define MDP_COLOR_GET_V_SUBSAMPLE(c) ((0x00040000 & (c)) >> 18) > +#define MDP_COLOR_BITS_PER_PIXEL(c) ((0x0003ff00 & (c)) >> 8) > +#define MDP_COLOR_GET_GROUP(c) ((0x000000c0 & (c)) >> 6) > +#define MDP_COLOR_IS_SWAPPED(c) ((0x00000020 & (c)) >> 5) > +#define MDP_COLOR_GET_UNIQUE_ID(c) ((0x0000001f & (c)) >> 0) > +#define MDP_COLOR_GET_HW_FORMAT(c) ((0x0000001f & (c)) >> 0) > + > +#define MDP_COLOR_IS_RGB(c) (MDP_COLOR_GET_GROUP(c) == 0) > +#define MDP_COLOR_IS_YUV(c) (MDP_COLOR_GET_GROUP(c) == 1) > +#define MDP_COLOR_IS_UV_COPLANE(c) ((MDP_COLOR_GET_PLANE_COUNT(c) == 2) &&\ > + MDP_COLOR_IS_YUV(c)) > + > +enum mdp_color { > + MDP_COLOR_UNKNOWN = 0, > + > + //MDP_COLOR_FULLG8, > + MDP_COLOR_FULLG8_RGGB = MDP_COLOR(0, 0, 0, 1, 0, 0, 8, 2, 0, 21), > + MDP_COLOR_FULLG8_GRBG = MDP_COLOR(0, 0, 0, 1, 0, 1, 8, 2, 0, 21), > + MDP_COLOR_FULLG8_GBRG = MDP_COLOR(0, 0, 0, 1, 1, 0, 8, 2, 0, 21), > + MDP_COLOR_FULLG8_BGGR = MDP_COLOR(0, 0, 0, 1, 1, 1, 8, 2, 0, 21), > + MDP_COLOR_FULLG8 = MDP_COLOR_FULLG8_BGGR, > + > + //MDP_COLOR_FULLG10, > + MDP_COLOR_FULLG10_RGGB = MDP_COLOR(0, 0, 0, 1, 0, 0, 10, 2, 0, 21), > + MDP_COLOR_FULLG10_GRBG = MDP_COLOR(0, 0, 0, 1, 0, 1, 10, 2, 0, 21), > + MDP_COLOR_FULLG10_GBRG = MDP_COLOR(0, 0, 0, 1, 1, 0, 10, 2, 0, 21), > + MDP_COLOR_FULLG10_BGGR = MDP_COLOR(0, 0, 0, 1, 1, 1, 10, 2, 0, 21), > + MDP_COLOR_FULLG10 = MDP_COLOR_FULLG10_BGGR, > + > + //MDP_COLOR_FULLG12, > + MDP_COLOR_FULLG12_RGGB = MDP_COLOR(0, 0, 0, 1, 0, 0, 12, 2, 0, 21), > + MDP_COLOR_FULLG12_GRBG = MDP_COLOR(0, 0, 0, 1, 0, 1, 12, 2, 0, 21), > + MDP_COLOR_FULLG12_GBRG = MDP_COLOR(0, 0, 0, 1, 1, 0, 12, 2, 0, 21), > + MDP_COLOR_FULLG12_BGGR = MDP_COLOR(0, 0, 0, 1, 1, 1, 12, 2, 0, 21), > + MDP_COLOR_FULLG12 = MDP_COLOR_FULLG12_BGGR, > + > + //MDP_COLOR_FULLG14, > + MDP_COLOR_FULLG14_RGGB = MDP_COLOR(0, 0, 0, 1, 0, 0, 14, 2, 0, 21), > + MDP_COLOR_FULLG14_GRBG = MDP_COLOR(0, 0, 0, 1, 0, 1, 14, 2, 0, 21), > + MDP_COLOR_FULLG14_GBRG = MDP_COLOR(0, 0, 0, 1, 1, 0, 14, 2, 0, 21), > + MDP_COLOR_FULLG14_BGGR = MDP_COLOR(0, 0, 0, 1, 1, 1, 14, 2, 0, 21), > + MDP_COLOR_FULLG14 = MDP_COLOR_FULLG14_BGGR, > + > + MDP_COLOR_UFO10 = MDP_COLOR(0, 0, 0, 1, 0, 0, 10, 2, 0, 24), > + > + //MDP_COLOR_BAYER8, > + MDP_COLOR_BAYER8_RGGB = MDP_COLOR(0, 0, 0, 1, 0, 0, 8, 2, 0, 20), > + MDP_COLOR_BAYER8_GRBG = MDP_COLOR(0, 0, 0, 1, 0, 1, 8, 2, 0, 20), > + MDP_COLOR_BAYER8_GBRG = MDP_COLOR(0, 0, 0, 1, 1, 0, 8, 2, 0, 20), > + MDP_COLOR_BAYER8_BGGR = MDP_COLOR(0, 0, 0, 1, 1, 1, 8, 2, 0, 20), > + MDP_COLOR_BAYER8 = MDP_COLOR_BAYER8_BGGR, > + > + //MDP_COLOR_BAYER10, > + MDP_COLOR_BAYER10_RGGB = MDP_COLOR(0, 0, 0, 1, 0, 0, 10, 2, 0, 20), > + MDP_COLOR_BAYER10_GRBG = MDP_COLOR(0, 0, 0, 1, 0, 1, 10, 2, 0, 20), > + MDP_COLOR_BAYER10_GBRG = MDP_COLOR(0, 0, 0, 1, 1, 0, 10, 2, 0, 20), > + MDP_COLOR_BAYER10_BGGR = MDP_COLOR(0, 0, 0, 1, 1, 1, 10, 2, 0, 20), > + MDP_COLOR_BAYER10 = MDP_COLOR_BAYER10_BGGR, > + > + //MDP_COLOR_BAYER12, > + MDP_COLOR_BAYER12_RGGB = MDP_COLOR(0, 0, 0, 1, 0, 0, 12, 2, 0, 20), > + MDP_COLOR_BAYER12_GRBG = MDP_COLOR(0, 0, 0, 1, 0, 1, 12, 2, 0, 20), > + MDP_COLOR_BAYER12_GBRG = MDP_COLOR(0, 0, 0, 1, 1, 0, 12, 2, 0, 20), > + MDP_COLOR_BAYER12_BGGR = MDP_COLOR(0, 0, 0, 1, 1, 1, 12, 2, 0, 20), > + MDP_COLOR_BAYER12 = MDP_COLOR_BAYER12_BGGR, > + > + MDP_COLOR_RGB48 = MDP_COLOR(0, 0, 0, 1, 0, 0, 48, 0, 0, 23), > + /* For bayer+mono raw-16 */ > + MDP_COLOR_RGB565_RAW = MDP_COLOR(0, 0, 0, 1, 0, 0, 16, 2, 0, 0), > + > + MDP_COLOR_BAYER8_UNPAK = MDP_COLOR(0, 0, 0, 1, 0, 0, 8, 2, 0, 22), > + MDP_COLOR_BAYER10_UNPAK = MDP_COLOR(0, 0, 0, 1, 0, 0, 10, 2, 0, 22), > + MDP_COLOR_BAYER12_UNPAK = MDP_COLOR(0, 0, 0, 1, 0, 0, 12, 2, 0, 22), > + MDP_COLOR_BAYER14_UNPAK = MDP_COLOR(0, 0, 0, 1, 0, 0, 14, 2, 0, 22), > + > + /* Unified formats */ > + MDP_COLOR_GREY = MDP_COLOR(0, 0, 0, 1, 0, 0, 8, 1, 0, 7), > + > + MDP_COLOR_RGB565 = MDP_COLOR(0, 0, 0, 1, 0, 0, 16, 0, 0, 0), > + MDP_COLOR_BGR565 = MDP_COLOR(0, 0, 0, 1, 0, 0, 16, 0, 1, 0), > + MDP_COLOR_RGB888 = MDP_COLOR(0, 0, 0, 1, 0, 0, 24, 0, 1, 1), > + MDP_COLOR_BGR888 = MDP_COLOR(0, 0, 0, 1, 0, 0, 24, 0, 0, 1), > + MDP_COLOR_RGBA8888 = MDP_COLOR(0, 0, 0, 1, 0, 0, 32, 0, 1, 2), > + MDP_COLOR_BGRA8888 = MDP_COLOR(0, 0, 0, 1, 0, 0, 32, 0, 0, 2), > + MDP_COLOR_ARGB8888 = MDP_COLOR(0, 0, 0, 1, 0, 0, 32, 0, 1, 3), > + MDP_COLOR_ABGR8888 = MDP_COLOR(0, 0, 0, 1, 0, 0, 32, 0, 0, 3), > + > + MDP_COLOR_UYVY = MDP_COLOR(0, 0, 0, 1, 1, 0, 16, 1, 0, 4), > + MDP_COLOR_VYUY = MDP_COLOR(0, 0, 0, 1, 1, 0, 16, 1, 1, 4), > + MDP_COLOR_YUYV = MDP_COLOR(0, 0, 0, 1, 1, 0, 16, 1, 0, 5), > + MDP_COLOR_YVYU = MDP_COLOR(0, 0, 0, 1, 1, 0, 16, 1, 1, 5), > + > + MDP_COLOR_I420 = MDP_COLOR(0, 0, 0, 3, 1, 1, 8, 1, 0, 8), > + MDP_COLOR_YV12 = MDP_COLOR(0, 0, 0, 3, 1, 1, 8, 1, 1, 8), > + MDP_COLOR_I422 = MDP_COLOR(0, 0, 0, 3, 1, 0, 8, 1, 0, 9), > + MDP_COLOR_YV16 = MDP_COLOR(0, 0, 0, 3, 1, 0, 8, 1, 1, 9), > + MDP_COLOR_I444 = MDP_COLOR(0, 0, 0, 3, 0, 0, 8, 1, 0, 10), > + MDP_COLOR_YV24 = MDP_COLOR(0, 0, 0, 3, 0, 0, 8, 1, 1, 10), > + > + MDP_COLOR_NV12 = MDP_COLOR(0, 0, 0, 2, 1, 1, 8, 1, 0, 12), > + MDP_COLOR_NV21 = MDP_COLOR(0, 0, 0, 2, 1, 1, 8, 1, 1, 12), > + MDP_COLOR_NV16 = MDP_COLOR(0, 0, 0, 2, 1, 0, 8, 1, 0, 13), > + MDP_COLOR_NV61 = MDP_COLOR(0, 0, 0, 2, 1, 0, 8, 1, 1, 13), > + MDP_COLOR_NV24 = MDP_COLOR(0, 0, 0, 2, 0, 0, 8, 1, 0, 14), > + MDP_COLOR_NV42 = MDP_COLOR(0, 0, 0, 2, 0, 0, 8, 1, 1, 14), > + > + /* Mediatek proprietary formats */ > + /* UFO encoded block mode */ > + MDP_COLOR_420_BLK_UFO = MDP_COLOR(0, 0, 5, 2, 1, 1, 256, 1, 0, 12), > + /* Block mode */ > + MDP_COLOR_420_BLK = MDP_COLOR(0, 0, 1, 2, 1, 1, 256, 1, 0, 12), > + /* Block mode + field mode */ > + MDP_COLOR_420_BLKI = MDP_COLOR(0, 0, 3, 2, 1, 1, 256, 1, 0, 12), > + /* Block mode */ > + MDP_COLOR_422_BLK = MDP_COLOR(0, 0, 1, 1, 1, 0, 512, 1, 0, 4), > + > + MDP_COLOR_IYU2 = MDP_COLOR(0, 0, 0, 1, 0, 0, 24, 1, 0, 25), > + MDP_COLOR_YUV444 = MDP_COLOR(0, 0, 0, 1, 0, 0, 24, 1, 0, 30), > + > + /* Packed 10-bit formats */ > + MDP_COLOR_RGBA1010102 = MDP_COLOR(1, 0, 0, 1, 0, 0, 32, 0, 1, 2), > + MDP_COLOR_BGRA1010102 = MDP_COLOR(1, 0, 0, 1, 0, 0, 32, 0, 0, 2), > + /* Packed 10-bit UYVY */ > + MDP_COLOR_UYVY_10P = MDP_COLOR(1, 0, 0, 1, 1, 0, 20, 1, 0, 4), > + /* Packed 10-bit NV21 */ > + MDP_COLOR_NV21_10P = MDP_COLOR(1, 0, 0, 2, 1, 1, 10, 1, 1, 12), > + /* 10-bit block mode */ > + MDP_COLOR_420_BLK_10_H = MDP_COLOR(1, 0, 1, 2, 1, 1, 320, 1, 0, 12), > + /* 10-bit HEVC tile mode */ > + MDP_COLOR_420_BLK_10_V = MDP_COLOR(1, 1, 1, 2, 1, 1, 320, 1, 0, 12), > + /* UFO encoded 10-bit block mode */ > + MDP_COLOR_420_BLK_U10_H = MDP_COLOR(1, 0, 5, 2, 1, 1, 320, 1, 0, 12), > + /* UFO encoded 10-bit HEVC tile mode */ > + MDP_COLOR_420_BLK_U10_V = MDP_COLOR(1, 1, 5, 2, 1, 1, 320, 1, 0, 12), > + > + /* Loose 10-bit formats */ > + MDP_COLOR_UYVY_10L = MDP_COLOR(0, 1, 0, 1, 1, 0, 20, 1, 0, 4), > + MDP_COLOR_VYUY_10L = MDP_COLOR(0, 1, 0, 1, 1, 0, 20, 1, 1, 4), > + MDP_COLOR_YUYV_10L = MDP_COLOR(0, 1, 0, 1, 1, 0, 20, 1, 0, 5), > + MDP_COLOR_YVYU_10L = MDP_COLOR(0, 1, 0, 1, 1, 0, 20, 1, 1, 5), > + MDP_COLOR_NV12_10L = MDP_COLOR(0, 1, 0, 2, 1, 1, 10, 1, 0, 12), > + MDP_COLOR_NV21_10L = MDP_COLOR(0, 1, 0, 2, 1, 1, 10, 1, 1, 12), > + MDP_COLOR_NV16_10L = MDP_COLOR(0, 1, 0, 2, 1, 0, 10, 1, 0, 13), > + MDP_COLOR_NV61_10L = MDP_COLOR(0, 1, 0, 2, 1, 0, 10, 1, 1, 13), > + MDP_COLOR_YV12_10L = MDP_COLOR(0, 1, 0, 3, 1, 1, 10, 1, 1, 8), > + MDP_COLOR_I420_10L = MDP_COLOR(0, 1, 0, 3, 1, 1, 10, 1, 0, 8), > +}; > + > +/* Minimum Y stride that is accepted by MDP HW */ > +static inline u32 mdp_color_get_min_y_stride(enum mdp_color c, u32 width) > +{ > + return ((MDP_COLOR_BITS_PER_PIXEL(c) * width) + 4) >> 3; > +} > + > +/* Minimum UV stride that is accepted by MDP HW */ > +static inline u32 mdp_color_get_min_uv_stride(enum mdp_color c, u32 width) > +{ > + u32 min_stride; > + > + if (MDP_COLOR_GET_PLANE_COUNT(c) == 1) > + return 0; > + min_stride = mdp_color_get_min_y_stride(c, width) > + >> MDP_COLOR_GET_H_SUBSAMPLE(c); > + if (MDP_COLOR_IS_UV_COPLANE(c) && !MDP_COLOR_IS_BLOCK_MODE(c)) > + min_stride = min_stride * 2; > + return min_stride; > +} > + > +/* Minimum Y plane size that is necessary in buffer */ > +static inline u32 mdp_color_get_min_y_size(enum mdp_color c, > + u32 width, u32 height) > +{ > + if (MDP_COLOR_IS_BLOCK_MODE(c)) > + return ((MDP_COLOR_BITS_PER_PIXEL(c) * width) >> 8) * height; > + return mdp_color_get_min_y_stride(c, width) * height; > +} > + > +/* Minimum UV plane size that is necessary in buffer */ > +static inline u32 mdp_color_get_min_uv_size(enum mdp_color c, > + u32 width, u32 height) > +{ > + height = height >> MDP_COLOR_GET_V_SUBSAMPLE(c); > + if (MDP_COLOR_IS_BLOCK_MODE(c) && (MDP_COLOR_GET_PLANE_COUNT(c) > 1)) > + return ((MDP_COLOR_BITS_PER_PIXEL(c) * width) >> 8) * height; > + return mdp_color_get_min_uv_stride(c, width) * height; > +} > + > +/* Combine colorspace, xfer_func, ycbcr_encoding, and quantization */ > +enum mdp_ycbcr_profile { > + /* V4L2_YCBCR_ENC_601 and V4L2_QUANTIZATION_LIM_RANGE */ > + MDP_YCBCR_PROFILE_BT601, > + /* V4L2_YCBCR_ENC_709 and V4L2_QUANTIZATION_LIM_RANGE */ > + MDP_YCBCR_PROFILE_BT709, > + /* V4L2_YCBCR_ENC_601 and V4L2_QUANTIZATION_FULL_RANGE */ > + MDP_YCBCR_PROFILE_JPEG, > + MDP_YCBCR_PROFILE_FULL_BT601 = MDP_YCBCR_PROFILE_JPEG, > + > + /* Colorspaces not support for capture */ > + /* V4L2_YCBCR_ENC_BT2020 and V4L2_QUANTIZATION_LIM_RANGE */ > + MDP_YCBCR_PROFILE_BT2020, > + /* V4L2_YCBCR_ENC_709 and V4L2_QUANTIZATION_FULL_RANGE */ > + MDP_YCBCR_PROFILE_FULL_BT709, > + /* V4L2_YCBCR_ENC_BT2020 and V4L2_QUANTIZATION_FULL_RANGE */ > + MDP_YCBCR_PROFILE_FULL_BT2020, > +}; > + > +#define MDP_FMT_FLAG_OUTPUT BIT(0) > +#define MDP_FMT_FLAG_CAPTURE BIT(1) > + > +struct mdp_format { > + u32 pixelformat; > + u32 mdp_color; > + u8 depth[VIDEO_MAX_PLANES]; > + u8 row_depth[VIDEO_MAX_PLANES]; > + u8 num_planes; > + u8 walign; > + u8 halign; > + u8 salign; > + u32 flags; > +}; > + > +struct mdp_pix_limit { > + u32 wmin; > + u32 hmin; > + u32 wmax; > + u32 hmax; > +}; > + > +struct mdp_limit { > + struct mdp_pix_limit out_limit; > + struct mdp_pix_limit cap_limit; > + u32 h_scale_up_max; > + u32 v_scale_up_max; > + u32 h_scale_down_max; > + u32 v_scale_down_max; > +}; > + > +enum mdp_stream_type { > + MDP_STREAM_TYPE_UNKNOWN, > + > + MDP_STREAM_TYPE_BITBLT, > + MDP_STREAM_TYPE_GPU_BITBLT, > + MDP_STREAM_TYPE_DUAL_BITBLT, > + MDP_STREAM_TYPE_2ND_BITBLT, > + > + /* MDP_STREAM_TYPE_FRAG, */ > + /* MDP_STREAM_TYPE_FRAG_JPEGDEC, */ > + > + MDP_STREAM_TYPE_ISP_IC, > + MDP_STREAM_TYPE_ISP_VR, > + MDP_STREAM_TYPE_ISP_ZSD, > + MDP_STREAM_TYPE_ISP_IP, > + MDP_STREAM_TYPE_ISP_VSS, > + MDP_STREAM_TYPE_ISP_ZSD_SLOW, > + /* MDP_STREAM_TYPE_ISP_ZSD_ONE, */ > + > + MDP_STREAM_TYPE_WPE, > + MDP_STREAM_TYPE_WPE2, > +}; > + > +struct mdp_crop { > + struct v4l2_rect c; > + struct v4l2_fract left_subpix; > + struct v4l2_fract top_subpix; > + struct v4l2_fract width_subpix; > + struct v4l2_fract height_subpix; > +}; > + > +struct mdp_frame { > + struct v4l2_format format; > + const struct mdp_format *mdp_fmt; > + u32 ycbcr_prof; /* enum mdp_ycbcr_profile */ > + u32 usage; /* enum mdp_buffer_usage */ > + struct mdp_crop crop; > + struct v4l2_rect compose; > + s32 rotation; > + u32 hflip:1; > + u32 vflip:1; > + u32 hdr:1; > + u32 dre:1; > + u32 sharpness:1; > + u32 dither:1; > +}; > + > +static inline bool mdp_target_is_crop(u32 target) > +{ > + return (target == V4L2_SEL_TGT_CROP) || > + (target == V4L2_SEL_TGT_CROP_DEFAULT) || > + (target == V4L2_SEL_TGT_CROP_BOUNDS); > +} > + > +static inline bool mdp_target_is_compose(u32 target) > +{ > + return (target == V4L2_SEL_TGT_COMPOSE) || > + (target == V4L2_SEL_TGT_COMPOSE_DEFAULT) || > + (target == V4L2_SEL_TGT_COMPOSE_BOUNDS); > +} > + > +#define MDP_MAX_CAPTURES IMG_MAX_HW_OUTPUTS > + > +#define MDP_VPU_INIT BIT(0) > +#define MDP_M2M_SRC_FMT BIT(1) > +#define MDP_M2M_DST_FMT BIT(2) > +#define MDP_M2M_CTX_ERROR BIT(5) > + > +struct mdp_frameparam { > + struct list_head list; > + /* synchronization protect for m2m context state */ > + struct mutex lock; This lock is only used to protect against concurrent accesses to state. Maybe rename it to state_lock and document this fact? > + u32 state; > + const struct mdp_limit *limit; > + u32 type; /* enum mdp_stream_type */ > + u32 frame_no; > + struct mdp_frame output; > + struct mdp_frame captures[MDP_MAX_CAPTURES]; > + u32 num_captures; > + /* __u8 pq_data[]; */ > + enum v4l2_colorspace colorspace; > + enum v4l2_ycbcr_encoding ycbcr_enc; > + enum v4l2_xfer_func xfer_func; > + enum v4l2_quantization quant; > +}; > + > +int mdp_enum_fmt_mplane(struct v4l2_fmtdesc *f); > +const struct mdp_format *mdp_try_fmt_mplane(struct v4l2_format *f, > + struct mdp_frameparam *param, > + u32 ctx_id); > +enum mdp_ycbcr_profile mdp_map_ycbcr_prof_mplane(struct v4l2_format *f, > + u32 mdp_color); > +int mdp_try_crop(struct v4l2_rect *r, const struct v4l2_selection *s, > + struct mdp_frame *frame, u32 ctx_id); > +int mdp_check_scaling_ratio(const struct v4l2_rect *crop, > + const struct v4l2_rect *compose, s32 rotation, > + const struct mdp_limit *limit); > +void mdp_set_src_config(struct img_input *in, > + struct mdp_frame *frame, struct vb2_buffer *vb); > +void mdp_set_dst_config(struct img_output *out, > + struct mdp_frame *frame, struct vb2_buffer *vb); > + > +struct mdp_frameparam *mdp_frameparam_init(void); > +void mdp_frameparam_release(struct mdp_frameparam *param); > + > +#endif /* __MTK_MDP3_REGS_H__ */ > + > diff --git a/drivers/media/platform/mtk-mdp3/mtk-mdp3-vpu.c b/drivers/media/platform/mtk-mdp3/mtk-mdp3-vpu.c > new file mode 100644 > index 000000000000..0ad443cb52e1 > --- /dev/null > +++ b/drivers/media/platform/mtk-mdp3/mtk-mdp3-vpu.c > @@ -0,0 +1,277 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (c) 2018 MediaTek Inc. > + * Author: Ping-Hsun Wu <ping-hsun.wu@xxxxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License 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. > + */ > + > +#include <linux/remoteproc.h> > +#include <linux/platform_data/mtk_scp.h> > +#include "mtk-mdp3-vpu.h" > +#include "mtk-mdp3-core.h" > + > +#define MDP_VPU_MESSAGE_TIMEOUT 500U > + > +static inline struct mdp_dev *vpu_to_mdp(struct mdp_vpu_dev *vpu) > +{ > + return container_of(vpu, struct mdp_dev, vpu); > +} > + > +static void mdp_vpu_ipi_handle_init_ack(void *data, unsigned int len, > + void *priv) > +{ > + struct mdp_ipi_init_msg *msg = (struct mdp_ipi_init_msg *)data; > + struct mdp_vpu_dev *vpu = > + (struct mdp_vpu_dev *)(unsigned long)msg->drv_data; > + > + if (!vpu->work_size) > + vpu->work_size = msg->work_size; > + else > + vpu->status = msg->status; Why is this in an else statement? Shouldn't we record the status in all cases? > + complete(&vpu->ipi_acked); > +} > + > +static void mdp_vpu_ipi_handle_deinit_ack(void *data, unsigned int len, > + void *priv) > +{ > + struct mdp_ipi_deinit_msg *msg = (struct mdp_ipi_deinit_msg *)data; > + struct mdp_vpu_dev *vpu = > + (struct mdp_vpu_dev *)(unsigned long)msg->drv_data; > + > + vpu->status = msg->status; > + complete(&vpu->ipi_acked); > +} > + > +static void mdp_vpu_ipi_handle_frame_ack(void *data, unsigned int len, > + void *priv) > +{ > + struct img_ipi_frameparam *param = (struct img_ipi_frameparam *)data; > + struct mdp_vpu_ctx *ctx = > + (struct mdp_vpu_ctx *)(unsigned long)param->drv_data; > + > + ctx->failure = param->state; You don't need mdp_vpu_ctx::failure at all as it is only used in this function. Remove it from struct mdp_vpu_ctx and work directly on param->state. > + if (ctx->failure) { > + struct mdp_dev *mdp = vpu_to_mdp(ctx->vpu_dev); > + > + dev_info(&mdp->pdev->dev, "VPU MDP failure:%d\n", ctx->failure); > + } > + complete(&ctx->vpu_dev->ipi_acked); > +} > + > +int mdp_vpu_register(struct platform_device *pdev) > +{ > + int err; > + > + err = scp_ipi_register(pdev, SCP_IPI_MDP_INIT, > + mdp_vpu_ipi_handle_init_ack, NULL); > + if (err) { > + dev_err(&pdev->dev, "scp_ipi_register failed %d\n", err); > + goto err_ipi_init; > + } > + err = scp_ipi_register(pdev, SCP_IPI_MDP_DEINIT, > + mdp_vpu_ipi_handle_deinit_ack, NULL); > + if (err) { > + dev_err(&pdev->dev, "scp_ipi_register failed %d\n", err); > + goto err_ipi_deinit; > + } > + err = scp_ipi_register(pdev, SCP_IPI_MDP_FRAME, > + mdp_vpu_ipi_handle_frame_ack, NULL); > + if (err) { > + dev_err(&pdev->dev, "scp_ipi_register failed %d\n", err); > + goto err_ipi_frame; > + } > + return 0; > + > +err_ipi_frame: > + /* vpu_ipi_unregister(IPI_MDP_DEINIT); */ > +err_ipi_deinit: > + /* vpu_ipi_unregister(IPI_MDP_INIT); */ > +err_ipi_init: > + > + return err; > +} > + > +void mdp_vpu_unregister(struct platform_device *pdev) > +{ > + /* vpu_ipi_unregister(IPI_MDP_INIT); */ > + /* vpu_ipi_unregister(IPI_MDP_DEINIT); */ > + /* vpu_ipi_unregister(IPI_MDP_FRAME); */ Isn't this necessary if we, say, unregister the driver? > +} > + > +static int mdp_vpu_sendmsg(struct mdp_vpu_dev *vpu, enum scp_ipi_id id, > + void *buf, unsigned int len) > +{ > + int ret; > + > + if (!vpu->pdev) { > + struct mdp_dev *mdp = vpu_to_mdp(vpu); > + > + dev_dbg(&mdp->pdev->dev, "vpu pdev is NULL"); > + return -EINVAL; > + } > + ret = scp_ipi_send(vpu->pdev, id, buf, len, 2000); > + > + if (ret) { > + dev_err(&vpu->pdev->dev, "scp_ipi_send failed %d\n", ret); > + return -EPERM; > + } > + ret = > + wait_for_completion_timeout(&vpu->ipi_acked, > + msecs_to_jiffies(MDP_VPU_MESSAGE_TIMEOUT)); > + if (!ret) > + ret = -ETIME; > + else if (vpu->status) > + ret = -EINVAL; > + else > + ret = 0; > + return ret; > +} > + > +int mdp_vpu_dev_init(struct mdp_vpu_dev *vpu, struct platform_device *pdev, > + struct mutex *lock) > +{ > + struct mdp_ipi_init_msg msg = { > + .drv_data = (unsigned long)vpu, > + }; > + phys_addr_t mem_size, pool; > + const size_t pool_size = sizeof(struct mdp_config_pool); > + struct mdp_dev *mdp = vpu_to_mdp(vpu); > + int err; > + > + init_completion(&vpu->ipi_acked); > + vpu->pdev = pdev; > + vpu->lock = lock; Is there any reason why the lock must come from the outside? I have the feeling that we could simplify the code a bit if vpu->lock was just a lock that we initialize here. > + vpu->work_size = 0; > + err = mdp_vpu_sendmsg(vpu, SCP_IPI_MDP_INIT, &msg, sizeof(msg)); > + if (err) > + goto err_work_size; > + /* vpu work_size was set in mdp_vpu_ipi_handle_init_ack */ > + > + vpu->work = scp_get_reserve_mem_virt(SCP_MDP_MEM_ID); > + vpu->work_addr = scp_get_reserve_mem_phys(SCP_MDP_MEM_ID); > + mem_size = scp_get_reserve_mem_size(SCP_MDP_MEM_ID); > + pool = ALIGN(vpu->work + vpu->work_size, 8); > + if (pool + pool_size - vpu->work > mem_size) { > + dev_err(&mdp->pdev->dev, > + "VPU memory insufficient: %lx + %lx > %llx", > + vpu->work_size, pool_size, mem_size); > + err = -ENOMEM; > + goto err_mem_size; > + } > + > + dev_info(&mdp->pdev->dev, > + "VPU work:%llx pa:%llx sz:%lx pool:%llx sz:%lx (mem sz:%llx)", > + vpu->work, vpu->work_addr, vpu->work_size, > + pool, pool_size, mem_size); > + vpu->pool = (struct mdp_config_pool *)pool; > + msg.work_addr = vpu->work_addr; > + msg.work_size = vpu->work_size; > + err = mdp_vpu_sendmsg(vpu, SCP_IPI_MDP_INIT, &msg, sizeof(msg)); Why do we need to invoke SCP_IPI_MDP_INIT twice? > + if (err) > + goto err_work_size; > + memset(vpu->pool, 0, sizeof(*vpu->pool)); > + return 0; > + > +err_work_size: > + switch (vpu->status) { > + case -MDP_IPI_EBUSY: > + err = -EBUSY; > + break; > + case -MDP_IPI_ENOMEM: > + err = -ENOSPC; /* -ENOMEM */ > + break; What if vpu->status is anything else? We need to manage default to set err to a valid error state, otherwise we will return an undefined value (likely 0 even though an error occured). > + } > +err_mem_size: > + return err; > +} > + > +int mdp_vpu_dev_deinit(struct mdp_vpu_dev *vpu) > +{ > + struct mdp_ipi_deinit_msg msg = { > + .drv_data = (unsigned long)vpu, > + .work_addr = vpu->work_addr, > + }; > + > + return mdp_vpu_sendmsg(vpu, SCP_IPI_MDP_DEINIT, &msg, sizeof(msg)); > +} > + > +static struct img_config *mdp_config_get(struct mdp_vpu_dev *vpu, > + enum mdp_config_id id, uint32_t *addr) > +{ > + struct img_config *config; > + > + if (id < 0 || id >= MDP_CONFIG_POOL_SIZE) > + return ERR_PTR(-EINVAL); > + if (vpu->lock) We always call mdp_vpu_dev_init with a lock, so we can drop this conditional. > + mutex_lock(vpu->lock); > + vpu->pool->cfg_count[id]++; > + config = &vpu->pool->configs[id]; > + *addr = vpu->work_addr + ((unsigned long)config - vpu->work); > + if (vpu->lock) > + mutex_unlock(vpu->lock); > + return config; > +} > + > +static int mdp_config_put(struct mdp_vpu_dev *vpu, > + enum mdp_config_id id, > + const struct img_config *config) > +{ > + int err = 0; > + > + if (id < 0 || id >= MDP_CONFIG_POOL_SIZE) > + return -EINVAL; > + if (vpu->lock) > + mutex_lock(vpu->lock); > + if (!vpu->pool->cfg_count[id] || config != &vpu->pool->configs[id]) > + err = -EINVAL; > + else > + vpu->pool->cfg_count[id]--; > + if (vpu->lock) > + mutex_unlock(vpu->lock); > + return err; > +} > + > +int mdp_vpu_ctx_init(struct mdp_vpu_ctx *ctx, struct mdp_vpu_dev *vpu, > + enum mdp_config_id id) > +{ > + ctx->config = mdp_config_get(vpu, id, &ctx->inst_addr); > + if (IS_ERR(ctx->config)) { > + int err = PTR_ERR(ctx->config); > + > + ctx->config = NULL; > + return err; > + } > + ctx->config_id = id; > + ctx->vpu_dev = vpu; > + return 0; > +} > + > +int mdp_vpu_ctx_deinit(struct mdp_vpu_ctx *ctx) > +{ > + int err = mdp_config_put(ctx->vpu_dev, ctx->config_id, ctx->config); > + > + ctx->config_id = 0; > + ctx->config = NULL; > + ctx->inst_addr = 0; > + return err; > +} > + > +int mdp_vpu_process(struct mdp_vpu_ctx *ctx, struct img_ipi_frameparam *param) > +{ > + memset((void *)ctx->vpu_dev->work, 0, ctx->vpu_dev->work_size); > + memset(ctx->config, 0, sizeof(*ctx->config)); > + param->config_data.va = (unsigned long)ctx->config; > + param->config_data.pa = ctx->inst_addr; > + param->drv_data = (unsigned long)ctx; > + return mdp_vpu_sendmsg(ctx->vpu_dev, SCP_IPI_MDP_FRAME, > + param, sizeof(*param)); > +} > + > diff --git a/drivers/media/platform/mtk-mdp3/mtk-mdp3-vpu.h b/drivers/media/platform/mtk-mdp3/mtk-mdp3-vpu.h > new file mode 100644 > index 000000000000..09b5b0026e4b > --- /dev/null > +++ b/drivers/media/platform/mtk-mdp3/mtk-mdp3-vpu.h > @@ -0,0 +1,90 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Copyright (c) 2018 MediaTek Inc. > + * Author: Ping-Hsun Wu <ping-hsun.wu@xxxxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License 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. > + */ > + > +#ifndef __MTK_MDP3_VPU_H__ > +#define __MTK_MDP3_VPU_H__ > + > +#include <linux/platform_device.h> > +#include "mtk-img-ipi.h" > + > +enum mdp_ipi_result { > + MDP_IPI_SUCCESS = 0, > + MDP_IPI_ENOMEM = 12, > + MDP_IPI_EBUSY = 16, > + MDP_IPI_EINVAL = 22, > + MDP_IPI_EMINST = 24, > + MDP_IPI_ERANGE = 34, > + MDP_IPI_NR_ERRNO, > + > + MDP_IPI_EOTHER = MDP_IPI_NR_ERRNO, > + MDP_IPI_PATH_CANT_MERGE, > + MDP_IPI_OP_FAIL, > +}; > + > +struct mdp_ipi_init_msg { > + u32 status; > + u64 drv_data; > + u32 work_addr; /* [in] working buffer address */ > + u32 work_size; /* [in] working buffer size */ > +} __attribute__ ((__packed__)); > + > +struct mdp_ipi_deinit_msg { > + u32 status; > + u64 drv_data; > + u32 work_addr; > +} __attribute__ ((__packed__)); > + > +enum mdp_config_id { > + MDP_DEV_M2M = 0, > + MDP_CONFIG_POOL_SIZE /* ALWAYS keep at the end */ > +}; > + > +struct mdp_config_pool { > + u64 cfg_count[MDP_CONFIG_POOL_SIZE]; > + struct img_config configs[MDP_CONFIG_POOL_SIZE]; > +}; > + > +struct mdp_vpu_dev { > + /* synchronization protect for image configuration reference count */ > + struct mutex *lock; > + struct platform_device *pdev; > + struct completion ipi_acked; > + phys_addr_t work; > + phys_addr_t work_addr; > + size_t work_size; > + struct mdp_config_pool *pool; > + u32 status; > +}; > + > +struct mdp_vpu_ctx { > + struct mdp_vpu_dev *vpu_dev; > + u32 config_id; > + struct img_config *config; > + u32 inst_addr; > + s32 failure; This member is only used in mdp_vpu_ipi_handle_frame_ack(). Therefore is can be replaced by a local variable and does not need to be defined in this struct. > +}; > + > +int mdp_vpu_register(struct platform_device *pdev); > +void mdp_vpu_unregister(struct platform_device *pdev); > +int mdp_vpu_dev_init(struct mdp_vpu_dev *vpu, struct platform_device *pdev, > + struct mutex *lock); > +int mdp_vpu_dev_deinit(struct mdp_vpu_dev *vpu); > +int mdp_vpu_ctx_init(struct mdp_vpu_ctx *ctx, struct mdp_vpu_dev *vpu, > + enum mdp_config_id id); > +int mdp_vpu_ctx_deinit(struct mdp_vpu_ctx *ctx); > +int mdp_vpu_process(struct mdp_vpu_ctx *vpu, struct img_ipi_frameparam *param); > + > +#endif /* __MTK_MDP3_VPU_H__ */ > + > -- > 2.18.0