Hi Frederic, On Tue, Sep 10, 2019 at 03:22:43AM +0800, frederic.chen@xxxxxxxxxxxx wrote: > From: Frederic Chen <frederic.chen@xxxxxxxxxxxx> > > This patch adds the driver of Digital Image Processing (DIP) > unit in Mediatek ISP system, providing image format > conversion, resizing, and rotation features. > > The mtk-isp directory will contain drivers for multiple IP > blocks found in Mediatek ISP system. It will include ISP > Pass 1 driver(CAM), sensor interface driver, DIP driver and > face detection driver. > > Signed-off-by: Frederic Chen <frederic.chen@xxxxxxxxxxxx> > --- > drivers/media/platform/mtk-isp/Makefile | 7 + > .../media/platform/mtk-isp/isp_50/Makefile | 7 + > .../platform/mtk-isp/isp_50/dip/Makefile | 18 + > .../platform/mtk-isp/isp_50/dip/mtk_dip-dev.c | 650 +++++ > .../platform/mtk-isp/isp_50/dip/mtk_dip-dev.h | 566 +++++ > .../platform/mtk-isp/isp_50/dip/mtk_dip-hw.h | 156 ++ > .../platform/mtk-isp/isp_50/dip/mtk_dip-sys.c | 521 ++++ > .../mtk-isp/isp_50/dip/mtk_dip-v4l2.c | 2255 +++++++++++++++++ > 8 files changed, 4180 insertions(+) > create mode 100644 drivers/media/platform/mtk-isp/Makefile > create mode 100644 drivers/media/platform/mtk-isp/isp_50/Makefile > create mode 100644 drivers/media/platform/mtk-isp/isp_50/dip/Makefile > create mode 100644 drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-dev.c > create mode 100644 drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-dev.h > create mode 100644 drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-hw.h > create mode 100644 drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-sys.c > create mode 100644 drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-v4l2.c > > diff --git a/drivers/media/platform/mtk-isp/Makefile b/drivers/media/platform/mtk-isp/Makefile > new file mode 100644 > index 000000000000..b08d3bdf2800 > --- /dev/null > +++ b/drivers/media/platform/mtk-isp/Makefile > @@ -0,0 +1,7 @@ > +# SPDX-License-Identifier: GPL-2.0 > +# > +# Copyright (C) 2019 MediaTek Inc. > +# > + > +obj-y += isp_50/ > + > diff --git a/drivers/media/platform/mtk-isp/isp_50/Makefile b/drivers/media/platform/mtk-isp/isp_50/Makefile > new file mode 100644 > index 000000000000..564c3889c34c > --- /dev/null > +++ b/drivers/media/platform/mtk-isp/isp_50/Makefile > @@ -0,0 +1,7 @@ > +# SPDX-License-Identifier: GPL-2.0 > +# > +# Copyright (C) 2019 MediaTek Inc. > +# > + > +obj-$(CONFIG_VIDEO_MEDIATEK_ISP_DIP) += dip/ > + > diff --git a/drivers/media/platform/mtk-isp/isp_50/dip/Makefile b/drivers/media/platform/mtk-isp/isp_50/dip/Makefile > new file mode 100644 > index 000000000000..99e760d7d5a9 > --- /dev/null > +++ b/drivers/media/platform/mtk-isp/isp_50/dip/Makefile > @@ -0,0 +1,18 @@ > +# SPDX-License-Identifier: GPL-2.0 > +# > +# Copyright (C) 2019 MediaTek Inc. > +# > + > +$(info $(srctree)) > +ccflags-y += -I$(srctree)/drivers/media/platform/mtk-mdp3 > + > +obj-$(CONFIG_VIDEO_MEDIATEK_ISP_DIP) += mtk_dip-v4l2.o > + > +# Utilities to provide frame-based streaming model > +# with v4l2 user interfaces and alloc context managing > +# memory shared between ISP and coprocessor > +mtk_dip_util-objs := \ > +mtk_dip-dev.o \ > +mtk_dip-sys.o Some indentation would be preferred. > + > +obj-$(CONFIG_VIDEO_MEDIATEK_ISP_DIP) += mtk_dip_util.o > diff --git a/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-dev.c b/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-dev.c > new file mode 100644 > index 000000000000..d964ae0c4700 > --- /dev/null > +++ b/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-dev.c > @@ -0,0 +1,650 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (c) 2018 MediaTek Inc. 2019 > + * > + * Author: Frederic Chen <frederic.chen@xxxxxxxxxxxx> > + * > + */ > + > +#include <linux/dma-mapping.h> > +#include <linux/platform_device.h> > +#include <media/videobuf2-dma-contig.h> > +#include <media/v4l2-event.h> > +#include "mtk_dip-dev.h" > +#include "mtk-mdp3-regs.h" > +#include "mtk-img-ipi.h" > + > +int mtk_dip_pipe_init(struct mtk_dip_dev *dip_dev, struct mtk_dip_pipe *pipe, > + const struct mtk_dip_pipe_desc *setting) > +{ > + int ret, i; unsigned int i > + > + pipe->dip_dev = dip_dev; > + pipe->desc = setting; > + pipe->nodes_enabled = 0; > + pipe->nodes_streaming = 0; > + > + atomic_set(&pipe->pipe_job_sequence, 0); > + INIT_LIST_HEAD(&pipe->pipe_job_running_list); > + INIT_LIST_HEAD(&pipe->pipe_job_pending_list); > + spin_lock_init(&pipe->job_lock); > + mutex_init(&pipe->lock); > + > + for (i = 0; i < pipe->desc->total_queues; i++) { > + pipe->nodes[i].desc = &pipe->desc->queue_descs[i]; > + pipe->nodes[i].flags = pipe->nodes[i].desc->flags; > + spin_lock_init(&pipe->nodes[i].buf_list_lock); > + INIT_LIST_HEAD(&pipe->nodes[i].buf_list); > + > + if (pipe->nodes[i].flags & MEDIA_LNK_FL_ENABLED) > + pipe->nodes_enabled |= 1 << i; BIT(i) > + > + pipe->nodes[i].crop.left = 0; > + pipe->nodes[i].crop.top = 0; > + pipe->nodes[i].crop.width = > + pipe->nodes[i].desc->frmsizeenum->stepwise.max_width; > + pipe->nodes[i].crop.height = > + pipe->nodes[i].desc->frmsizeenum->stepwise.max_height; > + pipe->nodes[i].compose.left = 0; > + pipe->nodes[i].compose.top = 0; > + pipe->nodes[i].compose.width = > + pipe->nodes[i].desc->frmsizeenum->stepwise.max_width; > + pipe->nodes[i].compose.height = > + pipe->nodes[i].desc->frmsizeenum->stepwise.max_height; > + } > + > + ret = mtk_dip_pipe_v4l2_register(pipe, &dip_dev->mdev, > + &dip_dev->v4l2_dev); > + if (ret) { > + dev_err(pipe->dip_dev->dev, > + "%s: failed(%d) to create V4L2 devices\n", > + pipe->desc->name, ret); > + > + goto err_destroy_pipe_lock; > + } > + > + return 0; > + > +err_destroy_pipe_lock: > + mutex_destroy(&pipe->lock); > + > + return ret; > +} > + > +int mtk_dip_pipe_release(struct mtk_dip_pipe *pipe) > +{ > + mtk_dip_pipe_v4l2_unregister(pipe); > + mutex_destroy(&pipe->lock); > + > + return 0; > +} > + > +int mtk_dip_pipe_next_job_id(struct mtk_dip_pipe *pipe) > +{ > + int global_job_id = atomic_inc_return(&pipe->pipe_job_sequence); u32; same for the return value. > + > + return (global_job_id & 0x0000ffff) | (pipe->desc->id << 16); > +} > + > +struct mtk_dip_request *mtk_dip_pipe_get_running_job(struct mtk_dip_pipe *pipe, > + int id) > +{ > + struct mtk_dip_request *req; > + > + spin_lock(&pipe->job_lock); > + list_for_each_entry(req, > + &pipe->pipe_job_running_list, list) { > + if (req->id == id) { > + spin_unlock(&pipe->job_lock); > + return req; > + } > + } > + spin_unlock(&pipe->job_lock); > + > + return NULL; > +} > + > +void mtk_dip_pipe_remove_job(struct mtk_dip_request *req) > +{ > + spin_lock(&req->dip_pipe->job_lock); > + list_del(&req->list); > + req->dip_pipe->num_jobs = req->dip_pipe->num_jobs - 1; > + spin_unlock(&req->dip_pipe->job_lock); > +} > + > +void mtk_dip_pipe_job_finish(struct mtk_dip_request *req, > + enum vb2_buffer_state vbf_state) > +{ > + struct mtk_dip_pipe *pipe = req->dip_pipe; > + struct mtk_dip_dev_buffer *in_buf; > + int i; > + > + in_buf = req->buf_map[MTK_DIP_VIDEO_NODE_ID_RAW_OUT]; > + > + for (i = 0; i < pipe->desc->total_queues; i++) { > + struct mtk_dip_dev_buffer *dev_buf = req->buf_map[i]; > + struct mtk_dip_video_device *node; > + > + if (!dev_buf) > + continue; > + > + if (dev_buf != in_buf) > + dev_buf->vbb.vb2_buf.timestamp = > + in_buf->vbb.vb2_buf.timestamp; > + > + vb2_buffer_done(&dev_buf->vbb.vb2_buf, vbf_state); > + > + node = mtk_dip_vbq_to_node(dev_buf->vbb.vb2_buf.vb2_queue); > + spin_lock(&node->buf_list_lock); > + list_del(&dev_buf->list); > + spin_unlock(&node->buf_list_lock); > + } > +} > + > +static u32 dip_pass1_fmt_get_stride(struct v4l2_pix_format_mplane *mfmt, > + const struct mtk_dip_dev_format *fmt, > + unsigned int plane) > +{ > + unsigned int width = ALIGN(mfmt->width, 4); > + unsigned int pixel_bits = fmt->row_depth[0]; > + unsigned int bpl, ppl; > + > + ppl = DIV_ROUND_UP(width * fmt->depth[0], fmt->row_depth[0]); > + bpl = DIV_ROUND_UP(ppl * pixel_bits, 8); > + > + return ALIGN(bpl, fmt->pass_1_align); > +} > + > +/* Stride that is accepted by MDP HW */ > +static u32 dip_mdp_fmt_get_stride(struct v4l2_pix_format_mplane *mfmt, > + const struct mtk_dip_dev_format *fmt, > + unsigned int plane) > +{ > + enum mdp_color c = fmt->mdp_color; > + u32 bytesperline = (mfmt->width * fmt->row_depth[plane]) / 8; > + u32 stride = (bytesperline * DIP_MCOLOR_BITS_PER_PIXEL(c)) > + / fmt->row_depth[0]; > + > + if (plane == 0) > + return stride; > + > + if (plane < DIP_MCOLOR_GET_PLANE_COUNT(c)) { > + if (DIP_MCOLOR_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 > +dip_mdp_fmt_get_stride_contig(const struct mtk_dip_dev_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 < DIP_MCOLOR_GET_PLANE_COUNT(c)) { > + stride = stride >> DIP_MCOLOR_GET_H_SUBSAMPLE(c); > + if (DIP_MCOLOR_IS_UV_COPLANE(c) && !DIP_MCOLOR_IS_BLOCK_MODE(c)) > + stride = stride * 2; stride *= 2; The same on bytesperline calculation below. > + return stride; > + } > + > + return 0; > +} > + > +/* Plane size that is accepted by MDP HW */ > +static u32 > +dip_mdp_fmt_get_plane_size(const struct mtk_dip_dev_format *fmt, > + u32 stride, u32 height, unsigned int plane) > +{ > + enum mdp_color c = fmt->mdp_color; > + u32 bytesperline; > + > + bytesperline = (stride * fmt->row_depth[0]) > + / DIP_MCOLOR_BITS_PER_PIXEL(c); > + > + if (plane == 0) > + return bytesperline * height; > + if (plane < DIP_MCOLOR_GET_PLANE_COUNT(c)) { > + height = height >> DIP_MCOLOR_GET_V_SUBSAMPLE(c); > + if (DIP_MCOLOR_IS_BLOCK_MODE(c)) > + bytesperline = bytesperline * 2; > + return bytesperline * height; > + } > + > + return 0; > +} > + > +static int mtk_dip_pipe_get_stride(struct mtk_dip_pipe *pipe, unsigned int? > + struct v4l2_pix_format_mplane *mfmt, > + const struct mtk_dip_dev_format *dfmt, > + int plane, const char *buf_name) > +{ > + int bpl; Ditto. > + > + if (dfmt->pass_1_align) > + bpl = dip_pass1_fmt_get_stride(mfmt, dfmt, plane); > + else > + bpl = dip_mdp_fmt_get_stride(mfmt, dfmt, plane); > + > + return bpl; > +} > + > +void mtk_dip_pipe_try_fmt(struct mtk_dip_pipe *pipe, > + struct mtk_dip_video_device *node, > + struct v4l2_format *fmt, > + const struct v4l2_format *ufmt, > + const struct mtk_dip_dev_format *dfmt) > +{ > + int i; unsigned int i > + > + fmt->type = ufmt->type; > + fmt->fmt.pix_mp.width = > + clamp_val(ufmt->fmt.pix_mp.width, > + node->desc->frmsizeenum->stepwise.min_width, > + node->desc->frmsizeenum->stepwise.max_width); > + fmt->fmt.pix_mp.height = > + clamp_val(ufmt->fmt.pix_mp.height, > + node->desc->frmsizeenum->stepwise.min_height, > + node->desc->frmsizeenum->stepwise.max_height); > + fmt->fmt.pix_mp.num_planes = ufmt->fmt.pix_mp.num_planes; > + fmt->fmt.pix_mp.field = V4L2_FIELD_NONE; > + > + if (ufmt->fmt.pix_mp.quantization != V4L2_QUANTIZATION_FULL_RANGE) > + fmt->fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT; > + else > + fmt->fmt.pix_mp.quantization = ufmt->fmt.pix_mp.quantization; > + > + if (ufmt->fmt.pix_mp.colorspace < 0xFF) Why 0xff? What are the valid colour spaces? Looking at the code below, this code would be nicer in an else branch of the if statement below. > + fmt->fmt.pix_mp.colorspace = ufmt->fmt.pix_mp.colorspace; > + else > + fmt->fmt.pix_mp.colorspace = V4L2_COLORSPACE_DEFAULT; > + > + /* Only MDP 0 and MDP 1 allow the color space change */ > + if (node->desc->id != MTK_DIP_VIDEO_NODE_ID_MDP0_CAPTURE && > + node->desc->id != MTK_DIP_VIDEO_NODE_ID_MDP1_CAPTURE) { > + fmt->fmt.pix_mp.quantization = V4L2_QUANTIZATION_FULL_RANGE; > + fmt->fmt.pix_mp.colorspace = V4L2_COLORSPACE_DEFAULT; > + } > + > + fmt->fmt.pix_mp.pixelformat = dfmt->format; > + fmt->fmt.pix_mp.num_planes = dfmt->num_planes; > + fmt->fmt.pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; > + fmt->fmt.pix_mp.field = V4L2_FIELD_NONE; > + > + for (i = 0; i < fmt->fmt.pix_mp.num_planes; ++i) { > + unsigned int stride; > + unsigned int sizeimage; > + > + stride = mtk_dip_pipe_get_stride(pipe, &fmt->fmt.pix_mp, > + dfmt, i, node->desc->name); > + if (dfmt->pass_1_align) > + sizeimage = stride * fmt->fmt.pix_mp.height; > + else > + sizeimage = DIV_ROUND_UP(stride * > + fmt->fmt.pix_mp.height * > + dfmt->depth[i], > + dfmt->row_depth[i]); > + > + fmt->fmt.pix_mp.plane_fmt[i].sizeimage = sizeimage; > + fmt->fmt.pix_mp.plane_fmt[i].bytesperline = stride; > + } > +} > + > +static void set_meta_fmt(struct mtk_dip_pipe *pipe, > + struct v4l2_meta_format *fmt, > + const struct mtk_dip_dev_format *dev_fmt) > +{ > + fmt->dataformat = dev_fmt->format; > + > + if (dev_fmt->buffer_size <= 0) > + fmt->buffersize = > + MTK_DIP_DEV_META_BUF_DEFAULT_SIZE; Fits on a single line. > + else > + fmt->buffersize = dev_fmt->buffer_size; > +} > + > +void mtk_dip_pipe_load_default_fmt(struct mtk_dip_pipe *pipe, > + struct mtk_dip_video_device *node, > + struct v4l2_format *fmt) > +{ > + int idx = node->desc->default_fmt_idx; unsigned int; please check the rest of the driver for similar pattern. > + > + if (idx >= node->desc->num_fmts) { > + dev_err(pipe->dip_dev->dev, > + "%s:%s: invalid idx(%d), must < num_fmts(%d)\n", > + __func__, node->desc->name, idx, node->desc->num_fmts); > + > + idx = 0; > + } > + > + fmt->type = node->desc->buf_type; > + if (mtk_dip_buf_is_meta(node->desc->buf_type)) { > + set_meta_fmt(pipe, &fmt->fmt.meta, > + &node->desc->fmts[idx]); Fits on a single line. > + } else { > + fmt->fmt.pix_mp.width = node->desc->default_width; > + fmt->fmt.pix_mp.height = node->desc->default_height; > + mtk_dip_pipe_try_fmt(pipe, node, fmt, fmt, > + &node->desc->fmts[idx]); > + } > +} > + > +const struct mtk_dip_dev_format* > +mtk_dip_pipe_find_fmt(struct mtk_dip_pipe *pipe, > + struct mtk_dip_video_device *node, > + u32 format) > +{ > + int i; > + > + for (i = 0; i < node->desc->num_fmts; i++) { > + if (node->desc->fmts[i].format == format) > + return &node->desc->fmts[i]; > + } > + > + return NULL; > +} > + > +static enum mdp_ycbcr_profile > +mtk_dip_map_ycbcr_prof_mplane(struct v4l2_pix_format_mplane *pix_mp, > + u32 mdp_color) > +{ > + switch (pix_mp->colorspace) { > + case V4L2_COLORSPACE_DEFAULT: > + if (pix_mp->quantization == V4L2_QUANTIZATION_FULL_RANGE) > + return MDP_YCBCR_PROFILE_FULL_BT601; > + return MDP_YCBCR_PROFILE_BT601; > + 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; > + } > + > + if (DIP_MCOLOR_IS_RGB(mdp_color)) > + return MDP_YCBCR_PROFILE_FULL_BT601; > + > + if (pix_mp->quantization == V4L2_QUANTIZATION_FULL_RANGE) > + return MDP_YCBCR_PROFILE_FULL_BT601; > + > + return MDP_YCBCR_PROFILE_BT601; > +} > + > +static inline int > +mtk_dip_is_contig_mp_buffer(struct mtk_dip_dev_buffer *dev_buf) > +{ > + return !(dev_buf->dev_fmt->num_cplanes == 1); > +} > + > +static void mtk_dip_fill_ipi_img_param_mp(struct mtk_dip_pipe *pipe, > + struct img_image_buffer *b, Where is img_image_buffer defined? > + struct mtk_dip_dev_buffer *dev_buf, > + char *buf_name) > +{ > + struct v4l2_pix_format_mplane *pix_mp = &dev_buf->fmt.fmt.pix_mp; > + const struct mtk_dip_dev_format *mdp_fmt = dev_buf->dev_fmt; > + unsigned int i; > + unsigned int total_plane_size = 0; > + > + b->usage = dev_buf->dma_port; > + b->format.colorformat = dev_buf->dev_fmt->mdp_color; > + b->format.width = dev_buf->fmt.fmt.pix_mp.width; > + b->format.height = dev_buf->fmt.fmt.pix_mp.height; > + b->format.ycbcr_prof = > + mtk_dip_map_ycbcr_prof_mplane(pix_mp, > + dev_buf->dev_fmt->mdp_color); > + > + for (i = 0; i < pix_mp->num_planes; ++i) { > + u32 stride = pix_mp->plane_fmt[i].bytesperline; > + > + /* > + * We use dip_mdp_fmt_get_plane_size() to get the plane sizes of > + * non-M multiple plane image buffers. In this case, > + * pix_mp->plane_fmt[0].sizeimage is the total size of all > + * b->format.plane_fmt[i]. > + */ > + b->format.plane_fmt[i].stride = stride; > + b->format.plane_fmt[i].size = > + dip_mdp_fmt_get_plane_size(mdp_fmt, stride, > + pix_mp->height, i); > + b->iova[i] = dev_buf->isp_daddr[i]; > + total_plane_size += b->format.plane_fmt[i].size; > + } > + > + if (!mtk_dip_is_contig_mp_buffer(dev_buf)) > + return; > + > + for (; i < DIP_MCOLOR_GET_PLANE_COUNT(b->format.colorformat); ++i) { > + u32 stride = dip_mdp_fmt_get_stride_contig > + (mdp_fmt, b->format.plane_fmt[0].stride, i); > + > + b->format.plane_fmt[i].stride = stride; > + b->format.plane_fmt[i].size = > + dip_mdp_fmt_get_plane_size(mdp_fmt, stride, > + pix_mp->height, i); > + b->iova[i] = b->iova[i - 1] + b->format.plane_fmt[i - 1].size; > + total_plane_size += b->format.plane_fmt[i].size; > + } > +} > + > +static void mtk_dip_fill_input_ipi_param(struct mtk_dip_pipe *pipe, > + struct img_input *iin, > + struct mtk_dip_dev_buffer *dev_buf, > + char *buf_name) > +{ > + mtk_dip_fill_ipi_img_param_mp(pipe, &iin->buffer, dev_buf, buf_name); > +} > + > +static void mtk_dip_fill_output_ipi_param(struct mtk_dip_pipe *pipe, > + struct img_output *iout, > + struct mtk_dip_dev_buffer *buf_out, > + struct mtk_dip_dev_buffer *buf_in, > + char *buf_name) > +{ > + mtk_dip_fill_ipi_img_param_mp(pipe, &iout->buffer, buf_out, buf_name); > + iout->crop.left = 0; > + iout->crop.top = 0; > + iout->crop.width = buf_in->fmt.fmt.pix_mp.width; > + iout->crop.height = buf_in->fmt.fmt.pix_mp.height; > + iout->crop.left_subpix = 0; > + iout->crop.top_subpix = 0; > + iout->crop.width_subpix = 0; > + iout->crop.height_subpix = 0; > + iout->rotation = 0; > +} > + > +static u32 mtk_dip_to_fixed(u32 *r, struct v4l2_fract *f) > +{ > + u32 q; > + > + if (f->denominator == 0) { > + *r = 0; > + return 0; > + } > + > + q = f->numerator / f->denominator; > + *r = (((u64)f->numerator - q * f->denominator) << IMG_SUBPIXEL_SHIFT) > + / f->denominator; > + return q; > +} > + > +static void mtk_dip_set_src_crop(struct img_crop *c, struct mtk_dip_crop *crop) > +{ > + c->left = crop->c.left > + + mtk_dip_to_fixed(&c->left_subpix, &crop->left_subpix); > + c->top = crop->c.top > + + mtk_dip_to_fixed(&c->top_subpix, &crop->top_subpix); > + c->width = crop->c.width > + + mtk_dip_to_fixed(&c->width_subpix, &crop->width_subpix); > + c->height = crop->c.height > + + mtk_dip_to_fixed(&c->height_subpix, &crop->height_subpix); > +} > + > +static void mtk_dip_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; > +} > + > +static void mtk_dip_set_crop_config(struct mtk_dip_dev *dip_dev, > + struct mtk_dip_dev_buffer *dev_buf_out, > + struct img_output *iout, char *buf_name) > +{ > + iout->buffer.format.width = dev_buf_out->compose.width; > + iout->buffer.format.height = dev_buf_out->compose.height; > + > + mtk_dip_set_src_crop(&iout->crop, &dev_buf_out->crop); > +} > + > +static void mtk_dip_set_rotate_config(struct mtk_dip_dev *dip_dev, > + struct mtk_dip_dev_buffer *dev_buf_in, > + struct mtk_dip_dev_buffer *dev_buf_out, > + struct img_output *iout, char *buf_name) > +{ > + mtk_dip_set_orientation(iout, dev_buf_out->rotation, > + dev_buf_out->hflip, dev_buf_out->vflip); > +} > + > +void mtk_dip_pipe_ipi_params_config(struct mtk_dip_request *req) > +{ > + struct mtk_dip_pipe *pipe = req->dip_pipe; > + int buf_out_idx; > + int buf_in_idx; > + struct img_ipi_frameparam *dip_param = &req->img_fparam.frameparam; > + struct mtk_dip_dev_buffer *buf_in; > + struct mtk_dip_dev_buffer *buf_out; > + struct mtk_dip_dev_buffer *buf_tuning; > + int i = 0; > + int mdp_ids[2] = {MTK_DIP_VIDEO_NODE_ID_MDP0_CAPTURE, > + MTK_DIP_VIDEO_NODE_ID_MDP1_CAPTURE}; > + char *mdp_names[2] = {"mdp0", "mdp1"}; > + > + memset(dip_param, 0, sizeof(*dip_param)); > + dip_param->index = req->id; > + dip_param->type = STREAM_ISP_IC; > + > + /* Tuning buffer */ > + buf_tuning = > + req->buf_map[MTK_DIP_VIDEO_NODE_ID_TUNING_OUT]; > + if (buf_tuning) { > + dip_param->tuning_data.pa = > + (uint32_t)buf_tuning->scp_daddr[0]; > + dip_param->tuning_data.present = true; > + dip_param->tuning_data.iova = > + (uint32_t)buf_tuning->isp_daddr[0]; > + } > + > + buf_in_idx = 0; > + > + /* > + * Raw-in buffer > + * The input buffer is guaranteed by .request_validate() > + */ > + buf_in = req->buf_map[MTK_DIP_VIDEO_NODE_ID_RAW_OUT]; > + mtk_dip_fill_input_ipi_param > + (pipe, &dip_param->inputs[buf_in_idx++], > + buf_in, "RAW"); > + > + /* NR buffer (optional input) */ > + if (req->buf_map[MTK_DIP_VIDEO_NODE_ID_NR_OUT]) > + mtk_dip_fill_input_ipi_param > + (pipe, &dip_param->inputs[buf_in_idx++], > + req->buf_map[MTK_DIP_VIDEO_NODE_ID_NR_OUT], > + "NR"); > + > + /* Shading buffer (optional input)*/ > + if (req->buf_map[MTK_DIP_VIDEO_NODE_ID_SHADING_OUT]) > + mtk_dip_fill_input_ipi_param > + (pipe, &dip_param->inputs[buf_in_idx++], > + req->buf_map[MTK_DIP_VIDEO_NODE_ID_SHADING_OUT], > + "Shading"); > + > + /* MDP buffers */ > + buf_out_idx = 0; > + > + for (i = 0; i < ARRAY_SIZE(mdp_ids); i++) { > + buf_out = > + req->buf_map[mdp_ids[i]]; > + if (buf_out) { > + struct img_output *iout = > + &dip_param->outputs[buf_out_idx++]; > + > + mtk_dip_fill_output_ipi_param(pipe, iout, buf_out, > + buf_in, mdp_names[i]); > + mtk_dip_set_crop_config(pipe->dip_dev, buf_out, > + iout, mdp_names[i]); > + > + /* MDP 0 support rotation */ > + if (i == 0) > + mtk_dip_set_rotate_config(pipe->dip_dev, > + buf_in, buf_out, iout, > + mdp_names[i]); > + } > + } > + > + /* IMG2O buffer */ > + buf_out = req->buf_map[MTK_DIP_VIDEO_NODE_ID_IMG2_CAPTURE]; > + if (req->buf_map[MTK_DIP_VIDEO_NODE_ID_IMG2_CAPTURE]) > + mtk_dip_fill_output_ipi_param > + (pipe, &dip_param->outputs[buf_out_idx++], > + buf_out, buf_in, "IMG2O"); > + > + /* IMG3O buffer */ > + buf_out = req->buf_map[MTK_DIP_VIDEO_NODE_ID_IMG3_CAPTURE]; > + if (buf_out) > + mtk_dip_fill_output_ipi_param > + (pipe, &dip_param->outputs[buf_out_idx++], > + buf_out, buf_in, "IMG3O"); > + > + dip_param->num_outputs = buf_out_idx; > + dip_param->num_inputs = buf_in_idx; > +} > + > +void mtk_dip_pipe_try_enqueue(struct mtk_dip_pipe *pipe) > +{ > + struct mtk_dip_request *req, *tmp_req; > + > + if (!pipe->streaming) > + return; > + > + spin_lock(&pipe->job_lock); > + list_for_each_entry_safe(req, tmp_req, > + &pipe->pipe_job_pending_list, list) { > + list_del(&req->list); > + pipe->num_pending_jobs--; > + list_add_tail(&req->list, > + &pipe->pipe_job_running_list); > + pipe->num_jobs++; > + mtk_dip_hw_enqueue(pipe->dip_dev, req); > + } > + spin_unlock(&pipe->job_lock); > +} > diff --git a/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-dev.h b/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-dev.h > new file mode 100644 > index 000000000000..54da4fed95b4 > --- /dev/null > +++ b/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-dev.h > @@ -0,0 +1,566 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Copyright (c) 2018 MediaTek Inc. > + * > + * Author: Frederic Chen <frederic.chen@xxxxxxxxxxxx> > + * > + */ > + > +#ifndef _MTK_DIP_DEV_H_ > +#define _MTK_DIP_DEV_H_ > + > +#include <linux/types.h> > +#include <linux/platform_device.h> > +#include <media/v4l2-ctrls.h> > +#include <media/v4l2-subdev.h> > +#include <media/v4l2-device.h> > +#include <linux/videodev2.h> > +#include <media/videobuf2-core.h> > +#include <media/videobuf2-v4l2.h> > +#include "mtk_dip-hw.h" > + > +#define MTK_DIP_PIPE_ID_PREVIEW 0 > +#define MTK_DIP_PIPE_ID_CAPTURE 1 > +#define MTK_DIP_PIPE_ID_REPROCESS 2 > +#define MTK_DIP_PIPE_ID_TOTAL_NUM 3 > + > +#define MTK_DIP_VIDEO_NODE_ID_RAW_OUT 0 > +#define MTK_DIP_VIDEO_NODE_ID_TUNING_OUT 1 > +#define MTK_DIP_VIDEO_NODE_ID_NR_OUT 2 > +#define MTK_DIP_VIDEO_NODE_ID_SHADING_OUT 3 > +#define MTK_DIP_VIDEO_NODE_ID_MDP0_CAPTURE 4 > +#define MTK_DIP_VIDEO_NODE_ID_MDP1_CAPTURE 5 > +#define MTK_DIP_VIDEO_NODE_ID_IMG2_CAPTURE 6 > +#define MTK_DIP_VIDEO_NODE_ID_IMG3_CAPTURE 7 > +#define MTK_DIP_VIDEO_NODE_ID_TOTAL_NUM 8 > + > +#define MTK_DIP_OUTPUT_MIN_WIDTH 2U > +#define MTK_DIP_OUTPUT_MIN_HEIGHT 2U > +#define MTK_DIP_OUTPUT_MAX_WIDTH 5376U > +#define MTK_DIP_OUTPUT_MAX_HEIGHT 4032U > +#define MTK_DIP_CAPTURE_MIN_WIDTH 2U > +#define MTK_DIP_CAPTURE_MIN_HEIGHT 2U > +#define MTK_DIP_CAPTURE_MAX_WIDTH 5376U > +#define MTK_DIP_CAPTURE_MAX_HEIGHT 4032U > + > +#define MTK_DIP_DEV_META_BUF_DEFAULT_SIZE (1024 * 128) > +#define MTK_DIP_DEV_META_BUF_POOL_MAX_SIZE (1024 * 1024 * 16) > + > +/** > + * struct mtk_dip_dev_format - DIP buffer format > + * @format: the corresponding V4L2 pixel format > + * @mdp_color: color information setting > + * @depth: bytes per pixel of the format > + * @row_depth: bits per pixel > + * @num_planes: the number of planes > + * @num_cplanes: the number of color planes carried in a > + * single v4l2 plane > + * @flags: flags setting which will be passed to media_create_pad_link() > + * @buffer_size: the buffer size of the buffer. It is used when it is > + * a meta type format. > + * @pass_1_align: the alignment setting of the stride .If pass_1_align > + * is greater than 0, the stride must be align with it. > + * > + * The structure defines the DIP internal buffer format information. The fields > + * is used in V4L2 try/set format calculation flow. > + */ > +struct mtk_dip_dev_format { > + u32 format; > + u32 mdp_color; > + u8 depth[VIDEO_MAX_PLANES]; > + u8 row_depth[VIDEO_MAX_PLANES]; > + u8 num_planes; > + u8 num_cplanes; > + u32 flags; > + u32 buffer_size; > + u8 pass_1_align; > +}; > + > +/** > + * struct mtk_dip_crop - DIP crop setting > + * @c: crop region > + * @left_subpix: the left subpixel part of the corp region > + * @top_subpix: the top subpixel part of the corp region > + * @width_subpix: the width subpixel part of the corp region > + * @height_subpix: the height subpixel part of the corp region > + * > + * The structure includes the crop setting which can be passed to > + * DIP hardware. > + */ > +struct mtk_dip_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 mtk_dip_dev_buffer - Buffer information used by DIP > + * @vbb: the vb2 buffer associated > + * @fmt: the corresponding v4l2 format > + * @dev_fmt: the DIP internal format > + * @pipe_job_id: the global id of the request owned the buffer > + * @isp_daddr: the address which can be used by ISP hardware > + * @scp_daddr: the address which can be used in coprocessor > + * @dma_port: dma port id of the buffer > + * @crop: corp setting of the buffer > + * @compose: compose setting of the buffer > + * @rotation: rotation degree of the buffer > + * @hflip: need horizontal flip or not > + * @vflip: need vertical flip or not > + * > + * The structure includes the Buffer setting which can be used by > + * DIP hardware. > + */ > +struct mtk_dip_dev_buffer { > + struct vb2_v4l2_buffer vbb; > + struct v4l2_format fmt; > + const struct mtk_dip_dev_format *dev_fmt; > + int pipe_job_id; > + dma_addr_t isp_daddr[VB2_MAX_PLANES]; > + dma_addr_t scp_daddr[VB2_MAX_PLANES]; > + unsigned int dma_port; > + struct mtk_dip_crop crop; > + struct v4l2_rect compose; > + int rotation; > + int hflip; > + int vflip; > + struct list_head list; > +}; > + > +/** > + * struct mtk_dip_pipe_desc - dip pipe descriptor > + * @name: name of the pipe, which will be used as a part of the video > + * device and sub device name > + * @id: the id of the pipe > + * @queue_descs: the setting of the queues belong to this pipe > + * @total_queues: the number of queue/video nodes supported by this pipe > + * > + * The structure describes the pipe of DIP. A pipe may contains a raw output > + * video device and at least one MDP capture device. > + */ > +struct mtk_dip_pipe_desc { > + const char *name; > + int id; > + const struct mtk_dip_video_device_desc *queue_descs; > + int total_queues; > +}; > + > +/** > + * struct mtk_dip_video_device_desc - video device descriptor > + * @name: name of the video device > + * @id: the id of the video device > + * @buf_type: buffer type of the video device > + * @cap: device capabilities > + * @smem_alloc: use the co-processor and CPU shared memory allocator or not > + * @supports_ctrls: support ctrl handler or not. If it is true, The DIP driver > + * initialized the ctrl handler for this video node. > + * @fmts: buffer format supported by this video device > + * @num_fmts: total number of buffer format supported by this video device > + * @description: description of the video device. It will be returned when > + * V4L2 enum_fmt calls > + * @dma_port: dma port id associated to this video device > + * @frmsizeenum: frame size supported > + * @ops: v4l2_ioctl_ops pointer used by this video device > + * @vb2_ops: vb2_ops pointer used by this video device > + * @flags: flags used in media_create_intf_link() > + * @default_fmt_idx: indeciate the default format with index of @fmts > + * > + * The structure describes the video device setting of DIP, which are used to > + * register the video devices and support the related V4L2 and VB2 operations. > + */ > +struct mtk_dip_video_device_desc { > + const char *name; > + int id; > + u32 buf_type; > + u32 cap; > + int smem_alloc; > + int supports_ctrls; > + const struct mtk_dip_dev_format *fmts; > + int num_fmts; > + const char *description; > + int default_width; > + int default_height; > + unsigned int dma_port; > + const struct v4l2_frmsizeenum *frmsizeenum; > + const struct v4l2_ioctl_ops *ops; > + const struct vb2_ops *vb2_ops; > + u32 flags; > + int default_fmt_idx; > +}; > + > +/** > + * struct mtk_dip_dev_queue - dip's video device queue > + * @vbq: vb2_queue of the dip's video device > + * @lock: serializes vb2 queue and video device operations. > + * @dev_fmt: buffer format of the video device > + * > + * The structure supports a vb2_queue of dip's video device with the DIP's > + * internal buffer format. > + */ > +struct mtk_dip_dev_queue { > + struct vb2_queue vbq; > + /* Serializes vb2 queue and video device operations */ > + struct mutex lock; > + const struct mtk_dip_dev_format *dev_fmt; > +}; > + > +/** > + * struct mtk_dip_video_device - DIP's video device > + * @vdev: video_device of the dip's video device > + * @dev_q: the mtk_dip_dev_queue providing vb2 device queue for the > + * video device > + * @vdev_fmt: the current v4l2 format of the video device > + * @vdev_pad: the pad connected to the dip sub device of the pipe > + * @pad_fmt: the pad format of vdev_pad > + * @ctrl_handler: the v4l2_ctrl_handler of the video device. Only the video > + * device supporting rotation initialized the handler. > + * @flags: the flags recording the link status between the video device > + * and the sub device of the pipe > + * @desc: setting of the video device. The driver initialize the video > + * device according to the settings. > + * @buf_list: the list of vb2 buffers enqueued through this video device > + * @buf_list_lock: protect the in-device buffer list > + * @crop: crop setting of the video device > + * @compose: compose setting the video device > + * @rotation: rotation setting of the video device > + * > + * The structure extends video_device and integrates the vb2 queue, a media_pad > + * connected to DIP's sub device, and a v4l2_ctrl_handler to handling ctrl. > + */ > +struct mtk_dip_video_device { > + struct video_device vdev; > + struct mtk_dip_dev_queue dev_q; > + struct v4l2_format vdev_fmt; > + struct media_pad vdev_pad; > + struct v4l2_mbus_framefmt pad_fmt; > + struct v4l2_ctrl_handler ctrl_handler; > + u32 flags; > + const struct mtk_dip_video_device_desc *desc; > + struct list_head buf_list; > + /* the list of vb2 buffers enqueued through this video device */ > + spinlock_t buf_list_lock; > + struct v4l2_rect crop; > + struct v4l2_rect compose; > + int rotation; > +}; > + > +/** > + * struct mtk_dip_pipe - DIP's pipe > + * @dip_dev: the dip driver device instance > + * @mtk_dip_video_device: the video devices of the pipe. The entry must be NULL > + * if there is no video devices for the ID > + * @nodes_streaming: bitmask records the video devices which are streaming > + * @nodes_enabled: bitmask records the video devices which are enabled > + * @streaming: true if the pipe is streaming > + * @subdev: sub device connected to the output and capture video > + * device named as the pipe's name > + * @pipe_job_sequence: the last sequence number of the pipe jobs > + * @pipe_job_pending_list: the list saving jobs before it has been put into > + * running state by mtk_dip_pipe_try_enqueue(). > + * @num_pending_jobs: number of jobs in pipe_job_pending_list > + * @pipe_job_running_list: the list saving jobs already scheduled into DIP > + * @num_jobs: number of jobs in pipe_job_running_list > + * @lock: serializes pipe's stream on/off and buffers enqueue > + * operations > + * @job_lock: protect the pipe job list > + * @desc: the settings of the pipe > + * > + * The structure represents a DIP pipe. A pipe may contains a raw output > + * video device and at least one MDP capture device. > + */ > +struct mtk_dip_pipe { > + struct mtk_dip_dev *dip_dev; > + struct mtk_dip_video_device nodes[MTK_DIP_VIDEO_NODE_ID_TOTAL_NUM]; > + unsigned int nodes_streaming; > + unsigned int nodes_enabled; > + int streaming; > + struct media_pad *subdev_pads; > + struct media_pipeline pipeline; > + struct v4l2_subdev subdev; > + atomic_t pipe_job_sequence; > + struct list_head pipe_job_pending_list; > + int num_pending_jobs; > + struct list_head pipe_job_running_list; > + int num_jobs; > + /*serializes pipe's stream on/off and buffers enqueue operations*/ > + struct mutex lock; > + spinlock_t job_lock; /* protect the pipe job list */ > + const struct mtk_dip_pipe_desc *desc; > +}; > + > +/** > + * struct mtk_dip_dev - DIP's device instance > + * @dev: the device structure of DIP > + * @mdev: media device of DIP. All video devices and sub devices of > + * DIP are registered to the media device. > + * @v4l2_dev: v4l2_device representing the device-level status of DIP > + * @dip_pipe: pipes of the DIP device. For example, capture, preview and > + * reprocessing pipes. > + * @clks: clocks required by DIP hardware > + * @num_clks: the total number of clocks of DIP hardware > + * @composer_wq: The work queue for jobs which are going to be sent to > + * coprocessor. > + * @num_composing: number of jobs in SCP > + * @mdp_wq: the work queue for jobs which are going to be sent to MDP > + * driver and GCE hardware. > + * > + * @flushing_waitq: the waiting queue to keep the process which are > + * waiting for the jobs in SCP to be finished. > + * @mdpcb_wq: the work queue for jobs with abnormal status back from MDP/GCE > + * , it need to pass to SCP to check the error status instead of > + * returning the buffer to user directly. > + * @mdp_pdev: mdp platform device which handling the MDP part jobs and > + * pass the task to GCE hardware. > + * @scp_pdev: SCP platform device which handling the commands to and from > + * coprocessor > + * @rproc_handle: remote processor handle to control SCP > + * @dip_freebufferlist: free working buffer list > + * @working_buf_mem_scp_daddr: the SCP caddress of the memory area of working > + * buffers > + * @working_buf_mem_vaddr: the cpu address of the memory area of working > + * buffers > + * @working_buf_mem_isp_daddr: the isp dma address of the memory area of > + * working buffers > + * @working_buf_mem_size: total size in bytes of the memory area of > + * working buffers > + * @working_buf: the working buffers of DIP > + * > + * @dip_enqueue_cnt: it is used to create the sequence number of the job > + * which is already enqueued to DIP. > + * @dip_stream_cnt: the number of streaming pipe in DIP > + * @hw_op_lock: serialize request operation to DIP coprocessor and > + * hardware > + * @sem: To restrict the max number of request be send to SCP. > + * > + * The structure maintains DIP's device level status. > + */ > +struct mtk_dip_dev { > + struct device *dev; > + struct media_device mdev; > + struct v4l2_device v4l2_dev; > + struct mtk_dip_pipe dip_pipe[MTK_DIP_PIPE_ID_TOTAL_NUM]; > + struct clk_bulk_data clks[MTK_DIP_CLK_NUM]; > + int num_clks; > + struct workqueue_struct *composer_wq; > + struct workqueue_struct *mdp_wq; > + wait_queue_head_t flushing_waitq; > + atomic_t num_composing; > + struct workqueue_struct *mdpcb_wq; > + struct platform_device *mdp_pdev; > + struct platform_device *scp_pdev; > + struct rproc *rproc_handle; > + struct mtk_dip_hw_working_buf_list dip_freebufferlist; > + dma_addr_t working_buf_mem_scp_daddr; > + void *working_buf_mem_vaddr; > + dma_addr_t working_buf_mem_isp_daddr; > + int working_buf_mem_size; > + struct mtk_dip_hw_subframe working_buf[DIP_SUB_FRM_DATA_NUM]; > + atomic_t dip_enqueue_cnt; > + int dip_stream_cnt; > + /* To serialize request opertion to DIP co-procrosser and hadrware */ > + struct mutex hw_op_lock; > + /* To restrict the max number of request be send to SCP */ > + struct semaphore sem; > +}; > + > +/** > + * struct mtk_dip_request - DIP's request > + * @req: the media_request object of the request > + * @dip_pipe: the pipe owning of the request; a request can only belongs one > + * DIP pipe > + * @id: the unique job id in DIP > + * @buf_map: the buffers of the request. The entry should be NULL if the > + * corresponding video device doesn't enqueue the buffer. > + * @img_fparam: frame related parameters which will be passed to coprocessor > + * @fw_work: work_struct used to be sent to composer_wq of mtk_dip_dev > + * @mdp_work: work_struct used to be sent to mdp_wq of mtk_dip_dev > + * @mdpcb_work: work_struct used to be sent to mdpcb_wq of mtk_dip_dev. > + * It is used only in error handling flow. > + * @working_buf: working buffer of the request > + * @buf_count: the number of buffers in the request > + * > + * The structure extends media_request and integrates a map of the buffers, > + * and the working buffer pointers. It is the job instance used in DIP's > + * drivers. > + */ > +struct mtk_dip_request { > + struct media_request req; > + struct mtk_dip_pipe *dip_pipe; > + int id; > + struct mtk_dip_dev_buffer *buf_map[MTK_DIP_VIDEO_NODE_ID_TOTAL_NUM]; > + struct img_frameparam img_fparam; > + struct work_struct fw_work; > + struct work_struct mdp_work; > + struct work_struct mdpcb_work; > + struct mtk_dip_hw_subframe *working_buf; > + atomic_t buf_count; > + struct list_head list; > +}; > + > +int mtk_dip_dev_media_register(struct device *dev, > + struct media_device *media_dev); > + > +void mtk_dip_dev_v4l2_release(struct mtk_dip_dev *dip_dev); > + > +int mtk_dip_dev_v4l2_register(struct device *dev, > + struct media_device *media_dev, > + struct v4l2_device *v4l2_dev); > + > +int mtk_dip_pipe_v4l2_register(struct mtk_dip_pipe *pipe, > + struct media_device *media_dev, > + struct v4l2_device *v4l2_dev); > + > +void mtk_dip_pipe_v4l2_unregister(struct mtk_dip_pipe *pipe); > + > +int mtk_dip_pipe_queue_buffers(struct media_request *req, int initial); > + > +int mtk_dip_pipe_init(struct mtk_dip_dev *dip_dev, struct mtk_dip_pipe *pipe, > + const struct mtk_dip_pipe_desc *setting); > + > +void mtk_dip_pipe_ipi_params_config(struct mtk_dip_request *req); > + > +int mtk_dip_pipe_release(struct mtk_dip_pipe *pipe); > + > +struct mtk_dip_request * > +mtk_dip_pipe_get_running_job(struct mtk_dip_pipe *pipe, > + int id); > + > +void mtk_dip_pipe_remove_job(struct mtk_dip_request *req); > + > +int mtk_dip_pipe_next_job_id(struct mtk_dip_pipe *pipe); > + > +void mtk_dip_pipe_debug_job(struct mtk_dip_pipe *pipe, > + struct mtk_dip_request *req); > + > +void mtk_dip_pipe_job_finish(struct mtk_dip_request *req, > + enum vb2_buffer_state vbf_state); > + > +const struct mtk_dip_dev_format * > +mtk_dip_pipe_find_fmt(struct mtk_dip_pipe *pipe, > + struct mtk_dip_video_device *node, > + u32 format); > + > +void mtk_dip_pipe_try_fmt(struct mtk_dip_pipe *pipe, > + struct mtk_dip_video_device *node, > + struct v4l2_format *fmt, > + const struct v4l2_format *ufmt, > + const struct mtk_dip_dev_format *dfmt); > + > +void mtk_dip_pipe_load_default_fmt(struct mtk_dip_pipe *pipe, > + struct mtk_dip_video_device *node, > + struct v4l2_format *fmt_to_fill); > + > +void mtk_dip_pipe_try_enqueue(struct mtk_dip_pipe *pipe); > + > +void mtk_dip_hw_enqueue(struct mtk_dip_dev *dip_dev, > + struct mtk_dip_request *req); > + > +int mtk_dip_hw_streamoff(struct mtk_dip_pipe *pipe); > + > +int mtk_dip_hw_streamon(struct mtk_dip_pipe *pipe); > + > +int mtk_dip_hw_working_buf_pool_init(struct mtk_dip_dev *dip_dev); > + > +void mtk_dip_hw_working_buf_pool_release(struct mtk_dip_dev *dip_dev); > + > +static inline struct mtk_dip_pipe* > +mtk_dip_dev_get_pipe(struct mtk_dip_dev *dip_dev, unsigned int pipe_id) > +{ > + if (pipe_id < 0 && pipe_id >= MTK_DIP_PIPE_ID_TOTAL_NUM) > + return NULL; > + > + return &dip_dev->dip_pipe[pipe_id]; > +} > + > +static inline struct mtk_dip_video_device* > +mtk_dip_file_to_node(struct file *file) > +{ > + return container_of(video_devdata(file), > + struct mtk_dip_video_device, vdev); > +} > + > +static inline struct mtk_dip_pipe* > +mtk_dip_subdev_to_pipe(struct v4l2_subdev *sd) > +{ > + return container_of(sd, struct mtk_dip_pipe, subdev); > +} > + > +static inline struct mtk_dip_dev* > +mtk_dip_mdev_to_dev(struct media_device *mdev) > +{ > + return container_of(mdev, struct mtk_dip_dev, mdev); > +} > + > +static inline struct mtk_dip_video_device* > +mtk_dip_vbq_to_node(struct vb2_queue *vq) > +{ > + return container_of(vq, struct mtk_dip_video_device, dev_q.vbq); > +} > + > +static inline struct mtk_dip_dev_buffer* > +mtk_dip_vb2_buf_to_dev_buf(struct vb2_buffer *vb) > +{ > + return container_of(vb, struct mtk_dip_dev_buffer, vbb.vb2_buf); > +} > + > +static inline struct mtk_dip_request* > +mtk_dip_media_req_to_dip_req(struct media_request *req) > +{ > + return container_of(req, struct mtk_dip_request, req); > +} > + > +static inline struct mtk_dip_request* > +mtk_dip_hw_fw_work_to_req(struct work_struct *fw_work) > +{ > + return container_of(fw_work, struct mtk_dip_request, fw_work); > +} > + > +static inline struct mtk_dip_request* > +mtk_dip_hw_mdp_work_to_req(struct work_struct *mdp_work) > +{ > + return container_of(mdp_work, struct mtk_dip_request, mdp_work); > +} > + > +static inline struct mtk_dip_request * > +mtk_dip_hw_mdpcb_work_to_req(struct work_struct *mdpcb_work) > +{ > + return container_of(mdpcb_work, struct mtk_dip_request, mdpcb_work); > +} > + > +static inline int mtk_dip_buf_is_meta(u32 type) > +{ > + return type == V4L2_BUF_TYPE_META_CAPTURE || > + type == V4L2_BUF_TYPE_META_OUTPUT; > +} > + > +static inline int mtk_dip_pipe_get_pipe_from_job_id(int id) > +{ > + return (id >> 16) & 0x0000ffff; > +} > + > +static inline void > +mtk_dip_wbuf_to_ipi_img_sw_addr(struct img_sw_addr *ipi_addr, > + struct mtk_dip_hw_working_buf *wbuf) > +{ > + ipi_addr->pa = (u32)wbuf->scp_daddr; > +} > + > +static inline void > +mtk_dip_wbuf_to_ipi_img_addr(struct img_addr *ipi_addr, > + struct mtk_dip_hw_working_buf *wbuf) > +{ > + ipi_addr->pa = (u32)wbuf->scp_daddr; > + ipi_addr->iova = (u32)wbuf->isp_daddr; > +} > + > +static inline void > +mtk_dip_wbuf_to_ipi_tuning_addr(struct tuning_addr *ipi_addr, > + struct mtk_dip_hw_working_buf *wbuf) > +{ > + ipi_addr->pa = (u32)wbuf->scp_daddr; > + ipi_addr->iova = (u32)wbuf->isp_daddr; > +} > + > +#endif /* _MTK_DIP_DEV_H_ */ > diff --git a/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-hw.h b/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-hw.h > new file mode 100644 > index 000000000000..9a414fd91094 > --- /dev/null > +++ b/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-hw.h > @@ -0,0 +1,156 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Copyright (c) 2018 MediaTek Inc. > + * > + * Author: Frederic Chen <frederic.chen@xxxxxxxxxxxx> > + * Holmes Chiou <holmes.chiou@xxxxxxxxxxxx> > + * > + */ > + > +#ifndef _MTK_DIP_HW_H_ > +#define _MTK_DIP_HW_H_ > + > +#include <linux/clk.h> > +#include "mtk-img-ipi.h" > + > +#define MTK_DIP_CLK_NUM 2 > + > +#define DIP_COMPOSING_MAX_NUM 3 > +#define DIP_FRM_SZ (84 * 1024) > +#define DIP_SUB_FRM_SZ (20 * 1024) > +#define DIP_TUNING_SZ (32 * 1024) > +#define DIP_COMP_SZ (28 * 1024) > +#define DIP_FRAMEPARAM_SZ (4 * 1024) > + > +#define DIP_TUNING_OFFSET DIP_SUB_FRM_SZ > +#define DIP_COMP_OFFSET (DIP_TUNING_OFFSET + DIP_TUNING_SZ) > +#define DIP_FRAMEPARAM_OFFSET (DIP_COMP_OFFSET + DIP_COMP_SZ) > +#define DIP_SUB_FRM_DATA_NUM 32 > + > +/* > + * 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 DIP_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 DIP_MCOLOR_IS_BLOCK_MODE(c) ((0x00800000 & (c)) >> 23) > +#define DIP_MCOLOR_GET_PLANE_COUNT(c) ((0x00600000 & (c)) >> 21) > +#define DIP_MCOLOR_GET_H_SUBSAMPLE(c) ((0x00180000 & (c)) >> 19) > +#define DIP_MCOLOR_GET_V_SUBSAMPLE(c) ((0x00040000 & (c)) >> 18) > +#define DIP_MCOLOR_BITS_PER_PIXEL(c) ((0x0003ff00 & (c)) >> 8) > +#define DIP_MCOLOR_GET_GROUP(c) ((0x000000c0 & (c)) >> 6) > +#define DIP_MCOLOR_IS_RGB(c) (DIP_MCOLOR_GET_GROUP(c) == 0) > +#define DIP_MCOLOR_IS_YUV(c) (DIP_MCOLOR_GET_GROUP(c) == 1) > +#define DIP_MCOLOR_IS_UV_COPLANE(c) ((DIP_MCOLOR_GET_PLANE_COUNT(c) == \ > + 2) && \ > + DIP_MCOLOR_IS_YUV(c)) > + > +enum DIP_MDP_COLOR { > + DIP_MCOLOR_UNKNOWN = 0, > + > + DIP_MCOLOR_FULLG8_RGGB = DIP_MDP_COLOR(0, 0, 0, 1, 0, 0, 8, 2, 0, 21), > + DIP_MCOLOR_FULLG8_GRBG = DIP_MDP_COLOR(0, 0, 0, 1, 0, 1, 8, 2, 0, 21), > + DIP_MCOLOR_FULLG8_GBRG = DIP_MDP_COLOR(0, 0, 0, 1, 1, 0, 8, 2, 0, 21), > + DIP_MCOLOR_FULLG8_BGGR = DIP_MDP_COLOR(0, 0, 0, 1, 1, 1, 8, 2, 0, 21), > + DIP_MCOLOR_FULLG8 = DIP_MCOLOR_FULLG8_BGGR, > + > + DIP_MCOLOR_FULLG10_RGGB = DIP_MDP_COLOR(0, 0, 0, 1, 0, 0, 10, 2, 0, 21), > + DIP_MCOLOR_FULLG10_GRBG = DIP_MDP_COLOR(0, 0, 0, 1, 0, 1, 10, 2, 0, 21), > + DIP_MCOLOR_FULLG10_GBRG = DIP_MDP_COLOR(0, 0, 0, 1, 1, 0, 10, 2, 0, 21), > + DIP_MCOLOR_FULLG10_BGGR = DIP_MDP_COLOR(0, 0, 0, 1, 1, 1, 10, 2, 0, 21), > + DIP_MCOLOR_FULLG10 = DIP_MCOLOR_FULLG10_BGGR, > + > + DIP_MCOLOR_FULLG12_RGGB = DIP_MDP_COLOR(0, 0, 0, 1, 0, 0, 12, 2, 0, 21), > + DIP_MCOLOR_FULLG12_GRBG = DIP_MDP_COLOR(0, 0, 0, 1, 0, 1, 12, 2, 0, 21), > + DIP_MCOLOR_FULLG12_GBRG = DIP_MDP_COLOR(0, 0, 0, 1, 1, 0, 12, 2, 0, 21), > + DIP_MCOLOR_FULLG12_BGGR = DIP_MDP_COLOR(0, 0, 0, 1, 1, 1, 12, 2, 0, 21), > + DIP_MCOLOR_FULLG12 = DIP_MCOLOR_FULLG12_BGGR, > + > + DIP_MCOLOR_FULLG14_RGGB = DIP_MDP_COLOR(0, 0, 0, 1, 0, 0, 14, 2, 0, 21), > + DIP_MCOLOR_FULLG14_GRBG = DIP_MDP_COLOR(0, 0, 0, 1, 0, 1, 14, 2, 0, 21), > + DIP_MCOLOR_FULLG14_GBRG = DIP_MDP_COLOR(0, 0, 0, 1, 1, 0, 14, 2, 0, 21), > + DIP_MCOLOR_FULLG14_BGGR = DIP_MDP_COLOR(0, 0, 0, 1, 1, 1, 14, 2, 0, 21), > + DIP_MCOLOR_FULLG14 = DIP_MCOLOR_FULLG14_BGGR, > + > + DIP_MCOLOR_BAYER8_RGGB = DIP_MDP_COLOR(0, 0, 0, 1, 0, 0, 8, 2, 0, 20), > + DIP_MCOLOR_BAYER8_GRBG = DIP_MDP_COLOR(0, 0, 0, 1, 0, 1, 8, 2, 0, 20), > + DIP_MCOLOR_BAYER8_GBRG = DIP_MDP_COLOR(0, 0, 0, 1, 1, 0, 8, 2, 0, 20), > + DIP_MCOLOR_BAYER8_BGGR = DIP_MDP_COLOR(0, 0, 0, 1, 1, 1, 8, 2, 0, 20), > + DIP_MCOLOR_BAYER8 = DIP_MCOLOR_BAYER8_BGGR, > + > + DIP_MCOLOR_BAYER10_RGGB = DIP_MDP_COLOR(0, 0, 0, 1, 0, 0, 10, 2, 0, 20), > + DIP_MCOLOR_BAYER10_GRBG = DIP_MDP_COLOR(0, 0, 0, 1, 0, 1, 10, 2, 0, 20), > + DIP_MCOLOR_BAYER10_GBRG = DIP_MDP_COLOR(0, 0, 0, 1, 1, 0, 10, 2, 0, 20), > + DIP_MCOLOR_BAYER10_BGGR = DIP_MDP_COLOR(0, 0, 0, 1, 1, 1, 10, 2, 0, 20), > + DIP_MCOLOR_BAYER10 = DIP_MCOLOR_BAYER10_BGGR, > + > + DIP_MCOLOR_BAYER12_RGGB = DIP_MDP_COLOR(0, 0, 0, 1, 0, 0, 12, 2, 0, 20), > + DIP_MCOLOR_BAYER12_GRBG = DIP_MDP_COLOR(0, 0, 0, 1, 0, 1, 12, 2, 0, 20), > + DIP_MCOLOR_BAYER12_GBRG = DIP_MDP_COLOR(0, 0, 0, 1, 1, 0, 12, 2, 0, 20), > + DIP_MCOLOR_BAYER12_BGGR = DIP_MDP_COLOR(0, 0, 0, 1, 1, 1, 12, 2, 0, 20), > + DIP_MCOLOR_BAYER12 = DIP_MCOLOR_BAYER12_BGGR, > + > + DIP_MCOLOR_BAYER14_RGGB = DIP_MDP_COLOR(0, 0, 0, 1, 0, 0, 14, 2, 0, 20), > + DIP_MCOLOR_BAYER14_GRBG = DIP_MDP_COLOR(0, 0, 0, 1, 0, 1, 14, 2, 0, 20), > + DIP_MCOLOR_BAYER14_GBRG = DIP_MDP_COLOR(0, 0, 0, 1, 1, 0, 14, 2, 0, 20), > + DIP_MCOLOR_BAYER14_BGGR = DIP_MDP_COLOR(0, 0, 0, 1, 1, 1, 14, 2, 0, 20), > + DIP_MCOLOR_BAYER14 = DIP_MCOLOR_BAYER14_BGGR, > + > + DIP_MCOLOR_UYVY = DIP_MDP_COLOR(0, 0, 0, 1, 1, 0, 16, 1, 0, 4), > + DIP_MCOLOR_VYUY = DIP_MDP_COLOR(0, 0, 0, 1, 1, 0, 16, 1, 1, 4), > + DIP_MCOLOR_YUYV = DIP_MDP_COLOR(0, 0, 0, 1, 1, 0, 16, 1, 0, 5), > + DIP_MCOLOR_YVYU = DIP_MDP_COLOR(0, 0, 0, 1, 1, 0, 16, 1, 1, 5), > + > + DIP_MCOLOR_I420 = DIP_MDP_COLOR(0, 0, 0, 3, 1, 1, 8, 1, 0, 8), > + DIP_MCOLOR_YV12 = DIP_MDP_COLOR(0, 0, 0, 3, 1, 1, 8, 1, 1, 8), > + > + DIP_MCOLOR_NV12 = DIP_MDP_COLOR(0, 0, 0, 2, 1, 1, 8, 1, 0, 12), > +}; > + > +#define FRAME_STATE_INIT 0 > +#define FRAME_STATE_HW_TIMEOUT 1 > + > +#define STREAM_UNKNOWN 0 > +#define STREAM_BITBLT 1 > +#define STREAM_GPU_BITBLT 2 > +#define STREAM_DUAL_BITBLT 3 > +#define STREAM_2ND_BITBLT 4 > +#define STREAM_ISP_IC 5 > +#define STREAM_ISP_VR 6 > +#define STREAM_ISP_ZSD 7 > +#define STREAM_ISP_IP 8 > +#define STREAM_ISP_VSS 9 > +#define STREAM_ISP_ZSD_SLOW 10 > +#define STREAM_WPE 11 > +#define STREAM_WPE2 12 > + > +struct mtk_dip_hw_working_buf { > + dma_addr_t scp_daddr; > + void *vaddr; > + dma_addr_t isp_daddr; > +}; > + > +struct mtk_dip_hw_subframe { > + struct mtk_dip_hw_working_buf buffer; > + int size; > + struct mtk_dip_hw_working_buf config_data; > + struct mtk_dip_hw_working_buf tuning_buf; > + struct mtk_dip_hw_working_buf frameparam; > + struct list_head list_entry; > +}; > + > +struct mtk_dip_hw_working_buf_list { > + struct list_head list; > + u32 cnt; > + spinlock_t lock; /* protect the list and cnt */ > +}; > + > +#endif /* _MTK_DIP_HW_H_ */ > + > diff --git a/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-sys.c b/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-sys.c > new file mode 100644 > index 000000000000..9a0456342fcd > --- /dev/null > +++ b/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-sys.c > @@ -0,0 +1,521 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (c) 2018 MediaTek Inc. > + * > + * Author: Frederic Chen <frederic.chen@xxxxxxxxxxxx> > + * Holmes Chiou <holmes.chiou@xxxxxxxxxxxx> > + * > + */ > + > +#include <linux/device.h> > +#include <linux/dma-iommu.h> > +#include <linux/freezer.h> > +#include <linux/pm_runtime.h> > +#include <linux/remoteproc.h> > +#include <linux/remoteproc/mtk_scp.h> > +#include <linux/slab.h> > +#include <linux/spinlock.h> > +#include <linux/wait.h> > +#include "mtk-mdp3-cmdq.h" > +#include "mtk_dip-dev.h" > +#include "mtk_dip-hw.h" > + > +int mtk_dip_hw_working_buf_pool_init(struct mtk_dip_dev *dip_dev) > +{ > + int i; > + const int working_buf_size = round_up(DIP_FRM_SZ, PAGE_SIZE); > + phys_addr_t working_buf_paddr; > + > + INIT_LIST_HEAD(&dip_dev->dip_freebufferlist.list); > + spin_lock_init(&dip_dev->dip_freebufferlist.lock); > + dip_dev->dip_freebufferlist.cnt = 0; > + > + dip_dev->working_buf_mem_size = DIP_SUB_FRM_DATA_NUM * > + working_buf_size; > + dip_dev->working_buf_mem_vaddr = > + dma_alloc_coherent(&dip_dev->scp_pdev->dev, > + dip_dev->working_buf_mem_size, > + &dip_dev->working_buf_mem_scp_daddr, > + GFP_KERNEL); > + if (!dip_dev->working_buf_mem_vaddr) { > + dev_err(dip_dev->dev, > + "memory alloc size %ld failed\n", > + dip_dev->working_buf_mem_size); > + return -ENOMEM; > + } > + > + /* > + * We got the incorrect physical address mapped when > + * using dma_map_single() so I used dma_map_page_attrs() > + * directly to workaround here. > + * > + * When I use dma_map_single() to map the address, the > + * physical address retrieved back with iommu_get_domain_for_dev() > + * and iommu_iova_to_phys() was not equal to the > + * SCP dma address (it should be the same as the physical address > + * since we don't have iommu), and was shifted by 0x4000000. > + */ > + working_buf_paddr = dip_dev->working_buf_mem_scp_daddr; > + > + dip_dev->working_buf_mem_isp_daddr = > + dma_map_resource(dip_dev->dev, working_buf_paddr, > + dip_dev->working_buf_mem_size, > + DMA_BIDIRECTIONAL, DMA_ATTR_SKIP_CPU_SYNC); > + if (dma_mapping_error(dip_dev->dev, > + dip_dev->working_buf_mem_isp_daddr)) { > + dev_err(dip_dev->dev, > + "failed to map buffer: s_daddr(%pad)\n", > + &dip_dev->working_buf_mem_scp_daddr); > + dma_free_coherent(&dip_dev->scp_pdev->dev, > + dip_dev->working_buf_mem_size, > + dip_dev->working_buf_mem_vaddr, > + dip_dev->working_buf_mem_scp_daddr); > + > + return -ENOMEM; > + } > + > + for (i = 0; i < DIP_SUB_FRM_DATA_NUM; i++) { > + struct mtk_dip_hw_subframe *buf = &dip_dev->working_buf[i]; > + int offset; > + > + /* > + * Total: 0 ~ 72 KB > + * SubFrame: 0 ~ 16 KB > + */ > + offset = i * working_buf_size; > + buf->buffer.scp_daddr = > + dip_dev->working_buf_mem_scp_daddr + offset; > + buf->buffer.vaddr = > + dip_dev->working_buf_mem_vaddr + offset; > + buf->buffer.isp_daddr = > + dip_dev->working_buf_mem_isp_daddr + offset; > + buf->size = working_buf_size; > + > + /* Tuning: 16 ~ 48 KB */ > + buf->tuning_buf.scp_daddr = > + buf->buffer.scp_daddr + DIP_TUNING_OFFSET; > + buf->tuning_buf.vaddr = > + buf->buffer.vaddr + DIP_TUNING_OFFSET; > + buf->tuning_buf.isp_daddr = > + buf->buffer.isp_daddr + DIP_TUNING_OFFSET; > + > + /* Config_data: 48 ~ 72 KB */ > + buf->config_data.scp_daddr = > + buf->buffer.scp_daddr + DIP_COMP_OFFSET; > + buf->config_data.vaddr = buf->buffer.vaddr + DIP_COMP_OFFSET; > + > + /* Frame parameters: 72 ~ 76 KB */ > + buf->frameparam.scp_daddr = > + buf->buffer.scp_daddr + DIP_FRAMEPARAM_OFFSET; > + buf->frameparam.vaddr = > + buf->buffer.vaddr + DIP_FRAMEPARAM_OFFSET; > + > + list_add_tail(&buf->list_entry, > + &dip_dev->dip_freebufferlist.list); > + dip_dev->dip_freebufferlist.cnt++; > + } > + > + return 0; > +} > + > +void mtk_dip_hw_working_buf_pool_release(struct mtk_dip_dev *dip_dev) > +{ > + /* All the buffer should be in the freebufferlist when release */ > + dma_unmap_resource(dip_dev->dev, > + dip_dev->working_buf_mem_isp_daddr, > + dip_dev->working_buf_mem_size, DMA_BIDIRECTIONAL, > + DMA_ATTR_SKIP_CPU_SYNC); > + > + dma_free_coherent(&dip_dev->scp_pdev->dev, > + dip_dev->working_buf_mem_size, > + dip_dev->working_buf_mem_vaddr, > + dip_dev->working_buf_mem_scp_daddr); > +} > + > +static void mtk_dip_hw_working_buf_free(struct mtk_dip_dev *dip_dev, > + struct mtk_dip_hw_subframe *working_buf) > +{ > + if (!working_buf) > + return; > + > + spin_lock(&dip_dev->dip_freebufferlist.lock); > + list_add_tail(&working_buf->list_entry, > + &dip_dev->dip_freebufferlist.list); > + dip_dev->dip_freebufferlist.cnt++; > + spin_unlock(&dip_dev->dip_freebufferlist.lock); > +} > + > +static struct mtk_dip_hw_subframe* > +mtk_dip_hw_working_buf_alloc(struct mtk_dip_dev *dip_dev) > +{ > + struct mtk_dip_hw_subframe *working_buf; > + > + spin_lock(&dip_dev->dip_freebufferlist.lock); > + if (list_empty(&dip_dev->dip_freebufferlist.list)) { > + spin_unlock(&dip_dev->dip_freebufferlist.lock); > + return NULL; > + } > + > + working_buf = list_first_entry(&dip_dev->dip_freebufferlist.list, > + struct mtk_dip_hw_subframe, list_entry); > + list_del(&working_buf->list_entry); > + dip_dev->dip_freebufferlist.cnt--; > + spin_unlock(&dip_dev->dip_freebufferlist.lock); > + > + return working_buf; > +} > + > +static void mtk_dip_notify(struct mtk_dip_request *req) > +{ > + struct mtk_dip_dev *dip_dev = req->dip_pipe->dip_dev; > + struct mtk_dip_pipe *pipe = req->dip_pipe; > + struct img_ipi_frameparam *iparam = &req->img_fparam.frameparam; > + enum vb2_buffer_state vbf_state; > + > + if (iparam->state != FRAME_STATE_HW_TIMEOUT) > + vbf_state = VB2_BUF_STATE_DONE; > + else > + vbf_state = VB2_BUF_STATE_ERROR; > + > + pm_runtime_mark_last_busy(dip_dev->dev); > + pm_runtime_put_autosuspend(dip_dev->dev); > + > + /* > + * The job may be aleady removed by streamoff, so I need to check > + * it by id here. > + */ > + if (mtk_dip_pipe_get_running_job(pipe, req->id)) { > + mtk_dip_pipe_remove_job(req); > + mtk_dip_pipe_job_finish(req, vbf_state); > + mtk_dip_hw_working_buf_free(dip_dev, req->working_buf); > + req->working_buf = NULL; > + wake_up(&dip_dev->flushing_waitq); > + } > +} > + > +static void mdp_cb_timeout_worker(struct work_struct *work) > +{ > + struct mtk_dip_request *req = mtk_dip_hw_mdpcb_work_to_req(work); > + struct img_ipi_param ipi_param; > + > + ipi_param.usage = IMG_IPI_DEBUG; > + scp_ipi_send(req->dip_pipe->dip_dev->scp_pdev, SCP_IPI_DIP, > + &ipi_param, sizeof(ipi_param), 0); > + mtk_dip_notify(req); > +} > + > +/* Maybe in IRQ context of cmdq */ > +static void dip_mdp_cb_func(struct cmdq_cb_data data) > +{ > + struct mtk_dip_request *req; > + struct mtk_dip_dev *dip_dev; > + > + if (!data.data) { > + pr_err("%s: data->data is NULL\n", > + __func__); > + return; > + } > + > + req = data.data; > + dip_dev = req->dip_pipe->dip_dev; > + > + dev_dbg(dip_dev->dev, "%s: req(%p), idx(%d), no(%d), s(%d), n_in(%d), n_out(%d)\n", > + __func__, req, req->img_fparam.frameparam.index, > + req->img_fparam.frameparam.frame_no, > + req->img_fparam.frameparam.state, > + req->img_fparam.frameparam.num_inputs, > + req->img_fparam.frameparam.num_outputs); > + > + if (data.sta != CMDQ_CB_NORMAL) { > + dev_err(dip_dev->dev, "%s: frame no(%d) HW timeout\n", > + __func__, req->img_fparam.frameparam.frame_no); > + req->img_fparam.frameparam.state = FRAME_STATE_HW_TIMEOUT; > + INIT_WORK(&req->mdpcb_work, mdp_cb_timeout_worker); > + queue_work(req->dip_pipe->dip_dev->mdpcb_wq, > + &req->mdpcb_work); > + } else { > + mtk_dip_notify(req); > + } > +} > + > +static void dip_runner_func(struct work_struct *work) > +{ > + struct mtk_dip_request *req = mtk_dip_hw_mdp_work_to_req(work); > + struct mtk_dip_dev *dip_dev = req->dip_pipe->dip_dev; > + struct img_config *config_data = > + (struct img_config *)req->working_buf->config_data.vaddr; > + > + /* > + * Call MDP/GCE API to do HW excecution > + * Pass the framejob to MDP driver > + */ > + pm_runtime_get_sync(dip_dev->dev); > + mdp_cmdq_sendtask(dip_dev->mdp_pdev, config_data, > + &req->img_fparam.frameparam, NULL, false, > + dip_mdp_cb_func, req); > +} > + > +static void dip_scp_handler(void *data, unsigned int len, void *priv) > +{ > + int job_id; > + struct mtk_dip_pipe *pipe; > + int pipe_id; > + struct mtk_dip_request *req; > + struct img_ipi_frameparam *frameparam; > + struct mtk_dip_dev *dip_dev = (struct mtk_dip_dev *)priv; > + struct img_ipi_param *ipi_param; > + u32 num; > + > + if (WARN_ONCE(!data, "%s: failed due to NULL data\n", __func__)) > + return; > + > + if (WARN_ONCE(len == sizeof(ipi_param), > + "%s: len(%d) not match ipi_param\n", __func__)) > + return; > + > + ipi_param = (struct img_ipi_param *)data; > + if (ipi_param->usage == IMG_IPI_INIT) > + return; > + > + if (ipi_param->usage != IMG_IPI_FRAME) { > + dev_warn(dip_dev->dev, > + "%s: recevied unknown ipi_param, usage(%d)\n", > + __func__, ipi_param->usage); > + return; > + } > + > + job_id = ipi_param->frm_param.handle; > + pipe_id = mtk_dip_pipe_get_pipe_from_job_id(job_id); > + pipe = mtk_dip_dev_get_pipe(dip_dev, pipe_id); > + if (!pipe) { > + dev_warn(dip_dev->dev, > + "%s: get invalid img_ipi_frameparam index(%d) from firmware\n", > + __func__, job_id); > + return; > + } > + > + req = mtk_dip_pipe_get_running_job(pipe, job_id); > + if (WARN_ONCE(!req, "%s: frame_no(%d) is lost\n", __func__, job_id)) > + return; > + > + frameparam = req->working_buf->frameparam.vaddr; > + req->img_fparam.frameparam = *frameparam; > + num = atomic_dec_return(&dip_dev->num_composing); > + up(&dip_dev->sem); > + > + dev_dbg(dip_dev->dev, > + "%s: frame_no(%d) is back, index(%d), composing num(%d)\n", > + __func__, frameparam->frame_no, frameparam->index, num); > + > + INIT_WORK(&req->mdp_work, dip_runner_func); > + queue_work(dip_dev->mdp_wq, &req->mdp_work); > +} > + > +static void dip_composer_workfunc(struct work_struct *work) > +{ > + struct mtk_dip_request *req = mtk_dip_hw_fw_work_to_req(work); > + struct mtk_dip_dev *dip_dev = req->dip_pipe->dip_dev; > + struct img_ipi_param ipi_param; > + struct mtk_dip_hw_subframe *buf; > + int ret; > + > + down(&dip_dev->sem); > + > + buf = mtk_dip_hw_working_buf_alloc(req->dip_pipe->dip_dev); > + if (!buf) { > + dev_err(req->dip_pipe->dip_dev->dev, > + "%s:%s:req(%p): no free working buffer available\n", > + __func__, req->dip_pipe->desc->name, req); > + } > + > + req->working_buf = buf; > + mtk_dip_wbuf_to_ipi_img_addr(&req->img_fparam.frameparam.subfrm_data, > + &buf->buffer); > + memset(buf->buffer.vaddr, 0, DIP_SUB_FRM_SZ); > + mtk_dip_wbuf_to_ipi_img_sw_addr(&req->img_fparam.frameparam.config_data, > + &buf->config_data); > + memset(buf->config_data.vaddr, 0, DIP_COMP_SZ); > + > + if (!req->img_fparam.frameparam.tuning_data.present) { > + /* > + * When user enqueued without tuning buffer, > + * it would use driver internal buffer. > + */ > + dev_dbg(dip_dev->dev, > + "%s: frame_no(%d) has no tuning_data\n", > + __func__, req->img_fparam.frameparam.frame_no); > + > + mtk_dip_wbuf_to_ipi_tuning_addr > + (&req->img_fparam.frameparam.tuning_data, > + &buf->tuning_buf); > + memset(buf->tuning_buf.vaddr, 0, DIP_TUNING_SZ); > + } > + > + mtk_dip_wbuf_to_ipi_img_sw_addr(&req->img_fparam.frameparam.self_data, > + &buf->frameparam); > + memcpy(buf->frameparam.vaddr, &req->img_fparam.frameparam, > + sizeof(req->img_fparam.frameparam)); > + ipi_param.usage = IMG_IPI_FRAME; > + ipi_param.frm_param.handle = req->id; > + ipi_param.frm_param.scp_addr = (u32)buf->frameparam.scp_daddr; > + > + mutex_lock(&dip_dev->hw_op_lock); > + atomic_inc(&dip_dev->num_composing); > + ret = scp_ipi_send(dip_dev->scp_pdev, SCP_IPI_DIP, &ipi_param, > + sizeof(ipi_param), 0); > + if (ret) { > + dev_err(dip_dev->dev, > + "%s: frame_no(%d) send SCP_IPI_DIP_FRAME failed %d\n", > + __func__, req->img_fparam.frameparam.frame_no, ret); > + mtk_dip_pipe_remove_job(req); > + mtk_dip_pipe_job_finish(req, VB2_BUF_STATE_ERROR); > + mtk_dip_hw_working_buf_free(dip_dev, req->working_buf); > + req->working_buf = NULL; > + wake_up(&dip_dev->flushing_waitq); > + } > + mutex_unlock(&dip_dev->hw_op_lock); > + > + dev_dbg(dip_dev->dev, > + "%s: frame_no(%d),idx(0x%x), composing num(%d)\n", > + __func__, req->img_fparam.frameparam.frame_no, > + req->img_fparam.frameparam.index, > + atomic_read(&dip_dev->num_composing)); > +} > + > +static int mtk_dip_hw_flush_pipe_jobs(struct mtk_dip_pipe *pipe) > +{ > + struct mtk_dip_request *req; > + struct list_head job_list = LIST_HEAD_INIT(job_list); > + int num; > + int ret; > + > + spin_lock(&pipe->job_lock); > + list_splice_init(&pipe->pipe_job_running_list, &job_list); > + pipe->num_jobs = 0; > + spin_unlock(&pipe->job_lock); > + > + ret = wait_event_freezable_timeout > + (pipe->dip_dev->flushing_waitq, > + !(num = atomic_read(&pipe->dip_dev->num_composing)), > + msecs_to_jiffies(1000 / 30 * DIP_COMPOSING_MAX_NUM * 3)); > + if (!ret && num) { > + dev_err(pipe->dip_dev->dev, > + "%s: flushing is aborted, num(%d)\n", > + __func__, num); > + return -EINVAL; > + } > + > + list_for_each_entry(req, &job_list, list) > + mtk_dip_pipe_job_finish(req, VB2_BUF_STATE_ERROR); > + > + return 0; > +} > + > +static int mtk_dip_hw_connect(struct mtk_dip_dev *dip_dev) > +{ > + int ret; > + struct img_ipi_param ipi_param; > + > + pm_runtime_get_sync(dip_dev->dev); > + scp_ipi_register(dip_dev->scp_pdev, SCP_IPI_DIP, dip_scp_handler, > + dip_dev); > + memset(&ipi_param, 0, sizeof(ipi_param)); > + ipi_param.usage = IMG_IPI_INIT; > + > + ret = scp_ipi_send(dip_dev->scp_pdev, SCP_IPI_DIP, &ipi_param, > + sizeof(ipi_param), 200); > + if (ret) { > + dev_err(dip_dev->dev, "%s: send SCP_IPI_DIP_FRAME failed %d\n", > + __func__, ret); > + return -EBUSY; > + } > + pm_runtime_mark_last_busy(dip_dev->dev); > + pm_runtime_put_autosuspend(dip_dev->dev); > + > + return 0; > +} > + > +static void mtk_dip_hw_disconnect(struct mtk_dip_dev *dip_dev) > +{ > + struct img_ipi_param ipi_param; > + int ret; > + > + ipi_param.usage = IMG_IPI_DEINIT; > + ret = scp_ipi_send(dip_dev->scp_pdev, SCP_IPI_DIP, &ipi_param, > + sizeof(ipi_param), 0); > + if (ret) { > + dev_err(dip_dev->dev, > + "%s: SCP IMG_IPI_DEINIT failed(%d)\n", __func__, ret); > + } > + > + scp_ipi_unregister(dip_dev->scp_pdev, SCP_IPI_DIP); > +} > + > +int mtk_dip_hw_streamon(struct mtk_dip_pipe *pipe) > +{ > + struct mtk_dip_dev *dip_dev = pipe->dip_dev; > + int ret; > + > + mutex_lock(&dip_dev->hw_op_lock); > + if (!dip_dev->dip_stream_cnt) { > + ret = mtk_dip_hw_connect(pipe->dip_dev); > + if (ret) { > + dev_err(pipe->dip_dev->dev, > + "%s:%s: pipe(%d) connect to dip_hw failed\n", > + __func__, pipe->desc->name, pipe->desc->id); > + > + mutex_unlock(&dip_dev->hw_op_lock); > + > + return ret; > + } > + } > + dip_dev->dip_stream_cnt++; > + mutex_unlock(&dip_dev->hw_op_lock); > + > + pipe->streaming = 1; > + mtk_dip_pipe_try_enqueue(pipe); > + > + return 0; > +} > + > +int mtk_dip_hw_streamoff(struct mtk_dip_pipe *pipe) > +{ > + struct mtk_dip_dev *dip_dev = pipe->dip_dev; > + int ret; > + > + pipe->streaming = 0; > + > + ret = mtk_dip_hw_flush_pipe_jobs(pipe); > + if (WARN_ON(ret != 0)) { > + dev_err(dip_dev->dev, > + "%s:%s: mtk_dip_hw_flush_pipe_jobs, ret(%d)\n", > + __func__, pipe->desc->name, ret); > + } > + > + /* Stop the hardware if there is no streaming pipe */ > + mutex_lock(&dip_dev->hw_op_lock); > + dip_dev->dip_stream_cnt--; > + if (!dip_dev->dip_stream_cnt) > + mtk_dip_hw_disconnect(dip_dev); > + > + mutex_unlock(&dip_dev->hw_op_lock); > + > + return 0; > +} > + > +void mtk_dip_hw_enqueue(struct mtk_dip_dev *dip_dev, > + struct mtk_dip_request *req) > +{ > + struct img_ipi_frameparam *frameparams = &req->img_fparam.frameparam; > + > + mtk_dip_pipe_ipi_params_config(req); > + frameparams->state = FRAME_STATE_INIT; > + frameparams->frame_no = atomic_inc_return(&dip_dev->dip_enqueue_cnt); > + > + dev_dbg(dip_dev->dev, > + "%s: hw job id(%d), frame_no(%d) into worklist\n", > + __func__, frameparams->index, frameparams->frame_no); > + > + INIT_WORK(&req->fw_work, dip_composer_workfunc); > + queue_work(dip_dev->composer_wq, &req->fw_work); > +} > diff --git a/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-v4l2.c b/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-v4l2.c > new file mode 100644 > index 000000000000..57a016438960 > --- /dev/null > +++ b/drivers/media/platform/mtk-isp/isp_50/dip/mtk_dip-v4l2.c > @@ -0,0 +1,2255 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (c) 2018 MediaTek Inc. > + * > + * Author: Frederic Chen <frederic.chen@xxxxxxxxxxxx> > + * > + */ > + > +#include <linux/platform_device.h> > +#include <linux/module.h> > +#include <linux/of_device.h> > +#include <linux/pm_runtime.h> > +#include <linux/remoteproc.h> > +#include <linux/remoteproc/mtk_scp.h> > +#include <linux/videodev2.h> > +#include <media/videobuf2-dma-contig.h> > +#include <media/v4l2-ioctl.h> > +#include <media/v4l2-subdev.h> > +#include <media/v4l2-event.h> > +#include "mtk_dip-dev.h" > +#include "mtk_dip-hw.h" > +#include "mtk-mdp3-cmdq.h" > + > +static int mtk_dip_subdev_open(struct v4l2_subdev *sd, > + struct v4l2_subdev_fh *fh) > +{ > + int i; > + struct mtk_dip_pipe *pipe = mtk_dip_subdev_to_pipe(sd); > + > + for (i = 0; i < pipe->desc->total_queues; i++) { > + *v4l2_subdev_get_try_format(&pipe->subdev, fh->pad, i) = > + pipe->nodes[i].pad_fmt; > + *v4l2_subdev_get_try_crop(&pipe->subdev, fh->pad, i) = > + pipe->nodes[i].crop; > + } > + > + return 0; > +} > + > +static int mtk_dip_subdev_s_stream(struct v4l2_subdev *sd, > + int enable) > +{ > + struct mtk_dip_pipe *pipe = mtk_dip_subdev_to_pipe(sd); > + int ret; > + > + if (enable) { > + ret = mtk_dip_hw_streamon(pipe); > + if (ret) > + dev_err(pipe->dip_dev->dev, > + "%s:%s: pipe(%d) streamon failed\n", > + __func__, pipe->desc->name, pipe->desc->id); > + } else { > + ret = mtk_dip_hw_streamoff(pipe); > + if (ret) > + dev_err(pipe->dip_dev->dev, > + "%s:%s: pipe(%d) streamon off with errors\n", > + __func__, pipe->desc->name, pipe->desc->id); > + } > + > + return ret; > +} > + > +static int mtk_dip_subdev_get_fmt(struct v4l2_subdev *sd, > + struct v4l2_subdev_pad_config *cfg, > + struct v4l2_subdev_format *fmt) > +{ > + struct mtk_dip_pipe *dip_pipe = mtk_dip_subdev_to_pipe(sd); > + struct v4l2_mbus_framefmt *mf; > + u32 pad = fmt->pad; > + > + if (pad == MTK_DIP_VIDEO_NODE_ID_TUNING_OUT) > + return -EINVAL; > + > + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { > + fmt->format = dip_pipe->nodes[pad].pad_fmt; > + } else { > + mf = v4l2_subdev_get_try_format(sd, cfg, pad); > + fmt->format = *mf; > + } > + > + return 0; > +} > + > +static int mtk_dip_subdev_set_fmt(struct v4l2_subdev *sd, > + struct v4l2_subdev_pad_config *cfg, > + struct v4l2_subdev_format *fmt) > +{ > + struct mtk_dip_pipe *dip_pipe = mtk_dip_subdev_to_pipe(sd); > + struct v4l2_mbus_framefmt *mf; > + u32 pad = fmt->pad; > + struct mtk_dip_video_device *node = &dip_pipe->nodes[pad]; > + > + if (pad == MTK_DIP_VIDEO_NODE_ID_TUNING_OUT) > + return -EINVAL; > + > + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) > + mf = v4l2_subdev_get_try_format(sd, cfg, pad); > + else > + mf = &dip_pipe->nodes[pad].pad_fmt; > + > + fmt->format.code = mf->code; > + fmt->format.width = > + clamp_val(fmt->format.width, > + node->desc->frmsizeenum->stepwise.min_width, > + node->desc->frmsizeenum->stepwise.max_width); > + fmt->format.height = > + clamp_val(fmt->format.height, > + node->desc->frmsizeenum->stepwise.min_height, > + node->desc->frmsizeenum->stepwise.max_height); > + > + *mf = fmt->format; > + > + return 0; > +} > + > +static int mtk_dip_subdev_get_selection(struct v4l2_subdev *sd, > + struct v4l2_subdev_pad_config *cfg, > + struct v4l2_subdev_selection *sel) > +{ > + struct v4l2_rect *try_sel, *r; > + struct mtk_dip_pipe *dip_pipe = mtk_dip_subdev_to_pipe(sd); > + > + if (sel->pad != MTK_DIP_VIDEO_NODE_ID_MDP0_CAPTURE && > + sel->pad != MTK_DIP_VIDEO_NODE_ID_MDP1_CAPTURE) { > + dev_dbg(dip_pipe->dip_dev->dev, > + "g_select failed(%s:%d):not support\n", > + dip_pipe->nodes[sel->pad].desc->name, sel->pad); > + return -EINVAL; > + } > + > + switch (sel->target) { > + case V4L2_SEL_TGT_CROP: > + try_sel = v4l2_subdev_get_try_crop(sd, cfg, sel->pad); > + r = &dip_pipe->nodes[sel->pad].crop; /* effective resolution */ > + break; > + default: > + dev_dbg(dip_pipe->dip_dev->dev, > + "s_select failed(%s:%d):target(%d) not support\n", > + dip_pipe->nodes[sel->pad].desc->name, sel->pad, > + sel->target); > + return -EINVAL; > + } > + > + if (sel->which == V4L2_SUBDEV_FORMAT_TRY) > + sel->r = *try_sel; > + else > + sel->r = *r; > + > + return 0; > +} > + > +static int mtk_dip_subdev_set_selection(struct v4l2_subdev *sd, > + struct v4l2_subdev_pad_config *cfg, > + struct v4l2_subdev_selection *sel) > +{ > + struct v4l2_rect *rect, *try_sel; > + struct mtk_dip_pipe *dip_pipe = mtk_dip_subdev_to_pipe(sd); > + > + if (sel->pad != MTK_DIP_VIDEO_NODE_ID_MDP0_CAPTURE && > + sel->pad != MTK_DIP_VIDEO_NODE_ID_MDP1_CAPTURE) { > + dev_dbg(dip_pipe->dip_dev->dev, > + "g_select failed(%s:%d):not support\n", > + dip_pipe->nodes[sel->pad].desc->name, sel->pad); > + return -EINVAL; > + } > + > + switch (sel->target) { > + case V4L2_SEL_TGT_CROP: > + try_sel = v4l2_subdev_get_try_crop(sd, cfg, sel->pad); > + rect = &dip_pipe->nodes[sel->pad].crop; > + break; > + default: > + dev_dbg(dip_pipe->dip_dev->dev, > + "s_select failed(%s:%d):target(%d) not support\n", > + dip_pipe->nodes[sel->pad].desc->name, sel->pad, > + sel->target); > + return -EINVAL; > + } > + > + if (sel->which == V4L2_SUBDEV_FORMAT_TRY) > + *try_sel = sel->r; > + else > + *rect = sel->r; > + > + return 0; > +} > + > +static int mtk_dip_link_setup(struct media_entity *entity, > + const struct media_pad *local, > + const struct media_pad *remote, > + u32 flags) > +{ > + struct mtk_dip_pipe *pipe = > + container_of(entity, struct mtk_dip_pipe, subdev.entity); > + u32 pad = local->index; > + > + WARN_ON(entity->obj_type != MEDIA_ENTITY_TYPE_V4L2_SUBDEV); > + WARN_ON(pad >= pipe->desc->total_queues); > + > + mutex_lock(&pipe->lock); > + > + if (flags & MEDIA_LNK_FL_ENABLED) > + pipe->nodes_enabled |= 1 << pad; > + else > + pipe->nodes_enabled &= ~(1 << pad); > + > + pipe->nodes[pad].flags &= ~MEDIA_LNK_FL_ENABLED; > + pipe->nodes[pad].flags |= flags & MEDIA_LNK_FL_ENABLED; > + > + mutex_unlock(&pipe->lock); > + > + return 0; > +} > + > +static int mtk_dip_vb2_meta_buf_prepare(struct vb2_buffer *vb) > +{ > + struct mtk_dip_pipe *pipe = vb2_get_drv_priv(vb->vb2_queue); > + struct mtk_dip_video_device *node = mtk_dip_vbq_to_node(vb->vb2_queue); > + struct device *dev = pipe->dip_dev->dev; > + const struct v4l2_format *fmt = &node->vdev_fmt; > + > + if (vb->planes[0].length < fmt->fmt.meta.buffersize) { > + dev_dbg(dev, > + "%s:%s:%s: size error(user:%d, required:%d)\n", > + __func__, pipe->desc->name, node->desc->name, > + vb->planes[0].length, fmt->fmt.meta.buffersize); > + return -EINVAL; > + } > + > + if (vb->planes[0].bytesused != fmt->fmt.meta.buffersize) { > + dev_err(dev, > + "%s:%s:%s: bytesused(%d) must be %d\n", > + __func__, pipe->desc->name, node->desc->name, > + vb->planes[0].bytesused, > + fmt->fmt.meta.buffersize); > + return -EINVAL; > + } > + > + return 0; > +} > + > +static int mtk_dip_vb2_video_buf_prepare(struct vb2_buffer *vb) > +{ > + struct mtk_dip_pipe *pipe = vb2_get_drv_priv(vb->vb2_queue); > + struct mtk_dip_video_device *node = mtk_dip_vbq_to_node(vb->vb2_queue); > + struct device *dev = pipe->dip_dev->dev; > + const struct v4l2_format *fmt = &node->vdev_fmt; > + unsigned int size; > + int i; > + > + for (i = 0; i < vb->num_planes; i++) { > + size = fmt->fmt.pix_mp.plane_fmt[i].sizeimage; > + if (vb->planes[i].length < size) { > + dev_dbg(dev, > + "%s:%s:%s: size error(user:%d, max:%d)\n", > + __func__, pipe->desc->name, node->desc->name, > + vb->planes[i].length, size); > + return -EINVAL; > + } > + } > + > + return 0; > +} > + > +static int mtk_dip_vb2_buf_out_validate(struct vb2_buffer *vb) > +{ > + struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb); > + > + if (v4l2_buf->field == V4L2_FIELD_ANY) > + v4l2_buf->field = V4L2_FIELD_NONE; > + > + if (v4l2_buf->field != V4L2_FIELD_NONE) > + return -EINVAL; > + > + return 0; > +} > + > +static int mtk_dip_vb2_meta_buf_init(struct vb2_buffer *vb) > +{ > + struct mtk_dip_dev_buffer *dev_buf = mtk_dip_vb2_buf_to_dev_buf(vb); > + struct mtk_dip_pipe *pipe = vb2_get_drv_priv(vb->vb2_queue); > + struct mtk_dip_video_device *node = mtk_dip_vbq_to_node(vb->vb2_queue); > + phys_addr_t buf_paddr; > + > + dev_buf->scp_daddr[0] = vb2_dma_contig_plane_dma_addr(vb, 0); > + buf_paddr = dev_buf->scp_daddr[0]; > + dev_buf->isp_daddr[0] = dma_map_resource(pipe->dip_dev->dev, > + buf_paddr, > + vb->planes[0].length, > + DMA_BIDIRECTIONAL, > + DMA_ATTR_SKIP_CPU_SYNC); > + if (dma_mapping_error(pipe->dip_dev->dev, > + dev_buf->isp_daddr[0])) { > + dev_err(pipe->dip_dev->dev, > + "%s:%s: failed to map buffer: s_daddr(%pad)\n", > + pipe->desc->name, node->desc->name, > + &dev_buf->scp_daddr[0]); > + return -EINVAL; > + } > + > + return 0; > +} > + > +static int mtk_dip_vb2_video_buf_init(struct vb2_buffer *vb) > +{ > + struct mtk_dip_dev_buffer *dev_buf = mtk_dip_vb2_buf_to_dev_buf(vb); > + int i; > + > + for (i = 0; i < vb->num_planes; i++) { > + dev_buf->scp_daddr[i] = 0; > + dev_buf->isp_daddr[i] = vb2_dma_contig_plane_dma_addr(vb, i); > + } > + > + return 0; > +} > + > +static void mtk_dip_vb2_queue_meta_buf_cleanup(struct vb2_buffer *vb) > +{ > + struct mtk_dip_dev_buffer *dev_buf = mtk_dip_vb2_buf_to_dev_buf(vb); > + struct mtk_dip_pipe *pipe = vb2_get_drv_priv(vb->vb2_queue); > + > + dma_unmap_resource(pipe->dip_dev->dev, dev_buf->isp_daddr[0], > + vb->planes[0].length, DMA_BIDIRECTIONAL, > + DMA_ATTR_SKIP_CPU_SYNC); > +} > + > +static void mtk_dip_vb2_buf_queue(struct vb2_buffer *vb) > +{ > + struct mtk_dip_dev_buffer *dev_buf = mtk_dip_vb2_buf_to_dev_buf(vb); > + struct mtk_dip_request *req = mtk_dip_media_req_to_dip_req(vb->request); > + struct mtk_dip_video_device *node = > + mtk_dip_vbq_to_node(vb->vb2_queue); > + struct mtk_dip_dev *dip_dev = dip_dev; > + int buf_count; > + > + dev_buf->fmt = node->vdev_fmt; > + dev_buf->dev_fmt = node->dev_q.dev_fmt; > + dev_buf->dma_port = node->desc->dma_port; > + dev_buf->rotation = node->rotation; > + dev_buf->crop.c = node->crop; > + dev_buf->compose = node->compose; > + > + spin_lock(&node->buf_list_lock); > + list_add_tail(&dev_buf->list, &node->buf_list); > + spin_unlock(&node->buf_list_lock); > + > + buf_count = atomic_dec_return(&req->buf_count); > + if (!buf_count) { > + mutex_lock(&req->dip_pipe->lock); > + mtk_dip_pipe_try_enqueue(req->dip_pipe); > + mutex_unlock(&req->dip_pipe->lock); > + } > +} > + > +static int mtk_dip_vb2_meta_queue_setup(struct vb2_queue *vq, > + unsigned int *num_buffers, > + unsigned int *num_planes, > + unsigned int sizes[], > + struct device *alloc_devs[]) > +{ > + struct mtk_dip_video_device *node = mtk_dip_vbq_to_node(vq); > + const struct v4l2_format *fmt = &node->vdev_fmt; > + unsigned int size; > + > + if (!*num_planes) > + *num_planes = 1; Some formats have num_planes == 3. This should be taken into account in queue_setup. > + > + if (sizes[0] <= 0) This will never happen. Is the size of the metadata buffer fixed? > + size = fmt->fmt.meta.buffersize; > + > + *num_buffers = clamp_val(*num_buffers, 1, VB2_MAX_FRAME); > + > + return 0; > +} > + > +static int mtk_dip_vb2_video_queue_setup(struct vb2_queue *vq, > + unsigned int *num_buffers, > + unsigned int *num_planes, > + unsigned int sizes[], > + struct device *alloc_devs[]) > +{ > + struct mtk_dip_pipe *pipe = vb2_get_drv_priv(vq); > + struct mtk_dip_video_device *node = mtk_dip_vbq_to_node(vq); > + const struct v4l2_format *fmt = &node->vdev_fmt; > + int i; > + > + if (!*num_planes) > + *num_planes = 1; > + > + for (i = 0; i < *num_planes; i++) { > + if (sizes[i] <= 0) { This will also never happen. Please ensure the buffer is large enough for the format. > + dev_dbg(pipe->dip_dev->dev, > + "%s:%s:%s: invalid buf: %u < %u\n", > + __func__, pipe->desc->name, > + node->desc->name, sizes[i], > + fmt->fmt.pix_mp.plane_fmt[i].sizeimage); > + sizes[i] = fmt->fmt.pix_mp.plane_fmt[i].sizeimage; > + } > + > + *num_buffers = clamp_val(*num_buffers, 1, VB2_MAX_FRAME); > + } > + > + return 0; > +} > + > +static void mtk_dip_return_all_buffers(struct mtk_dip_pipe *pipe, > + struct mtk_dip_video_device *node, > + enum vb2_buffer_state state) > +{ > + struct mtk_dip_dev_buffer *b, *b0; > + > + spin_lock(&node->buf_list_lock); > + list_for_each_entry_safe(b, b0, &node->buf_list, list) { > + list_del(&b->list); > + vb2_buffer_done(&b->vbb.vb2_buf, state); > + } > + spin_unlock(&node->buf_list_lock); > +} > + > +static int mtk_dip_vb2_start_streaming(struct vb2_queue *vq, unsigned int count) > +{ > + struct mtk_dip_pipe *pipe = vb2_get_drv_priv(vq); > + struct mtk_dip_video_device *node = mtk_dip_vbq_to_node(vq); > + int ret; > + > + mutex_lock(&pipe->lock); > + if (!pipe->nodes_streaming) { > + ret = media_pipeline_start(&node->vdev.entity, &pipe->pipeline); > + if (ret < 0) { > + dev_err(pipe->dip_dev->dev, > + "%s:%s: media_pipeline_start failed(%d)\n", > + pipe->desc->name, node->desc->name, ret); > + goto fail_return_bufs; > + } > + } > + > + if (!(node->flags & MEDIA_LNK_FL_ENABLED)) { > + dev_err(pipe->dip_dev->dev, > + "%s:%s: stream on failed, node is not enabled\n", > + pipe->desc->name, node->desc->name); > + > + ret = -ENOLINK; > + goto fail_stop_pipeline; > + } > + > + pipe->nodes_streaming |= 1 << node->desc->id; > + if (pipe->nodes_streaming == pipe->nodes_enabled) { > + /* Start streaming of the whole pipeline */ > + ret = v4l2_subdev_call(&pipe->subdev, video, s_stream, 1); > + if (ret < 0) { > + dev_err(pipe->dip_dev->dev, > + "%s:%s: sub dev s_stream(1) failed(%d)\n", > + pipe->desc->name, node->desc->name, ret); > + > + goto fail_stop_pipeline; > + } > + } > + > + mutex_unlock(&pipe->lock); > + > + return 0; > + > +fail_stop_pipeline: > + media_pipeline_stop(&node->vdev.entity); > + > +fail_return_bufs: > + mtk_dip_return_all_buffers(pipe, node, VB2_BUF_STATE_QUEUED); > + mutex_unlock(&pipe->lock); > + > + return ret; > +} > + > +static void mtk_dip_vb2_stop_streaming(struct vb2_queue *vq) > +{ > + struct mtk_dip_pipe *pipe = vb2_get_drv_priv(vq); > + struct mtk_dip_video_device *node = mtk_dip_vbq_to_node(vq); > + int ret; > + > + mutex_lock(&pipe->lock); > + > + if (pipe->streaming) { > + ret = v4l2_subdev_call(&pipe->subdev, video, s_stream, 0); > + if (ret) > + dev_err(pipe->dip_dev->dev, > + "%s:%s: sub dev s_stream(0) failed(%d)\n", > + pipe->desc->name, node->desc->name, ret); > + } > + > + pipe->nodes_streaming &= ~(1 << node->desc->id); > + if (!pipe->nodes_streaming) > + media_pipeline_stop(&node->vdev.entity); > + > + mtk_dip_return_all_buffers(pipe, node, VB2_BUF_STATE_ERROR); > + > + mutex_unlock(&pipe->lock); > +} > + > +static void mtk_dip_vb2_request_complete(struct vb2_buffer *vb) > +{ > + struct mtk_dip_video_device *node = mtk_dip_vbq_to_node(vb->vb2_queue); > + > + v4l2_ctrl_request_complete(vb->req_obj.req, > + &node->ctrl_handler); Fits on a single line. > +} > + > +static int mtk_dip_videoc_querycap(struct file *file, void *fh, > + struct v4l2_capability *cap) > +{ > + struct mtk_dip_pipe *pipe = video_drvdata(file); > + > + snprintf(cap->driver, sizeof(cap->driver), "%s %s", > + dev_driver_string(pipe->dip_dev->dev), pipe->desc->name); > + snprintf(cap->card, sizeof(cap->card), "%s %s", > + dev_driver_string(pipe->dip_dev->dev), pipe->desc->name); > + snprintf(cap->bus_info, sizeof(cap->bus_info), > + "platform:%s", dev_name(pipe->dip_dev->mdev.dev)); > + > + return 0; > +} > + > +static int mtk_dip_videoc_try_fmt(struct file *file, void *fh, > + struct v4l2_format *f) > +{ > + struct mtk_dip_pipe *pipe = video_drvdata(file); > + struct mtk_dip_video_device *node = mtk_dip_file_to_node(file); > + const struct mtk_dip_dev_format *dev_fmt; > + struct v4l2_format try_fmt; > + > + memset(&try_fmt, 0, sizeof(try_fmt)); > + > + dev_fmt = mtk_dip_pipe_find_fmt(pipe, node, > + f->fmt.pix_mp.pixelformat); > + if (!dev_fmt) { > + dev_fmt = &node->desc->fmts[node->desc->default_fmt_idx]; > + dev_dbg(pipe->dip_dev->dev, > + "%s:%s:%s: dev_fmt(%d) not found, use default(%d)\n", > + __func__, pipe->desc->name, node->desc->name, > + f->fmt.pix_mp.pixelformat, dev_fmt->format); > + } > + > + mtk_dip_pipe_try_fmt(pipe, node, &try_fmt, f, dev_fmt); > + *f = try_fmt; > + > + return 0; > +} > + > +static int mtk_dip_videoc_g_fmt(struct file *file, void *fh, > + struct v4l2_format *f) > +{ > + struct mtk_dip_video_device *node = mtk_dip_file_to_node(file); > + > + *f = node->vdev_fmt; > + > + return 0; > +} > + > +static int mtk_dip_videoc_s_fmt(struct file *file, void *fh, > + struct v4l2_format *f) > +{ > + struct mtk_dip_video_device *node = mtk_dip_file_to_node(file); > + struct mtk_dip_pipe *pipe = video_drvdata(file); > + const struct mtk_dip_dev_format *dev_fmt; > + > + if (pipe->streaming || vb2_is_busy(&node->dev_q.vbq)) > + return -EBUSY; > + > + dev_fmt = mtk_dip_pipe_find_fmt(pipe, node, > + f->fmt.pix_mp.pixelformat); > + if (!dev_fmt) { > + dev_fmt = &node->desc->fmts[node->desc->default_fmt_idx]; > + dev_dbg(pipe->dip_dev->dev, > + "%s:%s:%s: dev_fmt(%d) not found, use default(%d)\n", > + __func__, pipe->desc->name, node->desc->name, > + f->fmt.pix_mp.pixelformat, dev_fmt->format); > + } > + > + memset(&node->vdev_fmt, 0, sizeof(node->vdev_fmt)); > + > + mtk_dip_pipe_try_fmt(pipe, node, &node->vdev_fmt, f, dev_fmt); > + *f = node->vdev_fmt; > + > + node->dev_q.dev_fmt = dev_fmt; > + node->vdev_fmt = *f; > + node->crop.left = 0; /* reset crop setting of nodes */ > + node->crop.top = 0; > + node->crop.width = f->fmt.pix_mp.width; > + node->crop.height = f->fmt.pix_mp.height; > + node->compose.left = 0; > + node->compose.top = 0; > + node->compose.width = f->fmt.pix_mp.width; > + node->compose.height = f->fmt.pix_mp.height; > + > + return 0; > +} > + > +static int mtk_dip_videoc_enum_framesizes(struct file *file, void *priv, > + struct v4l2_frmsizeenum *sizes) > +{ > + struct mtk_dip_pipe *pipe = video_drvdata(file); > + struct mtk_dip_video_device *node = mtk_dip_file_to_node(file); > + const struct mtk_dip_dev_format *dev_fmt; > + > + dev_fmt = mtk_dip_pipe_find_fmt(pipe, node, sizes->pixel_format); > + > + if (!dev_fmt || sizes->index) > + return -EINVAL; > + > + sizes->type = node->desc->frmsizeenum->type; > + sizes->stepwise.max_width = > + node->desc->frmsizeenum->stepwise.max_width; > + sizes->stepwise.min_width = > + node->desc->frmsizeenum->stepwise.min_width; > + sizes->stepwise.max_height = > + node->desc->frmsizeenum->stepwise.max_height; > + sizes->stepwise.min_height = > + node->desc->frmsizeenum->stepwise.min_height; > + sizes->stepwise.step_height = > + node->desc->frmsizeenum->stepwise.step_height; > + sizes->stepwise.step_width = > + node->desc->frmsizeenum->stepwise.step_width; > + > + return 0; > +} > + > +static int mtk_dip_videoc_enum_fmt(struct file *file, void *fh, > + struct v4l2_fmtdesc *f) > +{ > + struct mtk_dip_video_device *node = mtk_dip_file_to_node(file); > + > + if (f->index >= node->desc->num_fmts) > + return -EINVAL; > + > + strscpy(f->description, node->desc->description, > + sizeof(f->description)); Not needed anymore; the V4L2 core does this. > + f->pixelformat = node->desc->fmts[f->index].format; > + f->flags = 0; > + > + return 0; > +} > + > +static int mtk_dip_meta_enum_format(struct file *file, void *fh, > + struct v4l2_fmtdesc *f) > +{ > + struct mtk_dip_video_device *node = mtk_dip_file_to_node(file); > + > + if (f->index > 0) > + return -EINVAL; > + > + strscpy(f->description, node->desc->description, > + sizeof(f->description)); Ditto. > + > + f->pixelformat = node->vdev_fmt.fmt.meta.dataformat; > + f->flags = 0; > + > + return 0; > +} > + > +static int mtk_dip_videoc_g_meta_fmt(struct file *file, void *fh, > + struct v4l2_format *f) > +{ > + struct mtk_dip_video_device *node = mtk_dip_file_to_node(file); One more newline, please. > + *f = node->vdev_fmt; > + > + return 0; > +} > + > +static int mtk_dip_video_device_s_ctrl(struct v4l2_ctrl *ctrl) > +{ > + struct mtk_dip_video_device *node = > + container_of(ctrl->handler, > + struct mtk_dip_video_device, ctrl_handler); > + > + if (ctrl->id != V4L2_CID_ROTATE) { > + pr_debug("[%s] doesn't support ctrl(%d)\n", > + node->desc->name, ctrl->id); > + return -EINVAL; > + } > + > + node->rotation = ctrl->val; > + > + return 0; > +} > + > +/******************** function pointers ********************/ > + > +static const struct v4l2_subdev_internal_ops mtk_dip_subdev_internal_ops = { > + .open = mtk_dip_subdev_open, > +}; > + > +static const struct v4l2_subdev_video_ops mtk_dip_subdev_video_ops = { > + .s_stream = mtk_dip_subdev_s_stream, > +}; > + > +static const struct v4l2_subdev_pad_ops mtk_dip_subdev_pad_ops = { > + .link_validate = v4l2_subdev_link_validate_default, > + .get_fmt = mtk_dip_subdev_get_fmt, > + .set_fmt = mtk_dip_subdev_set_fmt, > + .get_selection = mtk_dip_subdev_get_selection, > + .set_selection = mtk_dip_subdev_set_selection, > +}; > + > +static const struct v4l2_subdev_ops mtk_dip_subdev_ops = { > + .video = &mtk_dip_subdev_video_ops, > + .pad = &mtk_dip_subdev_pad_ops, > +}; > + > +static const struct media_entity_operations mtk_dip_media_ops = { > + .link_setup = mtk_dip_link_setup, > + .link_validate = v4l2_subdev_link_validate, > +}; > + > +static struct media_request *mtk_dip_request_alloc(struct media_device *mdev) > +{ > + struct mtk_dip_request *dip_req; > + > + dip_req = kzalloc(sizeof(*dip_req), GFP_KERNEL); > + > + return &dip_req->req; > +} > + > +static void mtk_dip_request_free(struct media_request *req) > +{ > + struct mtk_dip_request *dip_req = mtk_dip_media_req_to_dip_req(req); > + > + kfree(dip_req); > +} > + > +static int mtk_dip_vb2_request_validate(struct media_request *req) > +{ > + struct media_request_object *obj; > + struct mtk_dip_dev *dip_dev = mtk_dip_mdev_to_dev(req->mdev); > + struct mtk_dip_request *dip_req = mtk_dip_media_req_to_dip_req(req); > + struct mtk_dip_pipe *pipe = NULL; > + struct mtk_dip_pipe *pipe_prev = NULL; > + struct mtk_dip_dev_buffer **map = dip_req->buf_map; > + int buf_count = 0; > + > + memset(map, 0, sizeof(dip_req->buf_map)); > + > + list_for_each_entry(obj, &req->objects, list) { > + struct vb2_buffer *vb; > + struct mtk_dip_dev_buffer *dev_buf; > + struct mtk_dip_video_device *node; > + > + if (!vb2_request_object_is_buffer(obj)) > + continue; > + > + vb = container_of(obj, struct vb2_buffer, req_obj); > + node = mtk_dip_vbq_to_node(vb->vb2_queue); > + pipe = vb2_get_drv_priv(vb->vb2_queue); > + if (pipe_prev && pipe != pipe_prev) { > + dev_dbg(dip_dev->dev, > + "%s:%s:%s:found buf of different pipes(%p,%p)\n", > + __func__, node->desc->name, > + req->debug_str, pipe, pipe_prev); > + return -EINVAL; > + } > + > + pipe_prev = pipe; > + dev_buf = mtk_dip_vb2_buf_to_dev_buf(vb); > + dip_req->buf_map[node->desc->id] = dev_buf; > + buf_count++; > + } > + > + if (!pipe) { > + dev_dbg(dip_dev->dev, > + "%s: no buffer in the request(%p)\n", > + req->debug_str, req); > + > + return -EINVAL; > + } > + > + if (!map[MTK_DIP_VIDEO_NODE_ID_RAW_OUT] || > + (!map[MTK_DIP_VIDEO_NODE_ID_MDP0_CAPTURE] && > + !map[MTK_DIP_VIDEO_NODE_ID_MDP1_CAPTURE] && > + !map[MTK_DIP_VIDEO_NODE_ID_IMG3_CAPTURE])) { > + dev_dbg(dip_dev->dev, > + "won't trigger hw job: raw(%p), mdp0(%p), mdp1(%p), img3(%p)\n", > + map[MTK_DIP_VIDEO_NODE_ID_RAW_OUT], > + map[MTK_DIP_VIDEO_NODE_ID_MDP0_CAPTURE], > + map[MTK_DIP_VIDEO_NODE_ID_MDP1_CAPTURE], > + map[MTK_DIP_VIDEO_NODE_ID_IMG3_CAPTURE]); > + return -EINVAL; > + } > + > + atomic_set(&dip_req->buf_count, buf_count); > + dip_req->id = mtk_dip_pipe_next_job_id(pipe); > + dip_req->dip_pipe = pipe; > + > + return vb2_request_validate(req); > +} > + > +static void mtk_dip_vb2_request_queue(struct media_request *req) > +{ > + struct mtk_dip_request *dip_req = mtk_dip_media_req_to_dip_req(req); > + struct mtk_dip_pipe *pipe = dip_req->dip_pipe; > + > + spin_lock(&pipe->job_lock); > + list_add_tail(&dip_req->list, &pipe->pipe_job_pending_list); > + pipe->num_pending_jobs++; > + spin_unlock(&pipe->job_lock); > + > + vb2_request_queue(req); > +} > + > +static const struct media_device_ops mtk_dip_media_req_ops = { > + .req_validate = mtk_dip_vb2_request_validate, > + .req_queue = mtk_dip_vb2_request_queue, > + .req_alloc = mtk_dip_request_alloc, > + .req_free = mtk_dip_request_free, > +}; > + > +static const struct v4l2_ctrl_ops mtk_dip_video_device_ctrl_ops = { > + .s_ctrl = mtk_dip_video_device_s_ctrl, > +}; > + > +static const struct vb2_ops mtk_dip_vb2_meta_ops = { > + .buf_queue = mtk_dip_vb2_buf_queue, > + .queue_setup = mtk_dip_vb2_meta_queue_setup, > + .buf_init = mtk_dip_vb2_meta_buf_init, > + .buf_prepare = mtk_dip_vb2_meta_buf_prepare, > + .buf_out_validate = mtk_dip_vb2_buf_out_validate, > + .buf_cleanup = mtk_dip_vb2_queue_meta_buf_cleanup, > + .start_streaming = mtk_dip_vb2_start_streaming, > + .stop_streaming = mtk_dip_vb2_stop_streaming, > + .wait_prepare = vb2_ops_wait_prepare, > + .wait_finish = vb2_ops_wait_finish, > + .buf_request_complete = mtk_dip_vb2_request_complete, > +}; > + > +static const struct vb2_ops mtk_dip_vb2_video_ops = { > + .buf_queue = mtk_dip_vb2_buf_queue, > + .queue_setup = mtk_dip_vb2_video_queue_setup, > + .buf_init = mtk_dip_vb2_video_buf_init, > + .buf_prepare = mtk_dip_vb2_video_buf_prepare, > + .buf_out_validate = mtk_dip_vb2_buf_out_validate, > + .start_streaming = mtk_dip_vb2_start_streaming, > + .stop_streaming = mtk_dip_vb2_stop_streaming, > + .wait_prepare = vb2_ops_wait_prepare, > + .wait_finish = vb2_ops_wait_finish, > + .buf_request_complete = mtk_dip_vb2_request_complete, > +}; > + > +static const struct v4l2_file_operations mtk_dip_v4l2_fops = { > + .unlocked_ioctl = video_ioctl2, > + .open = v4l2_fh_open, > + .release = vb2_fop_release, > + .poll = vb2_fop_poll, > + .mmap = vb2_fop_mmap, > +#ifdef CONFIG_COMPAT > + .compat_ioctl32 = v4l2_compat_ioctl32, > +#endif > +}; > + > +int mtk_dip_dev_media_register(struct device *dev, > + struct media_device *media_dev) > +{ > + int ret; > + > + media_dev->dev = dev; > + strlcpy(media_dev->model, dev_driver_string(dev), > + sizeof(media_dev->model)); > + snprintf(media_dev->bus_info, sizeof(media_dev->bus_info), > + "platform:%s", dev_name(dev)); > + media_dev->hw_revision = 0; > + media_dev->ops = &mtk_dip_media_req_ops; > + media_device_init(media_dev); > + > + ret = media_device_register(media_dev); > + if (ret) { > + dev_err(dev, "failed to register media device (%d)\n", ret); > + media_device_unregister(media_dev); > + media_device_cleanup(media_dev); > + return ret; > + } > + > + return 0; > +} > + > +static int mtk_dip_video_device_v4l2_register(struct mtk_dip_pipe *pipe, > + struct mtk_dip_video_device *node) > +{ > + struct vb2_queue *vbq = &node->dev_q.vbq; > + struct video_device *vdev = &node->vdev; > + struct media_link *link; > + int ret; > + > + mutex_init(&node->dev_q.lock); > + > + vdev->device_caps = node->desc->cap; > + vdev->ioctl_ops = node->desc->ops; > + node->vdev_fmt.type = node->desc->buf_type; > + mtk_dip_pipe_load_default_fmt(pipe, node, &node->vdev_fmt); > + > + node->pad_fmt.width = node->vdev_fmt.fmt.pix_mp.width; > + node->pad_fmt.height = node->vdev_fmt.fmt.pix_mp.height; > + node->pad_fmt.code = MEDIA_BUS_FMT_FIXED; > + node->pad_fmt.field = node->vdev_fmt.fmt.pix_mp.field; > + node->pad_fmt.colorspace = node->vdev_fmt.fmt.pix_mp.colorspace; > + node->pad_fmt.quantization = node->vdev_fmt.fmt.pix_mp.quantization; > + node->crop.left = 0; > + node->crop.top = 0; > + node->crop.width = node->vdev_fmt.fmt.pix_mp.width; > + node->crop.height = node->vdev_fmt.fmt.pix_mp.height; > + node->compose.left = 0; > + node->compose.top = 0; > + node->compose.width = node->vdev_fmt.fmt.pix_mp.width; > + node->compose.height = node->vdev_fmt.fmt.pix_mp.height; > + > + ret = media_entity_pads_init(&vdev->entity, 1, &node->vdev_pad); > + if (ret) { > + dev_err(pipe->dip_dev->dev, > + "failed initialize media entity (%d)\n", ret); > + goto err_mutex_destroy; > + } > + > + node->vdev_pad.flags = V4L2_TYPE_IS_OUTPUT(node->desc->buf_type) ? > + MEDIA_PAD_FL_SOURCE : MEDIA_PAD_FL_SINK; > + > + vbq->type = node->vdev_fmt.type; > + vbq->io_modes = VB2_MMAP | VB2_DMABUF; > + vbq->ops = node->desc->vb2_ops; > + vbq->mem_ops = &vb2_dma_contig_memops; > + vbq->supports_requests = true; > + vbq->buf_struct_size = sizeof(struct mtk_dip_dev_buffer); > + vbq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; > + vbq->min_buffers_needed = 0; > + vbq->drv_priv = pipe; > + vbq->lock = &node->dev_q.lock; > + > + ret = vb2_queue_init(vbq); > + if (ret) { > + dev_err(pipe->dip_dev->dev, > + "%s:%s:%s: failed to init vb2 queue(%d)\n", > + __func__, pipe->desc->name, node->desc->name, > + ret); > + goto err_media_entity_cleanup; > + } > + > + snprintf(vdev->name, sizeof(vdev->name), "%s %s %s", > + dev_driver_string(pipe->dip_dev->dev), pipe->desc->name, > + node->desc->name); > + vdev->entity.name = vdev->name; > + vdev->entity.function = MEDIA_ENT_F_IO_V4L; > + vdev->entity.ops = NULL; > + vdev->release = video_device_release_empty; > + vdev->fops = &mtk_dip_v4l2_fops; > + vdev->lock = &node->dev_q.lock; > + if (node->desc->supports_ctrls) > + vdev->ctrl_handler = &node->ctrl_handler; > + else > + vdev->ctrl_handler = NULL; > + vdev->v4l2_dev = &pipe->dip_dev->v4l2_dev; > + vdev->queue = &node->dev_q.vbq; > + vdev->vfl_dir = V4L2_TYPE_IS_OUTPUT(node->desc->buf_type) ? > + VFL_DIR_TX : VFL_DIR_RX; > + > + if (node->desc->smem_alloc) > + vdev->queue->dev = &pipe->dip_dev->scp_pdev->dev; > + else > + vdev->queue->dev = pipe->dip_dev->dev; > + > + video_set_drvdata(vdev, pipe); > + > + ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); > + if (ret) { > + dev_err(pipe->dip_dev->dev, > + "failed to register video device (%d)\n", ret); > + goto err_vb2_queue_release; > + } > + > + if (V4L2_TYPE_IS_OUTPUT(node->desc->buf_type)) > + ret = media_create_pad_link(&vdev->entity, 0, > + &pipe->subdev.entity, > + node->desc->id, node->flags); > + else > + ret = media_create_pad_link(&pipe->subdev.entity, > + node->desc->id, &vdev->entity, > + 0, node->flags); > + if (ret) > + goto err_video_unregister_device; > + > + vdev->intf_devnode = media_devnode_create(&pipe->dip_dev->mdev, > + MEDIA_INTF_T_V4L_VIDEO, 0, > + VIDEO_MAJOR, vdev->minor); > + if (!vdev->intf_devnode) { > + ret = -ENOMEM; > + goto err_rm_links; > + } > + > + link = media_create_intf_link(&vdev->entity, > + &vdev->intf_devnode->intf, > + node->flags); > + if (!link) { > + ret = -ENOMEM; > + goto err_rm_devnode; > + } > + > + return 0; > + > +err_rm_devnode: > + media_devnode_remove(vdev->intf_devnode); > + > +err_rm_links: > + media_entity_remove_links(&vdev->entity); > + > +err_video_unregister_device: > + video_unregister_device(vdev); > + > +err_vb2_queue_release: > + vb2_queue_release(&node->dev_q.vbq); > + > +err_media_entity_cleanup: > + media_entity_cleanup(&node->vdev.entity); > + > +err_mutex_destroy: > + mutex_destroy(&node->dev_q.lock); > + > + return ret; > +} > + > +static int mtk_dip_pipe_v4l2_ctrl_init(struct mtk_dip_pipe *dip_pipe) > +{ > + int i, ret; > + struct mtk_dip_video_device *ctrl_node; > + > + for (i = 0; i < MTK_DIP_VIDEO_NODE_ID_TOTAL_NUM; i++) { > + ctrl_node = &dip_pipe->nodes[i]; > + if (!ctrl_node->desc->supports_ctrls) > + continue; > + > + v4l2_ctrl_handler_init(&ctrl_node->ctrl_handler, 1); > + v4l2_ctrl_new_std(&ctrl_node->ctrl_handler, > + &mtk_dip_video_device_ctrl_ops, > + V4L2_CID_ROTATE, 0, 270, 90, 0); > + ret = ctrl_node->ctrl_handler.error; > + if (ret) { > + dev_err(dip_pipe->dip_dev->dev, > + "%s create rotate ctrl failed:(%d)", > + ctrl_node->desc->name, ret); > + goto err_free_ctrl_handlers; > + } > + } > + > + return 0; > + > +err_free_ctrl_handlers: > + for (; i >= 0; i--) { > + ctrl_node = &dip_pipe->nodes[i]; > + if (!ctrl_node->desc->supports_ctrls) > + continue; > + v4l2_ctrl_handler_free(&ctrl_node->ctrl_handler); > + } > + > + return ret; > +} > + > +static void mtk_dip_pipe_v4l2_ctrl_release(struct mtk_dip_pipe *dip_pipe) > +{ > + struct mtk_dip_video_device *ctrl_node = > + &dip_pipe->nodes[MTK_DIP_VIDEO_NODE_ID_MDP0_CAPTURE]; > + > + v4l2_ctrl_handler_free(&ctrl_node->ctrl_handler); > +} > + > +int mtk_dip_pipe_v4l2_register(struct mtk_dip_pipe *pipe, > + struct media_device *media_dev, > + struct v4l2_device *v4l2_dev) > +{ > + int i, ret; > + > + ret = mtk_dip_pipe_v4l2_ctrl_init(pipe); > + if (ret) { > + dev_err(pipe->dip_dev->dev, > + "%s: failed(%d) to initialize ctrls\n", > + pipe->desc->name, ret); > + > + return ret; > + } > + > + pipe->streaming = 0; > + > + /* Initialize subdev media entity */ > + pipe->subdev_pads = devm_kcalloc(pipe->dip_dev->dev, > + pipe->desc->total_queues, > + sizeof(*pipe->subdev_pads), > + GFP_KERNEL); > + if (!pipe->subdev_pads) { > + dev_err(pipe->dip_dev->dev, > + "failed to alloc pipe->subdev_pads (%d)\n", ret); > + ret = -ENOMEM; > + goto err_release_ctrl; > + } > + ret = media_entity_pads_init(&pipe->subdev.entity, > + pipe->desc->total_queues, > + pipe->subdev_pads); > + if (ret) { > + dev_err(pipe->dip_dev->dev, > + "failed initialize subdev media entity (%d)\n", ret); > + goto err_free_subdev_pads; > + } > + > + /* Initialize subdev */ > + v4l2_subdev_init(&pipe->subdev, &mtk_dip_subdev_ops); > + > + pipe->subdev.entity.function = > + MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; > + pipe->subdev.entity.ops = &mtk_dip_media_ops; > + pipe->subdev.flags = > + V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; > + pipe->subdev.ctrl_handler = NULL; > + pipe->subdev.internal_ops = &mtk_dip_subdev_internal_ops; > + > + for (i = 0; i < pipe->desc->total_queues; i++) > + pipe->subdev_pads[i].flags = > + V4L2_TYPE_IS_OUTPUT(pipe->nodes[i].desc->buf_type) ? > + MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE; > + > + snprintf(pipe->subdev.name, sizeof(pipe->subdev.name), > + "%s", pipe->desc->name); > + v4l2_set_subdevdata(&pipe->subdev, pipe); > + > + ret = v4l2_device_register_subdev(&pipe->dip_dev->v4l2_dev, > + &pipe->subdev); > + if (ret) { > + dev_err(pipe->dip_dev->dev, > + "failed initialize subdev (%d)\n", ret); > + goto err_media_entity_cleanup; > + } > + > + /* Create video nodes and links */ > + for (i = 0; i < pipe->desc->total_queues; i++) { > + ret = mtk_dip_video_device_v4l2_register(pipe, > + &pipe->nodes[i]); > + if (ret) > + goto err_unregister_subdev; > + } > + > + return 0; > + > +err_unregister_subdev: > + v4l2_device_unregister_subdev(&pipe->subdev); > + > +err_media_entity_cleanup: > + media_entity_cleanup(&pipe->subdev.entity); > + > +err_free_subdev_pads: > + devm_kfree(pipe->dip_dev->dev, pipe->subdev_pads); > + > +err_release_ctrl: > + mtk_dip_pipe_v4l2_ctrl_release(pipe); > + > + return ret; > +} > + > +void mtk_dip_pipe_v4l2_unregister(struct mtk_dip_pipe *pipe) > +{ > + unsigned int i; > + > + for (i = 0; i < pipe->desc->total_queues; i++) { > + video_unregister_device(&pipe->nodes[i].vdev); > + vb2_queue_release(&pipe->nodes[i].dev_q.vbq); > + media_entity_cleanup(&pipe->nodes[i].vdev.entity); > + mutex_destroy(&pipe->nodes[i].dev_q.lock); > + } > + > + v4l2_device_unregister_subdev(&pipe->subdev); > + media_entity_cleanup(&pipe->subdev.entity); > + mtk_dip_pipe_v4l2_ctrl_release(pipe); > +} > + > +/******************************************** > + * MTK DIP V4L2 Settings * > + ********************************************/ > + > +static const struct v4l2_ioctl_ops mtk_dip_v4l2_video_out_ioctl_ops = { > + .vidioc_querycap = mtk_dip_videoc_querycap, > + > + .vidioc_enum_framesizes = mtk_dip_videoc_enum_framesizes, > + .vidioc_enum_fmt_vid_out = mtk_dip_videoc_enum_fmt, > + .vidioc_g_fmt_vid_out_mplane = mtk_dip_videoc_g_fmt, > + .vidioc_s_fmt_vid_out_mplane = mtk_dip_videoc_s_fmt, > + .vidioc_try_fmt_vid_out_mplane = mtk_dip_videoc_try_fmt, > + > + .vidioc_reqbufs = vb2_ioctl_reqbufs, > + .vidioc_create_bufs = vb2_ioctl_create_bufs, > + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, > + .vidioc_querybuf = vb2_ioctl_querybuf, > + .vidioc_qbuf = vb2_ioctl_qbuf, > + .vidioc_dqbuf = vb2_ioctl_dqbuf, > + .vidioc_streamon = vb2_ioctl_streamon, > + .vidioc_streamoff = vb2_ioctl_streamoff, > + .vidioc_expbuf = vb2_ioctl_expbuf, > + > + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, > + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, > +}; > + > +static const struct v4l2_ioctl_ops mtk_dip_v4l2_video_cap_ioctl_ops = { > + .vidioc_querycap = mtk_dip_videoc_querycap, > + > + .vidioc_enum_framesizes = mtk_dip_videoc_enum_framesizes, > + .vidioc_enum_fmt_vid_cap = mtk_dip_videoc_enum_fmt, > + .vidioc_g_fmt_vid_cap_mplane = mtk_dip_videoc_g_fmt, > + .vidioc_s_fmt_vid_cap_mplane = mtk_dip_videoc_s_fmt, > + .vidioc_try_fmt_vid_cap_mplane = mtk_dip_videoc_try_fmt, > + > + .vidioc_reqbufs = vb2_ioctl_reqbufs, > + .vidioc_create_bufs = vb2_ioctl_create_bufs, > + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, > + .vidioc_querybuf = vb2_ioctl_querybuf, > + .vidioc_qbuf = vb2_ioctl_qbuf, > + .vidioc_dqbuf = vb2_ioctl_dqbuf, > + .vidioc_streamon = vb2_ioctl_streamon, > + .vidioc_streamoff = vb2_ioctl_streamoff, > + .vidioc_expbuf = vb2_ioctl_expbuf, > + > + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, > + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, > +}; > + > +static const struct v4l2_ioctl_ops mtk_dip_v4l2_meta_out_ioctl_ops = { > + .vidioc_querycap = mtk_dip_videoc_querycap, > + > + .vidioc_enum_fmt_meta_out = mtk_dip_meta_enum_format, > + .vidioc_g_fmt_meta_out = mtk_dip_videoc_g_meta_fmt, > + .vidioc_s_fmt_meta_out = mtk_dip_videoc_g_meta_fmt, > + .vidioc_try_fmt_meta_out = mtk_dip_videoc_g_meta_fmt, > + > + .vidioc_reqbufs = vb2_ioctl_reqbufs, > + .vidioc_create_bufs = vb2_ioctl_create_bufs, > + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, > + .vidioc_querybuf = vb2_ioctl_querybuf, > + .vidioc_qbuf = vb2_ioctl_qbuf, > + .vidioc_dqbuf = vb2_ioctl_dqbuf, > + .vidioc_streamon = vb2_ioctl_streamon, > + .vidioc_streamoff = vb2_ioctl_streamoff, > + .vidioc_expbuf = vb2_ioctl_expbuf, > +}; > + > +static const struct mtk_dip_dev_format fw_param_fmts[] = { > + { > + .format = V4L2_META_FMT_MTISP_PARAMS, > + .buffer_size = 1024 * (128 + 288), > + }, > +}; > + > +static const struct mtk_dip_dev_format lcei_fmts[] = { > + { > + .format = V4L2_PIX_FMT_MTISP_SBGGR8, > + .mdp_color = DIP_MCOLOR_BAYER8, > + .depth = { 8 }, > + .row_depth = { 8 }, > + .num_planes = 1, > + .num_cplanes = 1, > + }, > +}; > + > +static const struct mtk_dip_dev_format in_fmts[] = { > + { > + .format = V4L2_PIX_FMT_VYUY, > + .mdp_color = DIP_MCOLOR_VYUY, > + .depth = { 16 }, > + .row_depth = { 16 }, > + .num_planes = 1, > + .num_cplanes = 1, > + }, > + { > + .format = V4L2_PIX_FMT_YUYV, > + .mdp_color = DIP_MCOLOR_YUYV, > + .depth = { 16 }, > + .row_depth = { 16 }, > + .num_planes = 1, > + .num_cplanes = 1, > + }, > + { > + .format = V4L2_PIX_FMT_YVYU, > + .mdp_color = DIP_MCOLOR_YVYU, > + .depth = { 16 }, > + .row_depth = { 16 }, > + .num_planes = 1, > + .num_cplanes = 1, > + }, > + { > + .format = V4L2_PIX_FMT_NV12, > + .mdp_color = DIP_MCOLOR_NV12, > + .depth = { 12 }, > + .row_depth = { 8 }, > + .num_planes = 1, > + .num_cplanes = 2, > + }, > + { > + .format = V4L2_PIX_FMT_MTISP_SBGGR8, > + .mdp_color = DIP_MCOLOR_BAYER8_BGGR, > + .depth = { 8 }, > + .row_depth = { 8 }, > + .num_planes = 1, > + .num_cplanes = 1, > + .pass_1_align = 4, > + }, > + { > + .format = V4L2_PIX_FMT_MTISP_SGBRG8, > + .mdp_color = DIP_MCOLOR_BAYER8_GBRG, > + .depth = { 8 }, > + .row_depth = { 8 }, > + .num_planes = 1, > + .num_cplanes = 1, > + .pass_1_align = 4, > + }, > + { > + .format = V4L2_PIX_FMT_MTISP_SGRBG8, > + .mdp_color = DIP_MCOLOR_BAYER8_GRBG, > + .depth = { 8 }, > + .row_depth = { 8 }, > + .num_planes = 1, > + .num_cplanes = 1, > + .pass_1_align = 4, > + }, > + { > + .format = V4L2_PIX_FMT_MTISP_SRGGB8, > + .mdp_color = DIP_MCOLOR_BAYER8_RGGB, > + .depth = { 8 }, > + .row_depth = { 8 }, > + .num_planes = 1, > + .num_cplanes = 1, > + .pass_1_align = 4, > + }, > + { > + .format = V4L2_PIX_FMT_MTISP_SBGGR8F, > + .mdp_color = DIP_MCOLOR_FULLG8_BGGR, > + .depth = { 12 }, > + .row_depth = { 8}, > + .num_planes = 1, > + .num_cplanes = 1, > + .pass_1_align = 8, > + }, > + { > + .format = V4L2_PIX_FMT_MTISP_SGBRG8F, > + .mdp_color = DIP_MCOLOR_FULLG8_GBRG, > + .depth = { 12 }, > + .row_depth = { 8 }, > + .num_planes = 1, > + .num_cplanes = 1, > + .pass_1_align = 8, > + }, > + { > + .format = V4L2_PIX_FMT_MTISP_SGRBG8F, > + .mdp_color = DIP_MCOLOR_FULLG8_GRBG, > + .depth = { 12 }, > + .row_depth = { 8 }, > + .num_planes = 1, > + .num_cplanes = 1, > + .pass_1_align = 8, > + }, > + { > + .format = V4L2_PIX_FMT_MTISP_SRGGB8F, > + .mdp_color = DIP_MCOLOR_FULLG8_RGGB, > + .depth = { 12 }, > + .row_depth = { 8 }, > + .num_planes = 1, > + .num_cplanes = 1, > + .pass_1_align = 8, > + }, > + { > + .format = V4L2_PIX_FMT_MTISP_SBGGR10, > + .mdp_color = DIP_MCOLOR_BAYER10_BGGR, > + .depth = { 10 }, > + .row_depth = { 10 }, > + .num_planes = 1, > + .num_cplanes = 1, > + .pass_1_align = 4, > + }, > + { > + .format = V4L2_PIX_FMT_MTISP_SGBRG10, > + .mdp_color = DIP_MCOLOR_BAYER10_GBRG, > + .depth = { 10 }, > + .row_depth = { 10 }, > + .num_planes = 1, > + .num_cplanes = 1, > + .pass_1_align = 4, > + }, > + { > + .format = V4L2_PIX_FMT_MTISP_SGRBG10, > + .mdp_color = DIP_MCOLOR_BAYER10_GRBG, > + .depth = { 10 }, > + .row_depth = { 10 }, > + .num_planes = 1, > + .num_cplanes = 1, > + .pass_1_align = 4, > + }, > + { > + .format = V4L2_PIX_FMT_MTISP_SRGGB10, > + .mdp_color = DIP_MCOLOR_BAYER10_RGGB, > + .depth = { 10 }, > + .row_depth = { 10 }, > + .num_planes = 1, > + .num_cplanes = 1, > + .pass_1_align = 4, > + }, > + { > + .format = V4L2_PIX_FMT_MTISP_SBGGR10F, > + .mdp_color = DIP_MCOLOR_FULLG10_BGGR, > + .depth = { 15 }, > + .row_depth = { 10 }, > + .num_planes = 1, > + .num_cplanes = 1, > + .pass_1_align = 4, > + }, > + { > + .format = V4L2_PIX_FMT_MTISP_SGBRG10F, > + .mdp_color = DIP_MCOLOR_FULLG10_GBRG, > + .depth = { 15 }, > + .row_depth = { 10 }, > + .num_planes = 1, > + .num_cplanes = 1, > + .pass_1_align = 4, > + }, > + { > + .format = V4L2_PIX_FMT_MTISP_SGRBG10F, > + .mdp_color = DIP_MCOLOR_FULLG10_GRBG, > + .depth = { 15 }, > + .row_depth = { 10 }, > + .num_planes = 1, > + .num_cplanes = 1, > + .pass_1_align = 4, > + }, > + { > + .format = V4L2_PIX_FMT_MTISP_SRGGB10F, > + .mdp_color = DIP_MCOLOR_FULLG10_RGGB, > + .depth = { 15 }, > + .row_depth = { 10 }, > + .num_planes = 1, > + .num_cplanes = 1, > + .pass_1_align = 4, > + }, > + { > + .format = V4L2_PIX_FMT_MTISP_SBGGR12, > + .mdp_color = DIP_MCOLOR_BAYER12_BGGR, > + .depth = { 12 }, > + .row_depth = { 12 }, > + .num_planes = 1, > + .num_cplanes = 1, > + .pass_1_align = 4, > + }, > + { > + .format = V4L2_PIX_FMT_MTISP_SGBRG12, > + .mdp_color = DIP_MCOLOR_BAYER12_GBRG, > + .depth = { 12 }, > + .row_depth = { 12 }, > + .num_planes = 1, > + .num_cplanes = 1, > + .pass_1_align = 4, > + }, > + { > + .format = V4L2_PIX_FMT_MTISP_SGRBG12, > + .mdp_color = DIP_MCOLOR_BAYER12_GRBG, > + .depth = { 12 }, > + .row_depth = { 12 }, > + .num_planes = 1, > + .num_cplanes = 1, > + .pass_1_align = 4, > + }, > + { > + .format = V4L2_PIX_FMT_MTISP_SRGGB12, > + .mdp_color = DIP_MCOLOR_BAYER12_RGGB, > + .depth = { 12 }, > + .row_depth = { 12 }, > + .num_planes = 1, > + .num_cplanes = 1, > + .pass_1_align = 4, > + }, > + { > + .format = V4L2_PIX_FMT_MTISP_SBGGR12F, > + .mdp_color = DIP_MCOLOR_FULLG12_BGGR, > + .depth = { 18 }, > + .row_depth = { 12 }, > + .num_planes = 1, > + .num_cplanes = 1, > + .pass_1_align = 8, > + }, > + { > + .format = V4L2_PIX_FMT_MTISP_SGBRG12F, > + .mdp_color = DIP_MCOLOR_FULLG12_GBRG, > + .depth = { 18 }, > + .row_depth = { 12 }, > + .num_planes = 1, > + .num_cplanes = 1, > + .pass_1_align = 8, > + }, > + { > + .format = V4L2_PIX_FMT_MTISP_SGRBG12F, > + .mdp_color = DIP_MCOLOR_FULLG12_GRBG, > + .depth = { 18 }, > + .row_depth = { 12 }, > + .num_planes = 1, > + .num_cplanes = 1, > + .pass_1_align = 8, > + }, > + { > + .format = V4L2_PIX_FMT_MTISP_SRGGB12F, > + .mdp_color = DIP_MCOLOR_FULLG12_RGGB, > + .depth = { 18 }, > + .row_depth = { 12 }, > + .num_planes = 1, > + .num_cplanes = 1, > + .pass_1_align = 8, > + }, > + { > + .format = V4L2_PIX_FMT_MTISP_SBGGR14F, > + .mdp_color = DIP_MCOLOR_FULLG14_BGGR, > + .depth = { 21 }, > + .row_depth = { 14 }, > + .num_planes = 1, > + .num_cplanes = 1, > + .pass_1_align = 8, > + }, > + { > + .format = V4L2_PIX_FMT_MTISP_SGBRG14F, > + .mdp_color = DIP_MCOLOR_FULLG14_GBRG, > + .depth = { 21 }, > + .row_depth = { 14 }, > + .num_planes = 1, > + .num_cplanes = 1, > + .pass_1_align = 8, > + }, > + { > + .format = V4L2_PIX_FMT_MTISP_SGRBG14F, > + .mdp_color = DIP_MCOLOR_FULLG14_GRBG, > + .depth = { 21 }, > + .row_depth = { 14 }, > + .num_planes = 1, > + .num_cplanes = 1, > + .pass_1_align = 8, > + }, > + { > + .format = V4L2_PIX_FMT_MTISP_SRGGB14F, > + .mdp_color = DIP_MCOLOR_FULLG14_RGGB, > + .depth = { 21 }, > + .row_depth = { 14 }, > + .num_planes = 1, > + .num_cplanes = 1, > + .pass_1_align = 8, > + }, > + { > + .format = V4L2_PIX_FMT_YUV420M, > + .mdp_color = DIP_MCOLOR_I420, > + .depth = { 8, 2, 2 }, > + .row_depth = { 8, 4, 4 }, > + .num_planes = 3, > + .num_cplanes = 1, > + }, > + { > + .format = V4L2_PIX_FMT_YVU420M, > + .mdp_color = DIP_MCOLOR_YV12, > + .depth = { 8, 2, 2 }, > + .row_depth = { 8, 4, 4 }, > + .num_planes = 3, > + .num_cplanes = 1, > + }, > + { > + .format = V4L2_PIX_FMT_NV12M, > + .mdp_color = DIP_MCOLOR_NV12, > + .depth = { 8, 4 }, > + .row_depth = { 8, 8 }, > + .num_planes = 2, > + .num_cplanes = 1, > + }, > +}; > + > +static const struct mtk_dip_dev_format mdp_fmts[] = { > + { > + .format = V4L2_PIX_FMT_VYUY, > + .mdp_color = DIP_MCOLOR_VYUY, > + .depth = { 16 }, > + .row_depth = { 16 }, > + .num_planes = 1, > + .num_cplanes = 1, > + }, > + { > + .format = V4L2_PIX_FMT_YUYV, > + .mdp_color = DIP_MCOLOR_YUYV, > + .depth = { 16 }, > + .row_depth = { 16 }, > + .num_planes = 1, > + .num_cplanes = 1, > + }, > + { > + .format = V4L2_PIX_FMT_YVYU, > + .mdp_color = DIP_MCOLOR_YVYU, > + .depth = { 16 }, > + .row_depth = { 16 }, > + .num_planes = 1, > + .num_cplanes = 1, > + }, > + { > + .format = V4L2_PIX_FMT_YVU420, > + .mdp_color = DIP_MCOLOR_YV12, > + .depth = { 12 }, > + .row_depth = { 8 }, > + .num_planes = 1, > + .num_cplanes = 3, > + }, > + { > + .format = V4L2_PIX_FMT_NV12, > + .mdp_color = DIP_MCOLOR_NV12, > + .depth = { 12 }, > + .row_depth = { 8 }, > + .num_planes = 1, > + .num_cplanes = 2, > + }, > + { > + .format = V4L2_PIX_FMT_YUV420M, > + .mdp_color = DIP_MCOLOR_I420, > + .depth = { 8, 2, 2 }, > + .row_depth = { 8, 4, 4 }, > + .num_planes = 3, > + .num_cplanes = 1, > + }, > + { > + .format = V4L2_PIX_FMT_YVU420M, > + .mdp_color = DIP_MCOLOR_YV12, > + .depth = { 8, 2, 2 }, > + .row_depth = { 8, 4, 4 }, > + .num_planes = 3, > + .num_cplanes = 1, > + }, > + { > + .format = V4L2_PIX_FMT_NV12M, > + .mdp_color = DIP_MCOLOR_NV12, > + .depth = { 8, 4 }, > + .row_depth = { 8, 8 }, > + .num_planes = 2, > + .num_cplanes = 1, > + } > +}; > + > +static const struct mtk_dip_dev_format img2_fmts[] = { > + { > + .format = V4L2_PIX_FMT_YUYV, > + .mdp_color = DIP_MCOLOR_YUYV, > + .depth = { 16 }, > + .row_depth = { 16 }, > + .num_planes = 1, > + .num_cplanes = 1, > + }, > +}; > + > +static const struct mtk_dip_dev_format img3_fmts[] = { > + { > + .format = V4L2_PIX_FMT_VYUY, > + .mdp_color = DIP_MCOLOR_VYUY, > + .depth = { 16 }, > + .row_depth = { 16 }, > + .num_planes = 1, > + .num_cplanes = 1, > + }, > + { > + .format = V4L2_PIX_FMT_YUYV, > + .mdp_color = DIP_MCOLOR_YUYV, > + .depth = { 16 }, > + .row_depth = { 16 }, > + .num_planes = 1, > + .num_cplanes = 1, > + }, > + { > + .format = V4L2_PIX_FMT_YVYU, > + .mdp_color = DIP_MCOLOR_YVYU, > + .depth = { 16 }, > + .row_depth = { 16 }, > + .num_planes = 1, > + .num_cplanes = 1, > + }, > + { > + .format = V4L2_PIX_FMT_YVU420, > + .mdp_color = DIP_MCOLOR_YV12, > + .depth = { 12 }, > + .row_depth = { 8 }, > + .num_planes = 1, > + .num_cplanes = 3, > + }, > + { > + .format = V4L2_PIX_FMT_NV12, > + .mdp_color = DIP_MCOLOR_NV12, > + .depth = { 12 }, > + .row_depth = { 8 }, > + .num_planes = 1, > + .num_cplanes = 2, > + } > +}; > + > +static const struct v4l2_frmsizeenum in_frmsizeenum = { > + .type = V4L2_FRMSIZE_TYPE_CONTINUOUS, > + .stepwise.max_width = MTK_DIP_CAPTURE_MAX_WIDTH, > + .stepwise.min_width = MTK_DIP_CAPTURE_MIN_WIDTH, > + .stepwise.max_height = MTK_DIP_CAPTURE_MAX_HEIGHT, > + .stepwise.min_height = MTK_DIP_CAPTURE_MIN_HEIGHT, > + .stepwise.step_height = 1, > + .stepwise.step_width = 1, > +}; > + > +static const struct v4l2_frmsizeenum out_frmsizeenum = { > + .type = V4L2_FRMSIZE_TYPE_CONTINUOUS, > + .stepwise.max_width = MTK_DIP_OUTPUT_MAX_WIDTH, > + .stepwise.min_width = MTK_DIP_OUTPUT_MIN_WIDTH, > + .stepwise.max_height = MTK_DIP_OUTPUT_MAX_HEIGHT, > + .stepwise.min_height = MTK_DIP_OUTPUT_MIN_HEIGHT, > + .stepwise.step_height = 1, > + .stepwise.step_width = 1, > +}; > + > +static const struct mtk_dip_video_device_desc > +queues_setting[MTK_DIP_VIDEO_NODE_ID_TOTAL_NUM] = { > + { > + .id = MTK_DIP_VIDEO_NODE_ID_RAW_OUT, > + .name = "Raw Input", > + .cap = V4L2_CAP_VIDEO_OUTPUT_MPLANE | V4L2_CAP_STREAMING, > + .buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, > + .smem_alloc = 0, > + .flags = MEDIA_LNK_FL_ENABLED, > + .fmts = in_fmts, > + .num_fmts = ARRAY_SIZE(in_fmts), > + .default_fmt_idx = 4, > + .default_width = MTK_DIP_OUTPUT_MAX_WIDTH, > + .default_height = MTK_DIP_OUTPUT_MAX_HEIGHT, > + .dma_port = 0, > + .frmsizeenum = &in_frmsizeenum, > + .ops = &mtk_dip_v4l2_video_out_ioctl_ops, > + .vb2_ops = &mtk_dip_vb2_video_ops, > + .description = "Main image source", > + }, > + { > + .id = MTK_DIP_VIDEO_NODE_ID_TUNING_OUT, > + .name = "Tuning", > + .cap = V4L2_CAP_META_OUTPUT | V4L2_CAP_STREAMING, > + .buf_type = V4L2_BUF_TYPE_META_OUTPUT, > + .smem_alloc = 1, > + .flags = 0, > + .fmts = fw_param_fmts, > + .num_fmts = ARRAY_SIZE(fw_param_fmts), > + .default_fmt_idx = 0, > + .dma_port = 0, > + .frmsizeenum = &in_frmsizeenum, > + .ops = &mtk_dip_v4l2_meta_out_ioctl_ops, > + .vb2_ops = &mtk_dip_vb2_meta_ops, > + .description = "Tuning data", > + }, > + { > + .id = MTK_DIP_VIDEO_NODE_ID_NR_OUT, > + .name = "NR Input", > + .cap = V4L2_CAP_VIDEO_OUTPUT_MPLANE | V4L2_CAP_STREAMING, > + .buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, > + .smem_alloc = 0, > + .flags = MEDIA_LNK_FL_DYNAMIC, > + .fmts = img3_fmts, > + .num_fmts = ARRAY_SIZE(img3_fmts), > + .default_fmt_idx = 1, > + .default_width = MTK_DIP_CAPTURE_MAX_WIDTH, > + .default_height = MTK_DIP_CAPTURE_MAX_HEIGHT, > + .dma_port = 1, > + .vb2_ops = &mtk_dip_vb2_video_ops, > + .frmsizeenum = &in_frmsizeenum, > + .ops = &mtk_dip_v4l2_video_out_ioctl_ops, > + .vb2_ops = &mtk_dip_vb2_video_ops, > + .description = "NR image source", > + }, > + { > + .id = MTK_DIP_VIDEO_NODE_ID_SHADING_OUT, > + .name = "Shading", > + .cap = V4L2_CAP_VIDEO_OUTPUT_MPLANE | V4L2_CAP_STREAMING, > + .buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, > + .smem_alloc = 0, > + .flags = MEDIA_LNK_FL_DYNAMIC, > + .fmts = lcei_fmts, > + .num_fmts = ARRAY_SIZE(lcei_fmts), > + .default_fmt_idx = 0, > + .default_width = MTK_DIP_CAPTURE_MAX_WIDTH, > + .default_height = MTK_DIP_CAPTURE_MAX_HEIGHT, > + .dma_port = 2, > + .frmsizeenum = &in_frmsizeenum, > + .ops = &mtk_dip_v4l2_video_out_ioctl_ops, > + .vb2_ops = &mtk_dip_vb2_video_ops, > + .description = "Shading image source", > + }, > + { > + .id = MTK_DIP_VIDEO_NODE_ID_MDP0_CAPTURE, > + .name = "MDP0", > + .cap = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING, > + .buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, > + .smem_alloc = 0, > + .supports_ctrls = true, > + .flags = MEDIA_LNK_FL_DYNAMIC, > + .fmts = mdp_fmts, > + .num_fmts = ARRAY_SIZE(mdp_fmts), > + .default_fmt_idx = 1, > + .default_width = MTK_DIP_CAPTURE_MAX_WIDTH, > + .default_height = MTK_DIP_CAPTURE_MAX_HEIGHT, > + .dma_port = 0, > + .frmsizeenum = &out_frmsizeenum, > + .ops = &mtk_dip_v4l2_video_cap_ioctl_ops, > + .vb2_ops = &mtk_dip_vb2_video_ops, > + .description = "Output quality enhanced image", > + }, > + { > + .id = MTK_DIP_VIDEO_NODE_ID_MDP1_CAPTURE, > + .name = "MDP1", > + .cap = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING, > + .buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, > + .smem_alloc = 0, > + .flags = MEDIA_LNK_FL_DYNAMIC, > + .fmts = mdp_fmts, > + .num_fmts = ARRAY_SIZE(mdp_fmts), > + .default_fmt_idx = 1, > + .default_width = MTK_DIP_CAPTURE_MAX_WIDTH, > + .default_height = MTK_DIP_CAPTURE_MAX_HEIGHT, > + .dma_port = 0, > + .frmsizeenum = &out_frmsizeenum, > + .ops = &mtk_dip_v4l2_video_cap_ioctl_ops, > + .vb2_ops = &mtk_dip_vb2_video_ops, > + .description = "Output quality enhanced image", > + > + }, > + { > + .id = MTK_DIP_VIDEO_NODE_ID_IMG2_CAPTURE, > + .name = "IMG2", > + .cap = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING, > + .buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, > + .smem_alloc = 0, > + .flags = MEDIA_LNK_FL_DYNAMIC, > + .fmts = img2_fmts, > + .num_fmts = ARRAY_SIZE(img2_fmts), > + .default_fmt_idx = 0, > + .default_width = MTK_DIP_CAPTURE_MAX_WIDTH, > + .default_height = MTK_DIP_CAPTURE_MAX_WIDTH, > + .dma_port = 1, > + .frmsizeenum = &out_frmsizeenum, > + .ops = &mtk_dip_v4l2_video_cap_ioctl_ops, > + .vb2_ops = &mtk_dip_vb2_video_ops, > + .description = "Output quality enhanced image", > + }, > + { > + .id = MTK_DIP_VIDEO_NODE_ID_IMG3_CAPTURE, > + .name = "IMG3", > + .cap = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING, > + .buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, > + .smem_alloc = 0, > + .flags = MEDIA_LNK_FL_DYNAMIC, > + .fmts = img3_fmts, > + .num_fmts = ARRAY_SIZE(img3_fmts), > + .default_fmt_idx = 1, > + .default_width = MTK_DIP_CAPTURE_MAX_WIDTH, > + .default_height = MTK_DIP_CAPTURE_MAX_WIDTH, > + .dma_port = 2, > + .frmsizeenum = &out_frmsizeenum, > + .ops = &mtk_dip_v4l2_video_cap_ioctl_ops, > + .vb2_ops = &mtk_dip_vb2_video_ops, > + .description = "Output quality enhanced image", > + > + }, > + > +}; > + > +static const struct mtk_dip_pipe_desc > +pipe_settings[MTK_DIP_PIPE_ID_TOTAL_NUM] = { > + { > + .name = "preview", > + .id = MTK_DIP_PIPE_ID_PREVIEW, > + .queue_descs = queues_setting, > + .total_queues = ARRAY_SIZE(queues_setting), > + }, > + { > + .name = "capture", > + .id = MTK_DIP_PIPE_ID_CAPTURE, > + .queue_descs = queues_setting, > + .total_queues = ARRAY_SIZE(queues_setting), > + > + }, > + { > + .name = "reprocess", > + .id = MTK_DIP_PIPE_ID_REPROCESS, > + .queue_descs = queues_setting, > + .total_queues = ARRAY_SIZE(queues_setting), > + }, > +}; > + > +static void mtk_dip_dev_media_unregister(struct mtk_dip_dev *dip_dev) > +{ > + media_device_unregister(&dip_dev->mdev); > + media_device_cleanup(&dip_dev->mdev); > +} > + > +static int mtk_dip_dev_v4l2_init(struct mtk_dip_dev *dip_dev) > +{ > + struct media_device *media_dev = &dip_dev->mdev; > + struct v4l2_device *v4l2_dev = &dip_dev->v4l2_dev; > + int i; > + int ret; > + > + ret = mtk_dip_dev_media_register(dip_dev->dev, media_dev); > + if (ret) { > + dev_err(dip_dev->dev, > + "%s: media device register failed(%d)\n", > + __func__, ret); > + return ret; > + } > + > + v4l2_dev->mdev = media_dev; > + v4l2_dev->ctrl_handler = NULL; > + > + ret = v4l2_device_register(dip_dev->dev, v4l2_dev); > + if (ret) { > + dev_err(dip_dev->dev, > + "%s: v4l2 device register failed(%d)\n", > + __func__, ret); > + goto err_release_media_device; > + } > + > + for (i = 0; i < MTK_DIP_PIPE_ID_TOTAL_NUM; i++) { > + ret = mtk_dip_pipe_init(dip_dev, &dip_dev->dip_pipe[i], > + &pipe_settings[i]); > + if (ret) { > + dev_err(dip_dev->dev, > + "%s: Pipe id(%d) init failed(%d)\n", > + dip_dev->dip_pipe[i].desc->name, > + i, ret); > + goto err_release_pipe; > + } > + } > + > + ret = v4l2_device_register_subdev_nodes(&dip_dev->v4l2_dev); > + if (ret) { > + dev_err(dip_dev->dev, > + "failed to register subdevs (%d)\n", ret); > + goto err_release_pipe; > + } > + > + return 0; > + > +err_release_pipe: > + for (i--; i >= 0; i--) > + mtk_dip_pipe_release(&dip_dev->dip_pipe[i]); > + > + v4l2_device_unregister(v4l2_dev); > + > +err_release_media_device: > + mtk_dip_dev_media_unregister(dip_dev); > + > + return ret; > +} > + > +void mtk_dip_dev_v4l2_release(struct mtk_dip_dev *dip_dev) > +{ > + int i; > + > + for (i = 0; i < MTK_DIP_PIPE_ID_TOTAL_NUM; i++) > + mtk_dip_pipe_release(&dip_dev->dip_pipe[i]); > + > + v4l2_device_unregister(&dip_dev->v4l2_dev); > + media_device_unregister(&dip_dev->mdev); > + media_device_cleanup(&dip_dev->mdev); > +} > + > +static int mtk_dip_res_init(struct platform_device *pdev, > + struct mtk_dip_dev *dip_dev) > +{ > + int ret; > + > + dip_dev->mdp_pdev = mdp_get_plat_device(pdev); > + if (!dip_dev->mdp_pdev) { > + dev_err(dip_dev->dev, > + "%s: failed to get MDP device\n", Fits on a single line. > + __func__); > + return -EINVAL; > + } > + > + dip_dev->mdpcb_wq = > + alloc_ordered_workqueue("%s", > + __WQ_LEGACY | WQ_MEM_RECLAIM | > + WQ_FREEZABLE, > + "mdp_callback"); > + if (!dip_dev->mdpcb_wq) { > + dev_err(dip_dev->dev, > + "%s: unable to alloc mdpcb workqueue\n", __func__); > + ret = -ENOMEM; > + goto destroy_mdpcb_wq; > + } > + > + dip_dev->composer_wq = > + alloc_ordered_workqueue("%s", > + __WQ_LEGACY | WQ_MEM_RECLAIM | > + WQ_FREEZABLE, > + "dip_composer"); > + if (!dip_dev->composer_wq) { > + dev_err(dip_dev->dev, > + "%s: unable to alloc composer workqueue\n", __func__); > + ret = -ENOMEM; > + goto destroy_dip_composer_wq; > + } > + > + dip_dev->mdp_wq = > + alloc_ordered_workqueue("%s", > + __WQ_LEGACY | WQ_MEM_RECLAIM | > + WQ_FREEZABLE, > + "dip_runner"); > + if (!dip_dev->mdp_wq) { > + dev_err(dip_dev->dev, > + "%s: unable to alloc dip_runner\n", __func__); > + ret = -ENOMEM; > + goto destroy_dip_runner_wq; > + } > + > + init_waitqueue_head(&dip_dev->flushing_waitq); > + > + return 0; > + > +destroy_dip_runner_wq: > + destroy_workqueue(dip_dev->mdp_wq); > + > +destroy_dip_composer_wq: > + destroy_workqueue(dip_dev->composer_wq); > + > +destroy_mdpcb_wq: > + destroy_workqueue(dip_dev->mdpcb_wq); > + > + return ret; > +} > + > +static void mtk_dip_res_release(struct mtk_dip_dev *dip_dev) > +{ > + flush_workqueue(dip_dev->mdp_wq); This seem to be redundant if you intend to call destroy_workqueue() next. Same below. > + destroy_workqueue(dip_dev->mdp_wq); > + dip_dev->mdp_wq = NULL; > + > + flush_workqueue(dip_dev->mdpcb_wq); > + destroy_workqueue(dip_dev->mdpcb_wq); > + dip_dev->mdpcb_wq = NULL; > + > + flush_workqueue(dip_dev->composer_wq); > + destroy_workqueue(dip_dev->composer_wq); > + dip_dev->composer_wq = NULL; > + > + atomic_set(&dip_dev->num_composing, 0); > + atomic_set(&dip_dev->dip_enqueue_cnt, 0); > +} > + > +static int mtk_dip_probe(struct platform_device *pdev) > +{ > + struct mtk_dip_dev *dip_dev; > + phandle rproc_phandle; > + int ret; > + > + dip_dev = devm_kzalloc(&pdev->dev, sizeof(*dip_dev), GFP_KERNEL); > + if (!dip_dev) > + return -ENOMEM; > + > + dip_dev->dev = &pdev->dev; > + dev_set_drvdata(&pdev->dev, dip_dev); > + dip_dev->dip_stream_cnt = 0; > + dip_dev->clks[0].id = "larb5"; > + dip_dev->clks[1].id = "dip"; > + dip_dev->num_clks = ARRAY_SIZE(dip_dev->clks); > + ret = devm_clk_bulk_get(&pdev->dev, dip_dev->num_clks, dip_dev->clks); > + if (ret) { > + dev_err(&pdev->dev, "Failed to get LARB5 and DIP clks:%d\n", > + ret); > + return ret; > + } > + > + dip_dev->scp_pdev = scp_get_pdev(pdev); > + if (!dip_dev->scp_pdev) { > + dev_err(dip_dev->dev, > + "%s: failed to get scp device\n", > + __func__); > + return -EINVAL; > + } > + > + if (of_property_read_u32(dip_dev->dev->of_node, "mediatek,scp", > + &rproc_phandle)) { > + dev_err(dip_dev->dev, > + "%s: could not get scp device\n", > + __func__); > + return -EINVAL; > + } > + > + dip_dev->rproc_handle = rproc_get_by_phandle(rproc_phandle); > + if (!dip_dev->rproc_handle) { > + dev_err(dip_dev->dev, > + "%s: could not get DIP's rproc_handle\n", > + __func__); > + return -EINVAL; > + } > + > + atomic_set(&dip_dev->dip_enqueue_cnt, 0); > + atomic_set(&dip_dev->num_composing, 0); > + mutex_init(&dip_dev->hw_op_lock); > + /* Limited by the co-processor side's stack size */ > + sema_init(&dip_dev->sem, DIP_COMPOSING_MAX_NUM); > + > + ret = mtk_dip_hw_working_buf_pool_init(dip_dev); > + if (ret) { > + dev_err(&pdev->dev, "working buffer init failed(%d)\n", ret); > + return ret; > + } > + > + ret = mtk_dip_dev_v4l2_init(dip_dev); > + if (ret) { > + mtk_dip_hw_working_buf_pool_release(dip_dev); > + dev_err(&pdev->dev, "v4l2 init failed(%d)\n", ret); > + > + goto err_release_working_buf_pool; > + } > + > + ret = mtk_dip_res_init(pdev, dip_dev); > + if (ret) { > + dev_err(dip_dev->dev, > + "%s: mtk_dip_res_init failed(%d)\n", __func__, ret); > + > + ret = -EBUSY; > + goto err_release_deinit_v4l2; > + } > + > + pm_runtime_set_autosuspend_delay(&pdev->dev, 1000 / 30 * > + DIP_COMPOSING_MAX_NUM * 3 * > + USEC_PER_MSEC); > + pm_runtime_use_autosuspend(&pdev->dev); > + pm_runtime_enable(&pdev->dev); > + > + return 0; > + > +err_release_deinit_v4l2: > + mtk_dip_dev_v4l2_release(dip_dev); > +err_release_working_buf_pool: > + mtk_dip_hw_working_buf_pool_release(dip_dev); > + > + return ret; > +} > + > +static int mtk_dip_remove(struct platform_device *pdev) > +{ > + struct mtk_dip_dev *dip_dev = dev_get_drvdata(&pdev->dev); > + > + mtk_dip_res_release(dip_dev); > + pm_runtime_disable(&pdev->dev); > + mtk_dip_dev_v4l2_release(dip_dev); > + mtk_dip_hw_working_buf_pool_release(dip_dev); > + mutex_destroy(&dip_dev->hw_op_lock); > + > + return 0; > +} > + > +static int __maybe_unused mtk_dip_runtime_suspend(struct device *dev) > +{ > + struct mtk_dip_dev *dip_dev = dev_get_drvdata(dev); > + > + rproc_shutdown(dip_dev->rproc_handle); > + clk_bulk_disable_unprepare(dip_dev->num_clks, > + dip_dev->clks); Fits on a single line. > + > + return 0; > +} > + > +static int __maybe_unused mtk_dip_runtime_resume(struct device *dev) > +{ > + struct mtk_dip_dev *dip_dev = dev_get_drvdata(dev); > + int ret; > + > + ret = clk_bulk_prepare_enable(dip_dev->num_clks, Ditto. > + dip_dev->clks); > + if (ret) { > + dev_err(dip_dev->dev, > + "%s: failed to enable dip clks(%d)\n", > + __func__, ret); > + return ret; > + } > + > + ret = rproc_boot(dip_dev->rproc_handle); > + if (ret) { > + dev_err(dev, "%s: FW load failed(rproc:%p):%d\n", > + __func__, dip_dev->rproc_handle, ret); > + clk_bulk_disable_unprepare(dip_dev->num_clks, > + dip_dev->clks); > + > + return ret; > + } > + > + return 0; > +} > + > +static int __maybe_unused mtk_dip_pm_suspend(struct device *dev) > +{ > + struct mtk_dip_dev *dip_dev = dev_get_drvdata(dev); > + int ret, num; > + > + if (pm_runtime_suspended(dev)) > + return 0; > + > + ret = wait_event_timeout > + (dip_dev->flushing_waitq, Please don't start a new line with an opening parenthesis around the list of arguments of a function call. > + !(num = atomic_read(&dip_dev->num_composing)), > + msecs_to_jiffies(1000 / 30 * DIP_COMPOSING_MAX_NUM * 3)); > + if (!ret && num) { > + dev_err(dev, "%s: flushing SCP job timeout, num(%d)\n", > + __func__, num); > + > + return -EBUSY; > + } > + > + ret = pm_runtime_force_suspend(dev); > + if (ret) > + return ret; > + > + return 0; > +} > + > +static int __maybe_unused mtk_dip_pm_resume(struct device *dev) > +{ > + int ret; > + > + if (pm_runtime_suspended(dev)) > + return 0; > + > + ret = pm_runtime_force_resume(dev); > + if (ret) > + return ret; > + > + return 0; > +} > + > +static const struct dev_pm_ops mtk_dip_pm_ops = { > + SET_SYSTEM_SLEEP_PM_OPS(mtk_dip_pm_suspend, mtk_dip_pm_resume) > + SET_RUNTIME_PM_OPS(mtk_dip_runtime_suspend, mtk_dip_runtime_resume, > + NULL) > +}; > + > +static const struct of_device_id mtk_dip_of_match[] = { > + { .compatible = "mediatek,mt8183-dip", }, > + {} > +}; > +MODULE_DEVICE_TABLE(of, mtk_dip_of_match); > + > +static struct platform_driver mtk_dip_driver = { > + .probe = mtk_dip_probe, > + .remove = mtk_dip_remove, > + .driver = { > + .name = "mtk-cam-dip", > + .pm = &mtk_dip_pm_ops, > + .of_match_table = of_match_ptr(mtk_dip_of_match), > + } > +}; > + > +module_platform_driver(mtk_dip_driver); > + > +MODULE_AUTHOR("Frederic Chen <frederic.chen@xxxxxxxxxxxx>"); > +MODULE_LICENSE("GPL v2"); > +MODULE_DESCRIPTION("Mediatek DIP driver"); -- Kind regards, Sakari Ailus