162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2011 - 2012 Samsung Electronics Co., Ltd.
462306a36Sopenharmony_ci *		http://www.samsung.com
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Samsung EXYNOS5 SoC series G-Scaler driver
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/kernel.h>
1162306a36Sopenharmony_ci#include <linux/types.h>
1262306a36Sopenharmony_ci#include <linux/errno.h>
1362306a36Sopenharmony_ci#include <linux/bug.h>
1462306a36Sopenharmony_ci#include <linux/interrupt.h>
1562306a36Sopenharmony_ci#include <linux/workqueue.h>
1662306a36Sopenharmony_ci#include <linux/device.h>
1762306a36Sopenharmony_ci#include <linux/platform_device.h>
1862306a36Sopenharmony_ci#include <linux/list.h>
1962306a36Sopenharmony_ci#include <linux/io.h>
2062306a36Sopenharmony_ci#include <linux/slab.h>
2162306a36Sopenharmony_ci#include <linux/clk.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include <media/v4l2-ioctl.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include "gsc-core.h"
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic int gsc_m2m_ctx_stop_req(struct gsc_ctx *ctx)
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	struct gsc_ctx *curr_ctx;
3062306a36Sopenharmony_ci	struct gsc_dev *gsc = ctx->gsc_dev;
3162306a36Sopenharmony_ci	int ret;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	curr_ctx = v4l2_m2m_get_curr_priv(gsc->m2m.m2m_dev);
3462306a36Sopenharmony_ci	if (!gsc_m2m_pending(gsc) || (curr_ctx != ctx))
3562306a36Sopenharmony_ci		return 0;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	gsc_ctx_state_lock_set(GSC_CTX_STOP_REQ, ctx);
3862306a36Sopenharmony_ci	ret = wait_event_timeout(gsc->irq_queue,
3962306a36Sopenharmony_ci			!gsc_ctx_state_is_set(GSC_CTX_STOP_REQ, ctx),
4062306a36Sopenharmony_ci			GSC_SHUTDOWN_TIMEOUT);
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	return ret == 0 ? -ETIMEDOUT : ret;
4362306a36Sopenharmony_ci}
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic void __gsc_m2m_job_abort(struct gsc_ctx *ctx)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	int ret;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	ret = gsc_m2m_ctx_stop_req(ctx);
5062306a36Sopenharmony_ci	if ((ret == -ETIMEDOUT) || (ctx->state & GSC_CTX_ABORT)) {
5162306a36Sopenharmony_ci		gsc_ctx_state_lock_clear(GSC_CTX_STOP_REQ | GSC_CTX_ABORT, ctx);
5262306a36Sopenharmony_ci		gsc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR);
5362306a36Sopenharmony_ci	}
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic int gsc_m2m_start_streaming(struct vb2_queue *q, unsigned int count)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	struct gsc_ctx *ctx = q->drv_priv;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	return pm_runtime_resume_and_get(&ctx->gsc_dev->pdev->dev);
6162306a36Sopenharmony_ci}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic void __gsc_m2m_cleanup_queue(struct gsc_ctx *ctx)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	struct vb2_v4l2_buffer *src_vb, *dst_vb;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	while (v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) > 0) {
6862306a36Sopenharmony_ci		src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
6962306a36Sopenharmony_ci		v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_ERROR);
7062306a36Sopenharmony_ci	}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	while (v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx) > 0) {
7362306a36Sopenharmony_ci		dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
7462306a36Sopenharmony_ci		v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_ERROR);
7562306a36Sopenharmony_ci	}
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic void gsc_m2m_stop_streaming(struct vb2_queue *q)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	struct gsc_ctx *ctx = q->drv_priv;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	__gsc_m2m_job_abort(ctx);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	__gsc_m2m_cleanup_queue(ctx);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	pm_runtime_put(&ctx->gsc_dev->pdev->dev);
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_civoid gsc_m2m_job_finish(struct gsc_ctx *ctx, int vb_state)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	struct vb2_v4l2_buffer *src_vb, *dst_vb;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	if (!ctx || !ctx->m2m_ctx)
9462306a36Sopenharmony_ci		return;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
9762306a36Sopenharmony_ci	dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	if (src_vb && dst_vb) {
10062306a36Sopenharmony_ci		dst_vb->vb2_buf.timestamp = src_vb->vb2_buf.timestamp;
10162306a36Sopenharmony_ci		dst_vb->timecode = src_vb->timecode;
10262306a36Sopenharmony_ci		dst_vb->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
10362306a36Sopenharmony_ci		dst_vb->flags |=
10462306a36Sopenharmony_ci			src_vb->flags
10562306a36Sopenharmony_ci			& V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci		v4l2_m2m_buf_done(src_vb, vb_state);
10862306a36Sopenharmony_ci		v4l2_m2m_buf_done(dst_vb, vb_state);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci		v4l2_m2m_job_finish(ctx->gsc_dev->m2m.m2m_dev,
11162306a36Sopenharmony_ci				    ctx->m2m_ctx);
11262306a36Sopenharmony_ci	}
11362306a36Sopenharmony_ci}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_cistatic void gsc_m2m_job_abort(void *priv)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	__gsc_m2m_job_abort((struct gsc_ctx *)priv);
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic int gsc_get_bufs(struct gsc_ctx *ctx)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	struct gsc_frame *s_frame, *d_frame;
12362306a36Sopenharmony_ci	struct vb2_v4l2_buffer *src_vb, *dst_vb;
12462306a36Sopenharmony_ci	int ret;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	s_frame = &ctx->s_frame;
12762306a36Sopenharmony_ci	d_frame = &ctx->d_frame;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	src_vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
13062306a36Sopenharmony_ci	ret = gsc_prepare_addr(ctx, &src_vb->vb2_buf, s_frame, &s_frame->addr);
13162306a36Sopenharmony_ci	if (ret)
13262306a36Sopenharmony_ci		return ret;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	dst_vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
13562306a36Sopenharmony_ci	ret = gsc_prepare_addr(ctx, &dst_vb->vb2_buf, d_frame, &d_frame->addr);
13662306a36Sopenharmony_ci	if (ret)
13762306a36Sopenharmony_ci		return ret;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	dst_vb->vb2_buf.timestamp = src_vb->vb2_buf.timestamp;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	return 0;
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cistatic void gsc_m2m_device_run(void *priv)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	struct gsc_ctx *ctx = priv;
14762306a36Sopenharmony_ci	struct gsc_dev *gsc;
14862306a36Sopenharmony_ci	unsigned long flags;
14962306a36Sopenharmony_ci	int ret;
15062306a36Sopenharmony_ci	bool is_set = false;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	if (WARN(!ctx, "null hardware context\n"))
15362306a36Sopenharmony_ci		return;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	gsc = ctx->gsc_dev;
15662306a36Sopenharmony_ci	spin_lock_irqsave(&gsc->slock, flags);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	set_bit(ST_M2M_PEND, &gsc->state);
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	/* Reconfigure hardware if the context has changed. */
16162306a36Sopenharmony_ci	if (gsc->m2m.ctx != ctx) {
16262306a36Sopenharmony_ci		pr_debug("gsc->m2m.ctx = 0x%p, current_ctx = 0x%p",
16362306a36Sopenharmony_ci				gsc->m2m.ctx, ctx);
16462306a36Sopenharmony_ci		ctx->state |= GSC_PARAMS;
16562306a36Sopenharmony_ci		gsc->m2m.ctx = ctx;
16662306a36Sopenharmony_ci	}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	is_set = ctx->state & GSC_CTX_STOP_REQ;
16962306a36Sopenharmony_ci	if (is_set) {
17062306a36Sopenharmony_ci		ctx->state &= ~GSC_CTX_STOP_REQ;
17162306a36Sopenharmony_ci		ctx->state |= GSC_CTX_ABORT;
17262306a36Sopenharmony_ci		wake_up(&gsc->irq_queue);
17362306a36Sopenharmony_ci		goto put_device;
17462306a36Sopenharmony_ci	}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	ret = gsc_get_bufs(ctx);
17762306a36Sopenharmony_ci	if (ret) {
17862306a36Sopenharmony_ci		pr_err("Wrong address");
17962306a36Sopenharmony_ci		goto put_device;
18062306a36Sopenharmony_ci	}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	gsc_set_prefbuf(gsc, &ctx->s_frame);
18362306a36Sopenharmony_ci	gsc_hw_set_input_addr(gsc, &ctx->s_frame.addr, GSC_M2M_BUF_NUM);
18462306a36Sopenharmony_ci	gsc_hw_set_output_addr(gsc, &ctx->d_frame.addr, GSC_M2M_BUF_NUM);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	if (ctx->state & GSC_PARAMS) {
18762306a36Sopenharmony_ci		gsc_hw_set_input_buf_masking(gsc, GSC_M2M_BUF_NUM, false);
18862306a36Sopenharmony_ci		gsc_hw_set_output_buf_masking(gsc, GSC_M2M_BUF_NUM, false);
18962306a36Sopenharmony_ci		gsc_hw_set_frm_done_irq_mask(gsc, false);
19062306a36Sopenharmony_ci		gsc_hw_set_gsc_irq_enable(gsc, true);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci		if (gsc_set_scaler_info(ctx)) {
19362306a36Sopenharmony_ci			pr_err("Scaler setup error");
19462306a36Sopenharmony_ci			goto put_device;
19562306a36Sopenharmony_ci		}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci		gsc_hw_set_input_path(ctx);
19862306a36Sopenharmony_ci		gsc_hw_set_in_size(ctx);
19962306a36Sopenharmony_ci		gsc_hw_set_in_image_format(ctx);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci		gsc_hw_set_output_path(ctx);
20262306a36Sopenharmony_ci		gsc_hw_set_out_size(ctx);
20362306a36Sopenharmony_ci		gsc_hw_set_out_image_format(ctx);
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci		gsc_hw_set_prescaler(ctx);
20662306a36Sopenharmony_ci		gsc_hw_set_mainscaler(ctx);
20762306a36Sopenharmony_ci		gsc_hw_set_rotation(ctx);
20862306a36Sopenharmony_ci		gsc_hw_set_global_alpha(ctx);
20962306a36Sopenharmony_ci	}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	/* update shadow registers */
21262306a36Sopenharmony_ci	gsc_hw_set_sfr_update(ctx);
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	ctx->state &= ~GSC_PARAMS;
21562306a36Sopenharmony_ci	gsc_hw_enable_control(gsc, true);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	spin_unlock_irqrestore(&gsc->slock, flags);
21862306a36Sopenharmony_ci	return;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ciput_device:
22162306a36Sopenharmony_ci	ctx->state &= ~GSC_PARAMS;
22262306a36Sopenharmony_ci	spin_unlock_irqrestore(&gsc->slock, flags);
22362306a36Sopenharmony_ci}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_cistatic int gsc_m2m_queue_setup(struct vb2_queue *vq,
22662306a36Sopenharmony_ci			unsigned int *num_buffers, unsigned int *num_planes,
22762306a36Sopenharmony_ci			unsigned int sizes[], struct device *alloc_devs[])
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	struct gsc_ctx *ctx = vb2_get_drv_priv(vq);
23062306a36Sopenharmony_ci	struct gsc_frame *frame;
23162306a36Sopenharmony_ci	int i;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	frame = ctx_get_frame(ctx, vq->type);
23462306a36Sopenharmony_ci	if (IS_ERR(frame))
23562306a36Sopenharmony_ci		return PTR_ERR(frame);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	if (!frame->fmt)
23862306a36Sopenharmony_ci		return -EINVAL;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	*num_planes = frame->fmt->num_planes;
24162306a36Sopenharmony_ci	for (i = 0; i < frame->fmt->num_planes; i++)
24262306a36Sopenharmony_ci		sizes[i] = frame->payload[i];
24362306a36Sopenharmony_ci	return 0;
24462306a36Sopenharmony_ci}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_cistatic int gsc_m2m_buf_prepare(struct vb2_buffer *vb)
24762306a36Sopenharmony_ci{
24862306a36Sopenharmony_ci	struct gsc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
24962306a36Sopenharmony_ci	struct gsc_frame *frame;
25062306a36Sopenharmony_ci	int i;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	frame = ctx_get_frame(ctx, vb->vb2_queue->type);
25362306a36Sopenharmony_ci	if (IS_ERR(frame))
25462306a36Sopenharmony_ci		return PTR_ERR(frame);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	if (V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type)) {
25762306a36Sopenharmony_ci		for (i = 0; i < frame->fmt->num_planes; i++)
25862306a36Sopenharmony_ci			vb2_set_plane_payload(vb, i, frame->payload[i]);
25962306a36Sopenharmony_ci	}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	return 0;
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_cistatic void gsc_m2m_buf_queue(struct vb2_buffer *vb)
26562306a36Sopenharmony_ci{
26662306a36Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
26762306a36Sopenharmony_ci	struct gsc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	pr_debug("ctx: %p, ctx->state: 0x%x", ctx, ctx->state);
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	if (ctx->m2m_ctx)
27262306a36Sopenharmony_ci		v4l2_m2m_buf_queue(ctx->m2m_ctx, vbuf);
27362306a36Sopenharmony_ci}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_cistatic const struct vb2_ops gsc_m2m_qops = {
27662306a36Sopenharmony_ci	.queue_setup	 = gsc_m2m_queue_setup,
27762306a36Sopenharmony_ci	.buf_prepare	 = gsc_m2m_buf_prepare,
27862306a36Sopenharmony_ci	.buf_queue	 = gsc_m2m_buf_queue,
27962306a36Sopenharmony_ci	.wait_prepare	 = vb2_ops_wait_prepare,
28062306a36Sopenharmony_ci	.wait_finish	 = vb2_ops_wait_finish,
28162306a36Sopenharmony_ci	.stop_streaming	 = gsc_m2m_stop_streaming,
28262306a36Sopenharmony_ci	.start_streaming = gsc_m2m_start_streaming,
28362306a36Sopenharmony_ci};
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_cistatic int gsc_m2m_querycap(struct file *file, void *fh,
28662306a36Sopenharmony_ci			   struct v4l2_capability *cap)
28762306a36Sopenharmony_ci{
28862306a36Sopenharmony_ci	strscpy(cap->driver, GSC_MODULE_NAME, sizeof(cap->driver));
28962306a36Sopenharmony_ci	strscpy(cap->card, GSC_MODULE_NAME " gscaler", sizeof(cap->card));
29062306a36Sopenharmony_ci	return 0;
29162306a36Sopenharmony_ci}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_cistatic int gsc_m2m_enum_fmt(struct file *file, void *priv,
29462306a36Sopenharmony_ci			    struct v4l2_fmtdesc *f)
29562306a36Sopenharmony_ci{
29662306a36Sopenharmony_ci	return gsc_enum_fmt(f);
29762306a36Sopenharmony_ci}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_cistatic int gsc_m2m_g_fmt_mplane(struct file *file, void *fh,
30062306a36Sopenharmony_ci			     struct v4l2_format *f)
30162306a36Sopenharmony_ci{
30262306a36Sopenharmony_ci	struct gsc_ctx *ctx = fh_to_ctx(fh);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	return gsc_g_fmt_mplane(ctx, f);
30562306a36Sopenharmony_ci}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_cistatic int gsc_m2m_try_fmt_mplane(struct file *file, void *fh,
30862306a36Sopenharmony_ci				  struct v4l2_format *f)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	struct gsc_ctx *ctx = fh_to_ctx(fh);
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	return gsc_try_fmt_mplane(ctx, f);
31362306a36Sopenharmony_ci}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cistatic int gsc_m2m_s_fmt_mplane(struct file *file, void *fh,
31662306a36Sopenharmony_ci				 struct v4l2_format *f)
31762306a36Sopenharmony_ci{
31862306a36Sopenharmony_ci	struct gsc_ctx *ctx = fh_to_ctx(fh);
31962306a36Sopenharmony_ci	struct vb2_queue *vq;
32062306a36Sopenharmony_ci	struct gsc_frame *frame;
32162306a36Sopenharmony_ci	struct v4l2_pix_format_mplane *pix;
32262306a36Sopenharmony_ci	int i, ret = 0;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	ret = gsc_m2m_try_fmt_mplane(file, fh, f);
32562306a36Sopenharmony_ci	if (ret)
32662306a36Sopenharmony_ci		return ret;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	if (vb2_is_streaming(vq)) {
33162306a36Sopenharmony_ci		pr_err("queue (%d) busy", f->type);
33262306a36Sopenharmony_ci		return -EBUSY;
33362306a36Sopenharmony_ci	}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	if (V4L2_TYPE_IS_OUTPUT(f->type))
33662306a36Sopenharmony_ci		frame = &ctx->s_frame;
33762306a36Sopenharmony_ci	else
33862306a36Sopenharmony_ci		frame = &ctx->d_frame;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	pix = &f->fmt.pix_mp;
34162306a36Sopenharmony_ci	frame->fmt = find_fmt(&pix->pixelformat, NULL, 0);
34262306a36Sopenharmony_ci	frame->colorspace = pix->colorspace;
34362306a36Sopenharmony_ci	if (!frame->fmt)
34462306a36Sopenharmony_ci		return -EINVAL;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	for (i = 0; i < frame->fmt->num_planes; i++)
34762306a36Sopenharmony_ci		frame->payload[i] = pix->plane_fmt[i].sizeimage;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	gsc_set_frame_size(frame, pix->width, pix->height);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
35262306a36Sopenharmony_ci		gsc_ctx_state_lock_set(GSC_PARAMS | GSC_DST_FMT, ctx);
35362306a36Sopenharmony_ci	else
35462306a36Sopenharmony_ci		gsc_ctx_state_lock_set(GSC_PARAMS | GSC_SRC_FMT, ctx);
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	pr_debug("f_w: %d, f_h: %d", frame->f_width, frame->f_height);
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	return 0;
35962306a36Sopenharmony_ci}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_cistatic int gsc_m2m_reqbufs(struct file *file, void *fh,
36262306a36Sopenharmony_ci			  struct v4l2_requestbuffers *reqbufs)
36362306a36Sopenharmony_ci{
36462306a36Sopenharmony_ci	struct gsc_ctx *ctx = fh_to_ctx(fh);
36562306a36Sopenharmony_ci	struct gsc_dev *gsc = ctx->gsc_dev;
36662306a36Sopenharmony_ci	u32 max_cnt;
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	max_cnt = (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ?
36962306a36Sopenharmony_ci		gsc->variant->in_buf_cnt : gsc->variant->out_buf_cnt;
37062306a36Sopenharmony_ci	if (reqbufs->count > max_cnt)
37162306a36Sopenharmony_ci		return -EINVAL;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
37462306a36Sopenharmony_ci}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_cistatic int gsc_m2m_expbuf(struct file *file, void *fh,
37762306a36Sopenharmony_ci				struct v4l2_exportbuffer *eb)
37862306a36Sopenharmony_ci{
37962306a36Sopenharmony_ci	struct gsc_ctx *ctx = fh_to_ctx(fh);
38062306a36Sopenharmony_ci	return v4l2_m2m_expbuf(file, ctx->m2m_ctx, eb);
38162306a36Sopenharmony_ci}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_cistatic int gsc_m2m_querybuf(struct file *file, void *fh,
38462306a36Sopenharmony_ci					struct v4l2_buffer *buf)
38562306a36Sopenharmony_ci{
38662306a36Sopenharmony_ci	struct gsc_ctx *ctx = fh_to_ctx(fh);
38762306a36Sopenharmony_ci	return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
38862306a36Sopenharmony_ci}
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_cistatic int gsc_m2m_qbuf(struct file *file, void *fh,
39162306a36Sopenharmony_ci			  struct v4l2_buffer *buf)
39262306a36Sopenharmony_ci{
39362306a36Sopenharmony_ci	struct gsc_ctx *ctx = fh_to_ctx(fh);
39462306a36Sopenharmony_ci	return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
39562306a36Sopenharmony_ci}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_cistatic int gsc_m2m_dqbuf(struct file *file, void *fh,
39862306a36Sopenharmony_ci			   struct v4l2_buffer *buf)
39962306a36Sopenharmony_ci{
40062306a36Sopenharmony_ci	struct gsc_ctx *ctx = fh_to_ctx(fh);
40162306a36Sopenharmony_ci	return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
40262306a36Sopenharmony_ci}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_cistatic int gsc_m2m_streamon(struct file *file, void *fh,
40562306a36Sopenharmony_ci			   enum v4l2_buf_type type)
40662306a36Sopenharmony_ci{
40762306a36Sopenharmony_ci	struct gsc_ctx *ctx = fh_to_ctx(fh);
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	/* The source and target color format need to be set */
41062306a36Sopenharmony_ci	if (V4L2_TYPE_IS_OUTPUT(type)) {
41162306a36Sopenharmony_ci		if (!gsc_ctx_state_is_set(GSC_SRC_FMT, ctx))
41262306a36Sopenharmony_ci			return -EINVAL;
41362306a36Sopenharmony_ci	} else if (!gsc_ctx_state_is_set(GSC_DST_FMT, ctx)) {
41462306a36Sopenharmony_ci		return -EINVAL;
41562306a36Sopenharmony_ci	}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
41862306a36Sopenharmony_ci}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_cistatic int gsc_m2m_streamoff(struct file *file, void *fh,
42162306a36Sopenharmony_ci			    enum v4l2_buf_type type)
42262306a36Sopenharmony_ci{
42362306a36Sopenharmony_ci	struct gsc_ctx *ctx = fh_to_ctx(fh);
42462306a36Sopenharmony_ci	return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
42562306a36Sopenharmony_ci}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci/* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */
42862306a36Sopenharmony_cistatic int is_rectangle_enclosed(struct v4l2_rect *a, struct v4l2_rect *b)
42962306a36Sopenharmony_ci{
43062306a36Sopenharmony_ci	if (a->left < b->left || a->top < b->top)
43162306a36Sopenharmony_ci		return 0;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	if (a->left + a->width > b->left + b->width)
43462306a36Sopenharmony_ci		return 0;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	if (a->top + a->height > b->top + b->height)
43762306a36Sopenharmony_ci		return 0;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	return 1;
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_cistatic int gsc_m2m_g_selection(struct file *file, void *fh,
44362306a36Sopenharmony_ci			struct v4l2_selection *s)
44462306a36Sopenharmony_ci{
44562306a36Sopenharmony_ci	struct gsc_frame *frame;
44662306a36Sopenharmony_ci	struct gsc_ctx *ctx = fh_to_ctx(fh);
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
44962306a36Sopenharmony_ci	    (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT))
45062306a36Sopenharmony_ci		return -EINVAL;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	frame = ctx_get_frame(ctx, s->type);
45362306a36Sopenharmony_ci	if (IS_ERR(frame))
45462306a36Sopenharmony_ci		return PTR_ERR(frame);
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	switch (s->target) {
45762306a36Sopenharmony_ci	case V4L2_SEL_TGT_COMPOSE_DEFAULT:
45862306a36Sopenharmony_ci	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
45962306a36Sopenharmony_ci	case V4L2_SEL_TGT_CROP_BOUNDS:
46062306a36Sopenharmony_ci	case V4L2_SEL_TGT_CROP_DEFAULT:
46162306a36Sopenharmony_ci		s->r.left = 0;
46262306a36Sopenharmony_ci		s->r.top = 0;
46362306a36Sopenharmony_ci		s->r.width = frame->f_width;
46462306a36Sopenharmony_ci		s->r.height = frame->f_height;
46562306a36Sopenharmony_ci		return 0;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	case V4L2_SEL_TGT_COMPOSE:
46862306a36Sopenharmony_ci	case V4L2_SEL_TGT_CROP:
46962306a36Sopenharmony_ci		s->r.left = frame->crop.left;
47062306a36Sopenharmony_ci		s->r.top = frame->crop.top;
47162306a36Sopenharmony_ci		s->r.width = frame->crop.width;
47262306a36Sopenharmony_ci		s->r.height = frame->crop.height;
47362306a36Sopenharmony_ci		return 0;
47462306a36Sopenharmony_ci	}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	return -EINVAL;
47762306a36Sopenharmony_ci}
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_cistatic int gsc_m2m_s_selection(struct file *file, void *fh,
48062306a36Sopenharmony_ci				struct v4l2_selection *s)
48162306a36Sopenharmony_ci{
48262306a36Sopenharmony_ci	struct gsc_frame *frame;
48362306a36Sopenharmony_ci	struct gsc_ctx *ctx = fh_to_ctx(fh);
48462306a36Sopenharmony_ci	struct gsc_variant *variant = ctx->gsc_dev->variant;
48562306a36Sopenharmony_ci	struct v4l2_selection sel = *s;
48662306a36Sopenharmony_ci	int ret;
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
48962306a36Sopenharmony_ci	    (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT))
49062306a36Sopenharmony_ci		return -EINVAL;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	ret = gsc_try_selection(ctx, &sel);
49362306a36Sopenharmony_ci	if (ret)
49462306a36Sopenharmony_ci		return ret;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	if (s->flags & V4L2_SEL_FLAG_LE &&
49762306a36Sopenharmony_ci	    !is_rectangle_enclosed(&sel.r, &s->r))
49862306a36Sopenharmony_ci		return -ERANGE;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	if (s->flags & V4L2_SEL_FLAG_GE &&
50162306a36Sopenharmony_ci	    !is_rectangle_enclosed(&s->r, &sel.r))
50262306a36Sopenharmony_ci		return -ERANGE;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	s->r = sel.r;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	switch (s->target) {
50762306a36Sopenharmony_ci	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
50862306a36Sopenharmony_ci	case V4L2_SEL_TGT_COMPOSE_DEFAULT:
50962306a36Sopenharmony_ci	case V4L2_SEL_TGT_COMPOSE:
51062306a36Sopenharmony_ci		frame = &ctx->s_frame;
51162306a36Sopenharmony_ci		break;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	case V4L2_SEL_TGT_CROP_BOUNDS:
51462306a36Sopenharmony_ci	case V4L2_SEL_TGT_CROP:
51562306a36Sopenharmony_ci	case V4L2_SEL_TGT_CROP_DEFAULT:
51662306a36Sopenharmony_ci		frame = &ctx->d_frame;
51762306a36Sopenharmony_ci		break;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	default:
52062306a36Sopenharmony_ci		return -EINVAL;
52162306a36Sopenharmony_ci	}
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	/* Check to see if scaling ratio is within supported range */
52462306a36Sopenharmony_ci	if (gsc_ctx_state_is_set(GSC_DST_FMT | GSC_SRC_FMT, ctx)) {
52562306a36Sopenharmony_ci		if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
52662306a36Sopenharmony_ci			ret = gsc_check_scaler_ratio(variant, sel.r.width,
52762306a36Sopenharmony_ci				sel.r.height, ctx->d_frame.crop.width,
52862306a36Sopenharmony_ci				ctx->d_frame.crop.height,
52962306a36Sopenharmony_ci				ctx->gsc_ctrls.rotate->val, ctx->out_path);
53062306a36Sopenharmony_ci		} else {
53162306a36Sopenharmony_ci			ret = gsc_check_scaler_ratio(variant,
53262306a36Sopenharmony_ci				ctx->s_frame.crop.width,
53362306a36Sopenharmony_ci				ctx->s_frame.crop.height, sel.r.width,
53462306a36Sopenharmony_ci				sel.r.height, ctx->gsc_ctrls.rotate->val,
53562306a36Sopenharmony_ci				ctx->out_path);
53662306a36Sopenharmony_ci		}
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci		if (ret) {
53962306a36Sopenharmony_ci			pr_err("Out of scaler range");
54062306a36Sopenharmony_ci			return -EINVAL;
54162306a36Sopenharmony_ci		}
54262306a36Sopenharmony_ci	}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	frame->crop = sel.r;
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	gsc_ctx_state_lock_set(GSC_PARAMS, ctx);
54762306a36Sopenharmony_ci	return 0;
54862306a36Sopenharmony_ci}
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops gsc_m2m_ioctl_ops = {
55162306a36Sopenharmony_ci	.vidioc_querycap		= gsc_m2m_querycap,
55262306a36Sopenharmony_ci	.vidioc_enum_fmt_vid_cap	= gsc_m2m_enum_fmt,
55362306a36Sopenharmony_ci	.vidioc_enum_fmt_vid_out	= gsc_m2m_enum_fmt,
55462306a36Sopenharmony_ci	.vidioc_g_fmt_vid_cap_mplane	= gsc_m2m_g_fmt_mplane,
55562306a36Sopenharmony_ci	.vidioc_g_fmt_vid_out_mplane	= gsc_m2m_g_fmt_mplane,
55662306a36Sopenharmony_ci	.vidioc_try_fmt_vid_cap_mplane	= gsc_m2m_try_fmt_mplane,
55762306a36Sopenharmony_ci	.vidioc_try_fmt_vid_out_mplane	= gsc_m2m_try_fmt_mplane,
55862306a36Sopenharmony_ci	.vidioc_s_fmt_vid_cap_mplane	= gsc_m2m_s_fmt_mplane,
55962306a36Sopenharmony_ci	.vidioc_s_fmt_vid_out_mplane	= gsc_m2m_s_fmt_mplane,
56062306a36Sopenharmony_ci	.vidioc_reqbufs			= gsc_m2m_reqbufs,
56162306a36Sopenharmony_ci	.vidioc_expbuf                  = gsc_m2m_expbuf,
56262306a36Sopenharmony_ci	.vidioc_querybuf		= gsc_m2m_querybuf,
56362306a36Sopenharmony_ci	.vidioc_qbuf			= gsc_m2m_qbuf,
56462306a36Sopenharmony_ci	.vidioc_dqbuf			= gsc_m2m_dqbuf,
56562306a36Sopenharmony_ci	.vidioc_streamon		= gsc_m2m_streamon,
56662306a36Sopenharmony_ci	.vidioc_streamoff		= gsc_m2m_streamoff,
56762306a36Sopenharmony_ci	.vidioc_g_selection		= gsc_m2m_g_selection,
56862306a36Sopenharmony_ci	.vidioc_s_selection		= gsc_m2m_s_selection
56962306a36Sopenharmony_ci};
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_cistatic int queue_init(void *priv, struct vb2_queue *src_vq,
57262306a36Sopenharmony_ci			struct vb2_queue *dst_vq)
57362306a36Sopenharmony_ci{
57462306a36Sopenharmony_ci	struct gsc_ctx *ctx = priv;
57562306a36Sopenharmony_ci	int ret;
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	memset(src_vq, 0, sizeof(*src_vq));
57862306a36Sopenharmony_ci	src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
57962306a36Sopenharmony_ci	src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
58062306a36Sopenharmony_ci	src_vq->drv_priv = ctx;
58162306a36Sopenharmony_ci	src_vq->ops = &gsc_m2m_qops;
58262306a36Sopenharmony_ci	src_vq->mem_ops = &vb2_dma_contig_memops;
58362306a36Sopenharmony_ci	src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
58462306a36Sopenharmony_ci	src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
58562306a36Sopenharmony_ci	src_vq->lock = &ctx->gsc_dev->lock;
58662306a36Sopenharmony_ci	src_vq->dev = &ctx->gsc_dev->pdev->dev;
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	ret = vb2_queue_init(src_vq);
58962306a36Sopenharmony_ci	if (ret)
59062306a36Sopenharmony_ci		return ret;
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	memset(dst_vq, 0, sizeof(*dst_vq));
59362306a36Sopenharmony_ci	dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
59462306a36Sopenharmony_ci	dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
59562306a36Sopenharmony_ci	dst_vq->drv_priv = ctx;
59662306a36Sopenharmony_ci	dst_vq->ops = &gsc_m2m_qops;
59762306a36Sopenharmony_ci	dst_vq->mem_ops = &vb2_dma_contig_memops;
59862306a36Sopenharmony_ci	dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
59962306a36Sopenharmony_ci	dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
60062306a36Sopenharmony_ci	dst_vq->lock = &ctx->gsc_dev->lock;
60162306a36Sopenharmony_ci	dst_vq->dev = &ctx->gsc_dev->pdev->dev;
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	return vb2_queue_init(dst_vq);
60462306a36Sopenharmony_ci}
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_cistatic int gsc_m2m_open(struct file *file)
60762306a36Sopenharmony_ci{
60862306a36Sopenharmony_ci	struct gsc_dev *gsc = video_drvdata(file);
60962306a36Sopenharmony_ci	struct gsc_ctx *ctx = NULL;
61062306a36Sopenharmony_ci	int ret;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	pr_debug("pid: %d, state: 0x%lx", task_pid_nr(current), gsc->state);
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	if (mutex_lock_interruptible(&gsc->lock))
61562306a36Sopenharmony_ci		return -ERESTARTSYS;
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
61862306a36Sopenharmony_ci	if (!ctx) {
61962306a36Sopenharmony_ci		ret = -ENOMEM;
62062306a36Sopenharmony_ci		goto unlock;
62162306a36Sopenharmony_ci	}
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	v4l2_fh_init(&ctx->fh, gsc->m2m.vfd);
62462306a36Sopenharmony_ci	ret = gsc_ctrls_create(ctx);
62562306a36Sopenharmony_ci	if (ret)
62662306a36Sopenharmony_ci		goto error_fh;
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	/* Use separate control handler per file handle */
62962306a36Sopenharmony_ci	ctx->fh.ctrl_handler = &ctx->ctrl_handler;
63062306a36Sopenharmony_ci	file->private_data = &ctx->fh;
63162306a36Sopenharmony_ci	v4l2_fh_add(&ctx->fh);
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	ctx->gsc_dev = gsc;
63462306a36Sopenharmony_ci	/* Default color format */
63562306a36Sopenharmony_ci	ctx->s_frame.fmt = get_format(0);
63662306a36Sopenharmony_ci	ctx->d_frame.fmt = get_format(0);
63762306a36Sopenharmony_ci	/* Setup the device context for mem2mem mode. */
63862306a36Sopenharmony_ci	ctx->state = GSC_CTX_M2M;
63962306a36Sopenharmony_ci	ctx->flags = 0;
64062306a36Sopenharmony_ci	ctx->in_path = GSC_DMA;
64162306a36Sopenharmony_ci	ctx->out_path = GSC_DMA;
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	ctx->m2m_ctx = v4l2_m2m_ctx_init(gsc->m2m.m2m_dev, ctx, queue_init);
64462306a36Sopenharmony_ci	if (IS_ERR(ctx->m2m_ctx)) {
64562306a36Sopenharmony_ci		pr_err("Failed to initialize m2m context");
64662306a36Sopenharmony_ci		ret = PTR_ERR(ctx->m2m_ctx);
64762306a36Sopenharmony_ci		goto error_ctrls;
64862306a36Sopenharmony_ci	}
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	if (gsc->m2m.refcnt++ == 0)
65162306a36Sopenharmony_ci		set_bit(ST_M2M_OPEN, &gsc->state);
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	pr_debug("gsc m2m driver is opened, ctx(0x%p)", ctx);
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	mutex_unlock(&gsc->lock);
65662306a36Sopenharmony_ci	return 0;
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_cierror_ctrls:
65962306a36Sopenharmony_ci	gsc_ctrls_delete(ctx);
66062306a36Sopenharmony_ci	v4l2_fh_del(&ctx->fh);
66162306a36Sopenharmony_cierror_fh:
66262306a36Sopenharmony_ci	v4l2_fh_exit(&ctx->fh);
66362306a36Sopenharmony_ci	kfree(ctx);
66462306a36Sopenharmony_ciunlock:
66562306a36Sopenharmony_ci	mutex_unlock(&gsc->lock);
66662306a36Sopenharmony_ci	return ret;
66762306a36Sopenharmony_ci}
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_cistatic int gsc_m2m_release(struct file *file)
67062306a36Sopenharmony_ci{
67162306a36Sopenharmony_ci	struct gsc_ctx *ctx = fh_to_ctx(file->private_data);
67262306a36Sopenharmony_ci	struct gsc_dev *gsc = ctx->gsc_dev;
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	pr_debug("pid: %d, state: 0x%lx, refcnt= %d",
67562306a36Sopenharmony_ci		task_pid_nr(current), gsc->state, gsc->m2m.refcnt);
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	mutex_lock(&gsc->lock);
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	v4l2_m2m_ctx_release(ctx->m2m_ctx);
68062306a36Sopenharmony_ci	gsc_ctrls_delete(ctx);
68162306a36Sopenharmony_ci	v4l2_fh_del(&ctx->fh);
68262306a36Sopenharmony_ci	v4l2_fh_exit(&ctx->fh);
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	if (--gsc->m2m.refcnt <= 0)
68562306a36Sopenharmony_ci		clear_bit(ST_M2M_OPEN, &gsc->state);
68662306a36Sopenharmony_ci	kfree(ctx);
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	mutex_unlock(&gsc->lock);
68962306a36Sopenharmony_ci	return 0;
69062306a36Sopenharmony_ci}
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_cistatic __poll_t gsc_m2m_poll(struct file *file,
69362306a36Sopenharmony_ci					struct poll_table_struct *wait)
69462306a36Sopenharmony_ci{
69562306a36Sopenharmony_ci	struct gsc_ctx *ctx = fh_to_ctx(file->private_data);
69662306a36Sopenharmony_ci	struct gsc_dev *gsc = ctx->gsc_dev;
69762306a36Sopenharmony_ci	__poll_t ret;
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	if (mutex_lock_interruptible(&gsc->lock))
70062306a36Sopenharmony_ci		return EPOLLERR;
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	ret = v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
70362306a36Sopenharmony_ci	mutex_unlock(&gsc->lock);
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	return ret;
70662306a36Sopenharmony_ci}
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_cistatic int gsc_m2m_mmap(struct file *file, struct vm_area_struct *vma)
70962306a36Sopenharmony_ci{
71062306a36Sopenharmony_ci	struct gsc_ctx *ctx = fh_to_ctx(file->private_data);
71162306a36Sopenharmony_ci	struct gsc_dev *gsc = ctx->gsc_dev;
71262306a36Sopenharmony_ci	int ret;
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	if (mutex_lock_interruptible(&gsc->lock))
71562306a36Sopenharmony_ci		return -ERESTARTSYS;
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	ret = v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
71862306a36Sopenharmony_ci	mutex_unlock(&gsc->lock);
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	return ret;
72162306a36Sopenharmony_ci}
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_cistatic const struct v4l2_file_operations gsc_m2m_fops = {
72462306a36Sopenharmony_ci	.owner		= THIS_MODULE,
72562306a36Sopenharmony_ci	.open		= gsc_m2m_open,
72662306a36Sopenharmony_ci	.release	= gsc_m2m_release,
72762306a36Sopenharmony_ci	.poll		= gsc_m2m_poll,
72862306a36Sopenharmony_ci	.unlocked_ioctl	= video_ioctl2,
72962306a36Sopenharmony_ci	.mmap		= gsc_m2m_mmap,
73062306a36Sopenharmony_ci};
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_cistatic const struct v4l2_m2m_ops gsc_m2m_ops = {
73362306a36Sopenharmony_ci	.device_run	= gsc_m2m_device_run,
73462306a36Sopenharmony_ci	.job_abort	= gsc_m2m_job_abort,
73562306a36Sopenharmony_ci};
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ciint gsc_register_m2m_device(struct gsc_dev *gsc)
73862306a36Sopenharmony_ci{
73962306a36Sopenharmony_ci	struct platform_device *pdev;
74062306a36Sopenharmony_ci	int ret;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	if (!gsc)
74362306a36Sopenharmony_ci		return -ENODEV;
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	pdev = gsc->pdev;
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	gsc->vdev.fops		= &gsc_m2m_fops;
74862306a36Sopenharmony_ci	gsc->vdev.ioctl_ops	= &gsc_m2m_ioctl_ops;
74962306a36Sopenharmony_ci	gsc->vdev.release	= video_device_release_empty;
75062306a36Sopenharmony_ci	gsc->vdev.lock		= &gsc->lock;
75162306a36Sopenharmony_ci	gsc->vdev.vfl_dir	= VFL_DIR_M2M;
75262306a36Sopenharmony_ci	gsc->vdev.v4l2_dev	= &gsc->v4l2_dev;
75362306a36Sopenharmony_ci	gsc->vdev.device_caps	= V4L2_CAP_STREAMING |
75462306a36Sopenharmony_ci				  V4L2_CAP_VIDEO_M2M_MPLANE;
75562306a36Sopenharmony_ci	snprintf(gsc->vdev.name, sizeof(gsc->vdev.name), "%s.%d:m2m",
75662306a36Sopenharmony_ci					GSC_MODULE_NAME, gsc->id);
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	video_set_drvdata(&gsc->vdev, gsc);
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	gsc->m2m.vfd = &gsc->vdev;
76162306a36Sopenharmony_ci	gsc->m2m.m2m_dev = v4l2_m2m_init(&gsc_m2m_ops);
76262306a36Sopenharmony_ci	if (IS_ERR(gsc->m2m.m2m_dev)) {
76362306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to initialize v4l2-m2m device\n");
76462306a36Sopenharmony_ci		return PTR_ERR(gsc->m2m.m2m_dev);
76562306a36Sopenharmony_ci	}
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	ret = video_register_device(&gsc->vdev, VFL_TYPE_VIDEO, -1);
76862306a36Sopenharmony_ci	if (ret) {
76962306a36Sopenharmony_ci		dev_err(&pdev->dev,
77062306a36Sopenharmony_ci			 "%s(): failed to register video device\n", __func__);
77162306a36Sopenharmony_ci		goto err_m2m_release;
77262306a36Sopenharmony_ci	}
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	pr_debug("gsc m2m driver registered as /dev/video%d", gsc->vdev.num);
77562306a36Sopenharmony_ci	return 0;
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_cierr_m2m_release:
77862306a36Sopenharmony_ci	v4l2_m2m_release(gsc->m2m.m2m_dev);
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	return ret;
78162306a36Sopenharmony_ci}
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_civoid gsc_unregister_m2m_device(struct gsc_dev *gsc)
78462306a36Sopenharmony_ci{
78562306a36Sopenharmony_ci	if (gsc) {
78662306a36Sopenharmony_ci		v4l2_m2m_release(gsc->m2m.m2m_dev);
78762306a36Sopenharmony_ci		video_unregister_device(&gsc->vdev);
78862306a36Sopenharmony_ci	}
78962306a36Sopenharmony_ci}
790