[PATCH v18 4/4] media: platform: mtk-mdp3: add Mediatek MDP3 driver
moudy.ho
moudy.ho at mediatek.com
Thu Jun 16 23:19:48 PDT 2022
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.
> >
> > The MDP3 driver is mainly used for Google Chromebook products to
> > import the new architecture to set the HW settings as shown below:
> > User -> V4L2 framework
> > -> MDP3 driver -> SCP (setting calculations)
> > -> MDP3 driver -> CMDQ (GCE driver) -> HW
> >
> > Each modules' related operation control is sited in mtk-mdp3-comp.c
> > Each modules' register table is defined in file with "mdp_reg_"
> > 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
> >
> > v4l2-compliance 1.22.1, 32 bits, 32-bit time_t
> >
> > Compliance test for mtk-mdp3 device /dev/video2:
> >
> > Driver Info:
> > Driver name : mtk-mdp3
> > Card type : 14001000.mdp3-rdma0
> > Bus info : platform:mtk-mdp3
> > Driver version : 5.18.0
> > Capabilities : 0x84204000
> > Video Memory-to-Memory Multiplanar
> > Streaming
> > Extended Pix Format
> > Device Capabilities
> > Device Caps : 0x04204000
> > Video Memory-to-Memory Multiplanar
> > Streaming
> > Extended Pix Format
> >
> > Required ioctls:
> > test VIDIOC_QUERYCAP: OK
> > test invalid ioctls: OK
> >
> > Allow for multiple opens:
> > test second /dev/video2 open: OK
> > test VIDIOC_QUERYCAP: OK
> > test VIDIOC_G/S_PRIORITY: OK
> > test for unlimited opens: OK
> >
> > Debug ioctls:
> > test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported)
> > test VIDIOC_LOG_STATUS: OK (Not Supported)
> >
> > Input ioctls:
> > test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
> > test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
> > test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
> > test VIDIOC_ENUMAUDIO: OK (Not Supported)
> > test VIDIOC_G/S/ENUMINPUT: OK (Not Supported)
> > test VIDIOC_G/S_AUDIO: OK (Not Supported)
> > Inputs: 0 Audio Inputs: 0 Tuners: 0
> >
> > Output ioctls:
> > test VIDIOC_G/S_MODULATOR: OK (Not Supported)
> > test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
> > test VIDIOC_ENUMAUDOUT: OK (Not Supported)
> > test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
> > test VIDIOC_G/S_AUDOUT: OK (Not Supported)
> > Outputs: 0 Audio Outputs: 0 Modulators: 0
> >
> > Input/Output configuration ioctls:
> > test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported)
> > test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK (Not Supported)
> > test VIDIOC_DV_TIMINGS_CAP: OK (Not Supported)
> > test VIDIOC_G/S_EDID: OK (Not Supported)
> >
> > Control ioctls:
> > test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK
> > test VIDIOC_QUERYCTRL: OK
> > test VIDIOC_G/S_CTRL: OK
> > test VIDIOC_G/S/TRY_EXT_CTRLS: OK
> > test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK
> > test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
> > Standard Controls: 4 Private Controls: 0
> >
> > Format ioctls:
> > test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
> > test VIDIOC_G/S_PARM: OK (Not Supported)
> > test VIDIOC_G_FBUF: OK (Not Supported)
> > test VIDIOC_G_FMT: OK
> > test VIDIOC_TRY_FMT: OK
> > test VIDIOC_S_FMT: OK
> > test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
> > test Cropping: OK
> > test Composing: OK
> > test Scaling: OK
> >
> > Codec ioctls:
> > test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
> > test VIDIOC_G_ENC_INDEX: OK (Not Supported)
> > test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)
> >
> > Buffer ioctls:
> > test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
> > test VIDIOC_EXPBUF: OK
> > test Requests: OK (Not Supported)
> > test TIME32/64: OK
> >
> > Test input 0:
> >
> > Streaming ioctls:
> > test read/write: OK (Not Supported)
> > test blocking wait: OK
> >
> > Video Output Multiplanar: Frame #002
> > Video Output Multiplanar: Frame #003
> > Video Output Multiplanar: Frame #004
> > Video Output Multiplanar: Frame #005
> > Video Output Multiplanar: Frame #006
> > Video Output Multiplanar: Frame #007
> > Video Output Multiplanar: Frame #008
> > Video Output Multiplanar: Frame #009
> > Video Output Multiplanar: Frame #010
> > Video Output Multiplanar: Frame #011
> > Video Output Multiplanar: Frame #012
> > Video Output Multiplanar: Frame #013
> > Video Output Multiplanar: Frame #014
> > Video Output Multiplanar: Frame #015
> > Video Output Multiplanar: Frame #016
> > Video Output Multiplanar: Frame #017
> > Video Output Multiplanar: Frame #018
> > Video Output Multiplanar: Frame #019
> > Video Output Multiplanar: Frame #020
> > Video Output Multiplanar: Frame #021
> > Video Output Multiplanar: Frame #022
> > Video Output Multiplanar: Frame #023
> > Video Output Multiplanar: Frame #024
> > Video Output Multiplanar: Frame #025
> > Video Output Multiplanar: Frame #026
> > Video Output Multiplanar: Frame #027
> > Video Output Multiplanar: Frame #028
> > Video Output Multiplanar: Frame #029
> > Video Output Multiplanar: Frame #030
> > Video Output Multiplanar: Frame #031
> > Video Output Multiplanar: Frame #032
> > Video Output Multiplanar: Frame #033
> > Video Output Multiplanar: Frame #034
> > Video Output Multiplanar: Frame #035
> > Video Output Multiplanar: Frame #036
> > Video Output Multiplanar: Frame #037
> > Video Output Multiplanar: Frame #038
> > Video Output Multiplanar: Frame #039
> > Video Output Multiplanar: Frame #040
> > Video Output Multiplanar: Frame #041
> > Video Output Multiplanar: Frame #042
> > Video Output Multiplanar: Frame #043
> > Video Output Multiplanar: Frame #044
> > Video Output Multiplanar: Frame #045
> > Video Output Multiplanar: Frame #046
> > Video Output Multiplanar: Frame #047
> > Video Output Multiplanar: Frame #048
> > Video Output Multiplanar: Frame #049
> > Video Output Multiplanar: Frame #050
> > Video Output Multiplanar: Frame #051
> > Video Output Multiplanar: Frame #052
> > Video Output Multiplanar: Frame #053
> > Video Output Multiplanar: Frame #054
> > Video Output Multiplanar: Frame #055
> > Video Output Multiplanar: Frame #056
> > Video Output Multiplanar: Frame #057
> > Video Output Multiplanar: Frame #058
> > Video Output Multiplanar: Frame #059
> >
> > Video Capture Multiplanar: Captured 58 buffers
> > test MMAP (no poll): OK
> >
> > Video Output Multiplanar: Frame #002 (select)
> > Video Output Multiplanar: Frame #003 (select)
> > Video Output Multiplanar: Frame #004 (select)
> > Video Output Multiplanar: Frame #005 (select)
> > Video Output Multiplanar: Frame #006 (select)
> > Video Output Multiplanar: Frame #007 (select)
> > Video Output Multiplanar: Frame #008 (select)
> > Video Output Multiplanar: Frame #009 (select)
> > Video Output Multiplanar: Frame #010 (select)
> > Video Output Multiplanar: Frame #011 (select)
> > Video Output Multiplanar: Frame #012 (select)
> > Video Output Multiplanar: Frame #013 (select)
> > Video Output Multiplanar: Frame #014 (select)
> > Video Output Multiplanar: Frame #015 (select)
> > Video Output Multiplanar: Frame #016 (select)
> > Video Output Multiplanar: Frame #017 (select)
> > Video Output Multiplanar: Frame #018 (select)
> > Video Output Multiplanar: Frame #019 (select)
> > Video Output Multiplanar: Frame #020 (select)
> > Video Output Multiplanar: Frame #021 (select)
> > Video Output Multiplanar: Frame #022 (select)
> > Video Output Multiplanar: Frame #023 (select)
> > Video Output Multiplanar: Frame #024 (select)
> > Video Output Multiplanar: Frame #025 (select)
> > Video Output Multiplanar: Frame #026 (select)
> > Video Output Multiplanar: Frame #027 (select)
> > Video Output Multiplanar: Frame #028 (select)
> > Video Output Multiplanar: Frame #029 (select)
> > Video Output Multiplanar: Frame #030 (select)
> > Video Output Multiplanar: Frame #031 (select)
> > Video Output Multiplanar: Frame #032 (select)
> > Video Output Multiplanar: Frame #033 (select)
> > Video Output Multiplanar: Frame #034 (select)
> > Video Output Multiplanar: Frame #035 (select)
> > Video Output Multiplanar: Frame #036 (select)
> > Video Output Multiplanar: Frame #037 (select)
> > Video Output Multiplanar: Frame #038 (select)
> > Video Output Multiplanar: Frame #039 (select)
> > Video Output Multiplanar: Frame #040 (select)
> > Video Output Multiplanar: Frame #041 (select)
> > Video Output Multiplanar: Frame #042 (select)
> > Video Output Multiplanar: Frame #043 (select)
> > Video Output Multiplanar: Frame #044 (select)
> > Video Output Multiplanar: Frame #045 (select)
> > Video Output Multiplanar: Frame #046 (select)
> > Video Output Multiplanar: Frame #047 (select)
> > Video Output Multiplanar: Frame #048 (select)
> > Video Output Multiplanar: Frame #049 (select)
> > Video Output Multiplanar: Frame #050 (select)
> > Video Output Multiplanar: Frame #051 (select)
> > Video Output Multiplanar: Frame #052 (select)
> > Video Output Multiplanar: Frame #053 (select)
> > Video Output Multiplanar: Frame #054 (select)
> > Video Output Multiplanar: Frame #055 (select)
> > Video Output Multiplanar: Frame #056 (select)
> > Video Output Multiplanar: Frame #057 (select)
> > Video Output Multiplanar: Frame #058 (select)
> > Video Output Multiplanar: Frame #059 (select)
> >
> > Video Capture Multiplanar: Captured 58 buffers
> > test MMAP (select): OK
> >
> > Video Output Multiplanar: Frame #002 (epoll)
> > Video Output Multiplanar: Frame #003 (epoll)
> > Video Output Multiplanar: Frame #004 (epoll)
> > Video Output Multiplanar: Frame #005 (epoll)
> > Video Output Multiplanar: Frame #006 (epoll)
> > Video Output Multiplanar: Frame #007 (epoll)
> > Video Output Multiplanar: Frame #008 (epoll)
> > Video Output Multiplanar: Frame #009 (epoll)
> > Video Output Multiplanar: Frame #010 (epoll)
> > Video Output Multiplanar: Frame #011 (epoll)
> > Video Output Multiplanar: Frame #012 (epoll)
> > Video Output Multiplanar: Frame #013 (epoll)
> > Video Output Multiplanar: Frame #014 (epoll)
> > Video Output Multiplanar: Frame #015 (epoll)
> > Video Output Multiplanar: Frame #016 (epoll)
> > Video Output Multiplanar: Frame #017 (epoll)
> > Video Output Multiplanar: Frame #018 (epoll)
> > Video Output Multiplanar: Frame #019 (epoll)
> > Video Output Multiplanar: Frame #020 (epoll)
> > Video Output Multiplanar: Frame #021 (epoll)
> > Video Output Multiplanar: Frame #022 (epoll)
> > Video Output Multiplanar: Frame #023 (epoll)
> > Video Output Multiplanar: Frame #024 (epoll)
> > Video Output Multiplanar: Frame #025 (epoll)
> > Video Output Multiplanar: Frame #026 (epoll)
> > Video Output Multiplanar: Frame #027 (epoll)
> > Video Output Multiplanar: Frame #028 (epoll)
> > Video Output Multiplanar: Frame #029 (epoll)
> > Video Output Multiplanar: Frame #030 (epoll)
> > Video Output Multiplanar: Frame #031 (epoll)
> > Video Output Multiplanar: Frame #032 (epoll)
> > Video Output Multiplanar: Frame #033 (epoll)
> > Video Output Multiplanar: Frame #034 (epoll)
> > Video Output Multiplanar: Frame #035 (epoll)
> > Video Output Multiplanar: Frame #036 (epoll)
> > Video Output Multiplanar: Frame #037 (epoll)
> > Video Output Multiplanar: Frame #038 (epoll)
> > Video Output Multiplanar: Frame #039 (epoll)
> > Video Output Multiplanar: Frame #040 (epoll)
> > Video Output Multiplanar: Frame #041 (epoll)
> > Video Output Multiplanar: Frame #042 (epoll)
> > Video Output Multiplanar: Frame #043 (epoll)
> > Video Output Multiplanar: Frame #044 (epoll)
> > Video Output Multiplanar: Frame #045 (epoll)
> > Video Output Multiplanar: Frame #046 (epoll)
> > Video Output Multiplanar: Frame #047 (epoll)
> > Video Output Multiplanar: Frame #048 (epoll)
> > Video Output Multiplanar: Frame #049 (epoll)
> > Video Output Multiplanar: Frame #050 (epoll)
> > Video Output Multiplanar: Frame #051 (epoll)
> > Video Output Multiplanar: Frame #052 (epoll)
> > Video Output Multiplanar: Frame #053 (epoll)
> > Video Output Multiplanar: Frame #054 (epoll)
> > Video Output Multiplanar: Frame #055 (epoll)
> > Video Output Multiplanar: Frame #056 (epoll)
> > Video Output Multiplanar: Frame #057 (epoll)
> > Video Output Multiplanar: Frame #058 (epoll)
> > Video Output Multiplanar: Frame #059 (epoll)
> >
> > Video Capture Multiplanar: Captured 58 buffers
> > test MMAP (epoll): OK
> > test USERPTR (no poll): OK (Not Supported)
> > test USERPTR (select): OK (Not Supported)
> > test DMABUF: Cannot test, specify --expbuf-device
> >
> > Total for mtk-mdp3 device /dev/video2: 53, Succeeded: 53, Failed:
> > 0, Warnings: 0
> >
> > Signed-off-by: Ping-Hsun Wu <ping-hsun.wu at mediatek.com>
> > Signed-off-by: daoyuan huang <daoyuan.huang at mediatek.com>
> > Signed-off-by: Moudy Ho <moudy.ho at mediatek.com>
> > Tested-by: AngeloGioacchino Del Regno <
> > angelogioacchino.delregno at collabora.com>
> > ---
> > drivers/media/platform/mediatek/Kconfig | 1 +
> > drivers/media/platform/mediatek/Makefile | 1 +
> > drivers/media/platform/mediatek/mdp3/Kconfig | 20 +
> > drivers/media/platform/mediatek/mdp3/Makefile | 6 +
> > .../platform/mediatek/mdp3/mdp_reg_ccorr.h | 19 +
> > .../platform/mediatek/mdp3/mdp_reg_rdma.h | 65 ++
> > .../platform/mediatek/mdp3/mdp_reg_rsz.h | 39 +
> > .../platform/mediatek/mdp3/mdp_reg_wdma.h | 47 +
> > .../platform/mediatek/mdp3/mdp_reg_wrot.h | 55 +
> > .../platform/mediatek/mdp3/mtk-img-ipi.h | 290 +++++
> > .../platform/mediatek/mdp3/mtk-mdp3-cmdq.c | 519 +++++++++
> > .../platform/mediatek/mdp3/mtk-mdp3-cmdq.h | 43 +
> > .../platform/mediatek/mdp3/mtk-mdp3-comp.c | 987
> > ++++++++++++++++++
> > .../platform/mediatek/mdp3/mtk-mdp3-comp.h | 185 ++++
> > .../platform/mediatek/mdp3/mtk-mdp3-core.c | 378 +++++++
> > .../platform/mediatek/mdp3/mtk-mdp3-core.h | 95 ++
> > .../platform/mediatek/mdp3/mtk-mdp3-m2m.c | 772 ++++++++++++++
> > .../platform/mediatek/mdp3/mtk-mdp3-m2m.h | 48 +
> > .../platform/mediatek/mdp3/mtk-mdp3-regs.c | 736 +++++++++++++
> > .../platform/mediatek/mdp3/mtk-mdp3-regs.h | 370 +++++++
> > .../platform/mediatek/mdp3/mtk-mdp3-vpu.c | 312 ++++++
> > .../platform/mediatek/mdp3/mtk-mdp3-vpu.h | 78 ++
> > 22 files changed, 5066 insertions(+)
> > create mode 100644 drivers/media/platform/mediatek/mdp3/Kconfig
> > create mode 100644 drivers/media/platform/mediatek/mdp3/Makefile
> > create mode 100644
> > drivers/media/platform/mediatek/mdp3/mdp_reg_ccorr.h
> > create mode 100644
> > drivers/media/platform/mediatek/mdp3/mdp_reg_rdma.h
> > create mode 100644
> > drivers/media/platform/mediatek/mdp3/mdp_reg_rsz.h
> > create mode 100644
> > drivers/media/platform/mediatek/mdp3/mdp_reg_wdma.h
> > create mode 100644
> > drivers/media/platform/mediatek/mdp3/mdp_reg_wrot.h
> > create mode 100644 drivers/media/platform/mediatek/mdp3/mtk-img-
> > ipi.h
> > create mode 100644 drivers/media/platform/mediatek/mdp3/mtk-mdp3-
> > cmdq.c
> > create mode 100644 drivers/media/platform/mediatek/mdp3/mtk-mdp3-
> > cmdq.h
> > create mode 100644 drivers/media/platform/mediatek/mdp3/mtk-mdp3-
> > comp.c
> > create mode 100644 drivers/media/platform/mediatek/mdp3/mtk-mdp3-
> > comp.h
> > create mode 100644 drivers/media/platform/mediatek/mdp3/mtk-mdp3-
> > core.c
> > create mode 100644 drivers/media/platform/mediatek/mdp3/mtk-mdp3-
> > core.h
> > create mode 100644 drivers/media/platform/mediatek/mdp3/mtk-mdp3-
> > m2m.c
> > create mode 100644 drivers/media/platform/mediatek/mdp3/mtk-mdp3-
> > m2m.h
> > create mode 100644 drivers/media/platform/mediatek/mdp3/mtk-mdp3-
> > regs.c
> > create mode 100644 drivers/media/platform/mediatek/mdp3/mtk-mdp3-
> > regs.h
> > create mode 100644 drivers/media/platform/mediatek/mdp3/mtk-mdp3-
> > vpu.c
> > create mode 100644 drivers/media/platform/mediatek/mdp3/mtk-mdp3-
> > vpu.h
> >
[snip]
> > diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c
> > b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c
> > new file mode 100644
> > index 000000000000..9fbb7f70272c
> > --- /dev/null
> > +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c
> > @@ -0,0 +1,519 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/*
> > + * Copyright (c) 2021 MediaTek Inc.
>
> nit: Do you want 2022 ?
>
Thanks for the reminder, I'll revise it.
> > + * Author: Ping-Hsun Wu <ping-hsun.wu at mediatek.com>
> > + */
> > +
> > +#include <linux/mailbox_controller.h>
> > +#include <linux/platform_device.h>
> > +#include "mtk-mdp3-cmdq.h"
> > +#include "mtk-mdp3-comp.h"
> > +#include "mtk-mdp3-core.h"
> > +#include "mtk-mdp3-m2m.h"
[snip]
> > +static int mdp_path_subfrm_require(struct mdp_path_subfrm *subfrm,
> > + const struct mdp_path *path,
> > + struct mdp_cmdq_cmd *cmd, u32 count)
> > +{
> > + const struct img_config *config = path->config;
> > + const struct mdp_comp_ctx *ctx;
> > + const struct mtk_mdp_driver_data *data = path->mdp_dev-
> > >mdp_data;
> > + struct device *dev = &path->mdp_dev->pdev->dev;
> > + struct mtk_mutex **mutex = path->mdp_dev->mdp_mutex;
> > + s32 mutex_id = -1;
> > + int index, id;
> > +
> > + /* Default value */
> > + memset(subfrm, 0, sizeof(*subfrm));
> > +
> > + /* Decide which mutex to use based on the current pipeline */
> > + switch (path->comps[0].comp->id) {
> > + case MDP_COMP_RDMA0:
> > + mutex_id = MDP_PIPE_RDMA0;
> > + break;
> > + case MDP_COMP_ISP_IMGI:
> > + mutex_id = MDP_PIPE_IMGI;
> > + break;
> > + case MDP_COMP_WPEI:
> > + mutex_id = MDP_PIPE_WPEI;
> > + break;
> > + case MDP_COMP_WPEI2:
> > + mutex_id = MDP_PIPE_WPEI2;
> > + break;
> > + default:
> > + dev_err(dev, "Unknown pipeline and no mutex is
> > assigned");
> > + return -EINVAL;
> > + }
> > + subfrm->mutex_id = mutex_id;
> > +
> > + /* Set mutex mod */
> > + for (index = 0; index < config->num_components; index++) {
> > + ctx = &path->comps[index];
> > + if (is_output_disable(ctx->param, count))
>
> nit: I think it should have been named "is_output_disabled()" as a
> proper
> conjugation (I'm not native english speaker either).
>
Ditto
> > + continue;
> > + id = ctx->comp->id;
> > + mtk_mutex_write_mod(mutex[mutex_id],
> > + data->mdp_mutex_table_idx[id],
> > false);
> > + }
> > +
> > + /* Set SOF */
> > + for (index = 0; index < config->num_components; index++) {
> > + ctx = &path->comps[index];
> > + if (is_output_disable(ctx->param, count))
> > + continue;
> > + id = ctx->comp->id;
> > + switch (id) {
> > + case MDP_COMP_WDMA:
> > + case MDP_COMP_WROT0:
> > + case MDP_COMP_TDSHP0:
> > + case MDP_COMP_RSZ1:
> > + case MDP_COMP_RSZ0:
> > + case MDP_COMP_RDMA0:
> > + subfrm->sofs[subfrm->num_sofs++] = id;
> > + break;
> > + default:
> > + break;
> > + }
> > + }
> > + mtk_mutex_write_sof(mutex[mutex_id],
> > + MUTEX_SOF_IDX_SINGLE_MODE);
> > +
> > + return 0;
> > +}
[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;
}
> > + 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?
> > + }
> > +
> > + 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
> > +{
> > + 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.
> > +
> > + 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.
> > +
> > + 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?
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_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_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