[PATCH v18 4/4] media: platform: mtk-mdp3: add Mediatek MDP3 driver
moudy.ho
moudy.ho at mediatek.com
Thu Jun 23 18:54:06 PDT 2022
On Tue, 2022-06-21 at 11:21 -0400, Nicolas Dufresne wrote:
> Le vendredi 17 juin 2022 à 14:19 +0800, moudy.ho a écrit :
> > Hi Nicolas,
> >
> > On Mon, 2022-06-13 at 11:06 -0400, Nicolas Dufresne wrote:
> > > Le vendredi 10 juin 2022 à 14:45 +0800, Moudy Ho a écrit :
> > > > This patch adds driver for Mediatek's Media Data Path ver.3
> > > > (MDP3).
> > > > It provides the following functions:
> > > > color transform, format conversion, resize, crop, rotate,
> > > > flip
> > > > and additional image quality enhancement.
>
> [snip]
>
> > > > +static void mdp_cmdq_pkt_destroy(struct cmdq_pkt *pkt)
> > > > +{
> > > > + struct cmdq_client *client = (struct cmdq_client *)pkt-
> > > > >cl;
> > > > +
> > > > + dma_unmap_single(client->chan->mbox->dev, pkt->pa_base,
> > > > pkt-
> > > > > buf_size,
> > > >
> > > > + DMA_TO_DEVICE);
> > >
> > > nit: On a theoretical base, since this may have no effect on this
> > > architecture,
> > > shouldn't a "destroy" function just discard any cached data ?
> > > Hence,
> > > shouldn't
> > > you use DMA_NONE here ?
> > >
> >
> > When I tried to adjust the direction of dma_unmap to DMA_NONE, the
> > unmap error occurred.
> >
> > [ 37.892801] kernel BUG at kernel/dma/mapping.c:167!
> > ...
> > [ 37.893014] Workqueue: mtk-mdp3-clock mdp_auto_release_work
> > [mtk_mdp3]
> > [ 37.893021] pstate: 60400005 (nZCv daif +PAN -UAO -TCO BTYPE=--)
> > [ 37.893034] pc : dma_unmap_page_attrs+0x1ac/0x1b0
> > [ 37.893043] lr : mdp_cmdq_pkt_destroy+0x3c/0x54 [mtk_mdp3]
> > [ 37.893046] sp : ffffffc010ac3d40
> > ...
> > [ 37.926819] Kernel panic - not syncing: Oops - BUG: Fatal
> > exception
> > ...
> >
> > Looking back, I found that the directions were limited to only 3 by
> > the
> > following checking:
> >
> > </linux/dma-direction.h>
> >
> > static inline int valid_dma_direction(enum dma_data_direction dir)
> > {
> > return dir == DMA_BIDIRECTIONAL || dir == DMA_TO_DEVICE ||
> > dir == DMA_FROM_DEVICE;
> > }
>
> My apology, I should have looked at the documentation before
> commenting.
>
> >
> > > > + kfree(pkt->va_base);
> > > > +}
> > > > +
> > > > +static void mdp_auto_release_work(struct work_struct *work)
> > > > +{
> > > > + struct mdp_cmdq_cmd *cmd;
> > > > + struct mdp_dev *mdp;
> > > > +
> > > > + cmd = container_of(work, struct mdp_cmdq_cmd,
> > > > auto_release_work);
> > > > + mdp = cmd->mdp;
> > > > +
> > > > + mtk_mutex_unprepare(mdp->mdp_mutex[MDP_PIPE_RDMA0]);
> > > > + mdp_comp_clocks_off(&mdp->pdev->dev, cmd->comps,
> > > > + cmd->num_comps);
> > > > +
> > > > + atomic_dec(&mdp->job_count);
> > > > + wake_up(&mdp->callback_wq);
> > > > +
> > > > + mdp_cmdq_pkt_destroy(&cmd->pkt);
> > > > + kfree(cmd->comps);
> > > > + kfree(cmd);
> > > > +}
> > > > +
> >
> > [snip]
> >
> > > > diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-
> > > > comp.c
> > > > b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.c
> > > > new file mode 100644
> > > > index 000000000000..e8081f246023
> > > > --- /dev/null
> > > > +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.c
> > > > @@ -0,0 +1,987 @@
> > > > +// SPDX-License-Identifier: GPL-2.0-only
> > > > +/*
> > > > + * Copyright (c) 2021 MediaTek Inc.
> > > > + * Author: Ping-Hsun Wu <ping-hsun.wu at mediatek.com>
> > > > + */
> > > > +
> > > > +#include <linux/clk.h>
> > > > +#include <linux/of_platform.h>
> > > > +#include <linux/of_address.h>
> > > > +#include <linux/pm_runtime.h>
> > > > +#include "mtk-mdp3-comp.h"
> > > > +#include "mtk-mdp3-core.h"
> > > > +#include "mtk-mdp3-regs.h"
> > > > +
> > > > +#include "mdp_reg_rdma.h"
> > > > +#include "mdp_reg_ccorr.h"
> > > > +#include "mdp_reg_rsz.h"
> > > > +#include "mdp_reg_wrot.h"
> > > > +#include "mdp_reg_wdma.h"
> > > > +
> > > > +u32 mdp_comp_alias_id[MDP_COMP_TYPE_COUNT];
> > > > +
> > > > +static const struct mdp_platform_config *__get_plat_cfg(const
> > > > struct mdp_comp_ctx *ctx)
> > > > +{
> > > > + if (!ctx)
> > > > + return NULL;
> > > > +
> > > > + return ctx->comp->mdp_dev->mdp_data->mdp_cfg;
> > > > +}
> > > > +
> > > > +static s64 get_comp_flag(const struct mdp_comp_ctx *ctx)
> > > > +{
> > > > + const struct mdp_platform_config *mdp_cfg =
> > > > __get_plat_cfg(ctx);
> > > > +
> > > > + if (mdp_cfg && mdp_cfg->rdma_rsz1_sram_sharing)
> > > > + if (ctx->comp->id == MDP_COMP_RDMA0)
> > > > + return BIT(MDP_COMP_RDMA0) |
> > > > BIT(MDP_COMP_RSZ1);
> > > > +
> > > > + return BIT(ctx->comp->id);
> > > > +}
> > > > +
> > > > +static int init_rdma(struct mdp_comp_ctx *ctx, struct
> > > > mdp_cmdq_cmd
> > > > *cmd)
> > > > +{
> > > > + const struct mdp_platform_config *mdp_cfg =
> > > > __get_plat_cfg(ctx);
> > > > + phys_addr_t base = ctx->comp->reg_base;
> > > > + u8 subsys_id = ctx->comp->subsys_id;
> > > > +
> > > > + if (mdp_cfg && mdp_cfg->rdma_support_10bit) {
> > > > + struct mdp_comp *prz1 = ctx->comp->mdp_dev-
> > > > > comp[MDP_COMP_RSZ1];
> > > >
> > > > +
> > > > + /* Disable RSZ1 */
> > > > + if (ctx->comp->id == MDP_COMP_RDMA0 && prz1)
> > > > + MM_REG_WRITE(cmd, subsys_id, prz1-
> > > > >reg_base,
> > > > PRZ_ENABLE,
> > > > + 0x0, BIT(0));
> > > > + }
> > > > +
> > > > + /* Reset RDMA */
> > > > + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_RESET,
> > > > BIT(0),
> > > > BIT(0));
> > > > + MM_REG_POLL(cmd, subsys_id, base, MDP_RDMA_MON_STA_1,
> > > > BIT(8),
> > > > BIT(8));
> > > > + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_RESET, 0x0,
> > > > BIT(0));
> > > > + return 0;
> > > > +}
> > > > +
> > > > +static int config_rdma_frame(struct mdp_comp_ctx *ctx,
> > > > + struct mdp_cmdq_cmd *cmd,
> > > > + const struct v4l2_rect *compose)
> > > > +{
> > > > + const struct mdp_rdma_data *rdma = &ctx->param->rdma;
> > > > + const struct mdp_platform_config *mdp_cfg =
> > > > __get_plat_cfg(ctx);
> > > > + u32 colorformat = ctx->input-
> > > > >buffer.format.colorformat;
> > > > + bool block10bit =
> > > > MDP_COLOR_IS_10BIT_PACKED(colorformat);
> > > > + bool en_ufo = MDP_COLOR_IS_UFP(colorformat);
> > > > + phys_addr_t base = ctx->comp->reg_base;
> > > > + u8 subsys_id = ctx->comp->subsys_id;
> > > > +
> > > > + if (mdp_cfg && mdp_cfg->rdma_support_10bit) {
> > > > + if (block10bit)
> > > > + MM_REG_WRITE(cmd, subsys_id, base,
> > > > + MDP_RDMA_RESV_DUMMY_0,
> > > > 0x7, 0x7);
> > > > + else
> > > > + MM_REG_WRITE(cmd, subsys_id, base,
> > > > + MDP_RDMA_RESV_DUMMY_0,
> > > > 0x0, 0x7);
> > > > + }
> > > > +
> > > > + /* Setup smi control */
> > > > + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_GMCIF_CON,
> > > > + (7 << 4) + //burst type to 8
> > > > + (1 << 16), //enable pre-ultra
> > > > + 0x00030071);
> > > > +
> > > > + /* Setup source frame info */
> > > > + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_CON,
> > > > rdma-
> > > > > src_ctrl,
> > > >
> > > > + 0x03C8FE0F);
> > > > +
> > > > + if (mdp_cfg)
> > > > + if (mdp_cfg->rdma_support_10bit && en_ufo) {
> > > > + /* Setup source buffer base */
> > > > + MM_REG_WRITE(cmd, subsys_id,
> > > > + base,
> > > > MDP_RDMA_UFO_DEC_LENGTH_BASE_Y,
> > > > + rdma->ufo_dec_y,
> > > > 0xFFFFFFFF);
> > > > + MM_REG_WRITE(cmd, subsys_id,
> > > > + base,
> > > > MDP_RDMA_UFO_DEC_LENGTH_BASE_C,
> > > > + rdma->ufo_dec_c,
> > > > 0xFFFFFFFF);
> > > > + /* Set 10bit source frame pitch */
> > > > + if (block10bit)
> > > > + MM_REG_WRITE(cmd, subsys_id,
> > > > + base,
> > > > MDP_RDMA_MF_BKGD_SIZE_IN_PXL,
> > > > + rdma-
> > > > >mf_bkgd_in_pxl,
> > > > 0x001FFFFF);
> > >
> > > I'm not against, but this is dead code as you didn't add a 10bit
> > > variant of MTK
> > > block format. Perhaps I'd be more happy if you commit into
> > > enabling
> > > this, or
> > > enabled it. This is personal opinion though.
> > >
> >
> > Maybe I should just remove these advanced features related to 10bit
> > format?
>
> Looking at it, the implementation looks quite complete, so adding and
> documenting the pixel format(s) that allow using this feature seems
> relatively
> short and recommended. Though, if that is impossible for you, I think
> yes, the
> code must be removed as it is dead code, and only re-added later with
> the
> required application facing code.
>
Hi Nicolas,
Thank you for your recommendation, I will leave a note in "mtk-mdp3-
regs.c" that the 10-bit format is not yet set in the basic functions of
mdp, and it need to be added before using the advanced feature.
> >
> > > > + }
> > > > +
> > > > + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_CON, rdma-
> > > > >control,
> > > > + 0x1110);
> > > > + /* Setup source buffer base */
> > > > + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_BASE_0,
> > > > rdma-
> > > > > iova[0],
> > > >
> > > > + 0xFFFFFFFF);
> > > > + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_BASE_1,
> > > > rdma-
> > > > > iova[1],
> > > >
> > > > + 0xFFFFFFFF);
> > > > + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_BASE_2,
> > > > rdma-
> > > > > iova[2],
> > > >
> > > > + 0xFFFFFFFF);
> > > > + /* Setup source buffer end */
> > > > + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_END_0,
> > > > + rdma->iova_end[0], 0xFFFFFFFF);
> > > > + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_END_1,
> > > > + rdma->iova_end[1], 0xFFFFFFFF);
> > > > + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_END_2,
> > > > + rdma->iova_end[2], 0xFFFFFFFF);
> > > > + /* Setup source frame pitch */
> > > > + MM_REG_WRITE(cmd, subsys_id, base,
> > > > MDP_RDMA_MF_BKGD_SIZE_IN_BYTE,
> > > > + rdma->mf_bkgd, 0x001FFFFF);
> > > > + MM_REG_WRITE(cmd, subsys_id, base,
> > > > MDP_RDMA_SF_BKGD_SIZE_IN_BYTE,
> > > > + rdma->sf_bkgd, 0x001FFFFF);
> > > > + /* Setup color transform */
> > > > + MM_REG_WRITE(cmd, subsys_id, base,
> > > > MDP_RDMA_TRANSFORM_0,
> > > > + rdma->transform, 0x0F110000);
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> >
> > [snip]
> >
> > > > +static int mdp_comp_get_id(enum mdp_comp_type type, int
> > > > alias_id)
> > > > +{
> > > > + int i;
> > > > +
> > > > + for (i = 0; i < ARRAY_SIZE(mdp_comp_matches); i++)
> > > > + if (mdp_comp_matches[i].type == type &&
> > > > + mdp_comp_matches[i].alias_id == alias_id)
> > > > + return i;
> > > > + return -ENODEV;
> > > > +}
> > > > +
> > > > +void mdp_comp_clock_on(struct device *dev, struct mdp_comp
> > > > *comp)
> > >
> > > This function can fail, yet it has no return value. I have a bit
> > > of a
> > > concern,
> > > in other drivers if you failed to enable the clocks, you risk
> > > system
> > > hangs on
> > > sram read/writes. I'd like to see this fixed, or a little comment
> > > saying its
> > > safe to ignore.
> > >
> >
> > Thanks for your suggestion, I will adjust this section and add
> > error
> > control.
> >
> > > > +{
> > > > + int i, err;
> > > > +
> > > > + if (comp->comp_dev) {
> > > > + err = pm_runtime_get_sync(comp->comp_dev);
> > > > + if (err < 0)
> > > > + dev_err(dev,
> > > > + "Failed to get power, err %d.
> > > > type:%d
> > > > id:%d\n",
> > > > + err, comp->type, comp->id);
> > > > + }
> > > > +
> > > > + for (i = 0; i < ARRAY_SIZE(comp->clks); i++) {
> > > > + if (IS_ERR_OR_NULL(comp->clks[i]))
> > > > + continue;
> > > > + err = clk_prepare_enable(comp->clks[i]);
> > > > + if (err)
> > > > + dev_err(dev,
> > > > + "Failed to enable clk %d.
> > > > type:%d
> > > > id:%d\n",
> > > > + i, comp->type, comp->id);
> > > > + }
> > > > +}
> > > > +
> > > > +void mdp_comp_clock_off(struct device *dev, struct mdp_comp
> > > > *comp)
> > > > +{
> > > > + int i;
> > > > +
> > > > + for (i = 0; i < ARRAY_SIZE(comp->clks); i++) {
> > > > + if (IS_ERR_OR_NULL(comp->clks[i]))
> > > > + continue;
> > > > + clk_disable_unprepare(comp->clks[i]);
> > > > + }
> > > > +
> > > > + if (comp->comp_dev)
> > > > + pm_runtime_put(comp->comp_dev);
> > > > +}
> > > > +
> > > > +void mdp_comp_clocks_on(struct device *dev, struct mdp_comp
> > > > *comps, int num)
> > >
> > > ... and this one would have to aggregate the error.
> > >
> >
> > Ditto
> >
> > > > +{
> > > > + int i;
> > > > +
> > > > + for (i = 0; i < num; i++)
> > > > + mdp_comp_clock_on(dev, &comps[i]);
> > > > +}
> > > > +
> >
> > [snip]
> >
> > > > +static void mdp_comp_deinit(struct mdp_comp *comp)
> > > > +{
> > > > + if (!comp)
> > > > + return;
> > > > +
> > > > + if (comp->regs)
> > > > + iounmap(comp->regs);
> > > > +}
> > > > +
> > > > +void mdp_component_deinit(struct mdp_dev *mdp)
> > >
> > > Perhaps you want to name that one mdp_components_deinit() or
> > > mdp_comps_deinit().
> > > Calling it mdp_component_deinit() is too confusing with another
> > > function called
> > > mdp_comp_deinit().
> > >
> >
> > What if it is adjusted as follows:
> >
> > mdp_comp_deinit -> keep
> > mdp_component_deinit -> mdp_comp_destroy
> > mdp_component_init -> mdp_comp_config
> > mdp_comp_ctx_init -> mdp_comp_ctx_config
>
> This looks good.
>
> >
> > > > +{
> > > > + int i;
> > > > +
> > > > + for (i = 0; i < MDP_PIPE_MAX; i++)
> > > > + mtk_mutex_put(mdp->mdp_mutex[i]);
> > >
> > > Without context, my impression is that this should happen after
> > > things get
> > > disabled/freed ?
> > >
> >
> > Thanks for the correction, I should move from this function
> >
> > > > +
> > > > + for (i = 0; i < ARRAY_SIZE(mdp->comp); i++) {
> > > > + if (mdp->comp[i]) {
> > > > + pm_runtime_disable(mdp->comp[i]-
> > > > >comp_dev);
> > > > + mdp_comp_deinit(mdp->comp[i]);
> > > > + kfree(mdp->comp[i]);
> > >
> > > I'd make this function safer and do mdp->comp[i] = NULL;
> > >
> >
> > Thanks for the suggestion, this adjustment will indeed be more
> > robust.
> >
> > > > + }
> > > > + }
> > > > +}
> > > > +
> > > > +int mdp_component_init(struct mdp_dev *mdp)
> > > > +{
> > > > + struct device *dev = &mdp->pdev->dev;
> > > > + struct device_node *node, *parent;
> > > > + struct platform_device *pdev;
> > > > + int ret;
> > > > +
> > > > + memset(mdp_comp_alias_id, 0,
> > > > sizeof(mdp_comp_alias_id));
> > > > +
> > > > + 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, alias_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_info(dev, "Skipping disabled
> > > > component
> > > > %pOF\n", node);
> > > > + continue;
> > > > + }
> > > > +
> > > > + type = (enum mdp_comp_type)of_id->data;
> > > > + alias_id = mdp_comp_alias_id[type];
> > > > + id = mdp_comp_get_id(type, alias_id);
> > > > + if (id < 0) {
> > > > + dev_err(dev,
> > > > + "Fail to get component id: type
> > > > %d
> > > > alias %d\n",
> > > > + type, alias_id);
> > > > + continue;
> > > > + }
> > > > + mdp_comp_alias_id[type]++;
> > > > +
> > > > + comp = mdp_comp_create(mdp, node, id);
> > > > + if (IS_ERR(comp)) {
> > > > + ret = PTR_ERR(comp);
> > > > + goto err_init_comps;
> > > > + }
> > > > +
> > > > + /* Only DMA capable components need the pm
> > > > control */
> > > > + comp->comp_dev = NULL;
> > > > + if (comp->type != MDP_COMP_TYPE_RDMA &&
> > > > + comp->type != MDP_COMP_TYPE_WROT &&
> > > > + comp->type != MDP_COMP_TYPE_WDMA)
> > > > + continue;
> > > > +
> > > > + pdev = of_find_device_by_node(node);
> > > > + if (!pdev) {
> > > > + dev_warn(dev, "can't find platform
> > > > device of
> > > > node:%s\n",
> > > > + node->name);
> > > > + return -ENODEV;
> > > > + }
> > > > +
> > > > + comp->comp_dev = &pdev->dev;
> > > > + pm_runtime_enable(comp->comp_dev);
> > > > + }
> > > > +
> > > > + ret = mdp_sub_comps_create(mdp);
> > > > + if (ret)
> > > > + goto err_init_comps;
> > > > +
> > > > + return 0;
> > > > +
> > > > +err_init_comps:
> > > > + mdp_component_deinit(mdp);
> > > > + return ret;
> > > > +}
> > > > +
> >
> > [snip]
> >
> > > > diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-
> > > > m2m.c
> > > > b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-m2m.c
> > > > new file mode 100644
> > > > index 000000000000..c2b2a18dd4f5
> > > > --- /dev/null
> > > > +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-m2m.c
> > > > @@ -0,0 +1,772 @@
> > > > +// SPDX-License-Identifier: GPL-2.0-only
> > > > +/*
> > > > + * Copyright (c) 2021 MediaTek Inc.
> > > > + * Author: Ping-Hsun Wu <ping-hsun.wu at mediatek.com>
> > > > + */
> > > > +
> > > > +#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;
> > > > + else
> > > > + return &ctx->curr_param.captures[0];
> > > > +}
> > > > +
> > > > +static void mdp_m2m_ctx_set_state(struct mdp_m2m_ctx *ctx, u32
> > > > state)
> > > > +{
> > > > + atomic_or(state, &ctx->curr_param.state);
> > > > +}
> > > > +
> > > > +static bool mdp_m2m_ctx_is_state_set(struct mdp_m2m_ctx *ctx,
> > > > u32
> > > > mask)
> > > > +{
> > > > + bool ret;
> > > > +
> > > > + ret = ((atomic_read(&ctx->curr_param.state) & mask) ==
> > > > mask);
> > > > +
> > > > + return ret;
> > > > +}
> > > > +
> > > > +static void mdp_m2m_process_done(void *priv, int vb_state)
> > > > +{
> > > > + struct mdp_m2m_ctx *ctx = priv;
> > > > + struct vb2_v4l2_buffer *src_vbuf, *dst_vbuf;
> > > > +
> > > > + src_vbuf = (struct vb2_v4l2_buffer *)
> > > > + v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
> > > > + dst_vbuf = (struct vb2_v4l2_buffer *)
> > > > + v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
> > > > + ctx->curr_param.frame_no = ctx-
> > > > >frame_count[MDP_M2M_SRC];
> > > > + src_vbuf->sequence = ctx->frame_count[MDP_M2M_SRC]++;
> > > > + dst_vbuf->sequence = ctx->frame_count[MDP_M2M_DST]++;
> > > > + v4l2_m2m_buf_copy_metadata(src_vbuf, dst_vbuf, true);
> > > > +
> > > > + 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);
> > > > +}
> > > > +
> > > > +static void mdp_m2m_device_run(void *priv)
> > > > +{
> > > > + struct mdp_m2m_ctx *ctx = priv;
> > > > + struct mdp_frame *frame;
> > > > + struct vb2_v4l2_buffer *src_vb, *dst_vb;
> > > > + struct img_ipi_frameparam param = {0};
> > > > + struct mdp_cmdq_param task = {0};
> > > > + enum vb2_buffer_state vb_state = VB2_BUF_STATE_ERROR;
> > > > + int ret;
> > > > +
> > > > + if (mdp_m2m_ctx_is_state_set(ctx, MDP_M2M_CTX_ERROR)) {
> > > > + dev_err(&ctx->mdp_dev->pdev->dev,
> > > > + "mdp_m2m_ctx is in error state\n");
> > > > + goto worker_end;
> > > > + }
> > > > +
> > > > + param.frame_no = ctx->curr_param.frame_no;
> > > > + param.type = ctx->curr_param.type;
> > > > + param.num_inputs = 1;
> > > > + param.num_outputs = 1;
> > > > +
> > > > + frame = ctx_get_frame(ctx,
> > > > V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
> > > > + src_vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
> > > > + mdp_set_src_config(¶m.inputs[0], frame, &src_vb-
> > > > >vb2_buf);
> > > > +
> > > > + frame = ctx_get_frame(ctx,
> > > > V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
> > > > + dst_vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
> > > > + mdp_set_dst_config(¶m.outputs[0], frame, &dst_vb-
> > > > >vb2_buf);
> > > > +
> > > > + param.timestamp.tv_sec = (u32)(src_vb-
> > > > >vb2_buf.timestamp >>
> > > > 32);
> > > > + param.timestamp.tv_usec = (u32)(src_vb-
> > > > >vb2_buf.timestamp &
> > > > 0xFFFFFFFF);
> > >
> > > This does not looks right, but perhaps a workaround ? See
> > > v4l2_buffer_set_timestamp() on how that conversion is usually
> > > done.
> > > Is that a
> > > limitation of the firmware API ? In any case, if you serialized
> > > 64bit
> > > into a
> > > timespec I'd rather place a comment here to explain that this is
> > > not
> > > a mistake
> > > but a workaround.
> > >
> >
> > Yes, it's a limitation of "ISP" API in FW, and maybe we should just
> > remove it.
>
> Noted, please add a comment so the readers knows this is intentional,
> (or remove
> it if its not strictly needed, I cannot judge this aspect here).
>
> >
> > > > +
> > > > + ret = mdp_vpu_process(&ctx->vpu, ¶m);
> > > > + if (ret) {
> > > > + dev_err(&ctx->mdp_dev->pdev->dev,
> > > > + "VPU MDP process failed: %d\n", ret);
> > > > + goto worker_end;
> > > > + }
> > > > +
> > > > + task.config = ctx->vpu.config;
> > > > + task.param = ¶m;
> > > > + task.composes[0] = &frame->compose;
> > > > + task.cmdq_cb = NULL;
> > > > + task.cb_data = NULL;
> > > > + task.mdp_ctx = ctx;
> > > > +
> > > > + ret = mdp_cmdq_send(ctx->mdp_dev, &task);
> > > > + if (ret) {
> > > > + dev_err(&ctx->mdp_dev->pdev->dev,
> > > > + "CMDQ sendtask failed: %d\n", ret);
> > > > + goto worker_end;
> > > > + }
> > > > +
> > > > + return;
> > > > +
> > > > +worker_end:
> > > > + mdp_m2m_process_done(ctx, vb_state);
> > > > +}
> > > > +
> > > > +static int mdp_m2m_start_streaming(struct vb2_queue *q,
> > > > unsigned
> > > > int count)
> > > > +{
> > > > + struct mdp_m2m_ctx *ctx = vb2_get_drv_priv(q);
> > > > +
> > > > + ctx->frame_count[MDP_M2M_SRC] = 0;
> > > > + ctx->frame_count[MDP_M2M_DST] = 0;
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> > > > +static struct vb2_v4l2_buffer *mdp_m2m_buf_remove(struct
> > > > mdp_m2m_ctx *ctx,
> > > > + unsigned int
> > > > type)
> > > > +{
> > > > + if (V4L2_TYPE_IS_OUTPUT(type))
> > > > + return (struct vb2_v4l2_buffer *)
> > > > + v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
> > > > + else
> > > > + return (struct vb2_v4l2_buffer *)
> > > > + v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
> > > > +}
> > > > +
> > > > +static void mdp_m2m_stop_streaming(struct vb2_queue *q)
> > > > +{
> > > > + struct mdp_m2m_ctx *ctx = vb2_get_drv_priv(q);
> > > > + struct vb2_v4l2_buffer *vb;
> > > > +
> > > > + vb = mdp_m2m_buf_remove(ctx, q->type);
> > > > + while (vb) {
> > > > + v4l2_m2m_buf_done(vb, VB2_BUF_STATE_ERROR);
> > > > + vb = mdp_m2m_buf_remove(ctx, q->type);
> > > > + }
> > > > +}
> > > > +
> > > > +static int mdp_m2m_queue_setup(struct vb2_queue *q,
> > > > + unsigned int *num_buffers,
> > > > + unsigned int *num_planes,
> > > > unsigned int
> > > > sizes[],
> > > > + struct device *alloc_devs[])
> > > > +{
> > > > + struct mdp_m2m_ctx *ctx = vb2_get_drv_priv(q);
> > > > + struct v4l2_pix_format_mplane *pix_mp;
> > > > + struct device *dev = &ctx->mdp_dev->pdev->dev;
> > > > + u32 i;
> > > > +
> > > > + pix_mp = &ctx_get_frame(ctx, q->type)-
> > > > >format.fmt.pix_mp;
> > > > +
> > > > + /* from VIDIOC_CREATE_BUFS */
> > > > + if (*num_planes) {
> > > > + if (*num_planes != pix_mp->num_planes)
> > > > + return -EINVAL;
> > > > + for (i = 0; i < pix_mp->num_planes; ++i)
> > > > + if (sizes[i] < pix_mp-
> > > > >plane_fmt[i].sizeimage)
> > > > + return -EINVAL;
> > > > + } else {/* from VIDIOC_REQBUFS */
> > > > + *num_planes = pix_mp->num_planes;
> > > > + for (i = 0; i < pix_mp->num_planes; ++i)
> > > > + sizes[i] = pix_mp-
> > > > >plane_fmt[i].sizeimage;
> > > > + }
> > > > +
> > > > + dev_info(dev, "[%d] type:%d, planes:%u, buffers:%u,
> > > > size:%u,%u,%u",
> > > > + ctx->id, q->type, *num_planes, *num_buffers,
> > > > + sizes[0], sizes[1], sizes[2]);
> > > > + return 0;
> > > > +}
> > > > +
> > > > +static int mdp_m2m_buf_prepare(struct vb2_buffer *vb)
> > > > +{
> > > > + struct mdp_m2m_ctx *ctx = vb2_get_drv_priv(vb-
> > > > >vb2_queue);
> > > > + struct v4l2_pix_format_mplane *pix_mp;
> > > > + struct vb2_v4l2_buffer *v4l2_buf =
> > > > to_vb2_v4l2_buffer(vb);
> > > > + u32 i;
> > > > +
> > > > + v4l2_buf->field = V4L2_FIELD_NONE;
> > > > +
> > > > + if (!V4L2_TYPE_IS_OUTPUT(vb->type)) {
> > > > + pix_mp = &ctx_get_frame(ctx, vb->type)-
> > > > > format.fmt.pix_mp;
> > > >
> > > > + for (i = 0; i < pix_mp->num_planes; ++i) {
> > > > + vb2_set_plane_payload(vb, i,
> > > > + pix_mp-
> > > > > plane_fmt[i].sizeimage);
> > > >
> > > > + }
> > > > + }
> > > > + return 0;
> > > > +}
> > > > +
> > > > +static int mdp_m2m_buf_out_validate(struct vb2_buffer *vb)
> > > > +{
> > > > + struct vb2_v4l2_buffer *v4l2_buf =
> > > > to_vb2_v4l2_buffer(vb);
> > > > +
> > > > + v4l2_buf->field = V4L2_FIELD_NONE;
> > >
> > > Could it be more flexible and just copy field from src to dst ?
> > > Or do
> > > we
> > > strictly can't support interlaced buffer ?
> > >
> >
> > Due to HW limitation, MDP can't handle formats other than
> > progressive.
>
> Noted.
>
> >
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> >
> > [snip]
> >
> > > > diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-
> > > > regs.c
> > > > b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-regs.c
> > > > new file mode 100644
> > > > index 000000000000..9adde4b61560
> > > > --- /dev/null
> > > > +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-regs.c
> > > > @@ -0,0 +1,736 @@
> >
> > [snip]
> >
> > > > +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;
> > > > + default:
> > > > + 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;
> > >
> > > Drivers with "steps" normally uses struct v4l2_frmsize_stepwise
> > > with
> > > v4l2_apply_frmsize_constraints I think. Perhaps give it a look,
> > > its
> > > seems fine
> > > otherwise.
> > >
> >
> > Thanks for the suggestion, I'll correct it accordingly.
> >
> > > > +}
> > > > +
> > > > +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;
> > > > +
> > > > + fmt = mdp_find_fmt_by_index(f->index, f->type);
> > > > + if (!fmt)
> > > > + return -EINVAL;
> > > > +
> > > > + 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 device *dev = ¶m->ctx->mdp_dev->pdev->dev;
> > > > + 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;
> > > > +
> > > > + fmt = mdp_find_fmt(pix_mp->pixelformat, f->type);
> > > > + if (!fmt) {
> > > > + fmt = mdp_find_fmt_by_index(0, f->type);
> > > > + if (!fmt) {
> > > > + dev_dbg(dev, "%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;
> > > > + }
> > > > +
> > > > + 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)
> > > > + dev_dbg(dev, "%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)
> > > >
> > > > + dev_dbg(dev, "%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;
> > >
> > > I see that you will accept user provided bytesperline if large
> > > enough, but I
> > > failed to find a place you validate the alignment of the provided
> > > bytesperline.
> > > Did you forget to validate it ?
> > >
> >
> > Indeed, It's a little confusing.
> > Maybe the result calculated by the formula should be used directly
> > instead of referring to the user's settings?
>
> That is one option, but if I remember correctly, application provided
> strides
> (bytesperline) is a usercase that came from ChromeOS, so removing it
> will break
> something there.
>
> I'd like to highlight the danger of user provided strides
> (byteperline) here,
> which hopefully will be useful to other devs. This is for me hot
> topic, as
> similar vulnerability have been address in GStreamer (a project into
> which I'm
> one of the maintainer). Over a handful of cases was found were a user
> provided
> size was used to calculate an alloation size (just like here).
>
> What happens is that u32 can wrap around. So Malicious user could
> potentially
> find a large enough value of bytesperline / height pair that will
> overflow. The
> consequence is that a very small buffer is allocate, which is enough
> to get the
> driver to overflow its buffer. The overflow might not be exploitable,
> but this
> kind of issue is often the root of a potential exploit.
>
> In short, on top of validating the alignment, there is some size
> check needed to
> ensure that the following allocation size calculation won't overflow.
>
> kind regards,
> Nicolas
>
Thanks for the precious experience. I will carefully check the
parameters set by the user and clamp them within a reasonable range to
avoid overflow problems.
Regards,
Moudy
> >
> > Thanks,
> > Moudy
> >
> > > > + 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;
> > > > + dev_dbg(dev, "%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 int mdp_clamp_start(s32 *x, int min, int max, unsigned
> > > > int
> > > > align,
> > > > + u32 flags)
> > > > +{
> > > > + 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 int mdp_clamp_end(s32 *x, int min, int max, unsigned
> > > > int
> > > > align,
> > > > + u32 flags)
> > > > +{
> > > > + if (flags & V4L2_SEL_FLAG_GE)
> > > > + min = *x;
> > > > + if (flags & V4L2_SEL_FLAG_LE)
> > > > + max = *x;
> > > > + return mdp_clamp_align(x, min, max, align);
> > > > +}
> > > > +
> > > > +int mdp_try_crop(struct mdp_m2m_ctx *ctx, struct v4l2_rect *r,
> > > > + const struct v4l2_selection *s, struct
> > > > mdp_frame
> > > > *frame)
> > > > +{
> > > > + struct device *dev = &ctx->mdp_dev->pdev->dev;
> > > > + s32 left, top, right, bottom;
> > > > + u32 framew, frameh, walign, halign;
> > > > + int ret;
> > > > +
> > > > + dev_dbg(dev, "%d target:%d, set:(%d,%d) %ux%u", ctx-
> > > > >id,
> > > > + s->target, s->r.left, s->r.top, s->r.width, s-
> > > > > r.height);
> > > >
> > > > +
> > > > + left = s->r.left;
> > > > + top = s->r.top;
> > > > + right = s->r.left + s->r.width;
> > > > + bottom = s->r.top + s->r.height;
> > > > + framew = frame->format.fmt.pix_mp.width;
> > > > + frameh = frame->format.fmt.pix_mp.height;
> > > > +
> > > > + if (mdp_target_is_crop(s->target)) {
> > > > + walign = 1;
> > > > + halign = 1;
> > > > + } else {
> > > > + walign = frame->mdp_fmt->walign;
> > > > + halign = frame->mdp_fmt->halign;
> > > > + }
> > > > +
> > > > + dev_dbg(dev, "%d align:%u,%u, bound:%ux%u", ctx->id,
> > > > + walign, halign, framew, frameh);
> > > > +
> > > > + ret = mdp_clamp_start(&left, 0, right, walign, s-
> > > > >flags);
> > > > + if (ret)
> > > > + return ret;
> > > > + ret = mdp_clamp_start(&top, 0, bottom, halign, s-
> > > > >flags);
> > > > + if (ret)
> > > > + return ret;
> > > > + ret = mdp_clamp_end(&right, left, framew, walign, s-
> > > > >flags);
> > > > + if (ret)
> > > > + return ret;
> > > > + ret = mdp_clamp_end(&bottom, top, frameh, halign, s-
> > > > >flags);
> > > > + if (ret)
> > > > + return ret;
> > > > +
> > > > + r->left = left;
> > > > + r->top = top;
> > > > + r->width = right - left;
> > > > + r->height = bottom - top;
> > > > +
> > > > + dev_dbg(dev, "%d crop:(%d,%d) %ux%u", ctx->id,
> > > > + r->left, r->top, r->width, r->height);
> > > > + return 0;
> > > > +}
> > > > +
> > > > +int mdp_check_scaling_ratio(const struct v4l2_rect *crop,
> > > > + const struct v4l2_rect *compose,
> > > > s32
> > > > rotation,
> > > > + const struct mdp_limit *limit)
> > > > +{
> > > > + u32 crop_w, crop_h, comp_w, comp_h;
> > > > +
> > > > + crop_w = crop->width;
> > > > + crop_h = crop->height;
> > > > + if (90 == rotation || 270 == rotation) {
> > > > + comp_w = compose->height;
> > > > + comp_h = compose->width;
> > > > + } else {
> > > > + comp_w = compose->width;
> > > > + comp_h = compose->height;
> > > > + }
> > > > +
> > > > + if ((crop_w / comp_w) > limit->h_scale_down_max ||
> > > > + (crop_h / comp_h) > limit->v_scale_down_max ||
> > > > + (comp_w / crop_w) > limit->h_scale_up_max ||
> > > > + (comp_h / crop_h) > limit->v_scale_up_max)
> > > > + return -ERANGE;
> > > > + return 0;
> > > > +}
> > > > +
> > > > +/* Stride that is accepted by MDP HW */
> > > > +static u32 mdp_fmt_get_stride(const struct mdp_format *fmt,
> > > > + u32 bytesperline, unsigned int
> > > > plane)
> > > > +{
> > > > + enum mdp_color c = fmt->mdp_color;
> > > > + u32 stride;
> > > > +
> > > > + stride = (bytesperline * MDP_COLOR_BITS_PER_PIXEL(c))
> > > > + / fmt->row_depth[0];
> > > > + if (plane == 0)
> > > > + return stride;
> > > > + if (plane < MDP_COLOR_GET_PLANE_COUNT(c)) {
> > > > + if (MDP_COLOR_IS_BLOCK_MODE(c))
> > > > + stride = stride / 2;
> > > > + return stride;
> > > > + }
> > > > + return 0;
> > > > +}
> > > > +
> > > > +/* Stride that is accepted by MDP HW of format with contiguous
> > > > planes */
> > > > +static u32 mdp_fmt_get_stride_contig(const struct mdp_format
> > > > *fmt,
> > > > + u32 pix_stride, unsigned
> > > > int
> > > > plane)
> > > > +{
> > > > + enum mdp_color c = fmt->mdp_color;
> > > > + u32 stride = pix_stride;
> > > > +
> > > > + if (plane == 0)
> > > > + return stride;
> > > > + if (plane < MDP_COLOR_GET_PLANE_COUNT(c)) {
> > > > + stride = stride >>
> > > > MDP_COLOR_GET_H_SUBSAMPLE(c);
> > > > + if (MDP_COLOR_IS_UV_COPLANE(c) &&
> > > > !MDP_COLOR_IS_BLOCK_MODE(c))
> > > > + stride = stride * 2;
> > > > + return stride;
> > > > + }
> > > > + return 0;
> > > > +}
> > > > +
> > > > +/* Plane size that is accepted by MDP HW */
> > > > +static u32 mdp_fmt_get_plane_size(const struct mdp_format
> > > > *fmt,
> > > > + u32 stride, u32 height,
> > > > unsigned int
> > > > plane)
> > > > +{
> > > > + enum mdp_color c = fmt->mdp_color;
> > > > + u32 bytesperline;
> > > > +
> > > > + bytesperline = (stride * fmt->row_depth[0])
> > > > + / MDP_COLOR_BITS_PER_PIXEL(c);
> > > > + if (plane == 0)
> > > > + return bytesperline * height;
> > > > + if (plane < MDP_COLOR_GET_PLANE_COUNT(c)) {
> > > > + height = height >>
> > > > MDP_COLOR_GET_V_SUBSAMPLE(c);
> > > > + if (MDP_COLOR_IS_BLOCK_MODE(c))
> > > > + bytesperline = bytesperline * 2;
> > > > + return bytesperline * height;
> > > > + }
> > > > + return 0;
> > > > +}
> > > > +
> > > > +static void mdp_prepare_buffer(struct img_image_buffer *b,
> > > > + struct mdp_frame *frame, struct
> > > > vb2_buffer *vb)
> > > > +{
> > > > + struct v4l2_pix_format_mplane *pix_mp = &frame-
> > > > > format.fmt.pix_mp;
> > > >
> > > > + unsigned int i;
> > > > +
> > > > + b->format.colorformat = frame->mdp_fmt->mdp_color;
> > > > + b->format.ycbcr_prof = frame->ycbcr_prof;
> > > > + for (i = 0; i < pix_mp->num_planes; ++i) {
> > > > + u32 stride = mdp_fmt_get_stride(frame->mdp_fmt,
> > > > + pix_mp->plane_fmt[i].bytesperline, i);
> > > > +
> > > > + b->format.plane_fmt[i].stride = stride;
> > > > + /*
> > > > + * TODO : The way to pass an offset within a
> > > > DMA-buf
> > > > + * is not defined in V4L2 specification, so we
> > > > abuse
> > > > + * data_offset for now. Fix it when we have the
> > > > right
> > > > interface,
> > > > + * including any necessary validation and
> > > > potential
> > > > alignment
> > > > + * issues.
> > > > + */
> > > > + b->format.plane_fmt[i].size =
> > > > + mdp_fmt_get_plane_size(frame->mdp_fmt,
> > > > stride,
> > > > + pix_mp->height,
> > > > i) -
> > > > + vb-
> > > > > planes[i].data_offset;
> > > >
> > > > + b->iova[i] = vb2_dma_contig_plane_dma_addr(vb,
> > > > i) +
> > > > + vb->planes[i].data_offset;
> > > > + }
> > > > + for (; i < MDP_COLOR_GET_PLANE_COUNT(b-
> > > > >format.colorformat);
> > > > ++i) {
> > > > + u32 stride = mdp_fmt_get_stride_contig(frame-
> > > > >mdp_fmt,
> > > > + b->format.plane_fmt[0].stride, i);
> > > > +
> > > > + b->format.plane_fmt[i].stride = stride;
> > > > + b->format.plane_fmt[i].size =
> > > > + mdp_fmt_get_plane_size(frame->mdp_fmt,
> > > > stride,
> > > > + pix_mp->height,
> > > > i);
> > > > + b->iova[i] = b->iova[i - 1] + b-
> > > > >format.plane_fmt[i -
> > > > 1].size;
> > > > + }
> > > > + b->usage = frame->usage;
> > > > +}
> > > > +
> > > > +void mdp_set_src_config(struct img_input *in,
> > > > + struct mdp_frame *frame, struct
> > > > vb2_buffer *vb)
> > > > +{
> > > > + in->buffer.format.width = frame-
> > > > >format.fmt.pix_mp.width;
> > > > + in->buffer.format.height = frame-
> > > > >format.fmt.pix_mp.height;
> > > > + mdp_prepare_buffer(&in->buffer, frame, vb);
> > > > +}
> > > > +
> > > > +static u32 mdp_to_fixed(u32 *r, struct v4l2_fract *f)
> > > > +{
> > > > + u32 q;
> > > > +
> > > > + if (f->denominator == 0) {
> > > > + *r = 0;
> > > > + return 0;
> > > > + }
> > > > +
> > > > + q = f->numerator / f->denominator;
> > > > + *r = div_u64(((u64)f->numerator - q * f->denominator)
> > > > <<
> > > > + IMG_SUBPIXEL_SHIFT, f->denominator);
> > > > + return q;
> > > > +}
> > > > +
> > > > +static void mdp_set_src_crop(struct img_crop *c, struct
> > > > mdp_crop
> > > > *crop)
> > > > +{
> > > > + c->left = crop->c.left
> > > > + + mdp_to_fixed(&c->left_subpix, &crop-
> > > > >left_subpix);
> > > > + c->top = crop->c.top
> > > > + + mdp_to_fixed(&c->top_subpix, &crop-
> > > > >top_subpix);
> > > > + c->width = crop->c.width
> > > > + + mdp_to_fixed(&c->width_subpix, &crop-
> > > > >width_subpix);
> > > > + c->height = crop->c.height
> > > > + + mdp_to_fixed(&c->height_subpix, &crop-
> > > > > height_subpix);
> > > >
> > > > +}
> > > > +
> > > > +static void mdp_set_orientation(struct img_output *out,
> > > > + s32 rotation, bool hflip, bool
> > > > vflip)
> > > > +{
> > > > + u8 flip = 0;
> > > > +
> > > > + if (hflip)
> > > > + flip ^= 1;
> > > > + if (vflip) {
> > > > + /*
> > > > + * A vertical flip is equivalent to
> > > > + * a 180-degree rotation with a horizontal flip
> > > > + */
> > > > + rotation += 180;
> > > > + flip ^= 1;
> > > > + }
> > > > +
> > > > + out->rotation = rotation % 360;
> > > > + if (flip != 0)
> > > > + out->flags |= IMG_CTRL_FLAG_HFLIP;
> > > > + else
> > > > + out->flags &= ~IMG_CTRL_FLAG_HFLIP;
> > > > +}
> > > > +
> > > > +void mdp_set_dst_config(struct img_output *out,
> > > > + struct mdp_frame *frame, struct
> > > > vb2_buffer *vb)
> > > > +{
> > > > + out->buffer.format.width = frame->compose.width;
> > > > + out->buffer.format.height = frame->compose.height;
> > > > + mdp_prepare_buffer(&out->buffer, frame, vb);
> > > > + mdp_set_src_crop(&out->crop, &frame->crop);
> > > > + mdp_set_orientation(out, frame->rotation, frame->hflip,
> > > > frame-
> > > > > vflip);
> > > >
> > > > +}
> > > > +
> > > > +int mdp_frameparam_init(struct mdp_frameparam *param)
> > > > +{
> > > > + struct mdp_frame *frame;
> > > > +
> > > > + if (!param)
> > > > + return -EINVAL;
> > > > +
> > > > + INIT_LIST_HEAD(¶m->list);
> > > > + param->limit = &mdp_def_limit;
> > > > + param->type = MDP_STREAM_TYPE_BITBLT;
> > > > +
> > > > + frame = ¶m->output;
> > > > + frame->format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
> > > > + frame->mdp_fmt = mdp_try_fmt_mplane(&frame->format,
> > > > param, 0);
> > > > + frame->ycbcr_prof =
> > > > + mdp_map_ycbcr_prof_mplane(&frame->format,
> > > > + frame->mdp_fmt-
> > > > >mdp_color);
> > > > + frame->usage = MDP_BUFFER_USAGE_HW_READ;
> > > > +
> > > > + param->num_captures = 1;
> > > > + frame = ¶m->captures[0];
> > > > + frame->format.type =
> > > > V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
> > > > + frame->mdp_fmt = mdp_try_fmt_mplane(&frame->format,
> > > > param, 0);
> > > > + frame->ycbcr_prof =
> > > > + mdp_map_ycbcr_prof_mplane(&frame->format,
> > > > + frame->mdp_fmt-
> > > > >mdp_color);
> > > > + frame->usage = MDP_BUFFER_USAGE_MDP;
> > > > + frame->crop.c.width = param-
> > > > >output.format.fmt.pix_mp.width;
> > > > + frame->crop.c.height = param-
> > > > >output.format.fmt.pix_mp.height;
> > > > + frame->compose.width = frame->format.fmt.pix_mp.width;
> > > > + frame->compose.height = frame-
> > > > >format.fmt.pix_mp.height;
> > > > +
> > > > + return 0;
> > > > +}
> > > > diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-
> > > > regs.h
> > > > b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-regs.h
> > > > new file mode 100644
> > > > index 000000000000..88f8b760f613
> > > > --- /dev/null
> > > > +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-regs.h
> > > > @@ -0,0 +1,370 @@
> > > > +/* SPDX-License-Identifier: GPL-2.0-only */
> > > > +/*
> > > > + * Copyright (c) 2021 MediaTek Inc.
> > > > + * Author: Ping-Hsun Wu <ping-hsun.wu at mediatek.com>
> > > > + */
> > > > +
> > > > +#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_C
> > > > OUNT(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_BAYER14,
> > > > + MDP_COLOR_BAYER14_RGGB = MDP_COLOR(0, 0, 0, 1, 0, 0,
> > > > 14,
> > > > 2, 0, 20),
> > > > + MDP_COLOR_BAYER14_GRBG = MDP_COLOR(0, 0, 0, 1, 0, 1,
> > > > 14,
> > > > 2, 0, 20),
> > > > + MDP_COLOR_BAYER14_GBRG = MDP_COLOR(0, 0, 0, 1, 1, 0,
> > > > 14,
> > > > 2, 0, 20),
> > > > + MDP_COLOR_BAYER14_BGGR = MDP_COLOR(0, 0, 0, 1, 1, 1,
> > > > 14,
> > > > 2, 0, 20),
> > > > + MDP_COLOR_BAYER14 = MDP_COLOR_BAYER14_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_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_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_CTX_ERROR BIT(1)
> > > > +
> > > > +struct mdp_frameparam {
> > > > + struct list_head list;
> > > > + struct mdp_m2m_ctx *ctx;
> > > > + atomic_t 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;
> > > > + 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 mdp_m2m_ctx *ctx, struct v4l2_rect *r,
> > > > + const struct v4l2_selection *s, struct
> > > > mdp_frame
> > > > *frame);
> > > > +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);
> > > > +int mdp_frameparam_init(struct mdp_frameparam *param);
> > > > +
> > > > +#endif /* __MTK_MDP3_REGS_H__ */
> > > > diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-
> > > > vpu.c
> > > > b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-vpu.c
> > > > new file mode 100644
> > > > index 000000000000..fc008d96c7c0
> > > > --- /dev/null
> > > > +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-vpu.c
> > > > @@ -0,0 +1,312 @@
> > > > +// SPDX-License-Identifier: GPL-2.0-only
> > > > +/*
> > > > + * Copyright (c) 2021 MediaTek Inc.
> > > > + * Author: Ping-Hsun Wu <ping-hsun.wu at mediatek.com>
> > > > + */
> > > > +
> > > > +#include <linux/remoteproc.h>
> > > > +#include <linux/remoteproc/mtk_scp.h>
> > > > +#include "mtk-mdp3-vpu.h"
> > > > +#include "mtk-mdp3-core.h"
> > > > +
> > > > +#define MDP_VPU_MESSAGE_TIMEOUT 500U
> > > > +#define vpu_alloc_size 0x600000
> > > > +
> > > > +static inline struct mdp_dev *vpu_to_mdp(struct mdp_vpu_dev
> > > > *vpu)
> > > > +{
> > > > + return container_of(vpu, struct mdp_dev, vpu);
> > > > +}
> > > > +
> > > > +static int mdp_vpu_shared_mem_alloc(struct mdp_vpu_dev *vpu)
> > > > +{
> > > > + if (vpu->work && vpu->work_addr)
> > > > + return 0;
> > > > +
> > > > + vpu->work = dma_alloc_coherent(scp_get_device(vpu-
> > > > >scp),
> > > > vpu_alloc_size,
> > > > + &vpu->work_addr,
> > > > GFP_KERNEL);
> > > > +
> > > > + if (!vpu->work)
> > > > + return -ENOMEM;
> > > > + else
> > > > + return 0;
> > > > +}
> > > > +
> > > > +void mdp_vpu_shared_mem_free(struct mdp_vpu_dev *vpu)
> > > > +{
> > > > + if (vpu->work && vpu->work_addr)
> > > > + dma_free_coherent(scp_get_device(vpu->scp),
> > > > vpu_alloc_size,
> > > > + vpu->work, vpu->work_addr);
> > > > +}
> > > > +
> > > > +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;
> > > > +
> > > > + vpu->status = msg->status;
> > > > + 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_sw_addr *addr = (struct img_sw_addr *)data;
> > > > + struct img_ipi_frameparam *param =
> > > > + (struct img_ipi_frameparam *)(unsigned
> > > > long)addr->va;
> > > > + struct mdp_vpu_ctx *ctx =
> > > > + (struct mdp_vpu_ctx *)(unsigned long)param-
> > > > >drv_data;
> > > > +
> > > > + if (param->state) {
> > > > + struct mdp_dev *mdp = vpu_to_mdp(ctx->vpu_dev);
> > > > +
> > > > + dev_err(&mdp->pdev->dev, "VPU MDP
> > > > failure:%d\n", param-
> > > > > state);
> > > >
> > > > + }
> > > > + complete(&ctx->vpu_dev->ipi_acked);
> > > > +}
> > > > +
> > > > +int mdp_vpu_register(struct mdp_dev *mdp)
> > > > +{
> > > > + int err;
> > > > + struct mtk_scp *scp = mdp->scp;
> > > > + struct device *dev = &mdp->pdev->dev;
> > > > +
> > > > + err = scp_ipi_register(scp, SCP_IPI_MDP_INIT,
> > > > + mdp_vpu_ipi_handle_init_ack,
> > > > NULL);
> > > > + if (err) {
> > > > + dev_err(dev, "scp_ipi_register failed %d\n",
> > > > err);
> > > > + goto err_ipi_init;
> > > > + }
> > > > + err = scp_ipi_register(scp, SCP_IPI_MDP_DEINIT,
> > > > + mdp_vpu_ipi_handle_deinit_ack,
> > > > NULL);
> > > > + if (err) {
> > > > + dev_err(dev, "scp_ipi_register failed %d\n",
> > > > err);
> > > > + goto err_ipi_deinit;
> > > > + }
> > > > + err = scp_ipi_register(scp, SCP_IPI_MDP_FRAME,
> > > > + mdp_vpu_ipi_handle_frame_ack,
> > > > NULL);
> > > > + if (err) {
> > > > + dev_err(dev, "scp_ipi_register failed %d\n",
> > > > err);
> > > > + goto err_ipi_frame;
> > > > + }
> > > > + return 0;
> > > > +
> > > > +err_ipi_frame:
> > > > + scp_ipi_unregister(scp, SCP_IPI_MDP_DEINIT);
> > > > +err_ipi_deinit:
> > > > + scp_ipi_unregister(scp, SCP_IPI_MDP_INIT);
> > > > +err_ipi_init:
> > > > +
> > > > + return err;
> > > > +}
> > > > +
> > > > +void mdp_vpu_unregister(struct mdp_dev *mdp)
> > > > +{
> > > > + scp_ipi_unregister(mdp->scp, SCP_IPI_MDP_INIT);
> > > > + scp_ipi_unregister(mdp->scp, SCP_IPI_MDP_DEINIT);
> > > > + scp_ipi_unregister(mdp->scp, SCP_IPI_MDP_FRAME);
> > > > +}
> > > > +
> > > > +static int mdp_vpu_sendmsg(struct mdp_vpu_dev *vpu, enum
> > > > scp_ipi_id id,
> > > > + void *buf, unsigned int len)
> > > > +{
> > > > + struct mdp_dev *mdp = vpu_to_mdp(vpu);
> > > > + unsigned int t = MDP_VPU_MESSAGE_TIMEOUT;
> > > > + int ret;
> > > > +
> > > > + if (!vpu->scp) {
> > > > + dev_dbg(&mdp->pdev->dev, "vpu scp is NULL");
> > > > + return -EINVAL;
> > > > + }
> > > > + ret = scp_ipi_send(vpu->scp, id, buf, len, 2000);
> > > > +
> > > > + if (ret) {
> > > > + dev_err(&mdp->pdev->dev, "scp_ipi_send failed
> > > > %d\n",
> > > > ret);
> > > > + return -EPERM;
> > > > + }
> > > > + ret = wait_for_completion_timeout(&vpu->ipi_acked,
> > > > + msecs_to_jiffies(t));
> > > > + 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 mtk_scp
> > > > *scp,
> > > > + struct mutex *lock)
> > > > +{
> > > > + struct mdp_ipi_init_msg msg = {
> > > > + .drv_data = (unsigned long)vpu,
> > > > + };
> > > > + size_t mem_size;
> > > > + phys_addr_t 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->scp = scp;
> > > > + vpu->lock = lock;
> > > > + 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
> > > > */
> > > > +
> > > > + mem_size = vpu_alloc_size;
> > > > + if (mdp_vpu_shared_mem_alloc(vpu)) {
> > > > + dev_err(&mdp->pdev->dev, "VPU memory alloc
> > > > fail!");
> > > > + goto err_mem_alloc;
> > > > + }
> > > > +
> > > > + pool = ALIGN((uintptr_t)vpu->work + vpu->work_size, 8);
> > > > + if (pool + pool_size - (uintptr_t)vpu->work > mem_size)
> > > > {
> > > > + dev_err(&mdp->pdev->dev,
> > > > + "VPU memory insufficient: %zx + %zx >
> > > > %zx",
> > > > + vpu->work_size, pool_size, mem_size);
> > > > + err = -ENOMEM;
> > > > + goto err_mem_size;
> > > > + }
> > > > +
> > > > + dev_dbg(&mdp->pdev->dev,
> > > > + "VPU work:%pK pa:%pad sz:%zx pool:%pa sz:%zx
> > > > (mem
> > > > sz:%zx)",
> > > > + vpu->work, &vpu->work_addr, vpu->work_size,
> > > > + &pool, pool_size, mem_size);
> > > > + vpu->pool = (struct mdp_config_pool *)(uintptr_t)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));
> > > > + 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;
> > > > + }
> > > > + return err;
> > > > +err_mem_size:
> > > > +err_mem_alloc:
> > > > + 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);
> > > > +
> > > > + mutex_lock(vpu->lock);
> > > > + vpu->pool->cfg_count[id]++;
> > > > + config = &vpu->pool->configs[id];
> > > > + *addr = vpu->work_addr + ((uintptr_t)config -
> > > > (uintptr_t)vpu-
> > > > > work);
> > > >
> > > > + 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)
> > > > +{
> > > > + struct mdp_vpu_dev *vpu = ctx->vpu_dev;
> > > > + struct mdp_dev *mdp = vpu_to_mdp(vpu);
> > > > + struct img_sw_addr addr;
> > > > +
> > > > + if (!ctx->vpu_dev->work || !ctx->vpu_dev->work_addr) {
> > > > + if (mdp_vpu_shared_mem_alloc(vpu)) {
> > > > + dev_err(&mdp->pdev->dev, "VPU memory
> > > > alloc
> > > > fail!");
> > > > + return -ENOMEM;
> > > > + }
> > > > + }
> > > > + 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;
> > > > +
> > > > + memcpy((void *)ctx->vpu_dev->work, param,
> > > > sizeof(*param));
> > > > + addr.pa = ctx->vpu_dev->work_addr;
> > > > + addr.va = (uintptr_t)ctx->vpu_dev->work;
> > > > + return mdp_vpu_sendmsg(ctx->vpu_dev, SCP_IPI_MDP_FRAME,
> > > > + &addr, sizeof(addr));
> > > > +}
> > > > diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-
> > > > vpu.h
> > > > b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-vpu.h
> > > > new file mode 100644
> > > > index 000000000000..e831070b44d0
> > > > --- /dev/null
> > > > +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-vpu.h
> > > > @@ -0,0 +1,78 @@
> > > > +/* SPDX-License-Identifier: GPL-2.0-only */
> > > > +/*
> > > > + * Copyright (c) 2021 MediaTek Inc.
> > > > + * Author: Ping-Hsun Wu <ping-hsun.wu at mediatek.com>
> > > > + */
> > > > +
> > > > +#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 */
> > > > +} __packed;
> > > > +
> > > > +struct mdp_ipi_deinit_msg {
> > > > + u32 status;
> > > > + u64 drv_data;
> > > > + u32 work_addr;
> > > > +} __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 accessing vpu working
> > > > buffer
> > > > info */
> > > > + struct mutex *lock;
> > > > + struct mtk_scp *scp;
> > > > + struct completion ipi_acked;
> > > > + void *work;
> > > > + dma_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;
> > > > +};
> > > > +
> > > > +void mdp_vpu_shared_mem_free(struct mdp_vpu_dev *vpu);
> > > > +int mdp_vpu_dev_init(struct mdp_vpu_dev *vpu, struct mtk_scp
> > > > *scp,
> > > > + struct mutex *lock /* for sync */);
> > > > +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__ */
> > >
> > >
>
>
More information about the Linux-mediatek
mailing list