[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(&param.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(&param.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, &param);
> > +	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 = &param->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) ? &param->limit-
> > >out_limit :
> > +						&param->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(&param->list);
> > +	param->limit = &mdp_def_limit;
> > +	param->type = MDP_STREAM_TYPE_BITBLT;
> > +
> > +	frame = &param->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 = &param->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-arm-kernel mailing list