All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jerry-ch Chen <Jerry-ch.Chen@mediatek.com>
Cc: "devicetree@vger.kernel.org" <devicetree@vger.kernel.org>,
	"Sean Cheng (鄭昇弘)" <Sean.Cheng@mediatek.com>,
	"laurent.pinchart+renesas@ideasonboard.com"
	<laurent.pinchart+renesas@ideasonboard.com>,
	"Rynn Wu (吳育恩)" <Rynn.Wu@mediatek.com>,
	"Christie Yu (游雅惠)" <christie.yu@mediatek.com>,
	srv_heupstream <srv_heupstream@mediatek.com>,
	"Jungo Lin (林明俊)" <jungo.lin@mediatek.com>,
	"Po-Yang Huang (黃柏陽)" <po-yang.huang@mediatek.com>,
	"linux-mediatek@lists.infradead.org"
	<linux-mediatek@lists.infradead.org>,
	"tfiga@chromium.org" <tfiga@chromium.org>,
	"CK Hu (胡俊光)" <ck.hu@mediatek.com>,
	"Sj Huang (黃信璋)" <sj.huang@mediatek.co>
Subject: Re: [RFC PATCH V3 3/3] platform: mtk-isp: Add Mediatek FD driver
Date: Tue, 15 Oct 2019 11:16:15 +0800	[thread overview]
Message-ID: <1571109375.3706.40.camel@mtksdccf07> (raw)
In-Reply-To: <20190906101125.3784-4-Jerry-Ch.chen@mediatek.com>

Hi Tomasz,

On Fri, 2019-09-06 at 18:11 +0800, Jerry-ch Chen wrote:
> From: Jerry-ch Chen <jerry-ch.chen@mediatek.com>
> 
> This patch adds the driver of Face Detection (FD) unit in
> Mediatek camera system, providing face detection function.
> 
> 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: Jerry-ch Chen <jerry-ch.chen@mediatek.com>
> ---
>  drivers/media/platform/Kconfig                |    2 +
>  drivers/media/platform/Makefile               |    2 +
>  drivers/media/platform/mtk-isp/fd/Kconfig     |   19 +
>  drivers/media/platform/mtk-isp/fd/Makefile    |    5 +
>  drivers/media/platform/mtk-isp/fd/mtk_fd.h    |  148 ++
>  drivers/media/platform/mtk-isp/fd/mtk_fd_40.c | 1219 +++++++++++++++++
>  include/uapi/linux/mtk-fd-v4l2-controls.h     |   69 +
>  include/uapi/linux/v4l2-controls.h            |    4 +
>  8 files changed, 1468 insertions(+)
>  create mode 100644 drivers/media/platform/mtk-isp/fd/Kconfig
>  create mode 100644 drivers/media/platform/mtk-isp/fd/Makefile
>  create mode 100644 drivers/media/platform/mtk-isp/fd/mtk_fd.h
>  create mode 100644 drivers/media/platform/mtk-isp/fd/mtk_fd_40.c
>  create mode 100644 include/uapi/linux/mtk-fd-v4l2-controls.h
> 

[snip]

> +
> +static void mtk_fd_hw_job_finish(struct mtk_fd_dev *fd,
> +				 enum vb2_buffer_state vb_state)
> +{
> +	struct mtk_fd_ctx *ctx;
> +	struct vb2_v4l2_buffer *src_vbuf = NULL, *dst_vbuf = NULL;
> +
> +	ctx = v4l2_m2m_get_curr_priv(fd->m2m_dev);
> +	src_vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
> +	dst_vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
> +
> +	v4l2_m2m_buf_copy_metadata(src_vbuf, dst_vbuf,
> +				   V4L2_BUF_FLAG_TSTAMP_SRC_MASK);
> +	v4l2_m2m_buf_done(src_vbuf, vb_state);
> +	v4l2_m2m_buf_done(dst_vbuf, vb_state);
> +	v4l2_m2m_job_finish(fd->m2m_dev, ctx->fh.m2m_ctx);
> +}
> +


> +static void mtk_fd_ipi_handler(void *data, unsigned int len, void *priv)
> +{
> +	struct mtk_fd_dev *fd = (struct mtk_fd_dev *)priv;
> +	struct device *dev = fd->dev;
> +	struct ipi_message *fd_ack_msg = (struct ipi_message *)data;
> +	struct fd_ack_param *fd_ack = &fd_ack_msg->fd_ack_param;
> +
> +	dev_dbg(fd->dev, "fd_ipi_ack_id: %d\n", fd_ack_msg->cmd_id);
> +	switch (fd_ack_msg->cmd_id) {
> +	case MTK_FD_IPI_CMD_INIT_ACK:
> +		return;
> +	case MTK_FD_IPI_CMD_ENQ_ACK:
> +		if (fd_ack->ret_code) {
> +			dev_err(dev, "ipi ret: %d, message: %d\n",
> +				fd_ack->ret_code, fd_ack->ret_msg);
> +			pm_runtime_put((fd->dev));
> +			mtk_fd_hw_job_finish(fd, VB2_BUF_STATE_ERROR);
> +		}
> +		return;
> +	case MTK_FD_IPI_CMD_RESET_ACK:
> +		return;
> +	}
> +}
> +
> +static int mtk_fd_hw_connect(struct mtk_fd_dev *fd)
> +{
> +	int ret;
> +
> +	ret = rproc_boot(fd->rproc_handle);
> +
> +	if (ret < 0) {
> +		/**
> +		 * Return 0 if downloading firmware successfully,
> +		 * otherwise it is failed
> +		 */
> +		dev_err(fd->dev, "Failed to boot rproc\n");
> +		return ret;
> +	}
> +
> +	ret = scp_ipi_register(fd->scp_pdev, SCP_IPI_FD_CMD,
> +			       mtk_fd_ipi_handler, fd);
> +	if (ret) {
> +		dev_err(fd->dev, "Failed to register IPI cmd handler\n");
> +		goto err_rproc_shutdown;
> +	}
> +
> +	fd->fd_stream_count++;
> +	if (fd->fd_stream_count == 1) {
> +		if (mtk_fd_hw_enable(fd)) {
> +			ret = -EINVAL;
> +			goto err_rproc_shutdown;
> +		}
> +	}
> +	return 0;
> +
> +err_rproc_shutdown:
> +	rproc_shutdown(fd->rproc_handle);
> +	return ret;
> +}
> +
> +static void mtk_fd_hw_disconnect(struct mtk_fd_dev *fd)
> +{
> +	fd->fd_stream_count--;
> +
> +	if (fd->fd_stream_count == 0) {
> +		if (!IS_ERR(fd->rs_dma_buf))
> +			mtk_fd_free_dma_handle(fd);
> +
> +		rproc_shutdown(fd->rproc_handle);
> +	}
> +}
> +
> +static int mtk_fd_hw_job_exec(struct mtk_fd_dev *fd,
> +			      struct fd_enq_param *fd_param)
> +{
> +	struct ipi_message fd_ipi_msg;
> +	int ret;
> +
> +	pm_runtime_get_sync((fd->dev));
> +
> +	reinit_completion(&fd->fd_irq_done);
> +	fd_ipi_msg.cmd_id = MTK_FD_IPI_CMD_ENQUEUE;
> +	memcpy(&fd_ipi_msg.fd_enq_param, fd_param, sizeof(struct fd_enq_param));
> +	ret = scp_ipi_send(fd->scp_pdev, SCP_IPI_FD_CMD, &fd_ipi_msg,
> +			   sizeof(fd_ipi_msg), MTK_FD_IPI_SEND_TIMEOUT);
> +	if (ret) {
> +		pm_runtime_put((fd->dev));
> +		mtk_fd_hw_job_finish(fd, VB2_BUF_STATE_ERROR);
> +		return ret;
> +	}
> +	return 0;
> +}
> +
> +static int mtk_fd_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_fd_vb2_buf_prepare(struct vb2_buffer *vb)
> +{
> +	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> +	struct vb2_queue *vq = vb->vb2_queue;
> +	struct mtk_fd_ctx *ctx = vb2_get_drv_priv(vq);
> +	struct device *dev = ctx->dev;
> +	struct v4l2_pix_format_mplane *pixfmt;
> +
> +	switch (vq->type) {
> +	case V4L2_BUF_TYPE_META_CAPTURE:
> +		if (vb2_plane_size(vb, 0) < ctx->dst_fmt.buffersize) {
> +			dev_dbg(dev, "meta size %d is too small\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> +		pixfmt = &ctx->src_fmt;
> +
> +		if (vbuf->field == V4L2_FIELD_ANY)
> +			vbuf->field = V4L2_FIELD_NONE;
> +
> +		if (vb->num_planes > 2 || vbuf->field != V4L2_FIELD_NONE) {
> +			dev_dbg(dev, "plane or field %d not supported\n",
> +				vb->num_planes, vbuf->field);
> +			return -EINVAL;
> +		}
> +		if (vb2_plane_size(vb, 0) < pixfmt->plane_fmt[0].sizeimage) {
> +			dev_dbg(dev, "plane %d is too small\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	}
> +
> +	return 0;
> +}
> +
> +static void mtk_fd_vb2_buf_queue(struct vb2_buffer *vb)
> +{
> +	struct mtk_fd_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
> +	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> +
> +	v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
> +}
> +
> +static int mtk_fd_vb2_queue_setup(struct vb2_queue *vq,
> +				  unsigned int *num_buffers,
> +				  unsigned int *num_planes,
> +				  unsigned int sizes[],
> +				  struct device *alloc_devs[])
> +{
> +	struct mtk_fd_ctx *ctx = vb2_get_drv_priv(vq);
> +	unsigned int size[2];
> +	unsigned int plane;
> +
> +	switch (vq->type) {
> +	case V4L2_BUF_TYPE_META_CAPTURE:
> +		size[0] = ctx->dst_fmt.buffersize;
> +		break;
> +	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> +		size[0] = ctx->src_fmt.plane_fmt[0].sizeimage;
> +		if (*num_planes == 2)
> +			size[1] = ctx->src_fmt.plane_fmt[1].sizeimage;
> +		break;
> +	}
> +
> +	if (*num_planes > 2)
> +		return -EINVAL;
> +	if (*num_planes == 0) {
> +		if (vq->type == V4L2_BUF_TYPE_META_CAPTURE) {
> +			sizes[0] = ctx->dst_fmt.buffersize;
> +			*num_planes = 1;
> +			return 0;
> +		}
> +
> +		*num_planes = ctx->src_fmt.num_planes;
> +		for (plane = 0; plane < *num_planes; plane++)
> +			sizes[plane] = ctx->src_fmt.plane_fmt[plane].sizeimage;
> +		return 0;
> +	}
> +
> +	for (plane = 0; plane < *num_planes; plane++) {
> +		if (sizes[plane] < size[plane])
> +			return -EINVAL;
> +	}
> +	return 0;
> +}
> +
> +static int mtk_fd_vb2_start_streaming(struct vb2_queue *vq, unsigned int count)
> +{
> +	struct mtk_fd_ctx *ctx = vb2_get_drv_priv(vq);
> +
> +	if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> +		return mtk_fd_hw_connect(ctx->fd_dev);
> +	else
> +		return 0;
> +}
> +
> +static int mtk_fd_job_abort(struct mtk_fd_dev *fd)
> +{
> +	u32 ret;
> +
> +	ret = wait_for_completion_timeout(&fd->fd_irq_done,
> +					  msecs_to_jiffies(MTK_FD_HW_TIMEOUT));
> +	/* Reset FD HW */
> +	if (!ret) {
> +		struct ipi_message fd_ipi_msg;
> +
> +		fd_ipi_msg.cmd_id = MTK_FD_IPI_CMD_RESET;
> +		if (scp_ipi_send(fd->scp_pdev, SCP_IPI_FD_CMD, &fd_ipi_msg,
> +				 sizeof(fd_ipi_msg), MTK_FD_IPI_SEND_TIMEOUT))
> +			dev_err(fd->dev, "FD Reset HW error\n");
> +		return -ETIMEDOUT;
> +	}
> +	return 0;
> +}
> +

Continue the discussion about job abort in RFC v2,

I think the job_abort callback in v4l2_m2m_ops() might be useful.

ref:
https://elixir.bootlin.com/linux/v5.4-rc2/source/drivers/media/v4l2-core/v4l2-mem2mem.c#L398
https://elixir.bootlin.com/linux/v5.4-rc2/source/include/media/v4l2-mem2mem.h#L43

in drivers/media/v4l2-core/v4l2-mem2mem.c #398 v4l2_m2m_cancel_job()
...
if (m2m_ctx->job_flags & TRANS_RUNNING) {
	spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
	if (m2m_dev->m2m_ops->job_abort)
		m2m_dev->m2m_ops->job_abort(m2m_ctx->priv);
	dprintk("m2m_ctx %p running, will wait to complete\n", m2m_ctx);
	wait_event(m2m_ctx->finished,
			!(m2m_ctx->job_flags & TRANS_RUNNING));
} ...

If this operation is set, we might use the v4l2_m2m_cancel_job() when
suspend, and it will do mtk_fd_job_abort() here.


> +static void mtk_fd_vb2_stop_streaming(struct vb2_queue *vq)
> +{
> +	struct mtk_fd_ctx *ctx = vb2_get_drv_priv(vq);
> +	struct mtk_fd_dev *fd = ctx->fd_dev;
> +	struct vb2_v4l2_buffer *vb;
> +	struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx;
> +	struct v4l2_m2m_queue_ctx *queue_ctx;
> +
> +	mtk_fd_job_abort(fd);
> +	queue_ctx = V4L2_TYPE_IS_OUTPUT(vq->type) ?
> +					&m2m_ctx->out_q_ctx :
> +					&m2m_ctx->cap_q_ctx;
> +	while ((vb = v4l2_m2m_buf_remove(queue_ctx)))
> +		v4l2_m2m_buf_done(vb, VB2_BUF_STATE_ERROR);
> +
> +	if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> +		mtk_fd_hw_disconnect(fd);
> +}
> +

[snip]

> +struct v4l2_ctrl_config mtk_fd_controls[] = {
> +	{
> +		.id = V4L2_CID_MTK_FD_SCALE_DOWN_IMG_WIDTH,
> +		.name = "FD scale image widths",
> +		.type = V4L2_CTRL_TYPE_U16,
> +		.min = MTK_FD_OUTPUT_MIN_WIDTH,
> +		.max = MTK_FD_OUTPUT_MAX_WIDTH,
> +		.step = 1,
> +		.def = MTK_FD_OUTPUT_MAX_WIDTH,
> +		.dims = { MTK_FD_SCALE_ARR_NUM },
> +	},
> +	{
> +		.id = V4L2_CID_MTK_FD_SCALE_DOWN_IMG_HEIGHT,
> +		.name = "FD scale image heights",
> +		.type = V4L2_CTRL_TYPE_U16,
> +		.min = MTK_FD_OUTPUT_MIN_HEIGHT,
> +		.max = MTK_FD_OUTPUT_MAX_HEIGHT,
> +		.step = 1,
> +		.def = MTK_FD_OUTPUT_MAX_HEIGHT,
> +		.dims = { MTK_FD_SCALE_ARR_NUM },
> +	},
> +	{
> +		.id = V4L2_CID_MTK_FD_SCALE_IMG_NUM,
> +		.name = "FD scale size counts",
> +		.type = V4L2_CTRL_TYPE_INTEGER,
> +		.min = 0,
> +		.max = MTK_FD_SCALE_ARR_NUM,
> +		.step = 1,
> +		.def = 0,
> +	},
> +	{
> +		.id = V4L2_CID_MTK_FD_DETECT_POSE,
> +		.name = "FD detect face angle and directions",
> +		.type = V4L2_CTRL_TYPE_U16,
> +		.min = 0,
> +		.max = 0xffff,
> +		.step = 1,
> +		.def = 0x3ff,
> +		.dims = { MTK_FD_FACE_ANGLE_NUM},
> +	},
> +	{
> +		.id = V4L2_CID_MTK_FD_DETECT_SPEED,
> +		.name = "FD detection speedup",
> +		.type = V4L2_CTRL_TYPE_INTEGER,
> +		.min = 0,
> +		.max = MTK_FD_MAX_SPEEDUP,
> +		.step = 1,
> +		.def = 0,
> +	},
> +	{
> +		.id = V4L2_CID_MTK_FD_DETECTION_MODEL,
> +		.name = "FD use extra model",
> +		.type = V4L2_CTRL_TYPE_BOOLEAN,
> +		.min = 0,
> +		.max = 1,
> +		.step = 1,
> +		.def = 0,
> +	},
> +};
> +

[snip]

> +static int mtk_vfd_release(struct file *filp)
> +{
> +	struct mtk_fd_ctx *ctx = container_of(filp->private_data,
> +					      struct mtk_fd_ctx, fh);
> +
> +	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
> +
> +	v4l2_ctrl_handler_free(&ctx->hdl);
> +	v4l2_fh_del(&ctx->fh);
> +	v4l2_fh_exit(&ctx->fh);
> +
> +	kfree(ctx);
> +
> +	return 0;
> +}
> +
> +static const struct v4l2_file_operations fd_video_fops = {
> +	.owner = THIS_MODULE,
> +	.open = mtk_vfd_open,
> +	.release = mtk_vfd_release,
> +	.poll = v4l2_m2m_fop_poll,
> +	.unlocked_ioctl = video_ioctl2,
> +	.mmap = v4l2_m2m_fop_mmap,
> +#ifdef CONFIG_COMPAT
> +	.compat_ioctl32 = v4l2_compat_ioctl32,
> +#endif
> +
> +};
> +
> +static void mtk_fd_fill_user_param(struct user_param *user_param,
> +				   struct v4l2_ctrl_handler *hdl)
> +{
> +	struct v4l2_ctrl *ctrl;
> +	int i;
> +
> +	ctrl = v4l2_ctrl_find(hdl, V4L2_CID_MTK_FD_SCALE_DOWN_IMG_WIDTH);
> +	if (ctrl)
> +		for (i = 0; i < ctrl->elems; i++)
> +			user_param->scale_img_width[i] = ctrl->p_new.p_u16[i];
> +	ctrl = v4l2_ctrl_find(hdl, V4L2_CID_MTK_FD_SCALE_DOWN_IMG_HEIGHT);
> +	if (ctrl)
> +		for (i = 0; i < ctrl->elems; i++)
> +			user_param->scale_img_height[i] = ctrl->p_new.p_u16[i];
> +	ctrl = v4l2_ctrl_find(hdl, V4L2_CID_MTK_FD_SCALE_IMG_NUM);
> +	if (ctrl)
> +		user_param->scale_img_num = ctrl->val;
> +
> +	ctrl = v4l2_ctrl_find(hdl, V4L2_CID_MTK_FD_DETECT_POSE);
> +	if (ctrl)
> +		for (i = 0; i < ctrl->elems; i++)
> +			user_param->face_directions[i] = ctrl->p_new.p_u16[i];
> +	ctrl = v4l2_ctrl_find(hdl, V4L2_CID_MTK_FD_DETECT_SPEED);
> +	if (ctrl)
> +		user_param->fd_speedup = ctrl->val;
> +	ctrl = v4l2_ctrl_find(hdl, V4L2_CID_MTK_FD_DETECTION_MODEL);
> +	if (ctrl)
> +		user_param->fd_extra_model = ctrl->val;
> +}
> +
> +static void mtk_fd_device_run(void *priv)
> +{
> +	struct mtk_fd_ctx *ctx = priv;
> +	struct mtk_fd_dev *fd = ctx->fd_dev;
> +	struct vb2_v4l2_buffer *src_buf, *dst_buf;
> +	struct fd_enq_param fd_param;
> +
> +	src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
> +	dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
> +
> +	fd_param.src_img[0].dma_addr =
> +		vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
> +	fd_param.user_result.dma_addr =
> +		vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0);
> +	fd_param.output_vaddr = (u64)vb2_plane_vaddr(&dst_buf->vb2_buf, 0);
> +	fd_param.user_param.src_img_fmt =
> +		get_fd_img_fmt(ctx->src_fmt.pixelformat);
> +	if (ctx->src_fmt.num_planes == 2)
> +		fd_param.src_img[1].dma_addr =
> +			vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 1);
> +	mtk_fd_fill_user_param(&fd_param.user_param, &ctx->hdl);
> +
> +	/* Complete request controls if any */
> +	v4l2_ctrl_request_complete(src_buf->vb2_buf.req_obj.req, &ctx->hdl);
> +
> +	fd->output = (struct fd_user_output *)fd_param.output_vaddr;
> +	mtk_fd_hw_job_exec(fd, &fd_param);
> +}
> +
> +static struct v4l2_m2m_ops fd_m2m_ops = {
> +	.device_run = mtk_fd_device_run,
> +};
> +
> +static const struct media_device_ops fd_m2m_media_ops = {
> +	.req_validate	= vb2_request_validate,
> +	.req_queue	= v4l2_m2m_request_queue,
> +};

[snip]

> +
> +static int mtk_fd_suspend(struct device *dev)
> +{
> +	struct mtk_fd_dev *fd = dev_get_drvdata(dev);
> +
> +	if (pm_runtime_suspended(dev))
> +		return 0;
> +
> +	if (fd->fd_stream_count)
> +		if (mtk_fd_job_abort(fd))
> +			mtk_fd_hw_job_finish(fd, VB2_BUF_STATE_ERROR);
> +

To avoid mtk_fd_hw_job_finish() trigger the next job,
I suppose that we could use v4l2_m2m_cancel_job instead of job_abort and
job_finish here.

/**
 * v4l2_m2m_cancel_job() - cancel pending jobs for the context
 * @m2m_ctx: m2m context with jobs to be canceled
 *
 * In case of streamoff or release called on any context,
 * 1] If the context is currently running, then abort job will be called
 * 2] If the context is queued, then the context will be removed from
 *    the job_queue
 */

or another way,
we may add a flag and implement mtk_fd_job_ready() that reads the flag
if we suspend, we set the flag and do job_abort and job_finish, even if
it try enqueue, it will still not really queue the job, until we reset
the flag in mtk_fd_resume().

how do you think?

> +	/* suspend FD HW */
> +	writel(0x0, fd->fd_base + MTK_FD_REG_OFFSET_INT_EN);
> +	writel(0x0, fd->fd_base + MTK_FD_REG_OFFSET_HW_ENABLE);
> +	clk_disable_unprepare(fd->fd_clk);
> +	dev_dbg(dev, "%s:disable clock\n", __func__);
> +
> +	return 0;
> +}
> +
> +static int mtk_fd_resume(struct device *dev)
> +{
> +	struct mtk_fd_dev *fd = dev_get_drvdata(dev);
> +	int ret;
> +
> +	if (pm_runtime_suspended(dev))
> +		return 0;
> +
> +	ret = clk_prepare_enable(fd->fd_clk);
> +	if (ret < 0) {
> +		dev_dbg(dev, "Failed to open fd clk:%d\n", ret);
> +		return ret;
> +	}
> +
> +	/* resume FD HW */
> +	writel(MTK_FD_SET_HW_ENABLE, fd->fd_base + MTK_FD_REG_OFFSET_HW_ENABLE);
> +	writel(0x1, fd->fd_base + MTK_FD_REG_OFFSET_INT_EN);
> +	dev_dbg(dev, "%s:enable clock\n", __func__);
> +
> +	return 0;
> +}
> +
> +static int mtk_fd_runtime_suspend(struct device *dev)
> +{
> +	struct mtk_fd_dev *fd = dev_get_drvdata(dev);
> +
> +	clk_disable_unprepare(fd->fd_clk);
> +	return 0;
> +}
> +
> +static int mtk_fd_runtime_resume(struct device *dev)
> +{
> +	struct mtk_fd_dev *fd = dev_get_drvdata(dev);
> +	int ret;
> +
> +	ret = clk_prepare_enable(fd->fd_clk);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to open fd clk:%d\n", ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct dev_pm_ops mtk_fd_pm_ops = {
> +	SET_SYSTEM_SLEEP_PM_OPS(mtk_fd_suspend, mtk_fd_resume)
> +	SET_RUNTIME_PM_OPS(mtk_fd_runtime_suspend, mtk_fd_runtime_resume, NULL)
> +};
> +
> +static const struct of_device_id mtk_fd_of_ids[] = {
> +	{ .compatible = "mediatek,mt8183-fd", },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, mtk_fd_of_ids);
> +
> +static struct platform_driver mtk_fd_driver = {
> +	.probe   = mtk_fd_probe,
> +	.remove  = mtk_fd_remove,
> +	.driver  = {
> +		.name  = "mtk-fd-4.0",
> +		.of_match_table = of_match_ptr(mtk_fd_of_ids),
> +		.pm = &mtk_fd_pm_ops,
> +	}
> +};
> +module_platform_driver(mtk_fd_driver);
> +
> +MODULE_DESCRIPTION("Mediatek FD driver");
> +MODULE_LICENSE("GPL");
> diff --git a/include/uapi/linux/mtk-fd-v4l2-controls.h b/include/uapi/linux/mtk-fd-v4l2-controls.h
> new file mode 100644
> index 000000000000..9b814de05b0c
> --- /dev/null
> +++ b/include/uapi/linux/mtk-fd-v4l2-controls.h
> @@ -0,0 +1,69 @@
> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> +//
> +// Copyright (c) 2019 MediaTek Inc.
> +/*
> + * For V4L2_CID_MTK_FD_DETECT_POSE, User can set the desired face direction to
> + * be detected for each face angle, there are five face angle and 12 directions.
> + * Below shows the definition of face angle and face direction,
> + * and a recommended usage of for face detection, the more selected directions
> + * the longer HW process time needed.
> + *
> + * enum face_angle {
> + *	MTK_FD_FACE_FRONT,
> + *	MTK_FD_FACE_RIGHT_50,
> + *	MTK_FD_FACE_LEFT_50,
> + *	MTK_FD_FACE_RIGHT_90,
> + *	MTK_FD_FACE_LEFT_90,
> + *	MTK_FD_FACE_ANGLE_NUM,
> + * };
> + *
> + * struct face_direction_def {
> + *	__u16 MTK_FD_FACE_DIR_0 : 1,
> + *		MTK_FD_FACE_DIR_30 : 1,
> + *		MTK_FD_FACE_DIR_60 : 1,
> + *		MTK_FD_FACE_DIR_90 : 1,
> + *		MTK_FD_FACE_DIR_120 : 1,
> + *		MTK_FD_FACE_DIR_150 : 1,
> + *		MTK_FD_FACE_DIR_180 : 1,
> + *		MTK_FD_FACE_DIR_210 : 1,
> + *		MTK_FD_FACE_DIR_240 : 1,
> + *		MTK_FD_FACE_DIR_270 : 1,
> + *		MTK_FD_FACE_DIR_300 : 1,
> + *		MTK_FD_FACE_DIR_330 : 1,
> + *		: 4;
> + * };
> + *
> + * Sample usage:
> + * u16 face_directions[MTK_FD_FACE_ANGLE_NUM] = {0};
> + *
> + *	face_directions[MTK_FD_FACE_FRONT] = 0x3ff;
> + *
> + */
> +
> +#ifndef __UAPI_MTK_FD_V4L2_CONTROLS_H__
> +#define __UAPI_MTK_FD_V4L2_CONTROLS_H__
> +
> +#include <linux/v4l2-controls.h>
> +
> +/* Set the face angle and directions to be detected */
> +#define V4L2_CID_MTK_FD_DETECT_POSE		(V4L2_CID_USER_MTK_FD_BASE + 1)
> +
> +/* Set image widths for an input image to be scaled down for face detection */
> +#define V4L2_CID_MTK_FD_SCALE_DOWN_IMG_WIDTH	(V4L2_CID_USER_MTK_FD_BASE + 2)
> +
> +/* Set image heights for an input image to be scaled down for face detection */
> +#define V4L2_CID_MTK_FD_SCALE_DOWN_IMG_HEIGHT	(V4L2_CID_USER_MTK_FD_BASE + 3)
> +
> +/* Set the length of scale down size array */
> +#define V4L2_CID_MTK_FD_SCALE_IMG_NUM		(V4L2_CID_USER_MTK_FD_BASE + 4)
> +
> +/* Set the detection speed, usually reducing accuracy. */
> +#define V4L2_CID_MTK_FD_DETECT_SPEED		(V4L2_CID_USER_MTK_FD_BASE + 5)
> +
> +/* Select the detection model or algorithm to be used. */
> +#define V4L2_CID_MTK_FD_DETECTION_MODEL		(V4L2_CID_USER_MTK_FD_BASE + 6)
> +
> +/* We reserve 16 controls for this driver. */
> +#define V4L2_CID_MTK_FD_MAX			16
> +

For these control IDs, I think the following should be remained as chip
specific controls.
V4L2_CID_MTK_FD_SCALE_DOWN_IMG_WIDTH,
V4L2_CID_MTK_FD_SCALE_DOWN_IMG_HEIGHT and V4L2_CID_MTK_FD_SCALE_IMG_NUM 

Hope there would be standardizing face detection api that cover the rest
controls: V4L2_CID_MTK_FD_DETECT_POSE, V4L2_CID_MTK_FD_DETECT_SPEED and
V4L2_CID_MTK_FD_DETECTION_MODEL

Would you have any suggestions on how to propose the standard face
detection apis?

Thanks and best regards
Jerry

> +#endif /* __UAPI_MTK_FD_V4L2_CONTROLS_H__ */
> diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
> index 3dcfc6148f99..eae876ea6d0a 100644
> --- a/include/uapi/linux/v4l2-controls.h
> +++ b/include/uapi/linux/v4l2-controls.h
> @@ -192,6 +192,10 @@ enum v4l2_colorfx {
>   * We reserve 16 controls for this driver. */
>  #define V4L2_CID_USER_IMX_BASE			(V4L2_CID_USER_BASE + 0x10b0)
>  
> +/* The base for the mediatek FD driver controls */
> +/* We reserve 16 controls for this driver. */
> +#define V4L2_CID_USER_MTK_FD_BASE		(V4L2_CID_USER_BASE + 0x10d0)
> +
>  /* MPEG-class control IDs */
>  /* The MPEG controls are applicable to all codec controls
>   * and the 'MPEG' part of the define is historical */

WARNING: multiple messages have this Message-ID (diff)
From: Jerry-ch Chen <Jerry-ch.Chen@mediatek.com>
To: <tfiga@chromium.org>
Cc: hans.verkuil@cisco.com,
	"laurent.pinchart+renesas@ideasonboard.com"
	<laurent.pinchart+renesas@ideasonboard.com>,
	"tfiga@chromium.org" <tfiga@chromium.org>,
	"matthias.bgg@gmail.com" <matthias.bgg@gmail.com>,
	"mchehab@kernel.org" <mchehab@kernel.org>,
	"lkml@metux.net" <lkml@metux.net>,
	"CK Hu (胡俊光)" <ck.hu@mediatek.com>,
	"yuzhao@chromium.org" <yuzhao@chromium.org>,
	"zwisler@chromium.org" <zwisler@chromium.org>,
	"linux-mediatek@lists.infradead.org"
	<linux-mediatek@lists.infradead.org>,
	"linux-arm-kernel@lists.infradead.org"
	<linux-arm-kernel@lists.infradead.org>,
	"Sean Cheng (鄭昇弘)" <Sean.Cheng@mediatek.com>,
	"Sj Huang (黃信璋)" <sj.huang@mediatek.com>,
	"Christie Yu (游雅惠)" <christie.yu@mediatek.com>,
	"Frederic Chen (陳俊元)" <Frederic.Chen@mediatek.com>,
	"Jungo Lin (林明俊)" <jungo.lin@mediatek.com>,
	"Po-Yang Huang (黃柏陽)" <po-yang.huang@mediatek.com>,
	"Rynn Wu (吳育恩)" <Rynn.Wu@mediatek.com>,
	"linux-media@vger.kernel.org" <linux-media@vger.kernel.org>,
	srv_heupstream <srv_heupstream@mediatek.com>,
	"devicetree@vger.kernel.org" <devicetree@vger.kernel.org>
Subject: Re: [RFC PATCH V3 3/3] platform: mtk-isp: Add Mediatek FD driver
Date: Tue, 15 Oct 2019 11:16:15 +0800	[thread overview]
Message-ID: <1571109375.3706.40.camel@mtksdccf07> (raw)
In-Reply-To: <20190906101125.3784-4-Jerry-Ch.chen@mediatek.com>

Hi Tomasz,

On Fri, 2019-09-06 at 18:11 +0800, Jerry-ch Chen wrote:
> From: Jerry-ch Chen <jerry-ch.chen@mediatek.com>
> 
> This patch adds the driver of Face Detection (FD) unit in
> Mediatek camera system, providing face detection function.
> 
> 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: Jerry-ch Chen <jerry-ch.chen@mediatek.com>
> ---
>  drivers/media/platform/Kconfig                |    2 +
>  drivers/media/platform/Makefile               |    2 +
>  drivers/media/platform/mtk-isp/fd/Kconfig     |   19 +
>  drivers/media/platform/mtk-isp/fd/Makefile    |    5 +
>  drivers/media/platform/mtk-isp/fd/mtk_fd.h    |  148 ++
>  drivers/media/platform/mtk-isp/fd/mtk_fd_40.c | 1219 +++++++++++++++++
>  include/uapi/linux/mtk-fd-v4l2-controls.h     |   69 +
>  include/uapi/linux/v4l2-controls.h            |    4 +
>  8 files changed, 1468 insertions(+)
>  create mode 100644 drivers/media/platform/mtk-isp/fd/Kconfig
>  create mode 100644 drivers/media/platform/mtk-isp/fd/Makefile
>  create mode 100644 drivers/media/platform/mtk-isp/fd/mtk_fd.h
>  create mode 100644 drivers/media/platform/mtk-isp/fd/mtk_fd_40.c
>  create mode 100644 include/uapi/linux/mtk-fd-v4l2-controls.h
> 

[snip]

> +
> +static void mtk_fd_hw_job_finish(struct mtk_fd_dev *fd,
> +				 enum vb2_buffer_state vb_state)
> +{
> +	struct mtk_fd_ctx *ctx;
> +	struct vb2_v4l2_buffer *src_vbuf = NULL, *dst_vbuf = NULL;
> +
> +	ctx = v4l2_m2m_get_curr_priv(fd->m2m_dev);
> +	src_vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
> +	dst_vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
> +
> +	v4l2_m2m_buf_copy_metadata(src_vbuf, dst_vbuf,
> +				   V4L2_BUF_FLAG_TSTAMP_SRC_MASK);
> +	v4l2_m2m_buf_done(src_vbuf, vb_state);
> +	v4l2_m2m_buf_done(dst_vbuf, vb_state);
> +	v4l2_m2m_job_finish(fd->m2m_dev, ctx->fh.m2m_ctx);
> +}
> +


> +static void mtk_fd_ipi_handler(void *data, unsigned int len, void *priv)
> +{
> +	struct mtk_fd_dev *fd = (struct mtk_fd_dev *)priv;
> +	struct device *dev = fd->dev;
> +	struct ipi_message *fd_ack_msg = (struct ipi_message *)data;
> +	struct fd_ack_param *fd_ack = &fd_ack_msg->fd_ack_param;
> +
> +	dev_dbg(fd->dev, "fd_ipi_ack_id: %d\n", fd_ack_msg->cmd_id);
> +	switch (fd_ack_msg->cmd_id) {
> +	case MTK_FD_IPI_CMD_INIT_ACK:
> +		return;
> +	case MTK_FD_IPI_CMD_ENQ_ACK:
> +		if (fd_ack->ret_code) {
> +			dev_err(dev, "ipi ret: %d, message: %d\n",
> +				fd_ack->ret_code, fd_ack->ret_msg);
> +			pm_runtime_put((fd->dev));
> +			mtk_fd_hw_job_finish(fd, VB2_BUF_STATE_ERROR);
> +		}
> +		return;
> +	case MTK_FD_IPI_CMD_RESET_ACK:
> +		return;
> +	}
> +}
> +
> +static int mtk_fd_hw_connect(struct mtk_fd_dev *fd)
> +{
> +	int ret;
> +
> +	ret = rproc_boot(fd->rproc_handle);
> +
> +	if (ret < 0) {
> +		/**
> +		 * Return 0 if downloading firmware successfully,
> +		 * otherwise it is failed
> +		 */
> +		dev_err(fd->dev, "Failed to boot rproc\n");
> +		return ret;
> +	}
> +
> +	ret = scp_ipi_register(fd->scp_pdev, SCP_IPI_FD_CMD,
> +			       mtk_fd_ipi_handler, fd);
> +	if (ret) {
> +		dev_err(fd->dev, "Failed to register IPI cmd handler\n");
> +		goto err_rproc_shutdown;
> +	}
> +
> +	fd->fd_stream_count++;
> +	if (fd->fd_stream_count == 1) {
> +		if (mtk_fd_hw_enable(fd)) {
> +			ret = -EINVAL;
> +			goto err_rproc_shutdown;
> +		}
> +	}
> +	return 0;
> +
> +err_rproc_shutdown:
> +	rproc_shutdown(fd->rproc_handle);
> +	return ret;
> +}
> +
> +static void mtk_fd_hw_disconnect(struct mtk_fd_dev *fd)
> +{
> +	fd->fd_stream_count--;
> +
> +	if (fd->fd_stream_count == 0) {
> +		if (!IS_ERR(fd->rs_dma_buf))
> +			mtk_fd_free_dma_handle(fd);
> +
> +		rproc_shutdown(fd->rproc_handle);
> +	}
> +}
> +
> +static int mtk_fd_hw_job_exec(struct mtk_fd_dev *fd,
> +			      struct fd_enq_param *fd_param)
> +{
> +	struct ipi_message fd_ipi_msg;
> +	int ret;
> +
> +	pm_runtime_get_sync((fd->dev));
> +
> +	reinit_completion(&fd->fd_irq_done);
> +	fd_ipi_msg.cmd_id = MTK_FD_IPI_CMD_ENQUEUE;
> +	memcpy(&fd_ipi_msg.fd_enq_param, fd_param, sizeof(struct fd_enq_param));
> +	ret = scp_ipi_send(fd->scp_pdev, SCP_IPI_FD_CMD, &fd_ipi_msg,
> +			   sizeof(fd_ipi_msg), MTK_FD_IPI_SEND_TIMEOUT);
> +	if (ret) {
> +		pm_runtime_put((fd->dev));
> +		mtk_fd_hw_job_finish(fd, VB2_BUF_STATE_ERROR);
> +		return ret;
> +	}
> +	return 0;
> +}
> +
> +static int mtk_fd_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_fd_vb2_buf_prepare(struct vb2_buffer *vb)
> +{
> +	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> +	struct vb2_queue *vq = vb->vb2_queue;
> +	struct mtk_fd_ctx *ctx = vb2_get_drv_priv(vq);
> +	struct device *dev = ctx->dev;
> +	struct v4l2_pix_format_mplane *pixfmt;
> +
> +	switch (vq->type) {
> +	case V4L2_BUF_TYPE_META_CAPTURE:
> +		if (vb2_plane_size(vb, 0) < ctx->dst_fmt.buffersize) {
> +			dev_dbg(dev, "meta size %d is too small\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> +		pixfmt = &ctx->src_fmt;
> +
> +		if (vbuf->field == V4L2_FIELD_ANY)
> +			vbuf->field = V4L2_FIELD_NONE;
> +
> +		if (vb->num_planes > 2 || vbuf->field != V4L2_FIELD_NONE) {
> +			dev_dbg(dev, "plane or field %d not supported\n",
> +				vb->num_planes, vbuf->field);
> +			return -EINVAL;
> +		}
> +		if (vb2_plane_size(vb, 0) < pixfmt->plane_fmt[0].sizeimage) {
> +			dev_dbg(dev, "plane %d is too small\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	}
> +
> +	return 0;
> +}
> +
> +static void mtk_fd_vb2_buf_queue(struct vb2_buffer *vb)
> +{
> +	struct mtk_fd_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
> +	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> +
> +	v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
> +}
> +
> +static int mtk_fd_vb2_queue_setup(struct vb2_queue *vq,
> +				  unsigned int *num_buffers,
> +				  unsigned int *num_planes,
> +				  unsigned int sizes[],
> +				  struct device *alloc_devs[])
> +{
> +	struct mtk_fd_ctx *ctx = vb2_get_drv_priv(vq);
> +	unsigned int size[2];
> +	unsigned int plane;
> +
> +	switch (vq->type) {
> +	case V4L2_BUF_TYPE_META_CAPTURE:
> +		size[0] = ctx->dst_fmt.buffersize;
> +		break;
> +	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> +		size[0] = ctx->src_fmt.plane_fmt[0].sizeimage;
> +		if (*num_planes == 2)
> +			size[1] = ctx->src_fmt.plane_fmt[1].sizeimage;
> +		break;
> +	}
> +
> +	if (*num_planes > 2)
> +		return -EINVAL;
> +	if (*num_planes == 0) {
> +		if (vq->type == V4L2_BUF_TYPE_META_CAPTURE) {
> +			sizes[0] = ctx->dst_fmt.buffersize;
> +			*num_planes = 1;
> +			return 0;
> +		}
> +
> +		*num_planes = ctx->src_fmt.num_planes;
> +		for (plane = 0; plane < *num_planes; plane++)
> +			sizes[plane] = ctx->src_fmt.plane_fmt[plane].sizeimage;
> +		return 0;
> +	}
> +
> +	for (plane = 0; plane < *num_planes; plane++) {
> +		if (sizes[plane] < size[plane])
> +			return -EINVAL;
> +	}
> +	return 0;
> +}
> +
> +static int mtk_fd_vb2_start_streaming(struct vb2_queue *vq, unsigned int count)
> +{
> +	struct mtk_fd_ctx *ctx = vb2_get_drv_priv(vq);
> +
> +	if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> +		return mtk_fd_hw_connect(ctx->fd_dev);
> +	else
> +		return 0;
> +}
> +
> +static int mtk_fd_job_abort(struct mtk_fd_dev *fd)
> +{
> +	u32 ret;
> +
> +	ret = wait_for_completion_timeout(&fd->fd_irq_done,
> +					  msecs_to_jiffies(MTK_FD_HW_TIMEOUT));
> +	/* Reset FD HW */
> +	if (!ret) {
> +		struct ipi_message fd_ipi_msg;
> +
> +		fd_ipi_msg.cmd_id = MTK_FD_IPI_CMD_RESET;
> +		if (scp_ipi_send(fd->scp_pdev, SCP_IPI_FD_CMD, &fd_ipi_msg,
> +				 sizeof(fd_ipi_msg), MTK_FD_IPI_SEND_TIMEOUT))
> +			dev_err(fd->dev, "FD Reset HW error\n");
> +		return -ETIMEDOUT;
> +	}
> +	return 0;
> +}
> +

Continue the discussion about job abort in RFC v2,

I think the job_abort callback in v4l2_m2m_ops() might be useful.

ref:
https://elixir.bootlin.com/linux/v5.4-rc2/source/drivers/media/v4l2-core/v4l2-mem2mem.c#L398
https://elixir.bootlin.com/linux/v5.4-rc2/source/include/media/v4l2-mem2mem.h#L43

in drivers/media/v4l2-core/v4l2-mem2mem.c #398 v4l2_m2m_cancel_job()
...
if (m2m_ctx->job_flags & TRANS_RUNNING) {
	spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
	if (m2m_dev->m2m_ops->job_abort)
		m2m_dev->m2m_ops->job_abort(m2m_ctx->priv);
	dprintk("m2m_ctx %p running, will wait to complete\n", m2m_ctx);
	wait_event(m2m_ctx->finished,
			!(m2m_ctx->job_flags & TRANS_RUNNING));
} ...

If this operation is set, we might use the v4l2_m2m_cancel_job() when
suspend, and it will do mtk_fd_job_abort() here.


> +static void mtk_fd_vb2_stop_streaming(struct vb2_queue *vq)
> +{
> +	struct mtk_fd_ctx *ctx = vb2_get_drv_priv(vq);
> +	struct mtk_fd_dev *fd = ctx->fd_dev;
> +	struct vb2_v4l2_buffer *vb;
> +	struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx;
> +	struct v4l2_m2m_queue_ctx *queue_ctx;
> +
> +	mtk_fd_job_abort(fd);
> +	queue_ctx = V4L2_TYPE_IS_OUTPUT(vq->type) ?
> +					&m2m_ctx->out_q_ctx :
> +					&m2m_ctx->cap_q_ctx;
> +	while ((vb = v4l2_m2m_buf_remove(queue_ctx)))
> +		v4l2_m2m_buf_done(vb, VB2_BUF_STATE_ERROR);
> +
> +	if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> +		mtk_fd_hw_disconnect(fd);
> +}
> +

[snip]

> +struct v4l2_ctrl_config mtk_fd_controls[] = {
> +	{
> +		.id = V4L2_CID_MTK_FD_SCALE_DOWN_IMG_WIDTH,
> +		.name = "FD scale image widths",
> +		.type = V4L2_CTRL_TYPE_U16,
> +		.min = MTK_FD_OUTPUT_MIN_WIDTH,
> +		.max = MTK_FD_OUTPUT_MAX_WIDTH,
> +		.step = 1,
> +		.def = MTK_FD_OUTPUT_MAX_WIDTH,
> +		.dims = { MTK_FD_SCALE_ARR_NUM },
> +	},
> +	{
> +		.id = V4L2_CID_MTK_FD_SCALE_DOWN_IMG_HEIGHT,
> +		.name = "FD scale image heights",
> +		.type = V4L2_CTRL_TYPE_U16,
> +		.min = MTK_FD_OUTPUT_MIN_HEIGHT,
> +		.max = MTK_FD_OUTPUT_MAX_HEIGHT,
> +		.step = 1,
> +		.def = MTK_FD_OUTPUT_MAX_HEIGHT,
> +		.dims = { MTK_FD_SCALE_ARR_NUM },
> +	},
> +	{
> +		.id = V4L2_CID_MTK_FD_SCALE_IMG_NUM,
> +		.name = "FD scale size counts",
> +		.type = V4L2_CTRL_TYPE_INTEGER,
> +		.min = 0,
> +		.max = MTK_FD_SCALE_ARR_NUM,
> +		.step = 1,
> +		.def = 0,
> +	},
> +	{
> +		.id = V4L2_CID_MTK_FD_DETECT_POSE,
> +		.name = "FD detect face angle and directions",
> +		.type = V4L2_CTRL_TYPE_U16,
> +		.min = 0,
> +		.max = 0xffff,
> +		.step = 1,
> +		.def = 0x3ff,
> +		.dims = { MTK_FD_FACE_ANGLE_NUM},
> +	},
> +	{
> +		.id = V4L2_CID_MTK_FD_DETECT_SPEED,
> +		.name = "FD detection speedup",
> +		.type = V4L2_CTRL_TYPE_INTEGER,
> +		.min = 0,
> +		.max = MTK_FD_MAX_SPEEDUP,
> +		.step = 1,
> +		.def = 0,
> +	},
> +	{
> +		.id = V4L2_CID_MTK_FD_DETECTION_MODEL,
> +		.name = "FD use extra model",
> +		.type = V4L2_CTRL_TYPE_BOOLEAN,
> +		.min = 0,
> +		.max = 1,
> +		.step = 1,
> +		.def = 0,
> +	},
> +};
> +

[snip]

> +static int mtk_vfd_release(struct file *filp)
> +{
> +	struct mtk_fd_ctx *ctx = container_of(filp->private_data,
> +					      struct mtk_fd_ctx, fh);
> +
> +	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
> +
> +	v4l2_ctrl_handler_free(&ctx->hdl);
> +	v4l2_fh_del(&ctx->fh);
> +	v4l2_fh_exit(&ctx->fh);
> +
> +	kfree(ctx);
> +
> +	return 0;
> +}
> +
> +static const struct v4l2_file_operations fd_video_fops = {
> +	.owner = THIS_MODULE,
> +	.open = mtk_vfd_open,
> +	.release = mtk_vfd_release,
> +	.poll = v4l2_m2m_fop_poll,
> +	.unlocked_ioctl = video_ioctl2,
> +	.mmap = v4l2_m2m_fop_mmap,
> +#ifdef CONFIG_COMPAT
> +	.compat_ioctl32 = v4l2_compat_ioctl32,
> +#endif
> +
> +};
> +
> +static void mtk_fd_fill_user_param(struct user_param *user_param,
> +				   struct v4l2_ctrl_handler *hdl)
> +{
> +	struct v4l2_ctrl *ctrl;
> +	int i;
> +
> +	ctrl = v4l2_ctrl_find(hdl, V4L2_CID_MTK_FD_SCALE_DOWN_IMG_WIDTH);
> +	if (ctrl)
> +		for (i = 0; i < ctrl->elems; i++)
> +			user_param->scale_img_width[i] = ctrl->p_new.p_u16[i];
> +	ctrl = v4l2_ctrl_find(hdl, V4L2_CID_MTK_FD_SCALE_DOWN_IMG_HEIGHT);
> +	if (ctrl)
> +		for (i = 0; i < ctrl->elems; i++)
> +			user_param->scale_img_height[i] = ctrl->p_new.p_u16[i];
> +	ctrl = v4l2_ctrl_find(hdl, V4L2_CID_MTK_FD_SCALE_IMG_NUM);
> +	if (ctrl)
> +		user_param->scale_img_num = ctrl->val;
> +
> +	ctrl = v4l2_ctrl_find(hdl, V4L2_CID_MTK_FD_DETECT_POSE);
> +	if (ctrl)
> +		for (i = 0; i < ctrl->elems; i++)
> +			user_param->face_directions[i] = ctrl->p_new.p_u16[i];
> +	ctrl = v4l2_ctrl_find(hdl, V4L2_CID_MTK_FD_DETECT_SPEED);
> +	if (ctrl)
> +		user_param->fd_speedup = ctrl->val;
> +	ctrl = v4l2_ctrl_find(hdl, V4L2_CID_MTK_FD_DETECTION_MODEL);
> +	if (ctrl)
> +		user_param->fd_extra_model = ctrl->val;
> +}
> +
> +static void mtk_fd_device_run(void *priv)
> +{
> +	struct mtk_fd_ctx *ctx = priv;
> +	struct mtk_fd_dev *fd = ctx->fd_dev;
> +	struct vb2_v4l2_buffer *src_buf, *dst_buf;
> +	struct fd_enq_param fd_param;
> +
> +	src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
> +	dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
> +
> +	fd_param.src_img[0].dma_addr =
> +		vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
> +	fd_param.user_result.dma_addr =
> +		vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0);
> +	fd_param.output_vaddr = (u64)vb2_plane_vaddr(&dst_buf->vb2_buf, 0);
> +	fd_param.user_param.src_img_fmt =
> +		get_fd_img_fmt(ctx->src_fmt.pixelformat);
> +	if (ctx->src_fmt.num_planes == 2)
> +		fd_param.src_img[1].dma_addr =
> +			vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 1);
> +	mtk_fd_fill_user_param(&fd_param.user_param, &ctx->hdl);
> +
> +	/* Complete request controls if any */
> +	v4l2_ctrl_request_complete(src_buf->vb2_buf.req_obj.req, &ctx->hdl);
> +
> +	fd->output = (struct fd_user_output *)fd_param.output_vaddr;
> +	mtk_fd_hw_job_exec(fd, &fd_param);
> +}
> +
> +static struct v4l2_m2m_ops fd_m2m_ops = {
> +	.device_run = mtk_fd_device_run,
> +};
> +
> +static const struct media_device_ops fd_m2m_media_ops = {
> +	.req_validate	= vb2_request_validate,
> +	.req_queue	= v4l2_m2m_request_queue,
> +};

[snip]

> +
> +static int mtk_fd_suspend(struct device *dev)
> +{
> +	struct mtk_fd_dev *fd = dev_get_drvdata(dev);
> +
> +	if (pm_runtime_suspended(dev))
> +		return 0;
> +
> +	if (fd->fd_stream_count)
> +		if (mtk_fd_job_abort(fd))
> +			mtk_fd_hw_job_finish(fd, VB2_BUF_STATE_ERROR);
> +

To avoid mtk_fd_hw_job_finish() trigger the next job,
I suppose that we could use v4l2_m2m_cancel_job instead of job_abort and
job_finish here.

/**
 * v4l2_m2m_cancel_job() - cancel pending jobs for the context
 * @m2m_ctx: m2m context with jobs to be canceled
 *
 * In case of streamoff or release called on any context,
 * 1] If the context is currently running, then abort job will be called
 * 2] If the context is queued, then the context will be removed from
 *    the job_queue
 */

or another way,
we may add a flag and implement mtk_fd_job_ready() that reads the flag
if we suspend, we set the flag and do job_abort and job_finish, even if
it try enqueue, it will still not really queue the job, until we reset
the flag in mtk_fd_resume().

how do you think?

> +	/* suspend FD HW */
> +	writel(0x0, fd->fd_base + MTK_FD_REG_OFFSET_INT_EN);
> +	writel(0x0, fd->fd_base + MTK_FD_REG_OFFSET_HW_ENABLE);
> +	clk_disable_unprepare(fd->fd_clk);
> +	dev_dbg(dev, "%s:disable clock\n", __func__);
> +
> +	return 0;
> +}
> +
> +static int mtk_fd_resume(struct device *dev)
> +{
> +	struct mtk_fd_dev *fd = dev_get_drvdata(dev);
> +	int ret;
> +
> +	if (pm_runtime_suspended(dev))
> +		return 0;
> +
> +	ret = clk_prepare_enable(fd->fd_clk);
> +	if (ret < 0) {
> +		dev_dbg(dev, "Failed to open fd clk:%d\n", ret);
> +		return ret;
> +	}
> +
> +	/* resume FD HW */
> +	writel(MTK_FD_SET_HW_ENABLE, fd->fd_base + MTK_FD_REG_OFFSET_HW_ENABLE);
> +	writel(0x1, fd->fd_base + MTK_FD_REG_OFFSET_INT_EN);
> +	dev_dbg(dev, "%s:enable clock\n", __func__);
> +
> +	return 0;
> +}
> +
> +static int mtk_fd_runtime_suspend(struct device *dev)
> +{
> +	struct mtk_fd_dev *fd = dev_get_drvdata(dev);
> +
> +	clk_disable_unprepare(fd->fd_clk);
> +	return 0;
> +}
> +
> +static int mtk_fd_runtime_resume(struct device *dev)
> +{
> +	struct mtk_fd_dev *fd = dev_get_drvdata(dev);
> +	int ret;
> +
> +	ret = clk_prepare_enable(fd->fd_clk);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to open fd clk:%d\n", ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct dev_pm_ops mtk_fd_pm_ops = {
> +	SET_SYSTEM_SLEEP_PM_OPS(mtk_fd_suspend, mtk_fd_resume)
> +	SET_RUNTIME_PM_OPS(mtk_fd_runtime_suspend, mtk_fd_runtime_resume, NULL)
> +};
> +
> +static const struct of_device_id mtk_fd_of_ids[] = {
> +	{ .compatible = "mediatek,mt8183-fd", },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, mtk_fd_of_ids);
> +
> +static struct platform_driver mtk_fd_driver = {
> +	.probe   = mtk_fd_probe,
> +	.remove  = mtk_fd_remove,
> +	.driver  = {
> +		.name  = "mtk-fd-4.0",
> +		.of_match_table = of_match_ptr(mtk_fd_of_ids),
> +		.pm = &mtk_fd_pm_ops,
> +	}
> +};
> +module_platform_driver(mtk_fd_driver);
> +
> +MODULE_DESCRIPTION("Mediatek FD driver");
> +MODULE_LICENSE("GPL");
> diff --git a/include/uapi/linux/mtk-fd-v4l2-controls.h b/include/uapi/linux/mtk-fd-v4l2-controls.h
> new file mode 100644
> index 000000000000..9b814de05b0c
> --- /dev/null
> +++ b/include/uapi/linux/mtk-fd-v4l2-controls.h
> @@ -0,0 +1,69 @@
> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> +//
> +// Copyright (c) 2019 MediaTek Inc.
> +/*
> + * For V4L2_CID_MTK_FD_DETECT_POSE, User can set the desired face direction to
> + * be detected for each face angle, there are five face angle and 12 directions.
> + * Below shows the definition of face angle and face direction,
> + * and a recommended usage of for face detection, the more selected directions
> + * the longer HW process time needed.
> + *
> + * enum face_angle {
> + *	MTK_FD_FACE_FRONT,
> + *	MTK_FD_FACE_RIGHT_50,
> + *	MTK_FD_FACE_LEFT_50,
> + *	MTK_FD_FACE_RIGHT_90,
> + *	MTK_FD_FACE_LEFT_90,
> + *	MTK_FD_FACE_ANGLE_NUM,
> + * };
> + *
> + * struct face_direction_def {
> + *	__u16 MTK_FD_FACE_DIR_0 : 1,
> + *		MTK_FD_FACE_DIR_30 : 1,
> + *		MTK_FD_FACE_DIR_60 : 1,
> + *		MTK_FD_FACE_DIR_90 : 1,
> + *		MTK_FD_FACE_DIR_120 : 1,
> + *		MTK_FD_FACE_DIR_150 : 1,
> + *		MTK_FD_FACE_DIR_180 : 1,
> + *		MTK_FD_FACE_DIR_210 : 1,
> + *		MTK_FD_FACE_DIR_240 : 1,
> + *		MTK_FD_FACE_DIR_270 : 1,
> + *		MTK_FD_FACE_DIR_300 : 1,
> + *		MTK_FD_FACE_DIR_330 : 1,
> + *		: 4;
> + * };
> + *
> + * Sample usage:
> + * u16 face_directions[MTK_FD_FACE_ANGLE_NUM] = {0};
> + *
> + *	face_directions[MTK_FD_FACE_FRONT] = 0x3ff;
> + *
> + */
> +
> +#ifndef __UAPI_MTK_FD_V4L2_CONTROLS_H__
> +#define __UAPI_MTK_FD_V4L2_CONTROLS_H__
> +
> +#include <linux/v4l2-controls.h>
> +
> +/* Set the face angle and directions to be detected */
> +#define V4L2_CID_MTK_FD_DETECT_POSE		(V4L2_CID_USER_MTK_FD_BASE + 1)
> +
> +/* Set image widths for an input image to be scaled down for face detection */
> +#define V4L2_CID_MTK_FD_SCALE_DOWN_IMG_WIDTH	(V4L2_CID_USER_MTK_FD_BASE + 2)
> +
> +/* Set image heights for an input image to be scaled down for face detection */
> +#define V4L2_CID_MTK_FD_SCALE_DOWN_IMG_HEIGHT	(V4L2_CID_USER_MTK_FD_BASE + 3)
> +
> +/* Set the length of scale down size array */
> +#define V4L2_CID_MTK_FD_SCALE_IMG_NUM		(V4L2_CID_USER_MTK_FD_BASE + 4)
> +
> +/* Set the detection speed, usually reducing accuracy. */
> +#define V4L2_CID_MTK_FD_DETECT_SPEED		(V4L2_CID_USER_MTK_FD_BASE + 5)
> +
> +/* Select the detection model or algorithm to be used. */
> +#define V4L2_CID_MTK_FD_DETECTION_MODEL		(V4L2_CID_USER_MTK_FD_BASE + 6)
> +
> +/* We reserve 16 controls for this driver. */
> +#define V4L2_CID_MTK_FD_MAX			16
> +

For these control IDs, I think the following should be remained as chip
specific controls.
V4L2_CID_MTK_FD_SCALE_DOWN_IMG_WIDTH,
V4L2_CID_MTK_FD_SCALE_DOWN_IMG_HEIGHT and V4L2_CID_MTK_FD_SCALE_IMG_NUM 

Hope there would be standardizing face detection api that cover the rest
controls: V4L2_CID_MTK_FD_DETECT_POSE, V4L2_CID_MTK_FD_DETECT_SPEED and
V4L2_CID_MTK_FD_DETECTION_MODEL

Would you have any suggestions on how to propose the standard face
detection apis?

Thanks and best regards
Jerry

> +#endif /* __UAPI_MTK_FD_V4L2_CONTROLS_H__ */
> diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
> index 3dcfc6148f99..eae876ea6d0a 100644
> --- a/include/uapi/linux/v4l2-controls.h
> +++ b/include/uapi/linux/v4l2-controls.h
> @@ -192,6 +192,10 @@ enum v4l2_colorfx {
>   * We reserve 16 controls for this driver. */
>  #define V4L2_CID_USER_IMX_BASE			(V4L2_CID_USER_BASE + 0x10b0)
>  
> +/* The base for the mediatek FD driver controls */
> +/* We reserve 16 controls for this driver. */
> +#define V4L2_CID_USER_MTK_FD_BASE		(V4L2_CID_USER_BASE + 0x10d0)
> +
>  /* MPEG-class control IDs */
>  /* The MPEG controls are applicable to all codec controls
>   * and the 'MPEG' part of the define is historical */




WARNING: multiple messages have this Message-ID (diff)
From: Jerry-ch Chen <Jerry-ch.Chen@mediatek.com>
To: <tfiga@chromium.org>
Cc: "devicetree@vger.kernel.org" <devicetree@vger.kernel.org>,
	"Sean Cheng (鄭昇弘)" <Sean.Cheng@mediatek.com>,
	"laurent.pinchart+renesas@ideasonboard.com"
	<laurent.pinchart+renesas@ideasonboard.com>,
	"Rynn Wu (吳育恩)" <Rynn.Wu@mediatek.com>,
	"Christie Yu (游雅惠)" <christie.yu@mediatek.com>,
	srv_heupstream <srv_heupstream@mediatek.com>,
	"Jungo Lin (林明俊)" <jungo.lin@mediatek.com>,
	"Po-Yang Huang (黃柏陽)" <po-yang.huang@mediatek.com>,
	"linux-mediatek@lists.infradead.org"
	<linux-mediatek@lists.infradead.org>,
	"tfiga@chromium.org" <tfiga@chromium.org>,
	"CK Hu (胡俊光)" <ck.hu@mediatek.com>,
	"Sj Huang (黃信璋)" <sj.huang@mediatek.com>,
	"yuzhao@chromium.org" <yuzhao@chromium.org>,
	"lkml@metux.net" <lkml@metux.net>,
	"zwisler@chromium.org" <zwisler@chromium.org>,
	"Frederic Chen (陳俊元)" <Frederic.Chen@mediatek.com>,
	"matthias.bgg@gmail.com" <matthias.bgg@gmail.com>,
	hans.verkuil@cisco.com, "mchehab@kernel.org" <mchehab@kernel.org>,
	"linux-arm-kernel@lists.infradead.org"
	<linux-arm-kernel@lists.infradead.org>,
	"linux-media@vger.kernel.org" <linux-media@vger.kernel.org>
Subject: Re: [RFC PATCH V3 3/3] platform: mtk-isp: Add Mediatek FD driver
Date: Tue, 15 Oct 2019 11:16:15 +0800	[thread overview]
Message-ID: <1571109375.3706.40.camel@mtksdccf07> (raw)
In-Reply-To: <20190906101125.3784-4-Jerry-Ch.chen@mediatek.com>

Hi Tomasz,

On Fri, 2019-09-06 at 18:11 +0800, Jerry-ch Chen wrote:
> From: Jerry-ch Chen <jerry-ch.chen@mediatek.com>
> 
> This patch adds the driver of Face Detection (FD) unit in
> Mediatek camera system, providing face detection function.
> 
> 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: Jerry-ch Chen <jerry-ch.chen@mediatek.com>
> ---
>  drivers/media/platform/Kconfig                |    2 +
>  drivers/media/platform/Makefile               |    2 +
>  drivers/media/platform/mtk-isp/fd/Kconfig     |   19 +
>  drivers/media/platform/mtk-isp/fd/Makefile    |    5 +
>  drivers/media/platform/mtk-isp/fd/mtk_fd.h    |  148 ++
>  drivers/media/platform/mtk-isp/fd/mtk_fd_40.c | 1219 +++++++++++++++++
>  include/uapi/linux/mtk-fd-v4l2-controls.h     |   69 +
>  include/uapi/linux/v4l2-controls.h            |    4 +
>  8 files changed, 1468 insertions(+)
>  create mode 100644 drivers/media/platform/mtk-isp/fd/Kconfig
>  create mode 100644 drivers/media/platform/mtk-isp/fd/Makefile
>  create mode 100644 drivers/media/platform/mtk-isp/fd/mtk_fd.h
>  create mode 100644 drivers/media/platform/mtk-isp/fd/mtk_fd_40.c
>  create mode 100644 include/uapi/linux/mtk-fd-v4l2-controls.h
> 

[snip]

> +
> +static void mtk_fd_hw_job_finish(struct mtk_fd_dev *fd,
> +				 enum vb2_buffer_state vb_state)
> +{
> +	struct mtk_fd_ctx *ctx;
> +	struct vb2_v4l2_buffer *src_vbuf = NULL, *dst_vbuf = NULL;
> +
> +	ctx = v4l2_m2m_get_curr_priv(fd->m2m_dev);
> +	src_vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
> +	dst_vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
> +
> +	v4l2_m2m_buf_copy_metadata(src_vbuf, dst_vbuf,
> +				   V4L2_BUF_FLAG_TSTAMP_SRC_MASK);
> +	v4l2_m2m_buf_done(src_vbuf, vb_state);
> +	v4l2_m2m_buf_done(dst_vbuf, vb_state);
> +	v4l2_m2m_job_finish(fd->m2m_dev, ctx->fh.m2m_ctx);
> +}
> +


> +static void mtk_fd_ipi_handler(void *data, unsigned int len, void *priv)
> +{
> +	struct mtk_fd_dev *fd = (struct mtk_fd_dev *)priv;
> +	struct device *dev = fd->dev;
> +	struct ipi_message *fd_ack_msg = (struct ipi_message *)data;
> +	struct fd_ack_param *fd_ack = &fd_ack_msg->fd_ack_param;
> +
> +	dev_dbg(fd->dev, "fd_ipi_ack_id: %d\n", fd_ack_msg->cmd_id);
> +	switch (fd_ack_msg->cmd_id) {
> +	case MTK_FD_IPI_CMD_INIT_ACK:
> +		return;
> +	case MTK_FD_IPI_CMD_ENQ_ACK:
> +		if (fd_ack->ret_code) {
> +			dev_err(dev, "ipi ret: %d, message: %d\n",
> +				fd_ack->ret_code, fd_ack->ret_msg);
> +			pm_runtime_put((fd->dev));
> +			mtk_fd_hw_job_finish(fd, VB2_BUF_STATE_ERROR);
> +		}
> +		return;
> +	case MTK_FD_IPI_CMD_RESET_ACK:
> +		return;
> +	}
> +}
> +
> +static int mtk_fd_hw_connect(struct mtk_fd_dev *fd)
> +{
> +	int ret;
> +
> +	ret = rproc_boot(fd->rproc_handle);
> +
> +	if (ret < 0) {
> +		/**
> +		 * Return 0 if downloading firmware successfully,
> +		 * otherwise it is failed
> +		 */
> +		dev_err(fd->dev, "Failed to boot rproc\n");
> +		return ret;
> +	}
> +
> +	ret = scp_ipi_register(fd->scp_pdev, SCP_IPI_FD_CMD,
> +			       mtk_fd_ipi_handler, fd);
> +	if (ret) {
> +		dev_err(fd->dev, "Failed to register IPI cmd handler\n");
> +		goto err_rproc_shutdown;
> +	}
> +
> +	fd->fd_stream_count++;
> +	if (fd->fd_stream_count == 1) {
> +		if (mtk_fd_hw_enable(fd)) {
> +			ret = -EINVAL;
> +			goto err_rproc_shutdown;
> +		}
> +	}
> +	return 0;
> +
> +err_rproc_shutdown:
> +	rproc_shutdown(fd->rproc_handle);
> +	return ret;
> +}
> +
> +static void mtk_fd_hw_disconnect(struct mtk_fd_dev *fd)
> +{
> +	fd->fd_stream_count--;
> +
> +	if (fd->fd_stream_count == 0) {
> +		if (!IS_ERR(fd->rs_dma_buf))
> +			mtk_fd_free_dma_handle(fd);
> +
> +		rproc_shutdown(fd->rproc_handle);
> +	}
> +}
> +
> +static int mtk_fd_hw_job_exec(struct mtk_fd_dev *fd,
> +			      struct fd_enq_param *fd_param)
> +{
> +	struct ipi_message fd_ipi_msg;
> +	int ret;
> +
> +	pm_runtime_get_sync((fd->dev));
> +
> +	reinit_completion(&fd->fd_irq_done);
> +	fd_ipi_msg.cmd_id = MTK_FD_IPI_CMD_ENQUEUE;
> +	memcpy(&fd_ipi_msg.fd_enq_param, fd_param, sizeof(struct fd_enq_param));
> +	ret = scp_ipi_send(fd->scp_pdev, SCP_IPI_FD_CMD, &fd_ipi_msg,
> +			   sizeof(fd_ipi_msg), MTK_FD_IPI_SEND_TIMEOUT);
> +	if (ret) {
> +		pm_runtime_put((fd->dev));
> +		mtk_fd_hw_job_finish(fd, VB2_BUF_STATE_ERROR);
> +		return ret;
> +	}
> +	return 0;
> +}
> +
> +static int mtk_fd_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_fd_vb2_buf_prepare(struct vb2_buffer *vb)
> +{
> +	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> +	struct vb2_queue *vq = vb->vb2_queue;
> +	struct mtk_fd_ctx *ctx = vb2_get_drv_priv(vq);
> +	struct device *dev = ctx->dev;
> +	struct v4l2_pix_format_mplane *pixfmt;
> +
> +	switch (vq->type) {
> +	case V4L2_BUF_TYPE_META_CAPTURE:
> +		if (vb2_plane_size(vb, 0) < ctx->dst_fmt.buffersize) {
> +			dev_dbg(dev, "meta size %d is too small\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> +		pixfmt = &ctx->src_fmt;
> +
> +		if (vbuf->field == V4L2_FIELD_ANY)
> +			vbuf->field = V4L2_FIELD_NONE;
> +
> +		if (vb->num_planes > 2 || vbuf->field != V4L2_FIELD_NONE) {
> +			dev_dbg(dev, "plane or field %d not supported\n",
> +				vb->num_planes, vbuf->field);
> +			return -EINVAL;
> +		}
> +		if (vb2_plane_size(vb, 0) < pixfmt->plane_fmt[0].sizeimage) {
> +			dev_dbg(dev, "plane %d is too small\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	}
> +
> +	return 0;
> +}
> +
> +static void mtk_fd_vb2_buf_queue(struct vb2_buffer *vb)
> +{
> +	struct mtk_fd_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
> +	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> +
> +	v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
> +}
> +
> +static int mtk_fd_vb2_queue_setup(struct vb2_queue *vq,
> +				  unsigned int *num_buffers,
> +				  unsigned int *num_planes,
> +				  unsigned int sizes[],
> +				  struct device *alloc_devs[])
> +{
> +	struct mtk_fd_ctx *ctx = vb2_get_drv_priv(vq);
> +	unsigned int size[2];
> +	unsigned int plane;
> +
> +	switch (vq->type) {
> +	case V4L2_BUF_TYPE_META_CAPTURE:
> +		size[0] = ctx->dst_fmt.buffersize;
> +		break;
> +	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> +		size[0] = ctx->src_fmt.plane_fmt[0].sizeimage;
> +		if (*num_planes == 2)
> +			size[1] = ctx->src_fmt.plane_fmt[1].sizeimage;
> +		break;
> +	}
> +
> +	if (*num_planes > 2)
> +		return -EINVAL;
> +	if (*num_planes == 0) {
> +		if (vq->type == V4L2_BUF_TYPE_META_CAPTURE) {
> +			sizes[0] = ctx->dst_fmt.buffersize;
> +			*num_planes = 1;
> +			return 0;
> +		}
> +
> +		*num_planes = ctx->src_fmt.num_planes;
> +		for (plane = 0; plane < *num_planes; plane++)
> +			sizes[plane] = ctx->src_fmt.plane_fmt[plane].sizeimage;
> +		return 0;
> +	}
> +
> +	for (plane = 0; plane < *num_planes; plane++) {
> +		if (sizes[plane] < size[plane])
> +			return -EINVAL;
> +	}
> +	return 0;
> +}
> +
> +static int mtk_fd_vb2_start_streaming(struct vb2_queue *vq, unsigned int count)
> +{
> +	struct mtk_fd_ctx *ctx = vb2_get_drv_priv(vq);
> +
> +	if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> +		return mtk_fd_hw_connect(ctx->fd_dev);
> +	else
> +		return 0;
> +}
> +
> +static int mtk_fd_job_abort(struct mtk_fd_dev *fd)
> +{
> +	u32 ret;
> +
> +	ret = wait_for_completion_timeout(&fd->fd_irq_done,
> +					  msecs_to_jiffies(MTK_FD_HW_TIMEOUT));
> +	/* Reset FD HW */
> +	if (!ret) {
> +		struct ipi_message fd_ipi_msg;
> +
> +		fd_ipi_msg.cmd_id = MTK_FD_IPI_CMD_RESET;
> +		if (scp_ipi_send(fd->scp_pdev, SCP_IPI_FD_CMD, &fd_ipi_msg,
> +				 sizeof(fd_ipi_msg), MTK_FD_IPI_SEND_TIMEOUT))
> +			dev_err(fd->dev, "FD Reset HW error\n");
> +		return -ETIMEDOUT;
> +	}
> +	return 0;
> +}
> +

Continue the discussion about job abort in RFC v2,

I think the job_abort callback in v4l2_m2m_ops() might be useful.

ref:
https://elixir.bootlin.com/linux/v5.4-rc2/source/drivers/media/v4l2-core/v4l2-mem2mem.c#L398
https://elixir.bootlin.com/linux/v5.4-rc2/source/include/media/v4l2-mem2mem.h#L43

in drivers/media/v4l2-core/v4l2-mem2mem.c #398 v4l2_m2m_cancel_job()
...
if (m2m_ctx->job_flags & TRANS_RUNNING) {
	spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
	if (m2m_dev->m2m_ops->job_abort)
		m2m_dev->m2m_ops->job_abort(m2m_ctx->priv);
	dprintk("m2m_ctx %p running, will wait to complete\n", m2m_ctx);
	wait_event(m2m_ctx->finished,
			!(m2m_ctx->job_flags & TRANS_RUNNING));
} ...

If this operation is set, we might use the v4l2_m2m_cancel_job() when
suspend, and it will do mtk_fd_job_abort() here.


> +static void mtk_fd_vb2_stop_streaming(struct vb2_queue *vq)
> +{
> +	struct mtk_fd_ctx *ctx = vb2_get_drv_priv(vq);
> +	struct mtk_fd_dev *fd = ctx->fd_dev;
> +	struct vb2_v4l2_buffer *vb;
> +	struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx;
> +	struct v4l2_m2m_queue_ctx *queue_ctx;
> +
> +	mtk_fd_job_abort(fd);
> +	queue_ctx = V4L2_TYPE_IS_OUTPUT(vq->type) ?
> +					&m2m_ctx->out_q_ctx :
> +					&m2m_ctx->cap_q_ctx;
> +	while ((vb = v4l2_m2m_buf_remove(queue_ctx)))
> +		v4l2_m2m_buf_done(vb, VB2_BUF_STATE_ERROR);
> +
> +	if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> +		mtk_fd_hw_disconnect(fd);
> +}
> +

[snip]

> +struct v4l2_ctrl_config mtk_fd_controls[] = {
> +	{
> +		.id = V4L2_CID_MTK_FD_SCALE_DOWN_IMG_WIDTH,
> +		.name = "FD scale image widths",
> +		.type = V4L2_CTRL_TYPE_U16,
> +		.min = MTK_FD_OUTPUT_MIN_WIDTH,
> +		.max = MTK_FD_OUTPUT_MAX_WIDTH,
> +		.step = 1,
> +		.def = MTK_FD_OUTPUT_MAX_WIDTH,
> +		.dims = { MTK_FD_SCALE_ARR_NUM },
> +	},
> +	{
> +		.id = V4L2_CID_MTK_FD_SCALE_DOWN_IMG_HEIGHT,
> +		.name = "FD scale image heights",
> +		.type = V4L2_CTRL_TYPE_U16,
> +		.min = MTK_FD_OUTPUT_MIN_HEIGHT,
> +		.max = MTK_FD_OUTPUT_MAX_HEIGHT,
> +		.step = 1,
> +		.def = MTK_FD_OUTPUT_MAX_HEIGHT,
> +		.dims = { MTK_FD_SCALE_ARR_NUM },
> +	},
> +	{
> +		.id = V4L2_CID_MTK_FD_SCALE_IMG_NUM,
> +		.name = "FD scale size counts",
> +		.type = V4L2_CTRL_TYPE_INTEGER,
> +		.min = 0,
> +		.max = MTK_FD_SCALE_ARR_NUM,
> +		.step = 1,
> +		.def = 0,
> +	},
> +	{
> +		.id = V4L2_CID_MTK_FD_DETECT_POSE,
> +		.name = "FD detect face angle and directions",
> +		.type = V4L2_CTRL_TYPE_U16,
> +		.min = 0,
> +		.max = 0xffff,
> +		.step = 1,
> +		.def = 0x3ff,
> +		.dims = { MTK_FD_FACE_ANGLE_NUM},
> +	},
> +	{
> +		.id = V4L2_CID_MTK_FD_DETECT_SPEED,
> +		.name = "FD detection speedup",
> +		.type = V4L2_CTRL_TYPE_INTEGER,
> +		.min = 0,
> +		.max = MTK_FD_MAX_SPEEDUP,
> +		.step = 1,
> +		.def = 0,
> +	},
> +	{
> +		.id = V4L2_CID_MTK_FD_DETECTION_MODEL,
> +		.name = "FD use extra model",
> +		.type = V4L2_CTRL_TYPE_BOOLEAN,
> +		.min = 0,
> +		.max = 1,
> +		.step = 1,
> +		.def = 0,
> +	},
> +};
> +

[snip]

> +static int mtk_vfd_release(struct file *filp)
> +{
> +	struct mtk_fd_ctx *ctx = container_of(filp->private_data,
> +					      struct mtk_fd_ctx, fh);
> +
> +	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
> +
> +	v4l2_ctrl_handler_free(&ctx->hdl);
> +	v4l2_fh_del(&ctx->fh);
> +	v4l2_fh_exit(&ctx->fh);
> +
> +	kfree(ctx);
> +
> +	return 0;
> +}
> +
> +static const struct v4l2_file_operations fd_video_fops = {
> +	.owner = THIS_MODULE,
> +	.open = mtk_vfd_open,
> +	.release = mtk_vfd_release,
> +	.poll = v4l2_m2m_fop_poll,
> +	.unlocked_ioctl = video_ioctl2,
> +	.mmap = v4l2_m2m_fop_mmap,
> +#ifdef CONFIG_COMPAT
> +	.compat_ioctl32 = v4l2_compat_ioctl32,
> +#endif
> +
> +};
> +
> +static void mtk_fd_fill_user_param(struct user_param *user_param,
> +				   struct v4l2_ctrl_handler *hdl)
> +{
> +	struct v4l2_ctrl *ctrl;
> +	int i;
> +
> +	ctrl = v4l2_ctrl_find(hdl, V4L2_CID_MTK_FD_SCALE_DOWN_IMG_WIDTH);
> +	if (ctrl)
> +		for (i = 0; i < ctrl->elems; i++)
> +			user_param->scale_img_width[i] = ctrl->p_new.p_u16[i];
> +	ctrl = v4l2_ctrl_find(hdl, V4L2_CID_MTK_FD_SCALE_DOWN_IMG_HEIGHT);
> +	if (ctrl)
> +		for (i = 0; i < ctrl->elems; i++)
> +			user_param->scale_img_height[i] = ctrl->p_new.p_u16[i];
> +	ctrl = v4l2_ctrl_find(hdl, V4L2_CID_MTK_FD_SCALE_IMG_NUM);
> +	if (ctrl)
> +		user_param->scale_img_num = ctrl->val;
> +
> +	ctrl = v4l2_ctrl_find(hdl, V4L2_CID_MTK_FD_DETECT_POSE);
> +	if (ctrl)
> +		for (i = 0; i < ctrl->elems; i++)
> +			user_param->face_directions[i] = ctrl->p_new.p_u16[i];
> +	ctrl = v4l2_ctrl_find(hdl, V4L2_CID_MTK_FD_DETECT_SPEED);
> +	if (ctrl)
> +		user_param->fd_speedup = ctrl->val;
> +	ctrl = v4l2_ctrl_find(hdl, V4L2_CID_MTK_FD_DETECTION_MODEL);
> +	if (ctrl)
> +		user_param->fd_extra_model = ctrl->val;
> +}
> +
> +static void mtk_fd_device_run(void *priv)
> +{
> +	struct mtk_fd_ctx *ctx = priv;
> +	struct mtk_fd_dev *fd = ctx->fd_dev;
> +	struct vb2_v4l2_buffer *src_buf, *dst_buf;
> +	struct fd_enq_param fd_param;
> +
> +	src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
> +	dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
> +
> +	fd_param.src_img[0].dma_addr =
> +		vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
> +	fd_param.user_result.dma_addr =
> +		vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0);
> +	fd_param.output_vaddr = (u64)vb2_plane_vaddr(&dst_buf->vb2_buf, 0);
> +	fd_param.user_param.src_img_fmt =
> +		get_fd_img_fmt(ctx->src_fmt.pixelformat);
> +	if (ctx->src_fmt.num_planes == 2)
> +		fd_param.src_img[1].dma_addr =
> +			vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 1);
> +	mtk_fd_fill_user_param(&fd_param.user_param, &ctx->hdl);
> +
> +	/* Complete request controls if any */
> +	v4l2_ctrl_request_complete(src_buf->vb2_buf.req_obj.req, &ctx->hdl);
> +
> +	fd->output = (struct fd_user_output *)fd_param.output_vaddr;
> +	mtk_fd_hw_job_exec(fd, &fd_param);
> +}
> +
> +static struct v4l2_m2m_ops fd_m2m_ops = {
> +	.device_run = mtk_fd_device_run,
> +};
> +
> +static const struct media_device_ops fd_m2m_media_ops = {
> +	.req_validate	= vb2_request_validate,
> +	.req_queue	= v4l2_m2m_request_queue,
> +};

[snip]

> +
> +static int mtk_fd_suspend(struct device *dev)
> +{
> +	struct mtk_fd_dev *fd = dev_get_drvdata(dev);
> +
> +	if (pm_runtime_suspended(dev))
> +		return 0;
> +
> +	if (fd->fd_stream_count)
> +		if (mtk_fd_job_abort(fd))
> +			mtk_fd_hw_job_finish(fd, VB2_BUF_STATE_ERROR);
> +

To avoid mtk_fd_hw_job_finish() trigger the next job,
I suppose that we could use v4l2_m2m_cancel_job instead of job_abort and
job_finish here.

/**
 * v4l2_m2m_cancel_job() - cancel pending jobs for the context
 * @m2m_ctx: m2m context with jobs to be canceled
 *
 * In case of streamoff or release called on any context,
 * 1] If the context is currently running, then abort job will be called
 * 2] If the context is queued, then the context will be removed from
 *    the job_queue
 */

or another way,
we may add a flag and implement mtk_fd_job_ready() that reads the flag
if we suspend, we set the flag and do job_abort and job_finish, even if
it try enqueue, it will still not really queue the job, until we reset
the flag in mtk_fd_resume().

how do you think?

> +	/* suspend FD HW */
> +	writel(0x0, fd->fd_base + MTK_FD_REG_OFFSET_INT_EN);
> +	writel(0x0, fd->fd_base + MTK_FD_REG_OFFSET_HW_ENABLE);
> +	clk_disable_unprepare(fd->fd_clk);
> +	dev_dbg(dev, "%s:disable clock\n", __func__);
> +
> +	return 0;
> +}
> +
> +static int mtk_fd_resume(struct device *dev)
> +{
> +	struct mtk_fd_dev *fd = dev_get_drvdata(dev);
> +	int ret;
> +
> +	if (pm_runtime_suspended(dev))
> +		return 0;
> +
> +	ret = clk_prepare_enable(fd->fd_clk);
> +	if (ret < 0) {
> +		dev_dbg(dev, "Failed to open fd clk:%d\n", ret);
> +		return ret;
> +	}
> +
> +	/* resume FD HW */
> +	writel(MTK_FD_SET_HW_ENABLE, fd->fd_base + MTK_FD_REG_OFFSET_HW_ENABLE);
> +	writel(0x1, fd->fd_base + MTK_FD_REG_OFFSET_INT_EN);
> +	dev_dbg(dev, "%s:enable clock\n", __func__);
> +
> +	return 0;
> +}
> +
> +static int mtk_fd_runtime_suspend(struct device *dev)
> +{
> +	struct mtk_fd_dev *fd = dev_get_drvdata(dev);
> +
> +	clk_disable_unprepare(fd->fd_clk);
> +	return 0;
> +}
> +
> +static int mtk_fd_runtime_resume(struct device *dev)
> +{
> +	struct mtk_fd_dev *fd = dev_get_drvdata(dev);
> +	int ret;
> +
> +	ret = clk_prepare_enable(fd->fd_clk);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to open fd clk:%d\n", ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct dev_pm_ops mtk_fd_pm_ops = {
> +	SET_SYSTEM_SLEEP_PM_OPS(mtk_fd_suspend, mtk_fd_resume)
> +	SET_RUNTIME_PM_OPS(mtk_fd_runtime_suspend, mtk_fd_runtime_resume, NULL)
> +};
> +
> +static const struct of_device_id mtk_fd_of_ids[] = {
> +	{ .compatible = "mediatek,mt8183-fd", },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, mtk_fd_of_ids);
> +
> +static struct platform_driver mtk_fd_driver = {
> +	.probe   = mtk_fd_probe,
> +	.remove  = mtk_fd_remove,
> +	.driver  = {
> +		.name  = "mtk-fd-4.0",
> +		.of_match_table = of_match_ptr(mtk_fd_of_ids),
> +		.pm = &mtk_fd_pm_ops,
> +	}
> +};
> +module_platform_driver(mtk_fd_driver);
> +
> +MODULE_DESCRIPTION("Mediatek FD driver");
> +MODULE_LICENSE("GPL");
> diff --git a/include/uapi/linux/mtk-fd-v4l2-controls.h b/include/uapi/linux/mtk-fd-v4l2-controls.h
> new file mode 100644
> index 000000000000..9b814de05b0c
> --- /dev/null
> +++ b/include/uapi/linux/mtk-fd-v4l2-controls.h
> @@ -0,0 +1,69 @@
> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> +//
> +// Copyright (c) 2019 MediaTek Inc.
> +/*
> + * For V4L2_CID_MTK_FD_DETECT_POSE, User can set the desired face direction to
> + * be detected for each face angle, there are five face angle and 12 directions.
> + * Below shows the definition of face angle and face direction,
> + * and a recommended usage of for face detection, the more selected directions
> + * the longer HW process time needed.
> + *
> + * enum face_angle {
> + *	MTK_FD_FACE_FRONT,
> + *	MTK_FD_FACE_RIGHT_50,
> + *	MTK_FD_FACE_LEFT_50,
> + *	MTK_FD_FACE_RIGHT_90,
> + *	MTK_FD_FACE_LEFT_90,
> + *	MTK_FD_FACE_ANGLE_NUM,
> + * };
> + *
> + * struct face_direction_def {
> + *	__u16 MTK_FD_FACE_DIR_0 : 1,
> + *		MTK_FD_FACE_DIR_30 : 1,
> + *		MTK_FD_FACE_DIR_60 : 1,
> + *		MTK_FD_FACE_DIR_90 : 1,
> + *		MTK_FD_FACE_DIR_120 : 1,
> + *		MTK_FD_FACE_DIR_150 : 1,
> + *		MTK_FD_FACE_DIR_180 : 1,
> + *		MTK_FD_FACE_DIR_210 : 1,
> + *		MTK_FD_FACE_DIR_240 : 1,
> + *		MTK_FD_FACE_DIR_270 : 1,
> + *		MTK_FD_FACE_DIR_300 : 1,
> + *		MTK_FD_FACE_DIR_330 : 1,
> + *		: 4;
> + * };
> + *
> + * Sample usage:
> + * u16 face_directions[MTK_FD_FACE_ANGLE_NUM] = {0};
> + *
> + *	face_directions[MTK_FD_FACE_FRONT] = 0x3ff;
> + *
> + */
> +
> +#ifndef __UAPI_MTK_FD_V4L2_CONTROLS_H__
> +#define __UAPI_MTK_FD_V4L2_CONTROLS_H__
> +
> +#include <linux/v4l2-controls.h>
> +
> +/* Set the face angle and directions to be detected */
> +#define V4L2_CID_MTK_FD_DETECT_POSE		(V4L2_CID_USER_MTK_FD_BASE + 1)
> +
> +/* Set image widths for an input image to be scaled down for face detection */
> +#define V4L2_CID_MTK_FD_SCALE_DOWN_IMG_WIDTH	(V4L2_CID_USER_MTK_FD_BASE + 2)
> +
> +/* Set image heights for an input image to be scaled down for face detection */
> +#define V4L2_CID_MTK_FD_SCALE_DOWN_IMG_HEIGHT	(V4L2_CID_USER_MTK_FD_BASE + 3)
> +
> +/* Set the length of scale down size array */
> +#define V4L2_CID_MTK_FD_SCALE_IMG_NUM		(V4L2_CID_USER_MTK_FD_BASE + 4)
> +
> +/* Set the detection speed, usually reducing accuracy. */
> +#define V4L2_CID_MTK_FD_DETECT_SPEED		(V4L2_CID_USER_MTK_FD_BASE + 5)
> +
> +/* Select the detection model or algorithm to be used. */
> +#define V4L2_CID_MTK_FD_DETECTION_MODEL		(V4L2_CID_USER_MTK_FD_BASE + 6)
> +
> +/* We reserve 16 controls for this driver. */
> +#define V4L2_CID_MTK_FD_MAX			16
> +

For these control IDs, I think the following should be remained as chip
specific controls.
V4L2_CID_MTK_FD_SCALE_DOWN_IMG_WIDTH,
V4L2_CID_MTK_FD_SCALE_DOWN_IMG_HEIGHT and V4L2_CID_MTK_FD_SCALE_IMG_NUM 

Hope there would be standardizing face detection api that cover the rest
controls: V4L2_CID_MTK_FD_DETECT_POSE, V4L2_CID_MTK_FD_DETECT_SPEED and
V4L2_CID_MTK_FD_DETECTION_MODEL

Would you have any suggestions on how to propose the standard face
detection apis?

Thanks and best regards
Jerry

> +#endif /* __UAPI_MTK_FD_V4L2_CONTROLS_H__ */
> diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
> index 3dcfc6148f99..eae876ea6d0a 100644
> --- a/include/uapi/linux/v4l2-controls.h
> +++ b/include/uapi/linux/v4l2-controls.h
> @@ -192,6 +192,10 @@ enum v4l2_colorfx {
>   * We reserve 16 controls for this driver. */
>  #define V4L2_CID_USER_IMX_BASE			(V4L2_CID_USER_BASE + 0x10b0)
>  
> +/* The base for the mediatek FD driver controls */
> +/* We reserve 16 controls for this driver. */
> +#define V4L2_CID_USER_MTK_FD_BASE		(V4L2_CID_USER_BASE + 0x10d0)
> +
>  /* MPEG-class control IDs */
>  /* The MPEG controls are applicable to all codec controls
>   * and the 'MPEG' part of the define is historical */




_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

  reply	other threads:[~2019-10-15  3:16 UTC|newest]

Thread overview: 36+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-09-06 10:11 [RFC PATCH V3 0/3] media: platform: Add support for Face Detection (FD) on mt8183 SoC Jerry-ch Chen
2019-09-06 10:11 ` Jerry-ch Chen
2019-09-06 10:11 ` Jerry-ch Chen
2019-09-06 10:11 ` [RFC PATCH V3 1/3] dt-bindings: mt8183: Added FD dt-bindings Jerry-ch Chen
2019-09-06 10:11   ` Jerry-ch Chen
2019-09-06 10:11   ` Jerry-ch Chen
     [not found]   ` <20190906101125.3784-2-Jerry-Ch.chen-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
2019-09-17 19:00     ` Rob Herring
2019-09-17 19:00       ` Rob Herring
2019-09-17 19:00       ` Rob Herring
2019-09-06 10:11 ` [RFC PATCH V3 2/3] dts: arm64: mt8183: Add FD nodes Jerry-ch Chen
2019-09-06 10:11   ` Jerry-ch Chen
2019-09-06 10:11   ` Jerry-ch Chen
2019-09-06 10:11 ` [RFC PATCH V3 3/3] platform: mtk-isp: Add Mediatek FD driver Jerry-ch Chen
2019-09-06 10:11   ` Jerry-ch Chen
2019-09-06 10:11   ` Jerry-ch Chen
2019-10-15  3:16   ` Jerry-ch Chen [this message]
2019-10-15  3:16     ` Jerry-ch Chen
2019-10-15  3:16     ` Jerry-ch Chen
2019-10-25  3:52     ` Tomasz Figa
2019-10-25  3:52       ` Tomasz Figa
2019-10-25  3:52       ` Tomasz Figa
2019-11-20  8:10       ` Jerry-ch Chen
2019-11-20  8:10         ` Jerry-ch Chen
2019-11-20  8:10         ` Jerry-ch Chen
2019-11-20  9:28         ` Tomasz Figa
2019-11-20  9:28           ` Tomasz Figa
2019-11-20  9:28           ` Tomasz Figa
2019-11-29 16:55           ` Jerry-ch Chen
2019-11-29 16:55             ` Jerry-ch Chen
2019-11-29 16:55             ` Jerry-ch Chen
2019-12-02  9:48             ` Tomasz Figa
2019-12-02  9:48               ` Tomasz Figa
2019-12-02  9:48               ` Tomasz Figa
2019-12-03  7:36               ` Jerry-ch Chen
2019-12-03  7:36                 ` Jerry-ch Chen
2019-12-03  7:36                 ` Jerry-ch Chen

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1571109375.3706.40.camel@mtksdccf07 \
    --to=jerry-ch.chen@mediatek.com \
    --cc=Rynn.Wu@mediatek.com \
    --cc=Sean.Cheng@mediatek.com \
    --cc=christie.yu@mediatek.com \
    --cc=ck.hu@mediatek.com \
    --cc=devicetree@vger.kernel.org \
    --cc=jungo.lin@mediatek.com \
    --cc=laurent.pinchart+renesas@ideasonboard.com \
    --cc=linux-mediatek@lists.infradead.org \
    --cc=po-yang.huang@mediatek.com \
    --cc=sj.huang@mediatek.co \
    --cc=srv_heupstream@mediatek.com \
    --cc=tfiga@chromium.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.