Subject: Re: [RFC PATCH V3 3/3] platform: mtk-isp: Add Mediatek FD driver

From: Jerry-ch Chen <[email-masked]>
Date: Tue, 15 Oct 2019 11:16:15 +0800

Hi Tomasz,

On Fri, 2019-09-06 at 18:11 +0800, Jerry-ch Chen wrote:
> From: Jerry-ch Chen <[email-masked]>
> 
> 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 <[email-masked]>
> ---
>  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
[email-masked]
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel