xref: /linux/drivers/media/platform/samsung/exynos-gsc/gsc-m2m.c (revision c771600c6af14749609b49565ffb4cac2959710d)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
25d718338SSungchun Kang /*
35d718338SSungchun Kang  * Copyright (c) 2011 - 2012 Samsung Electronics Co., Ltd.
45d718338SSungchun Kang  *		http://www.samsung.com
55d718338SSungchun Kang  *
65d718338SSungchun Kang  * Samsung EXYNOS5 SoC series G-Scaler driver
75d718338SSungchun Kang  */
85d718338SSungchun Kang 
95d718338SSungchun Kang #include <linux/module.h>
105d718338SSungchun Kang #include <linux/kernel.h>
115d718338SSungchun Kang #include <linux/types.h>
125d718338SSungchun Kang #include <linux/errno.h>
135d718338SSungchun Kang #include <linux/bug.h>
145d718338SSungchun Kang #include <linux/interrupt.h>
155d718338SSungchun Kang #include <linux/workqueue.h>
165d718338SSungchun Kang #include <linux/device.h>
175d718338SSungchun Kang #include <linux/platform_device.h>
185d718338SSungchun Kang #include <linux/list.h>
195d718338SSungchun Kang #include <linux/io.h>
205d718338SSungchun Kang #include <linux/slab.h>
215d718338SSungchun Kang #include <linux/clk.h>
225d718338SSungchun Kang 
235d718338SSungchun Kang #include <media/v4l2-ioctl.h>
245d718338SSungchun Kang 
255d718338SSungchun Kang #include "gsc-core.h"
265d718338SSungchun Kang 
gsc_m2m_ctx_stop_req(struct gsc_ctx * ctx)275d718338SSungchun Kang static int gsc_m2m_ctx_stop_req(struct gsc_ctx *ctx)
285d718338SSungchun Kang {
295d718338SSungchun Kang 	struct gsc_ctx *curr_ctx;
305d718338SSungchun Kang 	struct gsc_dev *gsc = ctx->gsc_dev;
315d718338SSungchun Kang 	int ret;
325d718338SSungchun Kang 
335d718338SSungchun Kang 	curr_ctx = v4l2_m2m_get_curr_priv(gsc->m2m.m2m_dev);
345d718338SSungchun Kang 	if (!gsc_m2m_pending(gsc) || (curr_ctx != ctx))
355d718338SSungchun Kang 		return 0;
365d718338SSungchun Kang 
375d718338SSungchun Kang 	gsc_ctx_state_lock_set(GSC_CTX_STOP_REQ, ctx);
385d718338SSungchun Kang 	ret = wait_event_timeout(gsc->irq_queue,
395d718338SSungchun Kang 			!gsc_ctx_state_is_set(GSC_CTX_STOP_REQ, ctx),
405d718338SSungchun Kang 			GSC_SHUTDOWN_TIMEOUT);
415d718338SSungchun Kang 
425d718338SSungchun Kang 	return ret == 0 ? -ETIMEDOUT : ret;
435d718338SSungchun Kang }
445d718338SSungchun Kang 
__gsc_m2m_job_abort(struct gsc_ctx * ctx)45d9315160SShaik Ameer Basha static void __gsc_m2m_job_abort(struct gsc_ctx *ctx)
46d9315160SShaik Ameer Basha {
47d9315160SShaik Ameer Basha 	int ret;
48d9315160SShaik Ameer Basha 
49d9315160SShaik Ameer Basha 	ret = gsc_m2m_ctx_stop_req(ctx);
50d9315160SShaik Ameer Basha 	if ((ret == -ETIMEDOUT) || (ctx->state & GSC_CTX_ABORT)) {
51d9315160SShaik Ameer Basha 		gsc_ctx_state_lock_clear(GSC_CTX_STOP_REQ | GSC_CTX_ABORT, ctx);
52d9315160SShaik Ameer Basha 		gsc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR);
53d9315160SShaik Ameer Basha 	}
54d9315160SShaik Ameer Basha }
55d9315160SShaik Ameer Basha 
gsc_m2m_start_streaming(struct vb2_queue * q,unsigned int count)565d718338SSungchun Kang static int gsc_m2m_start_streaming(struct vb2_queue *q, unsigned int count)
575d718338SSungchun Kang {
585d718338SSungchun Kang 	struct gsc_ctx *ctx = q->drv_priv;
595d718338SSungchun Kang 
60*59087b66SMauro Carvalho Chehab 	return pm_runtime_resume_and_get(&ctx->gsc_dev->pdev->dev);
615d718338SSungchun Kang }
625d718338SSungchun Kang 
__gsc_m2m_cleanup_queue(struct gsc_ctx * ctx)6376ca0824SJavier Martinez Canillas static void __gsc_m2m_cleanup_queue(struct gsc_ctx *ctx)
6476ca0824SJavier Martinez Canillas {
6576ca0824SJavier Martinez Canillas 	struct vb2_v4l2_buffer *src_vb, *dst_vb;
6676ca0824SJavier Martinez Canillas 
6776ca0824SJavier Martinez Canillas 	while (v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) > 0) {
6876ca0824SJavier Martinez Canillas 		src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
6976ca0824SJavier Martinez Canillas 		v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_ERROR);
7076ca0824SJavier Martinez Canillas 	}
7176ca0824SJavier Martinez Canillas 
7276ca0824SJavier Martinez Canillas 	while (v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx) > 0) {
7376ca0824SJavier Martinez Canillas 		dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
7476ca0824SJavier Martinez Canillas 		v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_ERROR);
7576ca0824SJavier Martinez Canillas 	}
7676ca0824SJavier Martinez Canillas }
7776ca0824SJavier Martinez Canillas 
gsc_m2m_stop_streaming(struct vb2_queue * q)78e37559b2SHans Verkuil static void gsc_m2m_stop_streaming(struct vb2_queue *q)
795d718338SSungchun Kang {
805d718338SSungchun Kang 	struct gsc_ctx *ctx = q->drv_priv;
815d718338SSungchun Kang 
82d9315160SShaik Ameer Basha 	__gsc_m2m_job_abort(ctx);
835d718338SSungchun Kang 
8476ca0824SJavier Martinez Canillas 	__gsc_m2m_cleanup_queue(ctx);
8576ca0824SJavier Martinez Canillas 
865d718338SSungchun Kang 	pm_runtime_put(&ctx->gsc_dev->pdev->dev);
875d718338SSungchun Kang }
885d718338SSungchun Kang 
gsc_m2m_job_finish(struct gsc_ctx * ctx,int vb_state)895d718338SSungchun Kang void gsc_m2m_job_finish(struct gsc_ctx *ctx, int vb_state)
905d718338SSungchun Kang {
912d700715SJunghak Sung 	struct vb2_v4l2_buffer *src_vb, *dst_vb;
925d718338SSungchun Kang 
935d718338SSungchun Kang 	if (!ctx || !ctx->m2m_ctx)
945d718338SSungchun Kang 		return;
955d718338SSungchun Kang 
965d718338SSungchun Kang 	src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
975d718338SSungchun Kang 	dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
985d718338SSungchun Kang 
995d718338SSungchun Kang 	if (src_vb && dst_vb) {
100d6dd645eSJunghak Sung 		dst_vb->vb2_buf.timestamp = src_vb->vb2_buf.timestamp;
1012d700715SJunghak Sung 		dst_vb->timecode = src_vb->timecode;
1022d700715SJunghak Sung 		dst_vb->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
1032d700715SJunghak Sung 		dst_vb->flags |=
1042d700715SJunghak Sung 			src_vb->flags
105309f4d62SSakari Ailus 			& V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
1069c303ec6SKamil Debski 
1075d718338SSungchun Kang 		v4l2_m2m_buf_done(src_vb, vb_state);
1085d718338SSungchun Kang 		v4l2_m2m_buf_done(dst_vb, vb_state);
1095d718338SSungchun Kang 
1105d718338SSungchun Kang 		v4l2_m2m_job_finish(ctx->gsc_dev->m2m.m2m_dev,
1115d718338SSungchun Kang 				    ctx->m2m_ctx);
1125d718338SSungchun Kang 	}
1135d718338SSungchun Kang }
1145d718338SSungchun Kang 
gsc_m2m_job_abort(void * priv)1155d718338SSungchun Kang static void gsc_m2m_job_abort(void *priv)
1165d718338SSungchun Kang {
117d9315160SShaik Ameer Basha 	__gsc_m2m_job_abort((struct gsc_ctx *)priv);
1185d718338SSungchun Kang }
1195d718338SSungchun Kang 
gsc_get_bufs(struct gsc_ctx * ctx)120f60e160eSShaik Ameer Basha static int gsc_get_bufs(struct gsc_ctx *ctx)
1215d718338SSungchun Kang {
1225d718338SSungchun Kang 	struct gsc_frame *s_frame, *d_frame;
1232d700715SJunghak Sung 	struct vb2_v4l2_buffer *src_vb, *dst_vb;
1245d718338SSungchun Kang 	int ret;
1255d718338SSungchun Kang 
1265d718338SSungchun Kang 	s_frame = &ctx->s_frame;
1275d718338SSungchun Kang 	d_frame = &ctx->d_frame;
1285d718338SSungchun Kang 
129f60e160eSShaik Ameer Basha 	src_vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
1302d700715SJunghak Sung 	ret = gsc_prepare_addr(ctx, &src_vb->vb2_buf, s_frame, &s_frame->addr);
1315d718338SSungchun Kang 	if (ret)
1325d718338SSungchun Kang 		return ret;
1335d718338SSungchun Kang 
134f60e160eSShaik Ameer Basha 	dst_vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
1352d700715SJunghak Sung 	ret = gsc_prepare_addr(ctx, &dst_vb->vb2_buf, d_frame, &d_frame->addr);
136f60e160eSShaik Ameer Basha 	if (ret)
137f60e160eSShaik Ameer Basha 		return ret;
138f60e160eSShaik Ameer Basha 
139d6dd645eSJunghak Sung 	dst_vb->vb2_buf.timestamp = src_vb->vb2_buf.timestamp;
140f60e160eSShaik Ameer Basha 
141f60e160eSShaik Ameer Basha 	return 0;
1425d718338SSungchun Kang }
1435d718338SSungchun Kang 
gsc_m2m_device_run(void * priv)1445d718338SSungchun Kang static void gsc_m2m_device_run(void *priv)
1455d718338SSungchun Kang {
1465d718338SSungchun Kang 	struct gsc_ctx *ctx = priv;
1475d718338SSungchun Kang 	struct gsc_dev *gsc;
1485d718338SSungchun Kang 	unsigned long flags;
1494bd0e030SShaik Ameer Basha 	int ret;
1505d718338SSungchun Kang 	bool is_set = false;
1515d718338SSungchun Kang 
1525d718338SSungchun Kang 	if (WARN(!ctx, "null hardware context\n"))
1535d718338SSungchun Kang 		return;
1545d718338SSungchun Kang 
1555d718338SSungchun Kang 	gsc = ctx->gsc_dev;
1565d718338SSungchun Kang 	spin_lock_irqsave(&gsc->slock, flags);
1575d718338SSungchun Kang 
1585d718338SSungchun Kang 	set_bit(ST_M2M_PEND, &gsc->state);
1595d718338SSungchun Kang 
1605d718338SSungchun Kang 	/* Reconfigure hardware if the context has changed. */
1615d718338SSungchun Kang 	if (gsc->m2m.ctx != ctx) {
1625d718338SSungchun Kang 		pr_debug("gsc->m2m.ctx = 0x%p, current_ctx = 0x%p",
1635d718338SSungchun Kang 				gsc->m2m.ctx, ctx);
1645d718338SSungchun Kang 		ctx->state |= GSC_PARAMS;
1655d718338SSungchun Kang 		gsc->m2m.ctx = ctx;
1665d718338SSungchun Kang 	}
1675d718338SSungchun Kang 
168d9315160SShaik Ameer Basha 	is_set = ctx->state & GSC_CTX_STOP_REQ;
1695d718338SSungchun Kang 	if (is_set) {
170d9315160SShaik Ameer Basha 		ctx->state &= ~GSC_CTX_STOP_REQ;
171d9315160SShaik Ameer Basha 		ctx->state |= GSC_CTX_ABORT;
1725d718338SSungchun Kang 		wake_up(&gsc->irq_queue);
1735d718338SSungchun Kang 		goto put_device;
1745d718338SSungchun Kang 	}
1755d718338SSungchun Kang 
176f60e160eSShaik Ameer Basha 	ret = gsc_get_bufs(ctx);
1775d718338SSungchun Kang 	if (ret) {
1785d718338SSungchun Kang 		pr_err("Wrong address");
1795d718338SSungchun Kang 		goto put_device;
1805d718338SSungchun Kang 	}
1815d718338SSungchun Kang 
1825d718338SSungchun Kang 	gsc_set_prefbuf(gsc, &ctx->s_frame);
1835d718338SSungchun Kang 	gsc_hw_set_input_addr(gsc, &ctx->s_frame.addr, GSC_M2M_BUF_NUM);
1845d718338SSungchun Kang 	gsc_hw_set_output_addr(gsc, &ctx->d_frame.addr, GSC_M2M_BUF_NUM);
1855d718338SSungchun Kang 
1865d718338SSungchun Kang 	if (ctx->state & GSC_PARAMS) {
1875d718338SSungchun Kang 		gsc_hw_set_input_buf_masking(gsc, GSC_M2M_BUF_NUM, false);
1885d718338SSungchun Kang 		gsc_hw_set_output_buf_masking(gsc, GSC_M2M_BUF_NUM, false);
1895d718338SSungchun Kang 		gsc_hw_set_frm_done_irq_mask(gsc, false);
1905d718338SSungchun Kang 		gsc_hw_set_gsc_irq_enable(gsc, true);
1915d718338SSungchun Kang 
1925d718338SSungchun Kang 		if (gsc_set_scaler_info(ctx)) {
1935d718338SSungchun Kang 			pr_err("Scaler setup error");
1945d718338SSungchun Kang 			goto put_device;
1955d718338SSungchun Kang 		}
1965d718338SSungchun Kang 
1975d718338SSungchun Kang 		gsc_hw_set_input_path(ctx);
1985d718338SSungchun Kang 		gsc_hw_set_in_size(ctx);
1995d718338SSungchun Kang 		gsc_hw_set_in_image_format(ctx);
2005d718338SSungchun Kang 
2015d718338SSungchun Kang 		gsc_hw_set_output_path(ctx);
2025d718338SSungchun Kang 		gsc_hw_set_out_size(ctx);
2035d718338SSungchun Kang 		gsc_hw_set_out_image_format(ctx);
2045d718338SSungchun Kang 
2055d718338SSungchun Kang 		gsc_hw_set_prescaler(ctx);
2065d718338SSungchun Kang 		gsc_hw_set_mainscaler(ctx);
2075d718338SSungchun Kang 		gsc_hw_set_rotation(ctx);
2085d718338SSungchun Kang 		gsc_hw_set_global_alpha(ctx);
2095d718338SSungchun Kang 	}
2105d718338SSungchun Kang 
2115d718338SSungchun Kang 	/* update shadow registers */
2125d718338SSungchun Kang 	gsc_hw_set_sfr_update(ctx);
2135d718338SSungchun Kang 
2145d718338SSungchun Kang 	ctx->state &= ~GSC_PARAMS;
2155d718338SSungchun Kang 	gsc_hw_enable_control(gsc, true);
2165d718338SSungchun Kang 
2175d718338SSungchun Kang 	spin_unlock_irqrestore(&gsc->slock, flags);
2185d718338SSungchun Kang 	return;
2195d718338SSungchun Kang 
2205d718338SSungchun Kang put_device:
2215d718338SSungchun Kang 	ctx->state &= ~GSC_PARAMS;
2225d718338SSungchun Kang 	spin_unlock_irqrestore(&gsc->slock, flags);
2235d718338SSungchun Kang }
2245d718338SSungchun Kang 
gsc_m2m_queue_setup(struct vb2_queue * vq,unsigned int * num_buffers,unsigned int * num_planes,unsigned int sizes[],struct device * alloc_devs[])2255d718338SSungchun Kang static int gsc_m2m_queue_setup(struct vb2_queue *vq,
2265d718338SSungchun Kang 			unsigned int *num_buffers, unsigned int *num_planes,
22736c0f8b3SHans Verkuil 			unsigned int sizes[], struct device *alloc_devs[])
2285d718338SSungchun Kang {
2295d718338SSungchun Kang 	struct gsc_ctx *ctx = vb2_get_drv_priv(vq);
2305d718338SSungchun Kang 	struct gsc_frame *frame;
2315d718338SSungchun Kang 	int i;
2325d718338SSungchun Kang 
2335d718338SSungchun Kang 	frame = ctx_get_frame(ctx, vq->type);
2345d718338SSungchun Kang 	if (IS_ERR(frame))
2355d718338SSungchun Kang 		return PTR_ERR(frame);
2365d718338SSungchun Kang 
2375d718338SSungchun Kang 	if (!frame->fmt)
2385d718338SSungchun Kang 		return -EINVAL;
2395d718338SSungchun Kang 
2405d718338SSungchun Kang 	*num_planes = frame->fmt->num_planes;
241c781e4a5SHans Verkuil 	for (i = 0; i < frame->fmt->num_planes; i++)
2425d718338SSungchun Kang 		sizes[i] = frame->payload[i];
2435d718338SSungchun Kang 	return 0;
2445d718338SSungchun Kang }
2455d718338SSungchun Kang 
gsc_m2m_buf_prepare(struct vb2_buffer * vb)2465d718338SSungchun Kang static int gsc_m2m_buf_prepare(struct vb2_buffer *vb)
2475d718338SSungchun Kang {
2485d718338SSungchun Kang 	struct gsc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
2495d718338SSungchun Kang 	struct gsc_frame *frame;
2505d718338SSungchun Kang 	int i;
2515d718338SSungchun Kang 
2525d718338SSungchun Kang 	frame = ctx_get_frame(ctx, vb->vb2_queue->type);
2535d718338SSungchun Kang 	if (IS_ERR(frame))
2545d718338SSungchun Kang 		return PTR_ERR(frame);
2555d718338SSungchun Kang 
256b3ab1c60SEzequiel Garcia 	if (V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type)) {
2575d718338SSungchun Kang 		for (i = 0; i < frame->fmt->num_planes; i++)
2585d718338SSungchun Kang 			vb2_set_plane_payload(vb, i, frame->payload[i]);
2595d718338SSungchun Kang 	}
2605d718338SSungchun Kang 
2615d718338SSungchun Kang 	return 0;
2625d718338SSungchun Kang }
2635d718338SSungchun Kang 
gsc_m2m_buf_queue(struct vb2_buffer * vb)2645d718338SSungchun Kang static void gsc_m2m_buf_queue(struct vb2_buffer *vb)
2655d718338SSungchun Kang {
2662d700715SJunghak Sung 	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
2675d718338SSungchun Kang 	struct gsc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
2685d718338SSungchun Kang 
2695d718338SSungchun Kang 	pr_debug("ctx: %p, ctx->state: 0x%x", ctx, ctx->state);
2705d718338SSungchun Kang 
2715d718338SSungchun Kang 	if (ctx->m2m_ctx)
2722d700715SJunghak Sung 		v4l2_m2m_buf_queue(ctx->m2m_ctx, vbuf);
2735d718338SSungchun Kang }
2745d718338SSungchun Kang 
275b7b361f0SJulia Lawall static const struct vb2_ops gsc_m2m_qops = {
2765d718338SSungchun Kang 	.queue_setup	 = gsc_m2m_queue_setup,
2775d718338SSungchun Kang 	.buf_prepare	 = gsc_m2m_buf_prepare,
2785d718338SSungchun Kang 	.buf_queue	 = gsc_m2m_buf_queue,
2795d718338SSungchun Kang 	.stop_streaming	 = gsc_m2m_stop_streaming,
2805d718338SSungchun Kang 	.start_streaming = gsc_m2m_start_streaming,
2815d718338SSungchun Kang };
2825d718338SSungchun Kang 
gsc_m2m_querycap(struct file * file,void * fh,struct v4l2_capability * cap)2835d718338SSungchun Kang static int gsc_m2m_querycap(struct file *file, void *fh,
2845d718338SSungchun Kang 			   struct v4l2_capability *cap)
2855d718338SSungchun Kang {
286c0decac1SMauro Carvalho Chehab 	strscpy(cap->driver, GSC_MODULE_NAME, sizeof(cap->driver));
287c0decac1SMauro Carvalho Chehab 	strscpy(cap->card, GSC_MODULE_NAME " gscaler", sizeof(cap->card));
2885d718338SSungchun Kang 	return 0;
2895d718338SSungchun Kang }
2905d718338SSungchun Kang 
gsc_m2m_enum_fmt(struct file * file,void * priv,struct v4l2_fmtdesc * f)2917e98b7b5SBoris Brezillon static int gsc_m2m_enum_fmt(struct file *file, void *priv,
2925d718338SSungchun Kang 			    struct v4l2_fmtdesc *f)
2935d718338SSungchun Kang {
2947e98b7b5SBoris Brezillon 	return gsc_enum_fmt(f);
2955d718338SSungchun Kang }
2965d718338SSungchun Kang 
gsc_m2m_g_fmt_mplane(struct file * file,void * fh,struct v4l2_format * f)2975d718338SSungchun Kang static int gsc_m2m_g_fmt_mplane(struct file *file, void *fh,
2985d718338SSungchun Kang 			     struct v4l2_format *f)
2995d718338SSungchun Kang {
3005d718338SSungchun Kang 	struct gsc_ctx *ctx = fh_to_ctx(fh);
3015d718338SSungchun Kang 
3025d718338SSungchun Kang 	return gsc_g_fmt_mplane(ctx, f);
3035d718338SSungchun Kang }
3045d718338SSungchun Kang 
gsc_m2m_try_fmt_mplane(struct file * file,void * fh,struct v4l2_format * f)3055d718338SSungchun Kang static int gsc_m2m_try_fmt_mplane(struct file *file, void *fh,
3065d718338SSungchun Kang 				  struct v4l2_format *f)
3075d718338SSungchun Kang {
3085d718338SSungchun Kang 	struct gsc_ctx *ctx = fh_to_ctx(fh);
3095d718338SSungchun Kang 
3105d718338SSungchun Kang 	return gsc_try_fmt_mplane(ctx, f);
3115d718338SSungchun Kang }
3125d718338SSungchun Kang 
gsc_m2m_s_fmt_mplane(struct file * file,void * fh,struct v4l2_format * f)3135d718338SSungchun Kang static int gsc_m2m_s_fmt_mplane(struct file *file, void *fh,
3145d718338SSungchun Kang 				 struct v4l2_format *f)
3155d718338SSungchun Kang {
3165d718338SSungchun Kang 	struct gsc_ctx *ctx = fh_to_ctx(fh);
3175d718338SSungchun Kang 	struct vb2_queue *vq;
3185d718338SSungchun Kang 	struct gsc_frame *frame;
3195d718338SSungchun Kang 	struct v4l2_pix_format_mplane *pix;
3205d718338SSungchun Kang 	int i, ret = 0;
3215d718338SSungchun Kang 
3225d718338SSungchun Kang 	ret = gsc_m2m_try_fmt_mplane(file, fh, f);
3235d718338SSungchun Kang 	if (ret)
3245d718338SSungchun Kang 		return ret;
3255d718338SSungchun Kang 
3265d718338SSungchun Kang 	vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
3275d718338SSungchun Kang 
3285d718338SSungchun Kang 	if (vb2_is_streaming(vq)) {
3295d718338SSungchun Kang 		pr_err("queue (%d) busy", f->type);
3305d718338SSungchun Kang 		return -EBUSY;
3315d718338SSungchun Kang 	}
3325d718338SSungchun Kang 
3335d718338SSungchun Kang 	if (V4L2_TYPE_IS_OUTPUT(f->type))
3345d718338SSungchun Kang 		frame = &ctx->s_frame;
3355d718338SSungchun Kang 	else
3365d718338SSungchun Kang 		frame = &ctx->d_frame;
3375d718338SSungchun Kang 
3385d718338SSungchun Kang 	pix = &f->fmt.pix_mp;
3395d718338SSungchun Kang 	frame->fmt = find_fmt(&pix->pixelformat, NULL, 0);
3405d718338SSungchun Kang 	frame->colorspace = pix->colorspace;
3415d718338SSungchun Kang 	if (!frame->fmt)
3425d718338SSungchun Kang 		return -EINVAL;
3435d718338SSungchun Kang 
3445d718338SSungchun Kang 	for (i = 0; i < frame->fmt->num_planes; i++)
3455d718338SSungchun Kang 		frame->payload[i] = pix->plane_fmt[i].sizeimage;
3465d718338SSungchun Kang 
3475d718338SSungchun Kang 	gsc_set_frame_size(frame, pix->width, pix->height);
3485d718338SSungchun Kang 
3495d718338SSungchun Kang 	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
3505d718338SSungchun Kang 		gsc_ctx_state_lock_set(GSC_PARAMS | GSC_DST_FMT, ctx);
3515d718338SSungchun Kang 	else
3525d718338SSungchun Kang 		gsc_ctx_state_lock_set(GSC_PARAMS | GSC_SRC_FMT, ctx);
3535d718338SSungchun Kang 
3545d718338SSungchun Kang 	pr_debug("f_w: %d, f_h: %d", frame->f_width, frame->f_height);
3555d718338SSungchun Kang 
3565d718338SSungchun Kang 	return 0;
3575d718338SSungchun Kang }
3585d718338SSungchun Kang 
gsc_m2m_reqbufs(struct file * file,void * fh,struct v4l2_requestbuffers * reqbufs)3595d718338SSungchun Kang static int gsc_m2m_reqbufs(struct file *file, void *fh,
3605d718338SSungchun Kang 			  struct v4l2_requestbuffers *reqbufs)
3615d718338SSungchun Kang {
3625d718338SSungchun Kang 	struct gsc_ctx *ctx = fh_to_ctx(fh);
3635d718338SSungchun Kang 	struct gsc_dev *gsc = ctx->gsc_dev;
3645d718338SSungchun Kang 	u32 max_cnt;
3655d718338SSungchun Kang 
3665d718338SSungchun Kang 	max_cnt = (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ?
3675d718338SSungchun Kang 		gsc->variant->in_buf_cnt : gsc->variant->out_buf_cnt;
368daba4dfbSJavier Martinez Canillas 	if (reqbufs->count > max_cnt)
3695d718338SSungchun Kang 		return -EINVAL;
3705d718338SSungchun Kang 
3715d718338SSungchun Kang 	return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
3725d718338SSungchun Kang }
3735d718338SSungchun Kang 
gsc_m2m_expbuf(struct file * file,void * fh,struct v4l2_exportbuffer * eb)374371a664eSShaik Ameer Basha static int gsc_m2m_expbuf(struct file *file, void *fh,
375371a664eSShaik Ameer Basha 				struct v4l2_exportbuffer *eb)
376371a664eSShaik Ameer Basha {
377371a664eSShaik Ameer Basha 	struct gsc_ctx *ctx = fh_to_ctx(fh);
378371a664eSShaik Ameer Basha 	return v4l2_m2m_expbuf(file, ctx->m2m_ctx, eb);
379371a664eSShaik Ameer Basha }
380371a664eSShaik Ameer Basha 
gsc_m2m_querybuf(struct file * file,void * fh,struct v4l2_buffer * buf)3815d718338SSungchun Kang static int gsc_m2m_querybuf(struct file *file, void *fh,
3825d718338SSungchun Kang 					struct v4l2_buffer *buf)
3835d718338SSungchun Kang {
3845d718338SSungchun Kang 	struct gsc_ctx *ctx = fh_to_ctx(fh);
3855d718338SSungchun Kang 	return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
3865d718338SSungchun Kang }
3875d718338SSungchun Kang 
gsc_m2m_qbuf(struct file * file,void * fh,struct v4l2_buffer * buf)3885d718338SSungchun Kang static int gsc_m2m_qbuf(struct file *file, void *fh,
3895d718338SSungchun Kang 			  struct v4l2_buffer *buf)
3905d718338SSungchun Kang {
3915d718338SSungchun Kang 	struct gsc_ctx *ctx = fh_to_ctx(fh);
3925d718338SSungchun Kang 	return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
3935d718338SSungchun Kang }
3945d718338SSungchun Kang 
gsc_m2m_dqbuf(struct file * file,void * fh,struct v4l2_buffer * buf)3955d718338SSungchun Kang static int gsc_m2m_dqbuf(struct file *file, void *fh,
3965d718338SSungchun Kang 			   struct v4l2_buffer *buf)
3975d718338SSungchun Kang {
3985d718338SSungchun Kang 	struct gsc_ctx *ctx = fh_to_ctx(fh);
3995d718338SSungchun Kang 	return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
4005d718338SSungchun Kang }
4015d718338SSungchun Kang 
gsc_m2m_streamon(struct file * file,void * fh,enum v4l2_buf_type type)4025d718338SSungchun Kang static int gsc_m2m_streamon(struct file *file, void *fh,
4035d718338SSungchun Kang 			   enum v4l2_buf_type type)
4045d718338SSungchun Kang {
4055d718338SSungchun Kang 	struct gsc_ctx *ctx = fh_to_ctx(fh);
4065d718338SSungchun Kang 
4075d718338SSungchun Kang 	/* The source and target color format need to be set */
4085d718338SSungchun Kang 	if (V4L2_TYPE_IS_OUTPUT(type)) {
4095d718338SSungchun Kang 		if (!gsc_ctx_state_is_set(GSC_SRC_FMT, ctx))
4105d718338SSungchun Kang 			return -EINVAL;
4115d718338SSungchun Kang 	} else if (!gsc_ctx_state_is_set(GSC_DST_FMT, ctx)) {
4125d718338SSungchun Kang 		return -EINVAL;
4135d718338SSungchun Kang 	}
4145d718338SSungchun Kang 
4155d718338SSungchun Kang 	return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
4165d718338SSungchun Kang }
4175d718338SSungchun Kang 
gsc_m2m_streamoff(struct file * file,void * fh,enum v4l2_buf_type type)4185d718338SSungchun Kang static int gsc_m2m_streamoff(struct file *file, void *fh,
4195d718338SSungchun Kang 			    enum v4l2_buf_type type)
4205d718338SSungchun Kang {
4215d718338SSungchun Kang 	struct gsc_ctx *ctx = fh_to_ctx(fh);
4225d718338SSungchun Kang 	return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
4235d718338SSungchun Kang }
4245d718338SSungchun Kang 
4255d718338SSungchun Kang /* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */
is_rectangle_enclosed(struct v4l2_rect * a,struct v4l2_rect * b)4265d718338SSungchun Kang static int is_rectangle_enclosed(struct v4l2_rect *a, struct v4l2_rect *b)
4275d718338SSungchun Kang {
4285d718338SSungchun Kang 	if (a->left < b->left || a->top < b->top)
4295d718338SSungchun Kang 		return 0;
4305d718338SSungchun Kang 
4315d718338SSungchun Kang 	if (a->left + a->width > b->left + b->width)
4325d718338SSungchun Kang 		return 0;
4335d718338SSungchun Kang 
4345d718338SSungchun Kang 	if (a->top + a->height > b->top + b->height)
4355d718338SSungchun Kang 		return 0;
4365d718338SSungchun Kang 
4375d718338SSungchun Kang 	return 1;
4385d718338SSungchun Kang }
4395d718338SSungchun Kang 
gsc_m2m_g_selection(struct file * file,void * fh,struct v4l2_selection * s)4405d718338SSungchun Kang static int gsc_m2m_g_selection(struct file *file, void *fh,
4415d718338SSungchun Kang 			struct v4l2_selection *s)
4425d718338SSungchun Kang {
4435d718338SSungchun Kang 	struct gsc_frame *frame;
4445d718338SSungchun Kang 	struct gsc_ctx *ctx = fh_to_ctx(fh);
4455d718338SSungchun Kang 
446eaec420fSHans Verkuil 	if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
447eaec420fSHans Verkuil 	    (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT))
4485d718338SSungchun Kang 		return -EINVAL;
4495d718338SSungchun Kang 
4505d718338SSungchun Kang 	frame = ctx_get_frame(ctx, s->type);
4515d718338SSungchun Kang 	if (IS_ERR(frame))
4525d718338SSungchun Kang 		return PTR_ERR(frame);
4535d718338SSungchun Kang 
4545d718338SSungchun Kang 	switch (s->target) {
4555d718338SSungchun Kang 	case V4L2_SEL_TGT_COMPOSE_DEFAULT:
4565d718338SSungchun Kang 	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
4575d718338SSungchun Kang 	case V4L2_SEL_TGT_CROP_BOUNDS:
4585d718338SSungchun Kang 	case V4L2_SEL_TGT_CROP_DEFAULT:
4595d718338SSungchun Kang 		s->r.left = 0;
4605d718338SSungchun Kang 		s->r.top = 0;
4615d718338SSungchun Kang 		s->r.width = frame->f_width;
4625d718338SSungchun Kang 		s->r.height = frame->f_height;
4635d718338SSungchun Kang 		return 0;
4645d718338SSungchun Kang 
4655d718338SSungchun Kang 	case V4L2_SEL_TGT_COMPOSE:
4665d718338SSungchun Kang 	case V4L2_SEL_TGT_CROP:
4675d718338SSungchun Kang 		s->r.left = frame->crop.left;
4685d718338SSungchun Kang 		s->r.top = frame->crop.top;
4695d718338SSungchun Kang 		s->r.width = frame->crop.width;
4705d718338SSungchun Kang 		s->r.height = frame->crop.height;
4715d718338SSungchun Kang 		return 0;
4725d718338SSungchun Kang 	}
4735d718338SSungchun Kang 
4745d718338SSungchun Kang 	return -EINVAL;
4755d718338SSungchun Kang }
4765d718338SSungchun Kang 
gsc_m2m_s_selection(struct file * file,void * fh,struct v4l2_selection * s)4775d718338SSungchun Kang static int gsc_m2m_s_selection(struct file *file, void *fh,
4785d718338SSungchun Kang 				struct v4l2_selection *s)
4795d718338SSungchun Kang {
4805d718338SSungchun Kang 	struct gsc_frame *frame;
4815d718338SSungchun Kang 	struct gsc_ctx *ctx = fh_to_ctx(fh);
4825d718338SSungchun Kang 	struct gsc_variant *variant = ctx->gsc_dev->variant;
4839ad763d0SHans Verkuil 	struct v4l2_selection sel = *s;
4845d718338SSungchun Kang 	int ret;
4855d718338SSungchun Kang 
486eaec420fSHans Verkuil 	if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
487eaec420fSHans Verkuil 	    (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT))
4885d718338SSungchun Kang 		return -EINVAL;
4895d718338SSungchun Kang 
4909ad763d0SHans Verkuil 	ret = gsc_try_selection(ctx, &sel);
4915d718338SSungchun Kang 	if (ret)
4925d718338SSungchun Kang 		return ret;
4935d718338SSungchun Kang 
4945d718338SSungchun Kang 	if (s->flags & V4L2_SEL_FLAG_LE &&
4959ad763d0SHans Verkuil 	    !is_rectangle_enclosed(&sel.r, &s->r))
4965d718338SSungchun Kang 		return -ERANGE;
4975d718338SSungchun Kang 
4985d718338SSungchun Kang 	if (s->flags & V4L2_SEL_FLAG_GE &&
4999ad763d0SHans Verkuil 	    !is_rectangle_enclosed(&s->r, &sel.r))
5005d718338SSungchun Kang 		return -ERANGE;
5015d718338SSungchun Kang 
5029ad763d0SHans Verkuil 	s->r = sel.r;
5035d718338SSungchun Kang 
5045d718338SSungchun Kang 	switch (s->target) {
5055d718338SSungchun Kang 	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
5065d718338SSungchun Kang 	case V4L2_SEL_TGT_COMPOSE_DEFAULT:
5075d718338SSungchun Kang 	case V4L2_SEL_TGT_COMPOSE:
5085d718338SSungchun Kang 		frame = &ctx->s_frame;
5095d718338SSungchun Kang 		break;
5105d718338SSungchun Kang 
5115d718338SSungchun Kang 	case V4L2_SEL_TGT_CROP_BOUNDS:
5125d718338SSungchun Kang 	case V4L2_SEL_TGT_CROP:
5135d718338SSungchun Kang 	case V4L2_SEL_TGT_CROP_DEFAULT:
5145d718338SSungchun Kang 		frame = &ctx->d_frame;
5155d718338SSungchun Kang 		break;
5165d718338SSungchun Kang 
5175d718338SSungchun Kang 	default:
5185d718338SSungchun Kang 		return -EINVAL;
5195d718338SSungchun Kang 	}
5205d718338SSungchun Kang 
5215d718338SSungchun Kang 	/* Check to see if scaling ratio is within supported range */
5225d718338SSungchun Kang 	if (gsc_ctx_state_is_set(GSC_DST_FMT | GSC_SRC_FMT, ctx)) {
5235d718338SSungchun Kang 		if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
5249ad763d0SHans Verkuil 			ret = gsc_check_scaler_ratio(variant, sel.r.width,
5259ad763d0SHans Verkuil 				sel.r.height, ctx->d_frame.crop.width,
5265d718338SSungchun Kang 				ctx->d_frame.crop.height,
5275d718338SSungchun Kang 				ctx->gsc_ctrls.rotate->val, ctx->out_path);
5285d718338SSungchun Kang 		} else {
5295d718338SSungchun Kang 			ret = gsc_check_scaler_ratio(variant,
5305d718338SSungchun Kang 				ctx->s_frame.crop.width,
5319ad763d0SHans Verkuil 				ctx->s_frame.crop.height, sel.r.width,
5329ad763d0SHans Verkuil 				sel.r.height, ctx->gsc_ctrls.rotate->val,
5335d718338SSungchun Kang 				ctx->out_path);
5345d718338SSungchun Kang 		}
5355d718338SSungchun Kang 
5365d718338SSungchun Kang 		if (ret) {
5375d718338SSungchun Kang 			pr_err("Out of scaler range");
5385d718338SSungchun Kang 			return -EINVAL;
5395d718338SSungchun Kang 		}
5405d718338SSungchun Kang 	}
5415d718338SSungchun Kang 
5429ad763d0SHans Verkuil 	frame->crop = sel.r;
5435d718338SSungchun Kang 
5445d718338SSungchun Kang 	gsc_ctx_state_lock_set(GSC_PARAMS, ctx);
5455d718338SSungchun Kang 	return 0;
5465d718338SSungchun Kang }
5475d718338SSungchun Kang 
5485d718338SSungchun Kang static const struct v4l2_ioctl_ops gsc_m2m_ioctl_ops = {
5495d718338SSungchun Kang 	.vidioc_querycap		= gsc_m2m_querycap,
5507e98b7b5SBoris Brezillon 	.vidioc_enum_fmt_vid_cap	= gsc_m2m_enum_fmt,
5517e98b7b5SBoris Brezillon 	.vidioc_enum_fmt_vid_out	= gsc_m2m_enum_fmt,
5525d718338SSungchun Kang 	.vidioc_g_fmt_vid_cap_mplane	= gsc_m2m_g_fmt_mplane,
5535d718338SSungchun Kang 	.vidioc_g_fmt_vid_out_mplane	= gsc_m2m_g_fmt_mplane,
5545d718338SSungchun Kang 	.vidioc_try_fmt_vid_cap_mplane	= gsc_m2m_try_fmt_mplane,
5555d718338SSungchun Kang 	.vidioc_try_fmt_vid_out_mplane	= gsc_m2m_try_fmt_mplane,
5565d718338SSungchun Kang 	.vidioc_s_fmt_vid_cap_mplane	= gsc_m2m_s_fmt_mplane,
5575d718338SSungchun Kang 	.vidioc_s_fmt_vid_out_mplane	= gsc_m2m_s_fmt_mplane,
5585d718338SSungchun Kang 	.vidioc_reqbufs			= gsc_m2m_reqbufs,
559371a664eSShaik Ameer Basha 	.vidioc_expbuf                  = gsc_m2m_expbuf,
5605d718338SSungchun Kang 	.vidioc_querybuf		= gsc_m2m_querybuf,
5615d718338SSungchun Kang 	.vidioc_qbuf			= gsc_m2m_qbuf,
5625d718338SSungchun Kang 	.vidioc_dqbuf			= gsc_m2m_dqbuf,
5635d718338SSungchun Kang 	.vidioc_streamon		= gsc_m2m_streamon,
5645d718338SSungchun Kang 	.vidioc_streamoff		= gsc_m2m_streamoff,
5655d718338SSungchun Kang 	.vidioc_g_selection		= gsc_m2m_g_selection,
5665d718338SSungchun Kang 	.vidioc_s_selection		= gsc_m2m_s_selection
5675d718338SSungchun Kang };
5685d718338SSungchun Kang 
queue_init(void * priv,struct vb2_queue * src_vq,struct vb2_queue * dst_vq)5695d718338SSungchun Kang static int queue_init(void *priv, struct vb2_queue *src_vq,
5705d718338SSungchun Kang 			struct vb2_queue *dst_vq)
5715d718338SSungchun Kang {
5725d718338SSungchun Kang 	struct gsc_ctx *ctx = priv;
5735d718338SSungchun Kang 	int ret;
5745d718338SSungchun Kang 
5755d718338SSungchun Kang 	memset(src_vq, 0, sizeof(*src_vq));
5765d718338SSungchun Kang 	src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
577371a664eSShaik Ameer Basha 	src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
5785d718338SSungchun Kang 	src_vq->drv_priv = ctx;
5795d718338SSungchun Kang 	src_vq->ops = &gsc_m2m_qops;
5805d718338SSungchun Kang 	src_vq->mem_ops = &vb2_dma_contig_memops;
5815d718338SSungchun Kang 	src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
582ade48681SSakari Ailus 	src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
5830637f054SPrabhakar Lad 	src_vq->lock = &ctx->gsc_dev->lock;
584c781e4a5SHans Verkuil 	src_vq->dev = &ctx->gsc_dev->pdev->dev;
5855d718338SSungchun Kang 
5865d718338SSungchun Kang 	ret = vb2_queue_init(src_vq);
5875d718338SSungchun Kang 	if (ret)
5885d718338SSungchun Kang 		return ret;
5895d718338SSungchun Kang 
5905d718338SSungchun Kang 	memset(dst_vq, 0, sizeof(*dst_vq));
5915d718338SSungchun Kang 	dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
592371a664eSShaik Ameer Basha 	dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
5935d718338SSungchun Kang 	dst_vq->drv_priv = ctx;
5945d718338SSungchun Kang 	dst_vq->ops = &gsc_m2m_qops;
5955d718338SSungchun Kang 	dst_vq->mem_ops = &vb2_dma_contig_memops;
5965d718338SSungchun Kang 	dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
597ade48681SSakari Ailus 	dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
5980637f054SPrabhakar Lad 	dst_vq->lock = &ctx->gsc_dev->lock;
599c781e4a5SHans Verkuil 	dst_vq->dev = &ctx->gsc_dev->pdev->dev;
6005d718338SSungchun Kang 
6015d718338SSungchun Kang 	return vb2_queue_init(dst_vq);
6025d718338SSungchun Kang }
6035d718338SSungchun Kang 
gsc_m2m_open(struct file * file)6045d718338SSungchun Kang static int gsc_m2m_open(struct file *file)
6055d718338SSungchun Kang {
6065d718338SSungchun Kang 	struct gsc_dev *gsc = video_drvdata(file);
6075d718338SSungchun Kang 	struct gsc_ctx *ctx = NULL;
6085d718338SSungchun Kang 	int ret;
6095d718338SSungchun Kang 
6105d718338SSungchun Kang 	pr_debug("pid: %d, state: 0x%lx", task_pid_nr(current), gsc->state);
6115d718338SSungchun Kang 
6125d718338SSungchun Kang 	if (mutex_lock_interruptible(&gsc->lock))
6135d718338SSungchun Kang 		return -ERESTARTSYS;
6145d718338SSungchun Kang 
6155d718338SSungchun Kang 	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
6165d718338SSungchun Kang 	if (!ctx) {
6175d718338SSungchun Kang 		ret = -ENOMEM;
6185d718338SSungchun Kang 		goto unlock;
6195d718338SSungchun Kang 	}
6205d718338SSungchun Kang 
6215d718338SSungchun Kang 	v4l2_fh_init(&ctx->fh, gsc->m2m.vfd);
6225d718338SSungchun Kang 	ret = gsc_ctrls_create(ctx);
6235d718338SSungchun Kang 	if (ret)
6245d718338SSungchun Kang 		goto error_fh;
6255d718338SSungchun Kang 
6265d718338SSungchun Kang 	/* Use separate control handler per file handle */
6275d718338SSungchun Kang 	ctx->fh.ctrl_handler = &ctx->ctrl_handler;
6285d718338SSungchun Kang 	file->private_data = &ctx->fh;
6295d718338SSungchun Kang 	v4l2_fh_add(&ctx->fh);
6305d718338SSungchun Kang 
6315d718338SSungchun Kang 	ctx->gsc_dev = gsc;
6325d718338SSungchun Kang 	/* Default color format */
6335d718338SSungchun Kang 	ctx->s_frame.fmt = get_format(0);
6345d718338SSungchun Kang 	ctx->d_frame.fmt = get_format(0);
6355d718338SSungchun Kang 	/* Setup the device context for mem2mem mode. */
6365d718338SSungchun Kang 	ctx->state = GSC_CTX_M2M;
6375d718338SSungchun Kang 	ctx->flags = 0;
6385d718338SSungchun Kang 	ctx->in_path = GSC_DMA;
6395d718338SSungchun Kang 	ctx->out_path = GSC_DMA;
6405d718338SSungchun Kang 
6415d718338SSungchun Kang 	ctx->m2m_ctx = v4l2_m2m_ctx_init(gsc->m2m.m2m_dev, ctx, queue_init);
6425d718338SSungchun Kang 	if (IS_ERR(ctx->m2m_ctx)) {
6435d718338SSungchun Kang 		pr_err("Failed to initialize m2m context");
6445d718338SSungchun Kang 		ret = PTR_ERR(ctx->m2m_ctx);
6455d718338SSungchun Kang 		goto error_ctrls;
6465d718338SSungchun Kang 	}
6475d718338SSungchun Kang 
6485d718338SSungchun Kang 	if (gsc->m2m.refcnt++ == 0)
6495d718338SSungchun Kang 		set_bit(ST_M2M_OPEN, &gsc->state);
6505d718338SSungchun Kang 
6515d718338SSungchun Kang 	pr_debug("gsc m2m driver is opened, ctx(0x%p)", ctx);
6525d718338SSungchun Kang 
6535d718338SSungchun Kang 	mutex_unlock(&gsc->lock);
6545d718338SSungchun Kang 	return 0;
6555d718338SSungchun Kang 
6565d718338SSungchun Kang error_ctrls:
6575d718338SSungchun Kang 	gsc_ctrls_delete(ctx);
6585d718338SSungchun Kang 	v4l2_fh_del(&ctx->fh);
6593a07a827SShailendra Verma error_fh:
6605d718338SSungchun Kang 	v4l2_fh_exit(&ctx->fh);
6615d718338SSungchun Kang 	kfree(ctx);
6625d718338SSungchun Kang unlock:
6635d718338SSungchun Kang 	mutex_unlock(&gsc->lock);
6645d718338SSungchun Kang 	return ret;
6655d718338SSungchun Kang }
6665d718338SSungchun Kang 
gsc_m2m_release(struct file * file)6675d718338SSungchun Kang static int gsc_m2m_release(struct file *file)
6685d718338SSungchun Kang {
6695d718338SSungchun Kang 	struct gsc_ctx *ctx = fh_to_ctx(file->private_data);
6705d718338SSungchun Kang 	struct gsc_dev *gsc = ctx->gsc_dev;
6715d718338SSungchun Kang 
6725d718338SSungchun Kang 	pr_debug("pid: %d, state: 0x%lx, refcnt= %d",
6735d718338SSungchun Kang 		task_pid_nr(current), gsc->state, gsc->m2m.refcnt);
6745d718338SSungchun Kang 
67598680180SSylwester Nawrocki 	mutex_lock(&gsc->lock);
6765d718338SSungchun Kang 
6775d718338SSungchun Kang 	v4l2_m2m_ctx_release(ctx->m2m_ctx);
6785d718338SSungchun Kang 	gsc_ctrls_delete(ctx);
6795d718338SSungchun Kang 	v4l2_fh_del(&ctx->fh);
6805d718338SSungchun Kang 	v4l2_fh_exit(&ctx->fh);
6815d718338SSungchun Kang 
6825d718338SSungchun Kang 	if (--gsc->m2m.refcnt <= 0)
6835d718338SSungchun Kang 		clear_bit(ST_M2M_OPEN, &gsc->state);
6845d718338SSungchun Kang 	kfree(ctx);
6855d718338SSungchun Kang 
6865d718338SSungchun Kang 	mutex_unlock(&gsc->lock);
6875d718338SSungchun Kang 	return 0;
6885d718338SSungchun Kang }
6895d718338SSungchun Kang 
gsc_m2m_poll(struct file * file,struct poll_table_struct * wait)690c23e0cb8SAl Viro static __poll_t gsc_m2m_poll(struct file *file,
6915d718338SSungchun Kang 					struct poll_table_struct *wait)
6925d718338SSungchun Kang {
6935d718338SSungchun Kang 	struct gsc_ctx *ctx = fh_to_ctx(file->private_data);
6945d718338SSungchun Kang 	struct gsc_dev *gsc = ctx->gsc_dev;
695c23e0cb8SAl Viro 	__poll_t ret;
6965d718338SSungchun Kang 
6975d718338SSungchun Kang 	if (mutex_lock_interruptible(&gsc->lock))
698541b647aSMauro Carvalho Chehab 		return EPOLLERR;
6995d718338SSungchun Kang 
7005d718338SSungchun Kang 	ret = v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
7015d718338SSungchun Kang 	mutex_unlock(&gsc->lock);
7025d718338SSungchun Kang 
7035d718338SSungchun Kang 	return ret;
7045d718338SSungchun Kang }
7055d718338SSungchun Kang 
gsc_m2m_mmap(struct file * file,struct vm_area_struct * vma)7065d718338SSungchun Kang static int gsc_m2m_mmap(struct file *file, struct vm_area_struct *vma)
7075d718338SSungchun Kang {
7085d718338SSungchun Kang 	struct gsc_ctx *ctx = fh_to_ctx(file->private_data);
7095d718338SSungchun Kang 	struct gsc_dev *gsc = ctx->gsc_dev;
7105d718338SSungchun Kang 	int ret;
7115d718338SSungchun Kang 
7125d718338SSungchun Kang 	if (mutex_lock_interruptible(&gsc->lock))
7135d718338SSungchun Kang 		return -ERESTARTSYS;
7145d718338SSungchun Kang 
7155d718338SSungchun Kang 	ret = v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
7165d718338SSungchun Kang 	mutex_unlock(&gsc->lock);
7175d718338SSungchun Kang 
7185d718338SSungchun Kang 	return ret;
7195d718338SSungchun Kang }
7205d718338SSungchun Kang 
7215d718338SSungchun Kang static const struct v4l2_file_operations gsc_m2m_fops = {
7225d718338SSungchun Kang 	.owner		= THIS_MODULE,
7235d718338SSungchun Kang 	.open		= gsc_m2m_open,
7245d718338SSungchun Kang 	.release	= gsc_m2m_release,
7255d718338SSungchun Kang 	.poll		= gsc_m2m_poll,
7265d718338SSungchun Kang 	.unlocked_ioctl	= video_ioctl2,
7275d718338SSungchun Kang 	.mmap		= gsc_m2m_mmap,
7285d718338SSungchun Kang };
7295d718338SSungchun Kang 
730729ce68cSJulia Lawall static const struct v4l2_m2m_ops gsc_m2m_ops = {
7315d718338SSungchun Kang 	.device_run	= gsc_m2m_device_run,
7325d718338SSungchun Kang 	.job_abort	= gsc_m2m_job_abort,
7335d718338SSungchun Kang };
7345d718338SSungchun Kang 
gsc_register_m2m_device(struct gsc_dev * gsc)7355d718338SSungchun Kang int gsc_register_m2m_device(struct gsc_dev *gsc)
7365d718338SSungchun Kang {
7375d718338SSungchun Kang 	struct platform_device *pdev;
7385d718338SSungchun Kang 	int ret;
7395d718338SSungchun Kang 
7405d718338SSungchun Kang 	if (!gsc)
7415d718338SSungchun Kang 		return -ENODEV;
7425d718338SSungchun Kang 
7435d718338SSungchun Kang 	pdev = gsc->pdev;
7445d718338SSungchun Kang 
7455d718338SSungchun Kang 	gsc->vdev.fops		= &gsc_m2m_fops;
7465d718338SSungchun Kang 	gsc->vdev.ioctl_ops	= &gsc_m2m_ioctl_ops;
7475d718338SSungchun Kang 	gsc->vdev.release	= video_device_release_empty;
7485d718338SSungchun Kang 	gsc->vdev.lock		= &gsc->lock;
74924fc681aSSylwester Nawrocki 	gsc->vdev.vfl_dir	= VFL_DIR_M2M;
750d0b1c313SArun Kumar K 	gsc->vdev.v4l2_dev	= &gsc->v4l2_dev;
7511ddc8a97SBoris Brezillon 	gsc->vdev.device_caps	= V4L2_CAP_STREAMING |
7521ddc8a97SBoris Brezillon 				  V4L2_CAP_VIDEO_M2M_MPLANE;
7535d718338SSungchun Kang 	snprintf(gsc->vdev.name, sizeof(gsc->vdev.name), "%s.%d:m2m",
7545d718338SSungchun Kang 					GSC_MODULE_NAME, gsc->id);
7555d718338SSungchun Kang 
7565d718338SSungchun Kang 	video_set_drvdata(&gsc->vdev, gsc);
7575d718338SSungchun Kang 
7585d718338SSungchun Kang 	gsc->m2m.vfd = &gsc->vdev;
7595d718338SSungchun Kang 	gsc->m2m.m2m_dev = v4l2_m2m_init(&gsc_m2m_ops);
7605d718338SSungchun Kang 	if (IS_ERR(gsc->m2m.m2m_dev)) {
7615d718338SSungchun Kang 		dev_err(&pdev->dev, "failed to initialize v4l2-m2m device\n");
762f4ca5030SJavier Martinez Canillas 		return PTR_ERR(gsc->m2m.m2m_dev);
7635d718338SSungchun Kang 	}
7645d718338SSungchun Kang 
76570cad449SHans Verkuil 	ret = video_register_device(&gsc->vdev, VFL_TYPE_VIDEO, -1);
7665d718338SSungchun Kang 	if (ret) {
7675d718338SSungchun Kang 		dev_err(&pdev->dev,
7685d718338SSungchun Kang 			 "%s(): failed to register video device\n", __func__);
769f4ca5030SJavier Martinez Canillas 		goto err_m2m_release;
7705d718338SSungchun Kang 	}
7715d718338SSungchun Kang 
7725d718338SSungchun Kang 	pr_debug("gsc m2m driver registered as /dev/video%d", gsc->vdev.num);
7735d718338SSungchun Kang 	return 0;
7745d718338SSungchun Kang 
775f4ca5030SJavier Martinez Canillas err_m2m_release:
7765d718338SSungchun Kang 	v4l2_m2m_release(gsc->m2m.m2m_dev);
7775d718338SSungchun Kang 
7785d718338SSungchun Kang 	return ret;
7795d718338SSungchun Kang }
7805d718338SSungchun Kang 
gsc_unregister_m2m_device(struct gsc_dev * gsc)7815d718338SSungchun Kang void gsc_unregister_m2m_device(struct gsc_dev *gsc)
7825d718338SSungchun Kang {
783115a16baSJavier Martinez Canillas 	if (gsc) {
7845d718338SSungchun Kang 		v4l2_m2m_release(gsc->m2m.m2m_dev);
785115a16baSJavier Martinez Canillas 		video_unregister_device(&gsc->vdev);
786115a16baSJavier Martinez Canillas 	}
7875d718338SSungchun Kang }
788