18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2011 - 2012 Samsung Electronics Co., Ltd.
48c2ecf20Sopenharmony_ci *		http://www.samsung.com
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Samsung EXYNOS5 SoC series G-Scaler driver
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <linux/types.h>
128c2ecf20Sopenharmony_ci#include <linux/errno.h>
138c2ecf20Sopenharmony_ci#include <linux/bug.h>
148c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
158c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
168c2ecf20Sopenharmony_ci#include <linux/device.h>
178c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
188c2ecf20Sopenharmony_ci#include <linux/list.h>
198c2ecf20Sopenharmony_ci#include <linux/io.h>
208c2ecf20Sopenharmony_ci#include <linux/slab.h>
218c2ecf20Sopenharmony_ci#include <linux/clk.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include <media/v4l2-ioctl.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include "gsc-core.h"
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic int gsc_m2m_ctx_stop_req(struct gsc_ctx *ctx)
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci	struct gsc_ctx *curr_ctx;
308c2ecf20Sopenharmony_ci	struct gsc_dev *gsc = ctx->gsc_dev;
318c2ecf20Sopenharmony_ci	int ret;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	curr_ctx = v4l2_m2m_get_curr_priv(gsc->m2m.m2m_dev);
348c2ecf20Sopenharmony_ci	if (!gsc_m2m_pending(gsc) || (curr_ctx != ctx))
358c2ecf20Sopenharmony_ci		return 0;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	gsc_ctx_state_lock_set(GSC_CTX_STOP_REQ, ctx);
388c2ecf20Sopenharmony_ci	ret = wait_event_timeout(gsc->irq_queue,
398c2ecf20Sopenharmony_ci			!gsc_ctx_state_is_set(GSC_CTX_STOP_REQ, ctx),
408c2ecf20Sopenharmony_ci			GSC_SHUTDOWN_TIMEOUT);
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	return ret == 0 ? -ETIMEDOUT : ret;
438c2ecf20Sopenharmony_ci}
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic void __gsc_m2m_job_abort(struct gsc_ctx *ctx)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	int ret;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	ret = gsc_m2m_ctx_stop_req(ctx);
508c2ecf20Sopenharmony_ci	if ((ret == -ETIMEDOUT) || (ctx->state & GSC_CTX_ABORT)) {
518c2ecf20Sopenharmony_ci		gsc_ctx_state_lock_clear(GSC_CTX_STOP_REQ | GSC_CTX_ABORT, ctx);
528c2ecf20Sopenharmony_ci		gsc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR);
538c2ecf20Sopenharmony_ci	}
548c2ecf20Sopenharmony_ci}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic int gsc_m2m_start_streaming(struct vb2_queue *q, unsigned int count)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	struct gsc_ctx *ctx = q->drv_priv;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	return pm_runtime_resume_and_get(&ctx->gsc_dev->pdev->dev);
618c2ecf20Sopenharmony_ci}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistatic void __gsc_m2m_cleanup_queue(struct gsc_ctx *ctx)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	struct vb2_v4l2_buffer *src_vb, *dst_vb;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	while (v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) > 0) {
688c2ecf20Sopenharmony_ci		src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
698c2ecf20Sopenharmony_ci		v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_ERROR);
708c2ecf20Sopenharmony_ci	}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	while (v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx) > 0) {
738c2ecf20Sopenharmony_ci		dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
748c2ecf20Sopenharmony_ci		v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_ERROR);
758c2ecf20Sopenharmony_ci	}
768c2ecf20Sopenharmony_ci}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cistatic void gsc_m2m_stop_streaming(struct vb2_queue *q)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	struct gsc_ctx *ctx = q->drv_priv;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	__gsc_m2m_job_abort(ctx);
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	__gsc_m2m_cleanup_queue(ctx);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	pm_runtime_put(&ctx->gsc_dev->pdev->dev);
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_civoid gsc_m2m_job_finish(struct gsc_ctx *ctx, int vb_state)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	struct vb2_v4l2_buffer *src_vb, *dst_vb;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	if (!ctx || !ctx->m2m_ctx)
948c2ecf20Sopenharmony_ci		return;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
978c2ecf20Sopenharmony_ci	dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	if (src_vb && dst_vb) {
1008c2ecf20Sopenharmony_ci		dst_vb->vb2_buf.timestamp = src_vb->vb2_buf.timestamp;
1018c2ecf20Sopenharmony_ci		dst_vb->timecode = src_vb->timecode;
1028c2ecf20Sopenharmony_ci		dst_vb->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
1038c2ecf20Sopenharmony_ci		dst_vb->flags |=
1048c2ecf20Sopenharmony_ci			src_vb->flags
1058c2ecf20Sopenharmony_ci			& V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci		v4l2_m2m_buf_done(src_vb, vb_state);
1088c2ecf20Sopenharmony_ci		v4l2_m2m_buf_done(dst_vb, vb_state);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci		v4l2_m2m_job_finish(ctx->gsc_dev->m2m.m2m_dev,
1118c2ecf20Sopenharmony_ci				    ctx->m2m_ctx);
1128c2ecf20Sopenharmony_ci	}
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistatic void gsc_m2m_job_abort(void *priv)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	__gsc_m2m_job_abort((struct gsc_ctx *)priv);
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic int gsc_get_bufs(struct gsc_ctx *ctx)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	struct gsc_frame *s_frame, *d_frame;
1238c2ecf20Sopenharmony_ci	struct vb2_v4l2_buffer *src_vb, *dst_vb;
1248c2ecf20Sopenharmony_ci	int ret;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	s_frame = &ctx->s_frame;
1278c2ecf20Sopenharmony_ci	d_frame = &ctx->d_frame;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	src_vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
1308c2ecf20Sopenharmony_ci	ret = gsc_prepare_addr(ctx, &src_vb->vb2_buf, s_frame, &s_frame->addr);
1318c2ecf20Sopenharmony_ci	if (ret)
1328c2ecf20Sopenharmony_ci		return ret;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	dst_vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
1358c2ecf20Sopenharmony_ci	ret = gsc_prepare_addr(ctx, &dst_vb->vb2_buf, d_frame, &d_frame->addr);
1368c2ecf20Sopenharmony_ci	if (ret)
1378c2ecf20Sopenharmony_ci		return ret;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	dst_vb->vb2_buf.timestamp = src_vb->vb2_buf.timestamp;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	return 0;
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_cistatic void gsc_m2m_device_run(void *priv)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	struct gsc_ctx *ctx = priv;
1478c2ecf20Sopenharmony_ci	struct gsc_dev *gsc;
1488c2ecf20Sopenharmony_ci	unsigned long flags;
1498c2ecf20Sopenharmony_ci	int ret;
1508c2ecf20Sopenharmony_ci	bool is_set = false;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	if (WARN(!ctx, "null hardware context\n"))
1538c2ecf20Sopenharmony_ci		return;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	gsc = ctx->gsc_dev;
1568c2ecf20Sopenharmony_ci	spin_lock_irqsave(&gsc->slock, flags);
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	set_bit(ST_M2M_PEND, &gsc->state);
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	/* Reconfigure hardware if the context has changed. */
1618c2ecf20Sopenharmony_ci	if (gsc->m2m.ctx != ctx) {
1628c2ecf20Sopenharmony_ci		pr_debug("gsc->m2m.ctx = 0x%p, current_ctx = 0x%p",
1638c2ecf20Sopenharmony_ci				gsc->m2m.ctx, ctx);
1648c2ecf20Sopenharmony_ci		ctx->state |= GSC_PARAMS;
1658c2ecf20Sopenharmony_ci		gsc->m2m.ctx = ctx;
1668c2ecf20Sopenharmony_ci	}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	is_set = ctx->state & GSC_CTX_STOP_REQ;
1698c2ecf20Sopenharmony_ci	if (is_set) {
1708c2ecf20Sopenharmony_ci		ctx->state &= ~GSC_CTX_STOP_REQ;
1718c2ecf20Sopenharmony_ci		ctx->state |= GSC_CTX_ABORT;
1728c2ecf20Sopenharmony_ci		wake_up(&gsc->irq_queue);
1738c2ecf20Sopenharmony_ci		goto put_device;
1748c2ecf20Sopenharmony_ci	}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	ret = gsc_get_bufs(ctx);
1778c2ecf20Sopenharmony_ci	if (ret) {
1788c2ecf20Sopenharmony_ci		pr_err("Wrong address");
1798c2ecf20Sopenharmony_ci		goto put_device;
1808c2ecf20Sopenharmony_ci	}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	gsc_set_prefbuf(gsc, &ctx->s_frame);
1838c2ecf20Sopenharmony_ci	gsc_hw_set_input_addr(gsc, &ctx->s_frame.addr, GSC_M2M_BUF_NUM);
1848c2ecf20Sopenharmony_ci	gsc_hw_set_output_addr(gsc, &ctx->d_frame.addr, GSC_M2M_BUF_NUM);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	if (ctx->state & GSC_PARAMS) {
1878c2ecf20Sopenharmony_ci		gsc_hw_set_input_buf_masking(gsc, GSC_M2M_BUF_NUM, false);
1888c2ecf20Sopenharmony_ci		gsc_hw_set_output_buf_masking(gsc, GSC_M2M_BUF_NUM, false);
1898c2ecf20Sopenharmony_ci		gsc_hw_set_frm_done_irq_mask(gsc, false);
1908c2ecf20Sopenharmony_ci		gsc_hw_set_gsc_irq_enable(gsc, true);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci		if (gsc_set_scaler_info(ctx)) {
1938c2ecf20Sopenharmony_ci			pr_err("Scaler setup error");
1948c2ecf20Sopenharmony_ci			goto put_device;
1958c2ecf20Sopenharmony_ci		}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci		gsc_hw_set_input_path(ctx);
1988c2ecf20Sopenharmony_ci		gsc_hw_set_in_size(ctx);
1998c2ecf20Sopenharmony_ci		gsc_hw_set_in_image_format(ctx);
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci		gsc_hw_set_output_path(ctx);
2028c2ecf20Sopenharmony_ci		gsc_hw_set_out_size(ctx);
2038c2ecf20Sopenharmony_ci		gsc_hw_set_out_image_format(ctx);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci		gsc_hw_set_prescaler(ctx);
2068c2ecf20Sopenharmony_ci		gsc_hw_set_mainscaler(ctx);
2078c2ecf20Sopenharmony_ci		gsc_hw_set_rotation(ctx);
2088c2ecf20Sopenharmony_ci		gsc_hw_set_global_alpha(ctx);
2098c2ecf20Sopenharmony_ci	}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	/* update shadow registers */
2128c2ecf20Sopenharmony_ci	gsc_hw_set_sfr_update(ctx);
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	ctx->state &= ~GSC_PARAMS;
2158c2ecf20Sopenharmony_ci	gsc_hw_enable_control(gsc, true);
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&gsc->slock, flags);
2188c2ecf20Sopenharmony_ci	return;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ciput_device:
2218c2ecf20Sopenharmony_ci	ctx->state &= ~GSC_PARAMS;
2228c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&gsc->slock, flags);
2238c2ecf20Sopenharmony_ci}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_cistatic int gsc_m2m_queue_setup(struct vb2_queue *vq,
2268c2ecf20Sopenharmony_ci			unsigned int *num_buffers, unsigned int *num_planes,
2278c2ecf20Sopenharmony_ci			unsigned int sizes[], struct device *alloc_devs[])
2288c2ecf20Sopenharmony_ci{
2298c2ecf20Sopenharmony_ci	struct gsc_ctx *ctx = vb2_get_drv_priv(vq);
2308c2ecf20Sopenharmony_ci	struct gsc_frame *frame;
2318c2ecf20Sopenharmony_ci	int i;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	frame = ctx_get_frame(ctx, vq->type);
2348c2ecf20Sopenharmony_ci	if (IS_ERR(frame))
2358c2ecf20Sopenharmony_ci		return PTR_ERR(frame);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	if (!frame->fmt)
2388c2ecf20Sopenharmony_ci		return -EINVAL;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	*num_planes = frame->fmt->num_planes;
2418c2ecf20Sopenharmony_ci	for (i = 0; i < frame->fmt->num_planes; i++)
2428c2ecf20Sopenharmony_ci		sizes[i] = frame->payload[i];
2438c2ecf20Sopenharmony_ci	return 0;
2448c2ecf20Sopenharmony_ci}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_cistatic int gsc_m2m_buf_prepare(struct vb2_buffer *vb)
2478c2ecf20Sopenharmony_ci{
2488c2ecf20Sopenharmony_ci	struct gsc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
2498c2ecf20Sopenharmony_ci	struct gsc_frame *frame;
2508c2ecf20Sopenharmony_ci	int i;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	frame = ctx_get_frame(ctx, vb->vb2_queue->type);
2538c2ecf20Sopenharmony_ci	if (IS_ERR(frame))
2548c2ecf20Sopenharmony_ci		return PTR_ERR(frame);
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	if (V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type)) {
2578c2ecf20Sopenharmony_ci		for (i = 0; i < frame->fmt->num_planes; i++)
2588c2ecf20Sopenharmony_ci			vb2_set_plane_payload(vb, i, frame->payload[i]);
2598c2ecf20Sopenharmony_ci	}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	return 0;
2628c2ecf20Sopenharmony_ci}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_cistatic void gsc_m2m_buf_queue(struct vb2_buffer *vb)
2658c2ecf20Sopenharmony_ci{
2668c2ecf20Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
2678c2ecf20Sopenharmony_ci	struct gsc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	pr_debug("ctx: %p, ctx->state: 0x%x", ctx, ctx->state);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	if (ctx->m2m_ctx)
2728c2ecf20Sopenharmony_ci		v4l2_m2m_buf_queue(ctx->m2m_ctx, vbuf);
2738c2ecf20Sopenharmony_ci}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_cistatic const struct vb2_ops gsc_m2m_qops = {
2768c2ecf20Sopenharmony_ci	.queue_setup	 = gsc_m2m_queue_setup,
2778c2ecf20Sopenharmony_ci	.buf_prepare	 = gsc_m2m_buf_prepare,
2788c2ecf20Sopenharmony_ci	.buf_queue	 = gsc_m2m_buf_queue,
2798c2ecf20Sopenharmony_ci	.wait_prepare	 = vb2_ops_wait_prepare,
2808c2ecf20Sopenharmony_ci	.wait_finish	 = vb2_ops_wait_finish,
2818c2ecf20Sopenharmony_ci	.stop_streaming	 = gsc_m2m_stop_streaming,
2828c2ecf20Sopenharmony_ci	.start_streaming = gsc_m2m_start_streaming,
2838c2ecf20Sopenharmony_ci};
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_cistatic int gsc_m2m_querycap(struct file *file, void *fh,
2868c2ecf20Sopenharmony_ci			   struct v4l2_capability *cap)
2878c2ecf20Sopenharmony_ci{
2888c2ecf20Sopenharmony_ci	struct gsc_ctx *ctx = fh_to_ctx(fh);
2898c2ecf20Sopenharmony_ci	struct gsc_dev *gsc = ctx->gsc_dev;
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	strscpy(cap->driver, GSC_MODULE_NAME, sizeof(cap->driver));
2928c2ecf20Sopenharmony_ci	strscpy(cap->card, GSC_MODULE_NAME " gscaler", sizeof(cap->card));
2938c2ecf20Sopenharmony_ci	snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
2948c2ecf20Sopenharmony_ci		 dev_name(&gsc->pdev->dev));
2958c2ecf20Sopenharmony_ci	return 0;
2968c2ecf20Sopenharmony_ci}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_cistatic int gsc_m2m_enum_fmt(struct file *file, void *priv,
2998c2ecf20Sopenharmony_ci			    struct v4l2_fmtdesc *f)
3008c2ecf20Sopenharmony_ci{
3018c2ecf20Sopenharmony_ci	return gsc_enum_fmt(f);
3028c2ecf20Sopenharmony_ci}
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_cistatic int gsc_m2m_g_fmt_mplane(struct file *file, void *fh,
3058c2ecf20Sopenharmony_ci			     struct v4l2_format *f)
3068c2ecf20Sopenharmony_ci{
3078c2ecf20Sopenharmony_ci	struct gsc_ctx *ctx = fh_to_ctx(fh);
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	return gsc_g_fmt_mplane(ctx, f);
3108c2ecf20Sopenharmony_ci}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_cistatic int gsc_m2m_try_fmt_mplane(struct file *file, void *fh,
3138c2ecf20Sopenharmony_ci				  struct v4l2_format *f)
3148c2ecf20Sopenharmony_ci{
3158c2ecf20Sopenharmony_ci	struct gsc_ctx *ctx = fh_to_ctx(fh);
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	return gsc_try_fmt_mplane(ctx, f);
3188c2ecf20Sopenharmony_ci}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_cistatic int gsc_m2m_s_fmt_mplane(struct file *file, void *fh,
3218c2ecf20Sopenharmony_ci				 struct v4l2_format *f)
3228c2ecf20Sopenharmony_ci{
3238c2ecf20Sopenharmony_ci	struct gsc_ctx *ctx = fh_to_ctx(fh);
3248c2ecf20Sopenharmony_ci	struct vb2_queue *vq;
3258c2ecf20Sopenharmony_ci	struct gsc_frame *frame;
3268c2ecf20Sopenharmony_ci	struct v4l2_pix_format_mplane *pix;
3278c2ecf20Sopenharmony_ci	int i, ret = 0;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	ret = gsc_m2m_try_fmt_mplane(file, fh, f);
3308c2ecf20Sopenharmony_ci	if (ret)
3318c2ecf20Sopenharmony_ci		return ret;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	if (vb2_is_streaming(vq)) {
3368c2ecf20Sopenharmony_ci		pr_err("queue (%d) busy", f->type);
3378c2ecf20Sopenharmony_ci		return -EBUSY;
3388c2ecf20Sopenharmony_ci	}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	if (V4L2_TYPE_IS_OUTPUT(f->type))
3418c2ecf20Sopenharmony_ci		frame = &ctx->s_frame;
3428c2ecf20Sopenharmony_ci	else
3438c2ecf20Sopenharmony_ci		frame = &ctx->d_frame;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	pix = &f->fmt.pix_mp;
3468c2ecf20Sopenharmony_ci	frame->fmt = find_fmt(&pix->pixelformat, NULL, 0);
3478c2ecf20Sopenharmony_ci	frame->colorspace = pix->colorspace;
3488c2ecf20Sopenharmony_ci	if (!frame->fmt)
3498c2ecf20Sopenharmony_ci		return -EINVAL;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	for (i = 0; i < frame->fmt->num_planes; i++)
3528c2ecf20Sopenharmony_ci		frame->payload[i] = pix->plane_fmt[i].sizeimage;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	gsc_set_frame_size(frame, pix->width, pix->height);
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
3578c2ecf20Sopenharmony_ci		gsc_ctx_state_lock_set(GSC_PARAMS | GSC_DST_FMT, ctx);
3588c2ecf20Sopenharmony_ci	else
3598c2ecf20Sopenharmony_ci		gsc_ctx_state_lock_set(GSC_PARAMS | GSC_SRC_FMT, ctx);
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	pr_debug("f_w: %d, f_h: %d", frame->f_width, frame->f_height);
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	return 0;
3648c2ecf20Sopenharmony_ci}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_cistatic int gsc_m2m_reqbufs(struct file *file, void *fh,
3678c2ecf20Sopenharmony_ci			  struct v4l2_requestbuffers *reqbufs)
3688c2ecf20Sopenharmony_ci{
3698c2ecf20Sopenharmony_ci	struct gsc_ctx *ctx = fh_to_ctx(fh);
3708c2ecf20Sopenharmony_ci	struct gsc_dev *gsc = ctx->gsc_dev;
3718c2ecf20Sopenharmony_ci	u32 max_cnt;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	max_cnt = (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ?
3748c2ecf20Sopenharmony_ci		gsc->variant->in_buf_cnt : gsc->variant->out_buf_cnt;
3758c2ecf20Sopenharmony_ci	if (reqbufs->count > max_cnt)
3768c2ecf20Sopenharmony_ci		return -EINVAL;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
3798c2ecf20Sopenharmony_ci}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_cistatic int gsc_m2m_expbuf(struct file *file, void *fh,
3828c2ecf20Sopenharmony_ci				struct v4l2_exportbuffer *eb)
3838c2ecf20Sopenharmony_ci{
3848c2ecf20Sopenharmony_ci	struct gsc_ctx *ctx = fh_to_ctx(fh);
3858c2ecf20Sopenharmony_ci	return v4l2_m2m_expbuf(file, ctx->m2m_ctx, eb);
3868c2ecf20Sopenharmony_ci}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_cistatic int gsc_m2m_querybuf(struct file *file, void *fh,
3898c2ecf20Sopenharmony_ci					struct v4l2_buffer *buf)
3908c2ecf20Sopenharmony_ci{
3918c2ecf20Sopenharmony_ci	struct gsc_ctx *ctx = fh_to_ctx(fh);
3928c2ecf20Sopenharmony_ci	return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
3938c2ecf20Sopenharmony_ci}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_cistatic int gsc_m2m_qbuf(struct file *file, void *fh,
3968c2ecf20Sopenharmony_ci			  struct v4l2_buffer *buf)
3978c2ecf20Sopenharmony_ci{
3988c2ecf20Sopenharmony_ci	struct gsc_ctx *ctx = fh_to_ctx(fh);
3998c2ecf20Sopenharmony_ci	return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
4008c2ecf20Sopenharmony_ci}
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_cistatic int gsc_m2m_dqbuf(struct file *file, void *fh,
4038c2ecf20Sopenharmony_ci			   struct v4l2_buffer *buf)
4048c2ecf20Sopenharmony_ci{
4058c2ecf20Sopenharmony_ci	struct gsc_ctx *ctx = fh_to_ctx(fh);
4068c2ecf20Sopenharmony_ci	return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
4078c2ecf20Sopenharmony_ci}
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_cistatic int gsc_m2m_streamon(struct file *file, void *fh,
4108c2ecf20Sopenharmony_ci			   enum v4l2_buf_type type)
4118c2ecf20Sopenharmony_ci{
4128c2ecf20Sopenharmony_ci	struct gsc_ctx *ctx = fh_to_ctx(fh);
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	/* The source and target color format need to be set */
4158c2ecf20Sopenharmony_ci	if (V4L2_TYPE_IS_OUTPUT(type)) {
4168c2ecf20Sopenharmony_ci		if (!gsc_ctx_state_is_set(GSC_SRC_FMT, ctx))
4178c2ecf20Sopenharmony_ci			return -EINVAL;
4188c2ecf20Sopenharmony_ci	} else if (!gsc_ctx_state_is_set(GSC_DST_FMT, ctx)) {
4198c2ecf20Sopenharmony_ci		return -EINVAL;
4208c2ecf20Sopenharmony_ci	}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
4238c2ecf20Sopenharmony_ci}
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_cistatic int gsc_m2m_streamoff(struct file *file, void *fh,
4268c2ecf20Sopenharmony_ci			    enum v4l2_buf_type type)
4278c2ecf20Sopenharmony_ci{
4288c2ecf20Sopenharmony_ci	struct gsc_ctx *ctx = fh_to_ctx(fh);
4298c2ecf20Sopenharmony_ci	return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
4308c2ecf20Sopenharmony_ci}
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci/* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */
4338c2ecf20Sopenharmony_cistatic int is_rectangle_enclosed(struct v4l2_rect *a, struct v4l2_rect *b)
4348c2ecf20Sopenharmony_ci{
4358c2ecf20Sopenharmony_ci	if (a->left < b->left || a->top < b->top)
4368c2ecf20Sopenharmony_ci		return 0;
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	if (a->left + a->width > b->left + b->width)
4398c2ecf20Sopenharmony_ci		return 0;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	if (a->top + a->height > b->top + b->height)
4428c2ecf20Sopenharmony_ci		return 0;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	return 1;
4458c2ecf20Sopenharmony_ci}
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_cistatic int gsc_m2m_g_selection(struct file *file, void *fh,
4488c2ecf20Sopenharmony_ci			struct v4l2_selection *s)
4498c2ecf20Sopenharmony_ci{
4508c2ecf20Sopenharmony_ci	struct gsc_frame *frame;
4518c2ecf20Sopenharmony_ci	struct gsc_ctx *ctx = fh_to_ctx(fh);
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
4548c2ecf20Sopenharmony_ci	    (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT))
4558c2ecf20Sopenharmony_ci		return -EINVAL;
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	frame = ctx_get_frame(ctx, s->type);
4588c2ecf20Sopenharmony_ci	if (IS_ERR(frame))
4598c2ecf20Sopenharmony_ci		return PTR_ERR(frame);
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	switch (s->target) {
4628c2ecf20Sopenharmony_ci	case V4L2_SEL_TGT_COMPOSE_DEFAULT:
4638c2ecf20Sopenharmony_ci	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
4648c2ecf20Sopenharmony_ci	case V4L2_SEL_TGT_CROP_BOUNDS:
4658c2ecf20Sopenharmony_ci	case V4L2_SEL_TGT_CROP_DEFAULT:
4668c2ecf20Sopenharmony_ci		s->r.left = 0;
4678c2ecf20Sopenharmony_ci		s->r.top = 0;
4688c2ecf20Sopenharmony_ci		s->r.width = frame->f_width;
4698c2ecf20Sopenharmony_ci		s->r.height = frame->f_height;
4708c2ecf20Sopenharmony_ci		return 0;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	case V4L2_SEL_TGT_COMPOSE:
4738c2ecf20Sopenharmony_ci	case V4L2_SEL_TGT_CROP:
4748c2ecf20Sopenharmony_ci		s->r.left = frame->crop.left;
4758c2ecf20Sopenharmony_ci		s->r.top = frame->crop.top;
4768c2ecf20Sopenharmony_ci		s->r.width = frame->crop.width;
4778c2ecf20Sopenharmony_ci		s->r.height = frame->crop.height;
4788c2ecf20Sopenharmony_ci		return 0;
4798c2ecf20Sopenharmony_ci	}
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	return -EINVAL;
4828c2ecf20Sopenharmony_ci}
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_cistatic int gsc_m2m_s_selection(struct file *file, void *fh,
4858c2ecf20Sopenharmony_ci				struct v4l2_selection *s)
4868c2ecf20Sopenharmony_ci{
4878c2ecf20Sopenharmony_ci	struct gsc_frame *frame;
4888c2ecf20Sopenharmony_ci	struct gsc_ctx *ctx = fh_to_ctx(fh);
4898c2ecf20Sopenharmony_ci	struct gsc_variant *variant = ctx->gsc_dev->variant;
4908c2ecf20Sopenharmony_ci	struct v4l2_selection sel = *s;
4918c2ecf20Sopenharmony_ci	int ret;
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
4948c2ecf20Sopenharmony_ci	    (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT))
4958c2ecf20Sopenharmony_ci		return -EINVAL;
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	ret = gsc_try_selection(ctx, &sel);
4988c2ecf20Sopenharmony_ci	if (ret)
4998c2ecf20Sopenharmony_ci		return ret;
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	if (s->flags & V4L2_SEL_FLAG_LE &&
5028c2ecf20Sopenharmony_ci	    !is_rectangle_enclosed(&sel.r, &s->r))
5038c2ecf20Sopenharmony_ci		return -ERANGE;
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	if (s->flags & V4L2_SEL_FLAG_GE &&
5068c2ecf20Sopenharmony_ci	    !is_rectangle_enclosed(&s->r, &sel.r))
5078c2ecf20Sopenharmony_ci		return -ERANGE;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	s->r = sel.r;
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	switch (s->target) {
5128c2ecf20Sopenharmony_ci	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
5138c2ecf20Sopenharmony_ci	case V4L2_SEL_TGT_COMPOSE_DEFAULT:
5148c2ecf20Sopenharmony_ci	case V4L2_SEL_TGT_COMPOSE:
5158c2ecf20Sopenharmony_ci		frame = &ctx->s_frame;
5168c2ecf20Sopenharmony_ci		break;
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	case V4L2_SEL_TGT_CROP_BOUNDS:
5198c2ecf20Sopenharmony_ci	case V4L2_SEL_TGT_CROP:
5208c2ecf20Sopenharmony_ci	case V4L2_SEL_TGT_CROP_DEFAULT:
5218c2ecf20Sopenharmony_ci		frame = &ctx->d_frame;
5228c2ecf20Sopenharmony_ci		break;
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	default:
5258c2ecf20Sopenharmony_ci		return -EINVAL;
5268c2ecf20Sopenharmony_ci	}
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	/* Check to see if scaling ratio is within supported range */
5298c2ecf20Sopenharmony_ci	if (gsc_ctx_state_is_set(GSC_DST_FMT | GSC_SRC_FMT, ctx)) {
5308c2ecf20Sopenharmony_ci		if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
5318c2ecf20Sopenharmony_ci			ret = gsc_check_scaler_ratio(variant, sel.r.width,
5328c2ecf20Sopenharmony_ci				sel.r.height, ctx->d_frame.crop.width,
5338c2ecf20Sopenharmony_ci				ctx->d_frame.crop.height,
5348c2ecf20Sopenharmony_ci				ctx->gsc_ctrls.rotate->val, ctx->out_path);
5358c2ecf20Sopenharmony_ci		} else {
5368c2ecf20Sopenharmony_ci			ret = gsc_check_scaler_ratio(variant,
5378c2ecf20Sopenharmony_ci				ctx->s_frame.crop.width,
5388c2ecf20Sopenharmony_ci				ctx->s_frame.crop.height, sel.r.width,
5398c2ecf20Sopenharmony_ci				sel.r.height, ctx->gsc_ctrls.rotate->val,
5408c2ecf20Sopenharmony_ci				ctx->out_path);
5418c2ecf20Sopenharmony_ci		}
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci		if (ret) {
5448c2ecf20Sopenharmony_ci			pr_err("Out of scaler range");
5458c2ecf20Sopenharmony_ci			return -EINVAL;
5468c2ecf20Sopenharmony_ci		}
5478c2ecf20Sopenharmony_ci	}
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	frame->crop = sel.r;
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	gsc_ctx_state_lock_set(GSC_PARAMS, ctx);
5528c2ecf20Sopenharmony_ci	return 0;
5538c2ecf20Sopenharmony_ci}
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_cistatic const struct v4l2_ioctl_ops gsc_m2m_ioctl_ops = {
5568c2ecf20Sopenharmony_ci	.vidioc_querycap		= gsc_m2m_querycap,
5578c2ecf20Sopenharmony_ci	.vidioc_enum_fmt_vid_cap	= gsc_m2m_enum_fmt,
5588c2ecf20Sopenharmony_ci	.vidioc_enum_fmt_vid_out	= gsc_m2m_enum_fmt,
5598c2ecf20Sopenharmony_ci	.vidioc_g_fmt_vid_cap_mplane	= gsc_m2m_g_fmt_mplane,
5608c2ecf20Sopenharmony_ci	.vidioc_g_fmt_vid_out_mplane	= gsc_m2m_g_fmt_mplane,
5618c2ecf20Sopenharmony_ci	.vidioc_try_fmt_vid_cap_mplane	= gsc_m2m_try_fmt_mplane,
5628c2ecf20Sopenharmony_ci	.vidioc_try_fmt_vid_out_mplane	= gsc_m2m_try_fmt_mplane,
5638c2ecf20Sopenharmony_ci	.vidioc_s_fmt_vid_cap_mplane	= gsc_m2m_s_fmt_mplane,
5648c2ecf20Sopenharmony_ci	.vidioc_s_fmt_vid_out_mplane	= gsc_m2m_s_fmt_mplane,
5658c2ecf20Sopenharmony_ci	.vidioc_reqbufs			= gsc_m2m_reqbufs,
5668c2ecf20Sopenharmony_ci	.vidioc_expbuf                  = gsc_m2m_expbuf,
5678c2ecf20Sopenharmony_ci	.vidioc_querybuf		= gsc_m2m_querybuf,
5688c2ecf20Sopenharmony_ci	.vidioc_qbuf			= gsc_m2m_qbuf,
5698c2ecf20Sopenharmony_ci	.vidioc_dqbuf			= gsc_m2m_dqbuf,
5708c2ecf20Sopenharmony_ci	.vidioc_streamon		= gsc_m2m_streamon,
5718c2ecf20Sopenharmony_ci	.vidioc_streamoff		= gsc_m2m_streamoff,
5728c2ecf20Sopenharmony_ci	.vidioc_g_selection		= gsc_m2m_g_selection,
5738c2ecf20Sopenharmony_ci	.vidioc_s_selection		= gsc_m2m_s_selection
5748c2ecf20Sopenharmony_ci};
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_cistatic int queue_init(void *priv, struct vb2_queue *src_vq,
5778c2ecf20Sopenharmony_ci			struct vb2_queue *dst_vq)
5788c2ecf20Sopenharmony_ci{
5798c2ecf20Sopenharmony_ci	struct gsc_ctx *ctx = priv;
5808c2ecf20Sopenharmony_ci	int ret;
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	memset(src_vq, 0, sizeof(*src_vq));
5838c2ecf20Sopenharmony_ci	src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
5848c2ecf20Sopenharmony_ci	src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
5858c2ecf20Sopenharmony_ci	src_vq->drv_priv = ctx;
5868c2ecf20Sopenharmony_ci	src_vq->ops = &gsc_m2m_qops;
5878c2ecf20Sopenharmony_ci	src_vq->mem_ops = &vb2_dma_contig_memops;
5888c2ecf20Sopenharmony_ci	src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
5898c2ecf20Sopenharmony_ci	src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
5908c2ecf20Sopenharmony_ci	src_vq->lock = &ctx->gsc_dev->lock;
5918c2ecf20Sopenharmony_ci	src_vq->dev = &ctx->gsc_dev->pdev->dev;
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	ret = vb2_queue_init(src_vq);
5948c2ecf20Sopenharmony_ci	if (ret)
5958c2ecf20Sopenharmony_ci		return ret;
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	memset(dst_vq, 0, sizeof(*dst_vq));
5988c2ecf20Sopenharmony_ci	dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
5998c2ecf20Sopenharmony_ci	dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
6008c2ecf20Sopenharmony_ci	dst_vq->drv_priv = ctx;
6018c2ecf20Sopenharmony_ci	dst_vq->ops = &gsc_m2m_qops;
6028c2ecf20Sopenharmony_ci	dst_vq->mem_ops = &vb2_dma_contig_memops;
6038c2ecf20Sopenharmony_ci	dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
6048c2ecf20Sopenharmony_ci	dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
6058c2ecf20Sopenharmony_ci	dst_vq->lock = &ctx->gsc_dev->lock;
6068c2ecf20Sopenharmony_ci	dst_vq->dev = &ctx->gsc_dev->pdev->dev;
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	return vb2_queue_init(dst_vq);
6098c2ecf20Sopenharmony_ci}
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_cistatic int gsc_m2m_open(struct file *file)
6128c2ecf20Sopenharmony_ci{
6138c2ecf20Sopenharmony_ci	struct gsc_dev *gsc = video_drvdata(file);
6148c2ecf20Sopenharmony_ci	struct gsc_ctx *ctx = NULL;
6158c2ecf20Sopenharmony_ci	int ret;
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci	pr_debug("pid: %d, state: 0x%lx", task_pid_nr(current), gsc->state);
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	if (mutex_lock_interruptible(&gsc->lock))
6208c2ecf20Sopenharmony_ci		return -ERESTARTSYS;
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
6238c2ecf20Sopenharmony_ci	if (!ctx) {
6248c2ecf20Sopenharmony_ci		ret = -ENOMEM;
6258c2ecf20Sopenharmony_ci		goto unlock;
6268c2ecf20Sopenharmony_ci	}
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci	v4l2_fh_init(&ctx->fh, gsc->m2m.vfd);
6298c2ecf20Sopenharmony_ci	ret = gsc_ctrls_create(ctx);
6308c2ecf20Sopenharmony_ci	if (ret)
6318c2ecf20Sopenharmony_ci		goto error_fh;
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	/* Use separate control handler per file handle */
6348c2ecf20Sopenharmony_ci	ctx->fh.ctrl_handler = &ctx->ctrl_handler;
6358c2ecf20Sopenharmony_ci	file->private_data = &ctx->fh;
6368c2ecf20Sopenharmony_ci	v4l2_fh_add(&ctx->fh);
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	ctx->gsc_dev = gsc;
6398c2ecf20Sopenharmony_ci	/* Default color format */
6408c2ecf20Sopenharmony_ci	ctx->s_frame.fmt = get_format(0);
6418c2ecf20Sopenharmony_ci	ctx->d_frame.fmt = get_format(0);
6428c2ecf20Sopenharmony_ci	/* Setup the device context for mem2mem mode. */
6438c2ecf20Sopenharmony_ci	ctx->state = GSC_CTX_M2M;
6448c2ecf20Sopenharmony_ci	ctx->flags = 0;
6458c2ecf20Sopenharmony_ci	ctx->in_path = GSC_DMA;
6468c2ecf20Sopenharmony_ci	ctx->out_path = GSC_DMA;
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	ctx->m2m_ctx = v4l2_m2m_ctx_init(gsc->m2m.m2m_dev, ctx, queue_init);
6498c2ecf20Sopenharmony_ci	if (IS_ERR(ctx->m2m_ctx)) {
6508c2ecf20Sopenharmony_ci		pr_err("Failed to initialize m2m context");
6518c2ecf20Sopenharmony_ci		ret = PTR_ERR(ctx->m2m_ctx);
6528c2ecf20Sopenharmony_ci		goto error_ctrls;
6538c2ecf20Sopenharmony_ci	}
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	if (gsc->m2m.refcnt++ == 0)
6568c2ecf20Sopenharmony_ci		set_bit(ST_M2M_OPEN, &gsc->state);
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	pr_debug("gsc m2m driver is opened, ctx(0x%p)", ctx);
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	mutex_unlock(&gsc->lock);
6618c2ecf20Sopenharmony_ci	return 0;
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_cierror_ctrls:
6648c2ecf20Sopenharmony_ci	gsc_ctrls_delete(ctx);
6658c2ecf20Sopenharmony_ci	v4l2_fh_del(&ctx->fh);
6668c2ecf20Sopenharmony_cierror_fh:
6678c2ecf20Sopenharmony_ci	v4l2_fh_exit(&ctx->fh);
6688c2ecf20Sopenharmony_ci	kfree(ctx);
6698c2ecf20Sopenharmony_ciunlock:
6708c2ecf20Sopenharmony_ci	mutex_unlock(&gsc->lock);
6718c2ecf20Sopenharmony_ci	return ret;
6728c2ecf20Sopenharmony_ci}
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_cistatic int gsc_m2m_release(struct file *file)
6758c2ecf20Sopenharmony_ci{
6768c2ecf20Sopenharmony_ci	struct gsc_ctx *ctx = fh_to_ctx(file->private_data);
6778c2ecf20Sopenharmony_ci	struct gsc_dev *gsc = ctx->gsc_dev;
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci	pr_debug("pid: %d, state: 0x%lx, refcnt= %d",
6808c2ecf20Sopenharmony_ci		task_pid_nr(current), gsc->state, gsc->m2m.refcnt);
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci	mutex_lock(&gsc->lock);
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci	v4l2_m2m_ctx_release(ctx->m2m_ctx);
6858c2ecf20Sopenharmony_ci	gsc_ctrls_delete(ctx);
6868c2ecf20Sopenharmony_ci	v4l2_fh_del(&ctx->fh);
6878c2ecf20Sopenharmony_ci	v4l2_fh_exit(&ctx->fh);
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	if (--gsc->m2m.refcnt <= 0)
6908c2ecf20Sopenharmony_ci		clear_bit(ST_M2M_OPEN, &gsc->state);
6918c2ecf20Sopenharmony_ci	kfree(ctx);
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	mutex_unlock(&gsc->lock);
6948c2ecf20Sopenharmony_ci	return 0;
6958c2ecf20Sopenharmony_ci}
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_cistatic __poll_t gsc_m2m_poll(struct file *file,
6988c2ecf20Sopenharmony_ci					struct poll_table_struct *wait)
6998c2ecf20Sopenharmony_ci{
7008c2ecf20Sopenharmony_ci	struct gsc_ctx *ctx = fh_to_ctx(file->private_data);
7018c2ecf20Sopenharmony_ci	struct gsc_dev *gsc = ctx->gsc_dev;
7028c2ecf20Sopenharmony_ci	__poll_t ret;
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	if (mutex_lock_interruptible(&gsc->lock))
7058c2ecf20Sopenharmony_ci		return EPOLLERR;
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	ret = v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
7088c2ecf20Sopenharmony_ci	mutex_unlock(&gsc->lock);
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	return ret;
7118c2ecf20Sopenharmony_ci}
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_cistatic int gsc_m2m_mmap(struct file *file, struct vm_area_struct *vma)
7148c2ecf20Sopenharmony_ci{
7158c2ecf20Sopenharmony_ci	struct gsc_ctx *ctx = fh_to_ctx(file->private_data);
7168c2ecf20Sopenharmony_ci	struct gsc_dev *gsc = ctx->gsc_dev;
7178c2ecf20Sopenharmony_ci	int ret;
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	if (mutex_lock_interruptible(&gsc->lock))
7208c2ecf20Sopenharmony_ci		return -ERESTARTSYS;
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	ret = v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
7238c2ecf20Sopenharmony_ci	mutex_unlock(&gsc->lock);
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	return ret;
7268c2ecf20Sopenharmony_ci}
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_cistatic const struct v4l2_file_operations gsc_m2m_fops = {
7298c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
7308c2ecf20Sopenharmony_ci	.open		= gsc_m2m_open,
7318c2ecf20Sopenharmony_ci	.release	= gsc_m2m_release,
7328c2ecf20Sopenharmony_ci	.poll		= gsc_m2m_poll,
7338c2ecf20Sopenharmony_ci	.unlocked_ioctl	= video_ioctl2,
7348c2ecf20Sopenharmony_ci	.mmap		= gsc_m2m_mmap,
7358c2ecf20Sopenharmony_ci};
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_cistatic const struct v4l2_m2m_ops gsc_m2m_ops = {
7388c2ecf20Sopenharmony_ci	.device_run	= gsc_m2m_device_run,
7398c2ecf20Sopenharmony_ci	.job_abort	= gsc_m2m_job_abort,
7408c2ecf20Sopenharmony_ci};
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ciint gsc_register_m2m_device(struct gsc_dev *gsc)
7438c2ecf20Sopenharmony_ci{
7448c2ecf20Sopenharmony_ci	struct platform_device *pdev;
7458c2ecf20Sopenharmony_ci	int ret;
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci	if (!gsc)
7488c2ecf20Sopenharmony_ci		return -ENODEV;
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	pdev = gsc->pdev;
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	gsc->vdev.fops		= &gsc_m2m_fops;
7538c2ecf20Sopenharmony_ci	gsc->vdev.ioctl_ops	= &gsc_m2m_ioctl_ops;
7548c2ecf20Sopenharmony_ci	gsc->vdev.release	= video_device_release_empty;
7558c2ecf20Sopenharmony_ci	gsc->vdev.lock		= &gsc->lock;
7568c2ecf20Sopenharmony_ci	gsc->vdev.vfl_dir	= VFL_DIR_M2M;
7578c2ecf20Sopenharmony_ci	gsc->vdev.v4l2_dev	= &gsc->v4l2_dev;
7588c2ecf20Sopenharmony_ci	gsc->vdev.device_caps	= V4L2_CAP_STREAMING |
7598c2ecf20Sopenharmony_ci				  V4L2_CAP_VIDEO_M2M_MPLANE;
7608c2ecf20Sopenharmony_ci	snprintf(gsc->vdev.name, sizeof(gsc->vdev.name), "%s.%d:m2m",
7618c2ecf20Sopenharmony_ci					GSC_MODULE_NAME, gsc->id);
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci	video_set_drvdata(&gsc->vdev, gsc);
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci	gsc->m2m.vfd = &gsc->vdev;
7668c2ecf20Sopenharmony_ci	gsc->m2m.m2m_dev = v4l2_m2m_init(&gsc_m2m_ops);
7678c2ecf20Sopenharmony_ci	if (IS_ERR(gsc->m2m.m2m_dev)) {
7688c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to initialize v4l2-m2m device\n");
7698c2ecf20Sopenharmony_ci		return PTR_ERR(gsc->m2m.m2m_dev);
7708c2ecf20Sopenharmony_ci	}
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	ret = video_register_device(&gsc->vdev, VFL_TYPE_VIDEO, -1);
7738c2ecf20Sopenharmony_ci	if (ret) {
7748c2ecf20Sopenharmony_ci		dev_err(&pdev->dev,
7758c2ecf20Sopenharmony_ci			 "%s(): failed to register video device\n", __func__);
7768c2ecf20Sopenharmony_ci		goto err_m2m_release;
7778c2ecf20Sopenharmony_ci	}
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci	pr_debug("gsc m2m driver registered as /dev/video%d", gsc->vdev.num);
7808c2ecf20Sopenharmony_ci	return 0;
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_cierr_m2m_release:
7838c2ecf20Sopenharmony_ci	v4l2_m2m_release(gsc->m2m.m2m_dev);
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	return ret;
7868c2ecf20Sopenharmony_ci}
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_civoid gsc_unregister_m2m_device(struct gsc_dev *gsc)
7898c2ecf20Sopenharmony_ci{
7908c2ecf20Sopenharmony_ci	if (gsc) {
7918c2ecf20Sopenharmony_ci		v4l2_m2m_release(gsc->m2m.m2m_dev);
7928c2ecf20Sopenharmony_ci		video_unregister_device(&gsc->vdev);
7938c2ecf20Sopenharmony_ci	}
7948c2ecf20Sopenharmony_ci}
795