162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Hantro VPU codec driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2018 Collabora, Ltd.
662306a36Sopenharmony_ci * Copyright 2018 Google LLC.
762306a36Sopenharmony_ci *	Tomasz Figa <tfiga@chromium.org>
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Based on s5p-mfc driver by Samsung Electronics Co., Ltd.
1062306a36Sopenharmony_ci * Copyright (C) 2011 Samsung Electronics Co., Ltd.
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/clk.h>
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/of.h>
1662306a36Sopenharmony_ci#include <linux/platform_device.h>
1762306a36Sopenharmony_ci#include <linux/pm.h>
1862306a36Sopenharmony_ci#include <linux/pm_runtime.h>
1962306a36Sopenharmony_ci#include <linux/slab.h>
2062306a36Sopenharmony_ci#include <linux/videodev2.h>
2162306a36Sopenharmony_ci#include <linux/workqueue.h>
2262306a36Sopenharmony_ci#include <media/v4l2-event.h>
2362306a36Sopenharmony_ci#include <media/v4l2-mem2mem.h>
2462306a36Sopenharmony_ci#include <media/videobuf2-core.h>
2562306a36Sopenharmony_ci#include <media/videobuf2-vmalloc.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#include "hantro_v4l2.h"
2862306a36Sopenharmony_ci#include "hantro.h"
2962306a36Sopenharmony_ci#include "hantro_hw.h"
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define DRIVER_NAME "hantro-vpu"
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ciint hantro_debug;
3462306a36Sopenharmony_cimodule_param_named(debug, hantro_debug, int, 0644);
3562306a36Sopenharmony_ciMODULE_PARM_DESC(debug,
3662306a36Sopenharmony_ci		 "Debug level - higher value produces more verbose messages");
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_civoid *hantro_get_ctrl(struct hantro_ctx *ctx, u32 id)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	struct v4l2_ctrl *ctrl;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	ctrl = v4l2_ctrl_find(&ctx->ctrl_handler, id);
4362306a36Sopenharmony_ci	return ctrl ? ctrl->p_cur.p : NULL;
4462306a36Sopenharmony_ci}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cidma_addr_t hantro_get_ref(struct hantro_ctx *ctx, u64 ts)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	struct vb2_queue *q = v4l2_m2m_get_dst_vq(ctx->fh.m2m_ctx);
4962306a36Sopenharmony_ci	struct vb2_buffer *buf;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	buf = vb2_find_buffer(q, ts);
5262306a36Sopenharmony_ci	if (!buf)
5362306a36Sopenharmony_ci		return 0;
5462306a36Sopenharmony_ci	return hantro_get_dec_buf_addr(ctx, buf);
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic const struct v4l2_event hantro_eos_event = {
5862306a36Sopenharmony_ci	.type = V4L2_EVENT_EOS
5962306a36Sopenharmony_ci};
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic void hantro_job_finish_no_pm(struct hantro_dev *vpu,
6262306a36Sopenharmony_ci				    struct hantro_ctx *ctx,
6362306a36Sopenharmony_ci				    enum vb2_buffer_state result)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	struct vb2_v4l2_buffer *src, *dst;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
6862306a36Sopenharmony_ci	dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	if (WARN_ON(!src))
7162306a36Sopenharmony_ci		return;
7262306a36Sopenharmony_ci	if (WARN_ON(!dst))
7362306a36Sopenharmony_ci		return;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	src->sequence = ctx->sequence_out++;
7662306a36Sopenharmony_ci	dst->sequence = ctx->sequence_cap++;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	if (v4l2_m2m_is_last_draining_src_buf(ctx->fh.m2m_ctx, src)) {
7962306a36Sopenharmony_ci		dst->flags |= V4L2_BUF_FLAG_LAST;
8062306a36Sopenharmony_ci		v4l2_event_queue_fh(&ctx->fh, &hantro_eos_event);
8162306a36Sopenharmony_ci		v4l2_m2m_mark_stopped(ctx->fh.m2m_ctx);
8262306a36Sopenharmony_ci	}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	v4l2_m2m_buf_done_and_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx,
8562306a36Sopenharmony_ci					 result);
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic void hantro_job_finish(struct hantro_dev *vpu,
8962306a36Sopenharmony_ci			      struct hantro_ctx *ctx,
9062306a36Sopenharmony_ci			      enum vb2_buffer_state result)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	pm_runtime_mark_last_busy(vpu->dev);
9362306a36Sopenharmony_ci	pm_runtime_put_autosuspend(vpu->dev);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	clk_bulk_disable(vpu->variant->num_clocks, vpu->clocks);
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	hantro_job_finish_no_pm(vpu, ctx, result);
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_civoid hantro_irq_done(struct hantro_dev *vpu,
10162306a36Sopenharmony_ci		     enum vb2_buffer_state result)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	struct hantro_ctx *ctx =
10462306a36Sopenharmony_ci		v4l2_m2m_get_curr_priv(vpu->m2m_dev);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	/*
10762306a36Sopenharmony_ci	 * If cancel_delayed_work returns false
10862306a36Sopenharmony_ci	 * the timeout expired. The watchdog is running,
10962306a36Sopenharmony_ci	 * and will take care of finishing the job.
11062306a36Sopenharmony_ci	 */
11162306a36Sopenharmony_ci	if (cancel_delayed_work(&vpu->watchdog_work)) {
11262306a36Sopenharmony_ci		if (result == VB2_BUF_STATE_DONE && ctx->codec_ops->done)
11362306a36Sopenharmony_ci			ctx->codec_ops->done(ctx);
11462306a36Sopenharmony_ci		hantro_job_finish(vpu, ctx, result);
11562306a36Sopenharmony_ci	}
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_civoid hantro_watchdog(struct work_struct *work)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	struct hantro_dev *vpu;
12162306a36Sopenharmony_ci	struct hantro_ctx *ctx;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	vpu = container_of(to_delayed_work(work),
12462306a36Sopenharmony_ci			   struct hantro_dev, watchdog_work);
12562306a36Sopenharmony_ci	ctx = v4l2_m2m_get_curr_priv(vpu->m2m_dev);
12662306a36Sopenharmony_ci	if (ctx) {
12762306a36Sopenharmony_ci		vpu_err("frame processing timed out!\n");
12862306a36Sopenharmony_ci		if (ctx->codec_ops->reset)
12962306a36Sopenharmony_ci			ctx->codec_ops->reset(ctx);
13062306a36Sopenharmony_ci		hantro_job_finish(vpu, ctx, VB2_BUF_STATE_ERROR);
13162306a36Sopenharmony_ci	}
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_civoid hantro_start_prepare_run(struct hantro_ctx *ctx)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	struct vb2_v4l2_buffer *src_buf;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	src_buf = hantro_get_src_buf(ctx);
13962306a36Sopenharmony_ci	v4l2_ctrl_request_setup(src_buf->vb2_buf.req_obj.req,
14062306a36Sopenharmony_ci				&ctx->ctrl_handler);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	if (!ctx->is_encoder && !ctx->dev->variant->late_postproc) {
14362306a36Sopenharmony_ci		if (hantro_needs_postproc(ctx, ctx->vpu_dst_fmt))
14462306a36Sopenharmony_ci			hantro_postproc_enable(ctx);
14562306a36Sopenharmony_ci		else
14662306a36Sopenharmony_ci			hantro_postproc_disable(ctx);
14762306a36Sopenharmony_ci	}
14862306a36Sopenharmony_ci}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_civoid hantro_end_prepare_run(struct hantro_ctx *ctx)
15162306a36Sopenharmony_ci{
15262306a36Sopenharmony_ci	struct vb2_v4l2_buffer *src_buf;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	if (!ctx->is_encoder && ctx->dev->variant->late_postproc) {
15562306a36Sopenharmony_ci		if (hantro_needs_postproc(ctx, ctx->vpu_dst_fmt))
15662306a36Sopenharmony_ci			hantro_postproc_enable(ctx);
15762306a36Sopenharmony_ci		else
15862306a36Sopenharmony_ci			hantro_postproc_disable(ctx);
15962306a36Sopenharmony_ci	}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	src_buf = hantro_get_src_buf(ctx);
16262306a36Sopenharmony_ci	v4l2_ctrl_request_complete(src_buf->vb2_buf.req_obj.req,
16362306a36Sopenharmony_ci				   &ctx->ctrl_handler);
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	/* Kick the watchdog. */
16662306a36Sopenharmony_ci	schedule_delayed_work(&ctx->dev->watchdog_work,
16762306a36Sopenharmony_ci			      msecs_to_jiffies(2000));
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_cistatic void device_run(void *priv)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	struct hantro_ctx *ctx = priv;
17362306a36Sopenharmony_ci	struct vb2_v4l2_buffer *src, *dst;
17462306a36Sopenharmony_ci	int ret;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	src = hantro_get_src_buf(ctx);
17762306a36Sopenharmony_ci	dst = hantro_get_dst_buf(ctx);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	ret = pm_runtime_resume_and_get(ctx->dev->dev);
18062306a36Sopenharmony_ci	if (ret < 0)
18162306a36Sopenharmony_ci		goto err_cancel_job;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	ret = clk_bulk_enable(ctx->dev->variant->num_clocks, ctx->dev->clocks);
18462306a36Sopenharmony_ci	if (ret)
18562306a36Sopenharmony_ci		goto err_cancel_job;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	v4l2_m2m_buf_copy_metadata(src, dst, true);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	if (ctx->codec_ops->run(ctx))
19062306a36Sopenharmony_ci		goto err_cancel_job;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	return;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_cierr_cancel_job:
19562306a36Sopenharmony_ci	hantro_job_finish_no_pm(ctx->dev, ctx, VB2_BUF_STATE_ERROR);
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_cistatic const struct v4l2_m2m_ops vpu_m2m_ops = {
19962306a36Sopenharmony_ci	.device_run = device_run,
20062306a36Sopenharmony_ci};
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_cistatic int
20362306a36Sopenharmony_ciqueue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	struct hantro_ctx *ctx = priv;
20662306a36Sopenharmony_ci	int ret;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
20962306a36Sopenharmony_ci	src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
21062306a36Sopenharmony_ci	src_vq->drv_priv = ctx;
21162306a36Sopenharmony_ci	src_vq->ops = &hantro_queue_ops;
21262306a36Sopenharmony_ci	src_vq->mem_ops = &vb2_dma_contig_memops;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	/*
21562306a36Sopenharmony_ci	 * Driver does mostly sequential access, so sacrifice TLB efficiency
21662306a36Sopenharmony_ci	 * for faster allocation. Also, no CPU access on the source queue,
21762306a36Sopenharmony_ci	 * so no kernel mapping needed.
21862306a36Sopenharmony_ci	 */
21962306a36Sopenharmony_ci	src_vq->dma_attrs = DMA_ATTR_ALLOC_SINGLE_PAGES |
22062306a36Sopenharmony_ci			    DMA_ATTR_NO_KERNEL_MAPPING;
22162306a36Sopenharmony_ci	src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
22262306a36Sopenharmony_ci	src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
22362306a36Sopenharmony_ci	src_vq->lock = &ctx->dev->vpu_mutex;
22462306a36Sopenharmony_ci	src_vq->dev = ctx->dev->v4l2_dev.dev;
22562306a36Sopenharmony_ci	src_vq->supports_requests = true;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	ret = vb2_queue_init(src_vq);
22862306a36Sopenharmony_ci	if (ret)
22962306a36Sopenharmony_ci		return ret;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	dst_vq->bidirectional = true;
23262306a36Sopenharmony_ci	dst_vq->mem_ops = &vb2_dma_contig_memops;
23362306a36Sopenharmony_ci	dst_vq->dma_attrs = DMA_ATTR_ALLOC_SINGLE_PAGES;
23462306a36Sopenharmony_ci	/*
23562306a36Sopenharmony_ci	 * The Kernel needs access to the JPEG destination buffer for the
23662306a36Sopenharmony_ci	 * JPEG encoder to fill in the JPEG headers.
23762306a36Sopenharmony_ci	 */
23862306a36Sopenharmony_ci	if (!ctx->is_encoder)
23962306a36Sopenharmony_ci		dst_vq->dma_attrs |= DMA_ATTR_NO_KERNEL_MAPPING;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
24262306a36Sopenharmony_ci	dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
24362306a36Sopenharmony_ci	dst_vq->drv_priv = ctx;
24462306a36Sopenharmony_ci	dst_vq->ops = &hantro_queue_ops;
24562306a36Sopenharmony_ci	dst_vq->buf_struct_size = sizeof(struct hantro_decoded_buffer);
24662306a36Sopenharmony_ci	dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
24762306a36Sopenharmony_ci	dst_vq->lock = &ctx->dev->vpu_mutex;
24862306a36Sopenharmony_ci	dst_vq->dev = ctx->dev->v4l2_dev.dev;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	return vb2_queue_init(dst_vq);
25162306a36Sopenharmony_ci}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_cistatic int hantro_try_ctrl(struct v4l2_ctrl *ctrl)
25462306a36Sopenharmony_ci{
25562306a36Sopenharmony_ci	if (ctrl->id == V4L2_CID_STATELESS_H264_SPS) {
25662306a36Sopenharmony_ci		const struct v4l2_ctrl_h264_sps *sps = ctrl->p_new.p_h264_sps;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci		if (sps->chroma_format_idc > 1)
25962306a36Sopenharmony_ci			/* Only 4:0:0 and 4:2:0 are supported */
26062306a36Sopenharmony_ci			return -EINVAL;
26162306a36Sopenharmony_ci		if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8)
26262306a36Sopenharmony_ci			/* Luma and chroma bit depth mismatch */
26362306a36Sopenharmony_ci			return -EINVAL;
26462306a36Sopenharmony_ci		if (sps->bit_depth_luma_minus8 != 0)
26562306a36Sopenharmony_ci			/* Only 8-bit is supported */
26662306a36Sopenharmony_ci			return -EINVAL;
26762306a36Sopenharmony_ci	} else if (ctrl->id == V4L2_CID_STATELESS_HEVC_SPS) {
26862306a36Sopenharmony_ci		const struct v4l2_ctrl_hevc_sps *sps = ctrl->p_new.p_hevc_sps;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci		if (sps->bit_depth_luma_minus8 != 0 && sps->bit_depth_luma_minus8 != 2)
27162306a36Sopenharmony_ci			/* Only 8-bit and 10-bit are supported */
27262306a36Sopenharmony_ci			return -EINVAL;
27362306a36Sopenharmony_ci	} else if (ctrl->id == V4L2_CID_STATELESS_VP9_FRAME) {
27462306a36Sopenharmony_ci		const struct v4l2_ctrl_vp9_frame *dec_params = ctrl->p_new.p_vp9_frame;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci		/* We only support profile 0 */
27762306a36Sopenharmony_ci		if (dec_params->profile != 0)
27862306a36Sopenharmony_ci			return -EINVAL;
27962306a36Sopenharmony_ci	} else if (ctrl->id == V4L2_CID_STATELESS_AV1_SEQUENCE) {
28062306a36Sopenharmony_ci		const struct v4l2_ctrl_av1_sequence *sequence = ctrl->p_new.p_av1_sequence;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci		if (sequence->bit_depth != 8 && sequence->bit_depth != 10)
28362306a36Sopenharmony_ci			return -EINVAL;
28462306a36Sopenharmony_ci	}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	return 0;
28762306a36Sopenharmony_ci}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_cistatic int hantro_jpeg_s_ctrl(struct v4l2_ctrl *ctrl)
29062306a36Sopenharmony_ci{
29162306a36Sopenharmony_ci	struct hantro_ctx *ctx;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	ctx = container_of(ctrl->handler,
29462306a36Sopenharmony_ci			   struct hantro_ctx, ctrl_handler);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	vpu_debug(1, "s_ctrl: id = %d, val = %d\n", ctrl->id, ctrl->val);
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	switch (ctrl->id) {
29962306a36Sopenharmony_ci	case V4L2_CID_JPEG_COMPRESSION_QUALITY:
30062306a36Sopenharmony_ci		ctx->jpeg_quality = ctrl->val;
30162306a36Sopenharmony_ci		break;
30262306a36Sopenharmony_ci	default:
30362306a36Sopenharmony_ci		return -EINVAL;
30462306a36Sopenharmony_ci	}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	return 0;
30762306a36Sopenharmony_ci}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_cistatic int hantro_vp9_s_ctrl(struct v4l2_ctrl *ctrl)
31062306a36Sopenharmony_ci{
31162306a36Sopenharmony_ci	struct hantro_ctx *ctx;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	ctx = container_of(ctrl->handler,
31462306a36Sopenharmony_ci			   struct hantro_ctx, ctrl_handler);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	switch (ctrl->id) {
31762306a36Sopenharmony_ci	case V4L2_CID_STATELESS_VP9_FRAME: {
31862306a36Sopenharmony_ci		int bit_depth = ctrl->p_new.p_vp9_frame->bit_depth;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci		if (ctx->bit_depth == bit_depth)
32162306a36Sopenharmony_ci			return 0;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci		return hantro_reset_raw_fmt(ctx, bit_depth, HANTRO_AUTO_POSTPROC);
32462306a36Sopenharmony_ci	}
32562306a36Sopenharmony_ci	default:
32662306a36Sopenharmony_ci		return -EINVAL;
32762306a36Sopenharmony_ci	}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	return 0;
33062306a36Sopenharmony_ci}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_cistatic int hantro_hevc_s_ctrl(struct v4l2_ctrl *ctrl)
33362306a36Sopenharmony_ci{
33462306a36Sopenharmony_ci	struct hantro_ctx *ctx;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	ctx = container_of(ctrl->handler,
33762306a36Sopenharmony_ci			   struct hantro_ctx, ctrl_handler);
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	switch (ctrl->id) {
34062306a36Sopenharmony_ci	case V4L2_CID_STATELESS_HEVC_SPS: {
34162306a36Sopenharmony_ci		const struct v4l2_ctrl_hevc_sps *sps = ctrl->p_new.p_hevc_sps;
34262306a36Sopenharmony_ci		int bit_depth = sps->bit_depth_luma_minus8 + 8;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci		if (ctx->bit_depth == bit_depth)
34562306a36Sopenharmony_ci			return 0;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci		return hantro_reset_raw_fmt(ctx, bit_depth, HANTRO_AUTO_POSTPROC);
34862306a36Sopenharmony_ci	}
34962306a36Sopenharmony_ci	default:
35062306a36Sopenharmony_ci		return -EINVAL;
35162306a36Sopenharmony_ci	}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	return 0;
35462306a36Sopenharmony_ci}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_cistatic int hantro_av1_s_ctrl(struct v4l2_ctrl *ctrl)
35762306a36Sopenharmony_ci{
35862306a36Sopenharmony_ci	struct hantro_ctx *ctx;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	ctx = container_of(ctrl->handler,
36162306a36Sopenharmony_ci			   struct hantro_ctx, ctrl_handler);
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	switch (ctrl->id) {
36462306a36Sopenharmony_ci	case V4L2_CID_STATELESS_AV1_SEQUENCE:
36562306a36Sopenharmony_ci	{
36662306a36Sopenharmony_ci		int bit_depth = ctrl->p_new.p_av1_sequence->bit_depth;
36762306a36Sopenharmony_ci		bool need_postproc = HANTRO_AUTO_POSTPROC;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci		if (ctrl->p_new.p_av1_sequence->flags
37062306a36Sopenharmony_ci		    & V4L2_AV1_SEQUENCE_FLAG_FILM_GRAIN_PARAMS_PRESENT)
37162306a36Sopenharmony_ci			need_postproc = HANTRO_FORCE_POSTPROC;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci		if (ctx->bit_depth == bit_depth &&
37462306a36Sopenharmony_ci		    ctx->need_postproc == need_postproc)
37562306a36Sopenharmony_ci			return 0;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci		return hantro_reset_raw_fmt(ctx, bit_depth, need_postproc);
37862306a36Sopenharmony_ci	}
37962306a36Sopenharmony_ci	default:
38062306a36Sopenharmony_ci		return -EINVAL;
38162306a36Sopenharmony_ci	}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	return 0;
38462306a36Sopenharmony_ci}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops hantro_ctrl_ops = {
38762306a36Sopenharmony_ci	.try_ctrl = hantro_try_ctrl,
38862306a36Sopenharmony_ci};
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops hantro_jpeg_ctrl_ops = {
39162306a36Sopenharmony_ci	.s_ctrl = hantro_jpeg_s_ctrl,
39262306a36Sopenharmony_ci};
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops hantro_vp9_ctrl_ops = {
39562306a36Sopenharmony_ci	.s_ctrl = hantro_vp9_s_ctrl,
39662306a36Sopenharmony_ci};
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops hantro_hevc_ctrl_ops = {
39962306a36Sopenharmony_ci	.try_ctrl = hantro_try_ctrl,
40062306a36Sopenharmony_ci	.s_ctrl = hantro_hevc_s_ctrl,
40162306a36Sopenharmony_ci};
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops hantro_av1_ctrl_ops = {
40462306a36Sopenharmony_ci	.try_ctrl = hantro_try_ctrl,
40562306a36Sopenharmony_ci	.s_ctrl = hantro_av1_s_ctrl,
40662306a36Sopenharmony_ci};
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci#define HANTRO_JPEG_ACTIVE_MARKERS	(V4L2_JPEG_ACTIVE_MARKER_APP0 | \
40962306a36Sopenharmony_ci					 V4L2_JPEG_ACTIVE_MARKER_COM | \
41062306a36Sopenharmony_ci					 V4L2_JPEG_ACTIVE_MARKER_DQT | \
41162306a36Sopenharmony_ci					 V4L2_JPEG_ACTIVE_MARKER_DHT)
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_cistatic const struct hantro_ctrl controls[] = {
41462306a36Sopenharmony_ci	{
41562306a36Sopenharmony_ci		.codec = HANTRO_JPEG_ENCODER,
41662306a36Sopenharmony_ci		.cfg = {
41762306a36Sopenharmony_ci			.id = V4L2_CID_JPEG_COMPRESSION_QUALITY,
41862306a36Sopenharmony_ci			.min = 5,
41962306a36Sopenharmony_ci			.max = 100,
42062306a36Sopenharmony_ci			.step = 1,
42162306a36Sopenharmony_ci			.def = 50,
42262306a36Sopenharmony_ci			.ops = &hantro_jpeg_ctrl_ops,
42362306a36Sopenharmony_ci		},
42462306a36Sopenharmony_ci	}, {
42562306a36Sopenharmony_ci		.codec = HANTRO_JPEG_ENCODER,
42662306a36Sopenharmony_ci		.cfg = {
42762306a36Sopenharmony_ci			.id = V4L2_CID_JPEG_ACTIVE_MARKER,
42862306a36Sopenharmony_ci			.max = HANTRO_JPEG_ACTIVE_MARKERS,
42962306a36Sopenharmony_ci			.def = HANTRO_JPEG_ACTIVE_MARKERS,
43062306a36Sopenharmony_ci			/*
43162306a36Sopenharmony_ci			 * Changing the set of active markers/segments also
43262306a36Sopenharmony_ci			 * messes up the alignment of the JPEG header, which
43362306a36Sopenharmony_ci			 * is needed to allow the hardware to write directly
43462306a36Sopenharmony_ci			 * to the output buffer. Implementing this introduces
43562306a36Sopenharmony_ci			 * a lot of complexity for little gain, as the markers
43662306a36Sopenharmony_ci			 * enabled is already the minimum required set.
43762306a36Sopenharmony_ci			 */
43862306a36Sopenharmony_ci			.flags = V4L2_CTRL_FLAG_READ_ONLY,
43962306a36Sopenharmony_ci		},
44062306a36Sopenharmony_ci	}, {
44162306a36Sopenharmony_ci		.codec = HANTRO_MPEG2_DECODER,
44262306a36Sopenharmony_ci		.cfg = {
44362306a36Sopenharmony_ci			.id = V4L2_CID_STATELESS_MPEG2_SEQUENCE,
44462306a36Sopenharmony_ci		},
44562306a36Sopenharmony_ci	}, {
44662306a36Sopenharmony_ci		.codec = HANTRO_MPEG2_DECODER,
44762306a36Sopenharmony_ci		.cfg = {
44862306a36Sopenharmony_ci			.id = V4L2_CID_STATELESS_MPEG2_PICTURE,
44962306a36Sopenharmony_ci		},
45062306a36Sopenharmony_ci	}, {
45162306a36Sopenharmony_ci		.codec = HANTRO_MPEG2_DECODER,
45262306a36Sopenharmony_ci		.cfg = {
45362306a36Sopenharmony_ci			.id = V4L2_CID_STATELESS_MPEG2_QUANTISATION,
45462306a36Sopenharmony_ci		},
45562306a36Sopenharmony_ci	}, {
45662306a36Sopenharmony_ci		.codec = HANTRO_VP8_DECODER,
45762306a36Sopenharmony_ci		.cfg = {
45862306a36Sopenharmony_ci			.id = V4L2_CID_STATELESS_VP8_FRAME,
45962306a36Sopenharmony_ci		},
46062306a36Sopenharmony_ci	}, {
46162306a36Sopenharmony_ci		.codec = HANTRO_H264_DECODER,
46262306a36Sopenharmony_ci		.cfg = {
46362306a36Sopenharmony_ci			.id = V4L2_CID_STATELESS_H264_DECODE_PARAMS,
46462306a36Sopenharmony_ci		},
46562306a36Sopenharmony_ci	}, {
46662306a36Sopenharmony_ci		.codec = HANTRO_H264_DECODER,
46762306a36Sopenharmony_ci		.cfg = {
46862306a36Sopenharmony_ci			.id = V4L2_CID_STATELESS_H264_SPS,
46962306a36Sopenharmony_ci			.ops = &hantro_ctrl_ops,
47062306a36Sopenharmony_ci		},
47162306a36Sopenharmony_ci	}, {
47262306a36Sopenharmony_ci		.codec = HANTRO_H264_DECODER,
47362306a36Sopenharmony_ci		.cfg = {
47462306a36Sopenharmony_ci			.id = V4L2_CID_STATELESS_H264_PPS,
47562306a36Sopenharmony_ci		},
47662306a36Sopenharmony_ci	}, {
47762306a36Sopenharmony_ci		.codec = HANTRO_H264_DECODER,
47862306a36Sopenharmony_ci		.cfg = {
47962306a36Sopenharmony_ci			.id = V4L2_CID_STATELESS_H264_SCALING_MATRIX,
48062306a36Sopenharmony_ci		},
48162306a36Sopenharmony_ci	}, {
48262306a36Sopenharmony_ci		.codec = HANTRO_H264_DECODER,
48362306a36Sopenharmony_ci		.cfg = {
48462306a36Sopenharmony_ci			.id = V4L2_CID_STATELESS_H264_DECODE_MODE,
48562306a36Sopenharmony_ci			.min = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED,
48662306a36Sopenharmony_ci			.def = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED,
48762306a36Sopenharmony_ci			.max = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED,
48862306a36Sopenharmony_ci		},
48962306a36Sopenharmony_ci	}, {
49062306a36Sopenharmony_ci		.codec = HANTRO_H264_DECODER,
49162306a36Sopenharmony_ci		.cfg = {
49262306a36Sopenharmony_ci			.id = V4L2_CID_STATELESS_H264_START_CODE,
49362306a36Sopenharmony_ci			.min = V4L2_STATELESS_H264_START_CODE_ANNEX_B,
49462306a36Sopenharmony_ci			.def = V4L2_STATELESS_H264_START_CODE_ANNEX_B,
49562306a36Sopenharmony_ci			.max = V4L2_STATELESS_H264_START_CODE_ANNEX_B,
49662306a36Sopenharmony_ci		},
49762306a36Sopenharmony_ci	}, {
49862306a36Sopenharmony_ci		.codec = HANTRO_H264_DECODER,
49962306a36Sopenharmony_ci		.cfg = {
50062306a36Sopenharmony_ci			.id = V4L2_CID_MPEG_VIDEO_H264_PROFILE,
50162306a36Sopenharmony_ci			.min = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE,
50262306a36Sopenharmony_ci			.max = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH,
50362306a36Sopenharmony_ci			.menu_skip_mask =
50462306a36Sopenharmony_ci			BIT(V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED),
50562306a36Sopenharmony_ci			.def = V4L2_MPEG_VIDEO_H264_PROFILE_MAIN,
50662306a36Sopenharmony_ci		}
50762306a36Sopenharmony_ci	}, {
50862306a36Sopenharmony_ci		.codec = HANTRO_HEVC_DECODER,
50962306a36Sopenharmony_ci		.cfg = {
51062306a36Sopenharmony_ci			.id = V4L2_CID_STATELESS_HEVC_DECODE_MODE,
51162306a36Sopenharmony_ci			.min = V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED,
51262306a36Sopenharmony_ci			.max = V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED,
51362306a36Sopenharmony_ci			.def = V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED,
51462306a36Sopenharmony_ci		},
51562306a36Sopenharmony_ci	}, {
51662306a36Sopenharmony_ci		.codec = HANTRO_HEVC_DECODER,
51762306a36Sopenharmony_ci		.cfg = {
51862306a36Sopenharmony_ci			.id = V4L2_CID_STATELESS_HEVC_START_CODE,
51962306a36Sopenharmony_ci			.min = V4L2_STATELESS_HEVC_START_CODE_ANNEX_B,
52062306a36Sopenharmony_ci			.max = V4L2_STATELESS_HEVC_START_CODE_ANNEX_B,
52162306a36Sopenharmony_ci			.def = V4L2_STATELESS_HEVC_START_CODE_ANNEX_B,
52262306a36Sopenharmony_ci		},
52362306a36Sopenharmony_ci	}, {
52462306a36Sopenharmony_ci		.codec = HANTRO_HEVC_DECODER,
52562306a36Sopenharmony_ci		.cfg = {
52662306a36Sopenharmony_ci			.id = V4L2_CID_MPEG_VIDEO_HEVC_PROFILE,
52762306a36Sopenharmony_ci			.min = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN,
52862306a36Sopenharmony_ci			.max = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10,
52962306a36Sopenharmony_ci			.def = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN,
53062306a36Sopenharmony_ci		},
53162306a36Sopenharmony_ci	}, {
53262306a36Sopenharmony_ci		.codec = HANTRO_HEVC_DECODER,
53362306a36Sopenharmony_ci		.cfg = {
53462306a36Sopenharmony_ci			.id = V4L2_CID_MPEG_VIDEO_HEVC_LEVEL,
53562306a36Sopenharmony_ci			.min = V4L2_MPEG_VIDEO_HEVC_LEVEL_1,
53662306a36Sopenharmony_ci			.max = V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1,
53762306a36Sopenharmony_ci		},
53862306a36Sopenharmony_ci	}, {
53962306a36Sopenharmony_ci		.codec = HANTRO_HEVC_DECODER,
54062306a36Sopenharmony_ci		.cfg = {
54162306a36Sopenharmony_ci			.id = V4L2_CID_STATELESS_HEVC_SPS,
54262306a36Sopenharmony_ci			.ops = &hantro_hevc_ctrl_ops,
54362306a36Sopenharmony_ci		},
54462306a36Sopenharmony_ci	}, {
54562306a36Sopenharmony_ci		.codec = HANTRO_HEVC_DECODER,
54662306a36Sopenharmony_ci		.cfg = {
54762306a36Sopenharmony_ci			.id = V4L2_CID_STATELESS_HEVC_PPS,
54862306a36Sopenharmony_ci		},
54962306a36Sopenharmony_ci	}, {
55062306a36Sopenharmony_ci		.codec = HANTRO_HEVC_DECODER,
55162306a36Sopenharmony_ci		.cfg = {
55262306a36Sopenharmony_ci			.id = V4L2_CID_STATELESS_HEVC_DECODE_PARAMS,
55362306a36Sopenharmony_ci		},
55462306a36Sopenharmony_ci	}, {
55562306a36Sopenharmony_ci		.codec = HANTRO_HEVC_DECODER,
55662306a36Sopenharmony_ci		.cfg = {
55762306a36Sopenharmony_ci			.id = V4L2_CID_STATELESS_HEVC_SCALING_MATRIX,
55862306a36Sopenharmony_ci		},
55962306a36Sopenharmony_ci	}, {
56062306a36Sopenharmony_ci		.codec = HANTRO_VP9_DECODER,
56162306a36Sopenharmony_ci		.cfg = {
56262306a36Sopenharmony_ci			.id = V4L2_CID_STATELESS_VP9_FRAME,
56362306a36Sopenharmony_ci			.ops = &hantro_vp9_ctrl_ops,
56462306a36Sopenharmony_ci		},
56562306a36Sopenharmony_ci	}, {
56662306a36Sopenharmony_ci		.codec = HANTRO_VP9_DECODER,
56762306a36Sopenharmony_ci		.cfg = {
56862306a36Sopenharmony_ci			.id = V4L2_CID_STATELESS_VP9_COMPRESSED_HDR,
56962306a36Sopenharmony_ci		},
57062306a36Sopenharmony_ci	}, {
57162306a36Sopenharmony_ci		.codec = HANTRO_AV1_DECODER,
57262306a36Sopenharmony_ci		.cfg = {
57362306a36Sopenharmony_ci			.id = V4L2_CID_STATELESS_AV1_FRAME,
57462306a36Sopenharmony_ci		},
57562306a36Sopenharmony_ci	}, {
57662306a36Sopenharmony_ci		.codec = HANTRO_AV1_DECODER,
57762306a36Sopenharmony_ci		.cfg = {
57862306a36Sopenharmony_ci			.id = V4L2_CID_STATELESS_AV1_TILE_GROUP_ENTRY,
57962306a36Sopenharmony_ci			.dims = { V4L2_AV1_MAX_TILE_COUNT },
58062306a36Sopenharmony_ci		},
58162306a36Sopenharmony_ci	}, {
58262306a36Sopenharmony_ci		.codec = HANTRO_AV1_DECODER,
58362306a36Sopenharmony_ci		.cfg = {
58462306a36Sopenharmony_ci			.id = V4L2_CID_STATELESS_AV1_SEQUENCE,
58562306a36Sopenharmony_ci			.ops = &hantro_av1_ctrl_ops,
58662306a36Sopenharmony_ci		},
58762306a36Sopenharmony_ci	}, {
58862306a36Sopenharmony_ci		.codec = HANTRO_AV1_DECODER,
58962306a36Sopenharmony_ci		.cfg = {
59062306a36Sopenharmony_ci			.id = V4L2_CID_STATELESS_AV1_FILM_GRAIN,
59162306a36Sopenharmony_ci		},
59262306a36Sopenharmony_ci	},
59362306a36Sopenharmony_ci};
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_cistatic int hantro_ctrls_setup(struct hantro_dev *vpu,
59662306a36Sopenharmony_ci			      struct hantro_ctx *ctx,
59762306a36Sopenharmony_ci			      int allowed_codecs)
59862306a36Sopenharmony_ci{
59962306a36Sopenharmony_ci	int i, num_ctrls = ARRAY_SIZE(controls);
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	v4l2_ctrl_handler_init(&ctx->ctrl_handler, num_ctrls);
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	for (i = 0; i < num_ctrls; i++) {
60462306a36Sopenharmony_ci		if (!(allowed_codecs & controls[i].codec))
60562306a36Sopenharmony_ci			continue;
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci		v4l2_ctrl_new_custom(&ctx->ctrl_handler,
60862306a36Sopenharmony_ci				     &controls[i].cfg, NULL);
60962306a36Sopenharmony_ci		if (ctx->ctrl_handler.error) {
61062306a36Sopenharmony_ci			vpu_err("Adding control (%d) failed %d\n",
61162306a36Sopenharmony_ci				controls[i].cfg.id,
61262306a36Sopenharmony_ci				ctx->ctrl_handler.error);
61362306a36Sopenharmony_ci			v4l2_ctrl_handler_free(&ctx->ctrl_handler);
61462306a36Sopenharmony_ci			return ctx->ctrl_handler.error;
61562306a36Sopenharmony_ci		}
61662306a36Sopenharmony_ci	}
61762306a36Sopenharmony_ci	return v4l2_ctrl_handler_setup(&ctx->ctrl_handler);
61862306a36Sopenharmony_ci}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci/*
62162306a36Sopenharmony_ci * V4L2 file operations.
62262306a36Sopenharmony_ci */
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_cistatic int hantro_open(struct file *filp)
62562306a36Sopenharmony_ci{
62662306a36Sopenharmony_ci	struct hantro_dev *vpu = video_drvdata(filp);
62762306a36Sopenharmony_ci	struct video_device *vdev = video_devdata(filp);
62862306a36Sopenharmony_ci	struct hantro_func *func = hantro_vdev_to_func(vdev);
62962306a36Sopenharmony_ci	struct hantro_ctx *ctx;
63062306a36Sopenharmony_ci	int allowed_codecs, ret;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	/*
63362306a36Sopenharmony_ci	 * We do not need any extra locking here, because we operate only
63462306a36Sopenharmony_ci	 * on local data here, except reading few fields from dev, which
63562306a36Sopenharmony_ci	 * do not change through device's lifetime (which is guaranteed by
63662306a36Sopenharmony_ci	 * reference on module from open()) and V4L2 internal objects (such
63762306a36Sopenharmony_ci	 * as vdev and ctx->fh), which have proper locking done in respective
63862306a36Sopenharmony_ci	 * helper functions used here.
63962306a36Sopenharmony_ci	 */
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
64262306a36Sopenharmony_ci	if (!ctx)
64362306a36Sopenharmony_ci		return -ENOMEM;
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	ctx->dev = vpu;
64662306a36Sopenharmony_ci	if (func->id == MEDIA_ENT_F_PROC_VIDEO_ENCODER) {
64762306a36Sopenharmony_ci		allowed_codecs = vpu->variant->codec & HANTRO_ENCODERS;
64862306a36Sopenharmony_ci		ctx->is_encoder = true;
64962306a36Sopenharmony_ci	} else if (func->id == MEDIA_ENT_F_PROC_VIDEO_DECODER) {
65062306a36Sopenharmony_ci		allowed_codecs = vpu->variant->codec & HANTRO_DECODERS;
65162306a36Sopenharmony_ci		ctx->is_encoder = false;
65262306a36Sopenharmony_ci	} else {
65362306a36Sopenharmony_ci		ret = -ENODEV;
65462306a36Sopenharmony_ci		goto err_ctx_free;
65562306a36Sopenharmony_ci	}
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(vpu->m2m_dev, ctx, queue_init);
65862306a36Sopenharmony_ci	if (IS_ERR(ctx->fh.m2m_ctx)) {
65962306a36Sopenharmony_ci		ret = PTR_ERR(ctx->fh.m2m_ctx);
66062306a36Sopenharmony_ci		goto err_ctx_free;
66162306a36Sopenharmony_ci	}
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	v4l2_fh_init(&ctx->fh, vdev);
66462306a36Sopenharmony_ci	filp->private_data = &ctx->fh;
66562306a36Sopenharmony_ci	v4l2_fh_add(&ctx->fh);
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	hantro_reset_fmts(ctx);
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	ret = hantro_ctrls_setup(vpu, ctx, allowed_codecs);
67062306a36Sopenharmony_ci	if (ret) {
67162306a36Sopenharmony_ci		vpu_err("Failed to set up controls\n");
67262306a36Sopenharmony_ci		goto err_fh_free;
67362306a36Sopenharmony_ci	}
67462306a36Sopenharmony_ci	ctx->fh.ctrl_handler = &ctx->ctrl_handler;
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	return 0;
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_cierr_fh_free:
67962306a36Sopenharmony_ci	v4l2_fh_del(&ctx->fh);
68062306a36Sopenharmony_ci	v4l2_fh_exit(&ctx->fh);
68162306a36Sopenharmony_cierr_ctx_free:
68262306a36Sopenharmony_ci	kfree(ctx);
68362306a36Sopenharmony_ci	return ret;
68462306a36Sopenharmony_ci}
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_cistatic int hantro_release(struct file *filp)
68762306a36Sopenharmony_ci{
68862306a36Sopenharmony_ci	struct hantro_ctx *ctx =
68962306a36Sopenharmony_ci		container_of(filp->private_data, struct hantro_ctx, fh);
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	/*
69262306a36Sopenharmony_ci	 * No need for extra locking because this was the last reference
69362306a36Sopenharmony_ci	 * to this file.
69462306a36Sopenharmony_ci	 */
69562306a36Sopenharmony_ci	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
69662306a36Sopenharmony_ci	v4l2_fh_del(&ctx->fh);
69762306a36Sopenharmony_ci	v4l2_fh_exit(&ctx->fh);
69862306a36Sopenharmony_ci	v4l2_ctrl_handler_free(&ctx->ctrl_handler);
69962306a36Sopenharmony_ci	kfree(ctx);
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	return 0;
70262306a36Sopenharmony_ci}
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_cistatic const struct v4l2_file_operations hantro_fops = {
70562306a36Sopenharmony_ci	.owner = THIS_MODULE,
70662306a36Sopenharmony_ci	.open = hantro_open,
70762306a36Sopenharmony_ci	.release = hantro_release,
70862306a36Sopenharmony_ci	.poll = v4l2_m2m_fop_poll,
70962306a36Sopenharmony_ci	.unlocked_ioctl = video_ioctl2,
71062306a36Sopenharmony_ci	.mmap = v4l2_m2m_fop_mmap,
71162306a36Sopenharmony_ci};
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_cistatic const struct of_device_id of_hantro_match[] = {
71462306a36Sopenharmony_ci#ifdef CONFIG_VIDEO_HANTRO_ROCKCHIP
71562306a36Sopenharmony_ci	{ .compatible = "rockchip,px30-vpu",   .data = &px30_vpu_variant, },
71662306a36Sopenharmony_ci	{ .compatible = "rockchip,rk3036-vpu", .data = &rk3036_vpu_variant, },
71762306a36Sopenharmony_ci	{ .compatible = "rockchip,rk3066-vpu", .data = &rk3066_vpu_variant, },
71862306a36Sopenharmony_ci	{ .compatible = "rockchip,rk3288-vpu", .data = &rk3288_vpu_variant, },
71962306a36Sopenharmony_ci	{ .compatible = "rockchip,rk3328-vpu", .data = &rk3328_vpu_variant, },
72062306a36Sopenharmony_ci	{ .compatible = "rockchip,rk3399-vpu", .data = &rk3399_vpu_variant, },
72162306a36Sopenharmony_ci	{ .compatible = "rockchip,rk3568-vepu", .data = &rk3568_vepu_variant, },
72262306a36Sopenharmony_ci	{ .compatible = "rockchip,rk3568-vpu", .data = &rk3568_vpu_variant, },
72362306a36Sopenharmony_ci	{ .compatible = "rockchip,rk3588-av1-vpu", .data = &rk3588_vpu981_variant, },
72462306a36Sopenharmony_ci#endif
72562306a36Sopenharmony_ci#ifdef CONFIG_VIDEO_HANTRO_IMX8M
72662306a36Sopenharmony_ci	{ .compatible = "nxp,imx8mm-vpu-g1", .data = &imx8mm_vpu_g1_variant, },
72762306a36Sopenharmony_ci	{ .compatible = "nxp,imx8mq-vpu", .data = &imx8mq_vpu_variant, },
72862306a36Sopenharmony_ci	{ .compatible = "nxp,imx8mq-vpu-g1", .data = &imx8mq_vpu_g1_variant },
72962306a36Sopenharmony_ci	{ .compatible = "nxp,imx8mq-vpu-g2", .data = &imx8mq_vpu_g2_variant },
73062306a36Sopenharmony_ci#endif
73162306a36Sopenharmony_ci#ifdef CONFIG_VIDEO_HANTRO_SAMA5D4
73262306a36Sopenharmony_ci	{ .compatible = "microchip,sama5d4-vdec", .data = &sama5d4_vdec_variant, },
73362306a36Sopenharmony_ci#endif
73462306a36Sopenharmony_ci#ifdef CONFIG_VIDEO_HANTRO_SUNXI
73562306a36Sopenharmony_ci	{ .compatible = "allwinner,sun50i-h6-vpu-g2", .data = &sunxi_vpu_variant, },
73662306a36Sopenharmony_ci#endif
73762306a36Sopenharmony_ci	{ /* sentinel */ }
73862306a36Sopenharmony_ci};
73962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, of_hantro_match);
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_cistatic int hantro_register_entity(struct media_device *mdev,
74262306a36Sopenharmony_ci				  struct media_entity *entity,
74362306a36Sopenharmony_ci				  const char *entity_name,
74462306a36Sopenharmony_ci				  struct media_pad *pads, int num_pads,
74562306a36Sopenharmony_ci				  int function, struct video_device *vdev)
74662306a36Sopenharmony_ci{
74762306a36Sopenharmony_ci	char *name;
74862306a36Sopenharmony_ci	int ret;
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	entity->obj_type = MEDIA_ENTITY_TYPE_BASE;
75162306a36Sopenharmony_ci	if (function == MEDIA_ENT_F_IO_V4L) {
75262306a36Sopenharmony_ci		entity->info.dev.major = VIDEO_MAJOR;
75362306a36Sopenharmony_ci		entity->info.dev.minor = vdev->minor;
75462306a36Sopenharmony_ci	}
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	name = devm_kasprintf(mdev->dev, GFP_KERNEL, "%s-%s", vdev->name,
75762306a36Sopenharmony_ci			      entity_name);
75862306a36Sopenharmony_ci	if (!name)
75962306a36Sopenharmony_ci		return -ENOMEM;
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	entity->name = name;
76262306a36Sopenharmony_ci	entity->function = function;
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	ret = media_entity_pads_init(entity, num_pads, pads);
76562306a36Sopenharmony_ci	if (ret)
76662306a36Sopenharmony_ci		return ret;
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	ret = media_device_register_entity(mdev, entity);
76962306a36Sopenharmony_ci	if (ret)
77062306a36Sopenharmony_ci		return ret;
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	return 0;
77362306a36Sopenharmony_ci}
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_cistatic int hantro_attach_func(struct hantro_dev *vpu,
77662306a36Sopenharmony_ci			      struct hantro_func *func)
77762306a36Sopenharmony_ci{
77862306a36Sopenharmony_ci	struct media_device *mdev = &vpu->mdev;
77962306a36Sopenharmony_ci	struct media_link *link;
78062306a36Sopenharmony_ci	int ret;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	/* Create the three encoder entities with their pads */
78362306a36Sopenharmony_ci	func->source_pad.flags = MEDIA_PAD_FL_SOURCE;
78462306a36Sopenharmony_ci	ret = hantro_register_entity(mdev, &func->vdev.entity, "source",
78562306a36Sopenharmony_ci				     &func->source_pad, 1, MEDIA_ENT_F_IO_V4L,
78662306a36Sopenharmony_ci				     &func->vdev);
78762306a36Sopenharmony_ci	if (ret)
78862306a36Sopenharmony_ci		return ret;
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	func->proc_pads[0].flags = MEDIA_PAD_FL_SINK;
79162306a36Sopenharmony_ci	func->proc_pads[1].flags = MEDIA_PAD_FL_SOURCE;
79262306a36Sopenharmony_ci	ret = hantro_register_entity(mdev, &func->proc, "proc",
79362306a36Sopenharmony_ci				     func->proc_pads, 2, func->id,
79462306a36Sopenharmony_ci				     &func->vdev);
79562306a36Sopenharmony_ci	if (ret)
79662306a36Sopenharmony_ci		goto err_rel_entity0;
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	func->sink_pad.flags = MEDIA_PAD_FL_SINK;
79962306a36Sopenharmony_ci	ret = hantro_register_entity(mdev, &func->sink, "sink",
80062306a36Sopenharmony_ci				     &func->sink_pad, 1, MEDIA_ENT_F_IO_V4L,
80162306a36Sopenharmony_ci				     &func->vdev);
80262306a36Sopenharmony_ci	if (ret)
80362306a36Sopenharmony_ci		goto err_rel_entity1;
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	/* Connect the three entities */
80662306a36Sopenharmony_ci	ret = media_create_pad_link(&func->vdev.entity, 0, &func->proc, 0,
80762306a36Sopenharmony_ci				    MEDIA_LNK_FL_IMMUTABLE |
80862306a36Sopenharmony_ci				    MEDIA_LNK_FL_ENABLED);
80962306a36Sopenharmony_ci	if (ret)
81062306a36Sopenharmony_ci		goto err_rel_entity2;
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	ret = media_create_pad_link(&func->proc, 1, &func->sink, 0,
81362306a36Sopenharmony_ci				    MEDIA_LNK_FL_IMMUTABLE |
81462306a36Sopenharmony_ci				    MEDIA_LNK_FL_ENABLED);
81562306a36Sopenharmony_ci	if (ret)
81662306a36Sopenharmony_ci		goto err_rm_links0;
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	/* Create video interface */
81962306a36Sopenharmony_ci	func->intf_devnode = media_devnode_create(mdev, MEDIA_INTF_T_V4L_VIDEO,
82062306a36Sopenharmony_ci						  0, VIDEO_MAJOR,
82162306a36Sopenharmony_ci						  func->vdev.minor);
82262306a36Sopenharmony_ci	if (!func->intf_devnode) {
82362306a36Sopenharmony_ci		ret = -ENOMEM;
82462306a36Sopenharmony_ci		goto err_rm_links1;
82562306a36Sopenharmony_ci	}
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	/* Connect the two DMA engines to the interface */
82862306a36Sopenharmony_ci	link = media_create_intf_link(&func->vdev.entity,
82962306a36Sopenharmony_ci				      &func->intf_devnode->intf,
83062306a36Sopenharmony_ci				      MEDIA_LNK_FL_IMMUTABLE |
83162306a36Sopenharmony_ci				      MEDIA_LNK_FL_ENABLED);
83262306a36Sopenharmony_ci	if (!link) {
83362306a36Sopenharmony_ci		ret = -ENOMEM;
83462306a36Sopenharmony_ci		goto err_rm_devnode;
83562306a36Sopenharmony_ci	}
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	link = media_create_intf_link(&func->sink, &func->intf_devnode->intf,
83862306a36Sopenharmony_ci				      MEDIA_LNK_FL_IMMUTABLE |
83962306a36Sopenharmony_ci				      MEDIA_LNK_FL_ENABLED);
84062306a36Sopenharmony_ci	if (!link) {
84162306a36Sopenharmony_ci		ret = -ENOMEM;
84262306a36Sopenharmony_ci		goto err_rm_devnode;
84362306a36Sopenharmony_ci	}
84462306a36Sopenharmony_ci	return 0;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_cierr_rm_devnode:
84762306a36Sopenharmony_ci	media_devnode_remove(func->intf_devnode);
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_cierr_rm_links1:
85062306a36Sopenharmony_ci	media_entity_remove_links(&func->sink);
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_cierr_rm_links0:
85362306a36Sopenharmony_ci	media_entity_remove_links(&func->proc);
85462306a36Sopenharmony_ci	media_entity_remove_links(&func->vdev.entity);
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_cierr_rel_entity2:
85762306a36Sopenharmony_ci	media_device_unregister_entity(&func->sink);
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_cierr_rel_entity1:
86062306a36Sopenharmony_ci	media_device_unregister_entity(&func->proc);
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_cierr_rel_entity0:
86362306a36Sopenharmony_ci	media_device_unregister_entity(&func->vdev.entity);
86462306a36Sopenharmony_ci	return ret;
86562306a36Sopenharmony_ci}
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_cistatic void hantro_detach_func(struct hantro_func *func)
86862306a36Sopenharmony_ci{
86962306a36Sopenharmony_ci	media_devnode_remove(func->intf_devnode);
87062306a36Sopenharmony_ci	media_entity_remove_links(&func->sink);
87162306a36Sopenharmony_ci	media_entity_remove_links(&func->proc);
87262306a36Sopenharmony_ci	media_entity_remove_links(&func->vdev.entity);
87362306a36Sopenharmony_ci	media_device_unregister_entity(&func->sink);
87462306a36Sopenharmony_ci	media_device_unregister_entity(&func->proc);
87562306a36Sopenharmony_ci	media_device_unregister_entity(&func->vdev.entity);
87662306a36Sopenharmony_ci}
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_cistatic int hantro_add_func(struct hantro_dev *vpu, unsigned int funcid)
87962306a36Sopenharmony_ci{
88062306a36Sopenharmony_ci	const struct of_device_id *match;
88162306a36Sopenharmony_ci	struct hantro_func *func;
88262306a36Sopenharmony_ci	struct video_device *vfd;
88362306a36Sopenharmony_ci	int ret;
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	match = of_match_node(of_hantro_match, vpu->dev->of_node);
88662306a36Sopenharmony_ci	func = devm_kzalloc(vpu->dev, sizeof(*func), GFP_KERNEL);
88762306a36Sopenharmony_ci	if (!func) {
88862306a36Sopenharmony_ci		v4l2_err(&vpu->v4l2_dev, "Failed to allocate video device\n");
88962306a36Sopenharmony_ci		return -ENOMEM;
89062306a36Sopenharmony_ci	}
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	func->id = funcid;
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	vfd = &func->vdev;
89562306a36Sopenharmony_ci	vfd->fops = &hantro_fops;
89662306a36Sopenharmony_ci	vfd->release = video_device_release_empty;
89762306a36Sopenharmony_ci	vfd->lock = &vpu->vpu_mutex;
89862306a36Sopenharmony_ci	vfd->v4l2_dev = &vpu->v4l2_dev;
89962306a36Sopenharmony_ci	vfd->vfl_dir = VFL_DIR_M2M;
90062306a36Sopenharmony_ci	vfd->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE;
90162306a36Sopenharmony_ci	vfd->ioctl_ops = &hantro_ioctl_ops;
90262306a36Sopenharmony_ci	snprintf(vfd->name, sizeof(vfd->name), "%s-%s", match->compatible,
90362306a36Sopenharmony_ci		 funcid == MEDIA_ENT_F_PROC_VIDEO_ENCODER ? "enc" : "dec");
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	if (funcid == MEDIA_ENT_F_PROC_VIDEO_ENCODER) {
90662306a36Sopenharmony_ci		vpu->encoder = func;
90762306a36Sopenharmony_ci		v4l2_disable_ioctl(vfd, VIDIOC_TRY_DECODER_CMD);
90862306a36Sopenharmony_ci		v4l2_disable_ioctl(vfd, VIDIOC_DECODER_CMD);
90962306a36Sopenharmony_ci	} else {
91062306a36Sopenharmony_ci		vpu->decoder = func;
91162306a36Sopenharmony_ci		v4l2_disable_ioctl(vfd, VIDIOC_TRY_ENCODER_CMD);
91262306a36Sopenharmony_ci		v4l2_disable_ioctl(vfd, VIDIOC_ENCODER_CMD);
91362306a36Sopenharmony_ci	}
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	video_set_drvdata(vfd, vpu);
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1);
91862306a36Sopenharmony_ci	if (ret) {
91962306a36Sopenharmony_ci		v4l2_err(&vpu->v4l2_dev, "Failed to register video device\n");
92062306a36Sopenharmony_ci		return ret;
92162306a36Sopenharmony_ci	}
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	ret = hantro_attach_func(vpu, func);
92462306a36Sopenharmony_ci	if (ret) {
92562306a36Sopenharmony_ci		v4l2_err(&vpu->v4l2_dev,
92662306a36Sopenharmony_ci			 "Failed to attach functionality to the media device\n");
92762306a36Sopenharmony_ci		goto err_unreg_dev;
92862306a36Sopenharmony_ci	}
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	v4l2_info(&vpu->v4l2_dev, "registered %s as /dev/video%d\n", vfd->name,
93162306a36Sopenharmony_ci		  vfd->num);
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	return 0;
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_cierr_unreg_dev:
93662306a36Sopenharmony_ci	video_unregister_device(vfd);
93762306a36Sopenharmony_ci	return ret;
93862306a36Sopenharmony_ci}
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_cistatic int hantro_add_enc_func(struct hantro_dev *vpu)
94162306a36Sopenharmony_ci{
94262306a36Sopenharmony_ci	if (!vpu->variant->enc_fmts)
94362306a36Sopenharmony_ci		return 0;
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	return hantro_add_func(vpu, MEDIA_ENT_F_PROC_VIDEO_ENCODER);
94662306a36Sopenharmony_ci}
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_cistatic int hantro_add_dec_func(struct hantro_dev *vpu)
94962306a36Sopenharmony_ci{
95062306a36Sopenharmony_ci	if (!vpu->variant->dec_fmts)
95162306a36Sopenharmony_ci		return 0;
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	return hantro_add_func(vpu, MEDIA_ENT_F_PROC_VIDEO_DECODER);
95462306a36Sopenharmony_ci}
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_cistatic void hantro_remove_func(struct hantro_dev *vpu,
95762306a36Sopenharmony_ci			       unsigned int funcid)
95862306a36Sopenharmony_ci{
95962306a36Sopenharmony_ci	struct hantro_func *func;
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci	if (funcid == MEDIA_ENT_F_PROC_VIDEO_ENCODER)
96262306a36Sopenharmony_ci		func = vpu->encoder;
96362306a36Sopenharmony_ci	else
96462306a36Sopenharmony_ci		func = vpu->decoder;
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	if (!func)
96762306a36Sopenharmony_ci		return;
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci	hantro_detach_func(func);
97062306a36Sopenharmony_ci	video_unregister_device(&func->vdev);
97162306a36Sopenharmony_ci}
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_cistatic void hantro_remove_enc_func(struct hantro_dev *vpu)
97462306a36Sopenharmony_ci{
97562306a36Sopenharmony_ci	hantro_remove_func(vpu, MEDIA_ENT_F_PROC_VIDEO_ENCODER);
97662306a36Sopenharmony_ci}
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_cistatic void hantro_remove_dec_func(struct hantro_dev *vpu)
97962306a36Sopenharmony_ci{
98062306a36Sopenharmony_ci	hantro_remove_func(vpu, MEDIA_ENT_F_PROC_VIDEO_DECODER);
98162306a36Sopenharmony_ci}
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_cistatic const struct media_device_ops hantro_m2m_media_ops = {
98462306a36Sopenharmony_ci	.req_validate = vb2_request_validate,
98562306a36Sopenharmony_ci	.req_queue = v4l2_m2m_request_queue,
98662306a36Sopenharmony_ci};
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_cistatic int hantro_probe(struct platform_device *pdev)
98962306a36Sopenharmony_ci{
99062306a36Sopenharmony_ci	const struct of_device_id *match;
99162306a36Sopenharmony_ci	struct hantro_dev *vpu;
99262306a36Sopenharmony_ci	int num_bases;
99362306a36Sopenharmony_ci	int i, ret;
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	vpu = devm_kzalloc(&pdev->dev, sizeof(*vpu), GFP_KERNEL);
99662306a36Sopenharmony_ci	if (!vpu)
99762306a36Sopenharmony_ci		return -ENOMEM;
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	vpu->dev = &pdev->dev;
100062306a36Sopenharmony_ci	vpu->pdev = pdev;
100162306a36Sopenharmony_ci	mutex_init(&vpu->vpu_mutex);
100262306a36Sopenharmony_ci	spin_lock_init(&vpu->irqlock);
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci	match = of_match_node(of_hantro_match, pdev->dev.of_node);
100562306a36Sopenharmony_ci	vpu->variant = match->data;
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	/*
100862306a36Sopenharmony_ci	 * Support for nxp,imx8mq-vpu is kept for backwards compatibility
100962306a36Sopenharmony_ci	 * but it's deprecated. Please update your DTS file to use
101062306a36Sopenharmony_ci	 * nxp,imx8mq-vpu-g1 or nxp,imx8mq-vpu-g2 instead.
101162306a36Sopenharmony_ci	 */
101262306a36Sopenharmony_ci	if (of_device_is_compatible(pdev->dev.of_node, "nxp,imx8mq-vpu"))
101362306a36Sopenharmony_ci		dev_warn(&pdev->dev, "%s compatible is deprecated\n",
101462306a36Sopenharmony_ci			 match->compatible);
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci	INIT_DELAYED_WORK(&vpu->watchdog_work, hantro_watchdog);
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	vpu->clocks = devm_kcalloc(&pdev->dev, vpu->variant->num_clocks,
101962306a36Sopenharmony_ci				   sizeof(*vpu->clocks), GFP_KERNEL);
102062306a36Sopenharmony_ci	if (!vpu->clocks)
102162306a36Sopenharmony_ci		return -ENOMEM;
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	if (vpu->variant->num_clocks > 1) {
102462306a36Sopenharmony_ci		for (i = 0; i < vpu->variant->num_clocks; i++)
102562306a36Sopenharmony_ci			vpu->clocks[i].id = vpu->variant->clk_names[i];
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci		ret = devm_clk_bulk_get(&pdev->dev, vpu->variant->num_clocks,
102862306a36Sopenharmony_ci					vpu->clocks);
102962306a36Sopenharmony_ci		if (ret)
103062306a36Sopenharmony_ci			return ret;
103162306a36Sopenharmony_ci	} else {
103262306a36Sopenharmony_ci		/*
103362306a36Sopenharmony_ci		 * If the driver has a single clk, chances are there will be no
103462306a36Sopenharmony_ci		 * actual name in the DT bindings.
103562306a36Sopenharmony_ci		 */
103662306a36Sopenharmony_ci		vpu->clocks[0].clk = devm_clk_get(&pdev->dev, NULL);
103762306a36Sopenharmony_ci		if (IS_ERR(vpu->clocks[0].clk))
103862306a36Sopenharmony_ci			return PTR_ERR(vpu->clocks[0].clk);
103962306a36Sopenharmony_ci	}
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	vpu->resets = devm_reset_control_array_get_optional_exclusive(&pdev->dev);
104262306a36Sopenharmony_ci	if (IS_ERR(vpu->resets))
104362306a36Sopenharmony_ci		return PTR_ERR(vpu->resets);
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci	num_bases = vpu->variant->num_regs ?: 1;
104662306a36Sopenharmony_ci	vpu->reg_bases = devm_kcalloc(&pdev->dev, num_bases,
104762306a36Sopenharmony_ci				      sizeof(*vpu->reg_bases), GFP_KERNEL);
104862306a36Sopenharmony_ci	if (!vpu->reg_bases)
104962306a36Sopenharmony_ci		return -ENOMEM;
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci	for (i = 0; i < num_bases; i++) {
105262306a36Sopenharmony_ci		vpu->reg_bases[i] = vpu->variant->reg_names ?
105362306a36Sopenharmony_ci		      devm_platform_ioremap_resource_byname(pdev, vpu->variant->reg_names[i]) :
105462306a36Sopenharmony_ci		      devm_platform_ioremap_resource(pdev, 0);
105562306a36Sopenharmony_ci		if (IS_ERR(vpu->reg_bases[i]))
105662306a36Sopenharmony_ci			return PTR_ERR(vpu->reg_bases[i]);
105762306a36Sopenharmony_ci	}
105862306a36Sopenharmony_ci	vpu->enc_base = vpu->reg_bases[0] + vpu->variant->enc_offset;
105962306a36Sopenharmony_ci	vpu->dec_base = vpu->reg_bases[0] + vpu->variant->dec_offset;
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci	/**
106262306a36Sopenharmony_ci	 * TODO: Eventually allow taking advantage of full 64-bit address space.
106362306a36Sopenharmony_ci	 * Until then we assume the MSB portion of buffers' base addresses is
106462306a36Sopenharmony_ci	 * always 0 due to this masking operation.
106562306a36Sopenharmony_ci	 */
106662306a36Sopenharmony_ci	ret = dma_set_coherent_mask(vpu->dev, DMA_BIT_MASK(32));
106762306a36Sopenharmony_ci	if (ret) {
106862306a36Sopenharmony_ci		dev_err(vpu->dev, "Could not set DMA coherent mask.\n");
106962306a36Sopenharmony_ci		return ret;
107062306a36Sopenharmony_ci	}
107162306a36Sopenharmony_ci	vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32));
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	for (i = 0; i < vpu->variant->num_irqs; i++) {
107462306a36Sopenharmony_ci		const char *irq_name;
107562306a36Sopenharmony_ci		int irq;
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci		if (!vpu->variant->irqs[i].handler)
107862306a36Sopenharmony_ci			continue;
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci		if (vpu->variant->num_irqs > 1) {
108162306a36Sopenharmony_ci			irq_name = vpu->variant->irqs[i].name;
108262306a36Sopenharmony_ci			irq = platform_get_irq_byname(vpu->pdev, irq_name);
108362306a36Sopenharmony_ci		} else {
108462306a36Sopenharmony_ci			/*
108562306a36Sopenharmony_ci			 * If the driver has a single IRQ, chances are there
108662306a36Sopenharmony_ci			 * will be no actual name in the DT bindings.
108762306a36Sopenharmony_ci			 */
108862306a36Sopenharmony_ci			irq_name = "default";
108962306a36Sopenharmony_ci			irq = platform_get_irq(vpu->pdev, 0);
109062306a36Sopenharmony_ci		}
109162306a36Sopenharmony_ci		if (irq < 0)
109262306a36Sopenharmony_ci			return irq;
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci		ret = devm_request_irq(vpu->dev, irq,
109562306a36Sopenharmony_ci				       vpu->variant->irqs[i].handler, 0,
109662306a36Sopenharmony_ci				       dev_name(vpu->dev), vpu);
109762306a36Sopenharmony_ci		if (ret) {
109862306a36Sopenharmony_ci			dev_err(vpu->dev, "Could not request %s IRQ.\n",
109962306a36Sopenharmony_ci				irq_name);
110062306a36Sopenharmony_ci			return ret;
110162306a36Sopenharmony_ci		}
110262306a36Sopenharmony_ci	}
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci	if (vpu->variant->init) {
110562306a36Sopenharmony_ci		ret = vpu->variant->init(vpu);
110662306a36Sopenharmony_ci		if (ret) {
110762306a36Sopenharmony_ci			dev_err(&pdev->dev, "Failed to init VPU hardware\n");
110862306a36Sopenharmony_ci			return ret;
110962306a36Sopenharmony_ci		}
111062306a36Sopenharmony_ci	}
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci	pm_runtime_set_autosuspend_delay(vpu->dev, 100);
111362306a36Sopenharmony_ci	pm_runtime_use_autosuspend(vpu->dev);
111462306a36Sopenharmony_ci	pm_runtime_enable(vpu->dev);
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	ret = reset_control_deassert(vpu->resets);
111762306a36Sopenharmony_ci	if (ret) {
111862306a36Sopenharmony_ci		dev_err(&pdev->dev, "Failed to deassert resets\n");
111962306a36Sopenharmony_ci		goto err_pm_disable;
112062306a36Sopenharmony_ci	}
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci	ret = clk_bulk_prepare(vpu->variant->num_clocks, vpu->clocks);
112362306a36Sopenharmony_ci	if (ret) {
112462306a36Sopenharmony_ci		dev_err(&pdev->dev, "Failed to prepare clocks\n");
112562306a36Sopenharmony_ci		goto err_rst_assert;
112662306a36Sopenharmony_ci	}
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci	ret = v4l2_device_register(&pdev->dev, &vpu->v4l2_dev);
112962306a36Sopenharmony_ci	if (ret) {
113062306a36Sopenharmony_ci		dev_err(&pdev->dev, "Failed to register v4l2 device\n");
113162306a36Sopenharmony_ci		goto err_clk_unprepare;
113262306a36Sopenharmony_ci	}
113362306a36Sopenharmony_ci	platform_set_drvdata(pdev, vpu);
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	vpu->m2m_dev = v4l2_m2m_init(&vpu_m2m_ops);
113662306a36Sopenharmony_ci	if (IS_ERR(vpu->m2m_dev)) {
113762306a36Sopenharmony_ci		v4l2_err(&vpu->v4l2_dev, "Failed to init mem2mem device\n");
113862306a36Sopenharmony_ci		ret = PTR_ERR(vpu->m2m_dev);
113962306a36Sopenharmony_ci		goto err_v4l2_unreg;
114062306a36Sopenharmony_ci	}
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci	vpu->mdev.dev = vpu->dev;
114362306a36Sopenharmony_ci	strscpy(vpu->mdev.model, DRIVER_NAME, sizeof(vpu->mdev.model));
114462306a36Sopenharmony_ci	media_device_init(&vpu->mdev);
114562306a36Sopenharmony_ci	vpu->mdev.ops = &hantro_m2m_media_ops;
114662306a36Sopenharmony_ci	vpu->v4l2_dev.mdev = &vpu->mdev;
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	ret = hantro_add_enc_func(vpu);
114962306a36Sopenharmony_ci	if (ret) {
115062306a36Sopenharmony_ci		dev_err(&pdev->dev, "Failed to register encoder\n");
115162306a36Sopenharmony_ci		goto err_m2m_rel;
115262306a36Sopenharmony_ci	}
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci	ret = hantro_add_dec_func(vpu);
115562306a36Sopenharmony_ci	if (ret) {
115662306a36Sopenharmony_ci		dev_err(&pdev->dev, "Failed to register decoder\n");
115762306a36Sopenharmony_ci		goto err_rm_enc_func;
115862306a36Sopenharmony_ci	}
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci	ret = media_device_register(&vpu->mdev);
116162306a36Sopenharmony_ci	if (ret) {
116262306a36Sopenharmony_ci		v4l2_err(&vpu->v4l2_dev, "Failed to register mem2mem media device\n");
116362306a36Sopenharmony_ci		goto err_rm_dec_func;
116462306a36Sopenharmony_ci	}
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci	return 0;
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_cierr_rm_dec_func:
116962306a36Sopenharmony_ci	hantro_remove_dec_func(vpu);
117062306a36Sopenharmony_cierr_rm_enc_func:
117162306a36Sopenharmony_ci	hantro_remove_enc_func(vpu);
117262306a36Sopenharmony_cierr_m2m_rel:
117362306a36Sopenharmony_ci	media_device_cleanup(&vpu->mdev);
117462306a36Sopenharmony_ci	v4l2_m2m_release(vpu->m2m_dev);
117562306a36Sopenharmony_cierr_v4l2_unreg:
117662306a36Sopenharmony_ci	v4l2_device_unregister(&vpu->v4l2_dev);
117762306a36Sopenharmony_cierr_clk_unprepare:
117862306a36Sopenharmony_ci	clk_bulk_unprepare(vpu->variant->num_clocks, vpu->clocks);
117962306a36Sopenharmony_cierr_rst_assert:
118062306a36Sopenharmony_ci	reset_control_assert(vpu->resets);
118162306a36Sopenharmony_cierr_pm_disable:
118262306a36Sopenharmony_ci	pm_runtime_dont_use_autosuspend(vpu->dev);
118362306a36Sopenharmony_ci	pm_runtime_disable(vpu->dev);
118462306a36Sopenharmony_ci	return ret;
118562306a36Sopenharmony_ci}
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_cistatic void hantro_remove(struct platform_device *pdev)
118862306a36Sopenharmony_ci{
118962306a36Sopenharmony_ci	struct hantro_dev *vpu = platform_get_drvdata(pdev);
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci	v4l2_info(&vpu->v4l2_dev, "Removing %s\n", pdev->name);
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	media_device_unregister(&vpu->mdev);
119462306a36Sopenharmony_ci	hantro_remove_dec_func(vpu);
119562306a36Sopenharmony_ci	hantro_remove_enc_func(vpu);
119662306a36Sopenharmony_ci	media_device_cleanup(&vpu->mdev);
119762306a36Sopenharmony_ci	v4l2_m2m_release(vpu->m2m_dev);
119862306a36Sopenharmony_ci	v4l2_device_unregister(&vpu->v4l2_dev);
119962306a36Sopenharmony_ci	clk_bulk_unprepare(vpu->variant->num_clocks, vpu->clocks);
120062306a36Sopenharmony_ci	reset_control_assert(vpu->resets);
120162306a36Sopenharmony_ci	pm_runtime_dont_use_autosuspend(vpu->dev);
120262306a36Sopenharmony_ci	pm_runtime_disable(vpu->dev);
120362306a36Sopenharmony_ci}
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci#ifdef CONFIG_PM
120662306a36Sopenharmony_cistatic int hantro_runtime_resume(struct device *dev)
120762306a36Sopenharmony_ci{
120862306a36Sopenharmony_ci	struct hantro_dev *vpu = dev_get_drvdata(dev);
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci	if (vpu->variant->runtime_resume)
121162306a36Sopenharmony_ci		return vpu->variant->runtime_resume(vpu);
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci	return 0;
121462306a36Sopenharmony_ci}
121562306a36Sopenharmony_ci#endif
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_cistatic const struct dev_pm_ops hantro_pm_ops = {
121862306a36Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
121962306a36Sopenharmony_ci				pm_runtime_force_resume)
122062306a36Sopenharmony_ci	SET_RUNTIME_PM_OPS(NULL, hantro_runtime_resume, NULL)
122162306a36Sopenharmony_ci};
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_cistatic struct platform_driver hantro_driver = {
122462306a36Sopenharmony_ci	.probe = hantro_probe,
122562306a36Sopenharmony_ci	.remove_new = hantro_remove,
122662306a36Sopenharmony_ci	.driver = {
122762306a36Sopenharmony_ci		   .name = DRIVER_NAME,
122862306a36Sopenharmony_ci		   .of_match_table = of_hantro_match,
122962306a36Sopenharmony_ci		   .pm = &hantro_pm_ops,
123062306a36Sopenharmony_ci	},
123162306a36Sopenharmony_ci};
123262306a36Sopenharmony_cimodule_platform_driver(hantro_driver);
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
123562306a36Sopenharmony_ciMODULE_AUTHOR("Alpha Lin <Alpha.Lin@Rock-Chips.com>");
123662306a36Sopenharmony_ciMODULE_AUTHOR("Tomasz Figa <tfiga@chromium.org>");
123762306a36Sopenharmony_ciMODULE_AUTHOR("Ezequiel Garcia <ezequiel@collabora.com>");
123862306a36Sopenharmony_ciMODULE_DESCRIPTION("Hantro VPU codec driver");
1239