1*3bae07d4SMauro Carvalho Chehab // SPDX-License-Identifier: GPL-2.0-or-later
2*3bae07d4SMauro Carvalho Chehab /*
3*3bae07d4SMauro Carvalho Chehab  * Copyright (c) 2011 - 2012 Samsung Electronics Co., Ltd.
4*3bae07d4SMauro Carvalho Chehab  *		http://www.samsung.com
5*3bae07d4SMauro Carvalho Chehab  *
6*3bae07d4SMauro Carvalho Chehab  * Samsung EXYNOS5 SoC series G-Scaler driver
7*3bae07d4SMauro Carvalho Chehab  */
8*3bae07d4SMauro Carvalho Chehab 
9*3bae07d4SMauro Carvalho Chehab #include <linux/module.h>
10*3bae07d4SMauro Carvalho Chehab #include <linux/kernel.h>
11*3bae07d4SMauro Carvalho Chehab #include <linux/types.h>
12*3bae07d4SMauro Carvalho Chehab #include <linux/errno.h>
13*3bae07d4SMauro Carvalho Chehab #include <linux/bug.h>
14*3bae07d4SMauro Carvalho Chehab #include <linux/interrupt.h>
15*3bae07d4SMauro Carvalho Chehab #include <linux/workqueue.h>
16*3bae07d4SMauro Carvalho Chehab #include <linux/device.h>
17*3bae07d4SMauro Carvalho Chehab #include <linux/platform_device.h>
18*3bae07d4SMauro Carvalho Chehab #include <linux/list.h>
19*3bae07d4SMauro Carvalho Chehab #include <linux/io.h>
20*3bae07d4SMauro Carvalho Chehab #include <linux/slab.h>
21*3bae07d4SMauro Carvalho Chehab #include <linux/clk.h>
22*3bae07d4SMauro Carvalho Chehab 
23*3bae07d4SMauro Carvalho Chehab #include <media/v4l2-ioctl.h>
24*3bae07d4SMauro Carvalho Chehab 
25*3bae07d4SMauro Carvalho Chehab #include "gsc-core.h"
26*3bae07d4SMauro Carvalho Chehab 
gsc_m2m_ctx_stop_req(struct gsc_ctx * ctx)27*3bae07d4SMauro Carvalho Chehab static int gsc_m2m_ctx_stop_req(struct gsc_ctx *ctx)
28*3bae07d4SMauro Carvalho Chehab {
29*3bae07d4SMauro Carvalho Chehab 	struct gsc_ctx *curr_ctx;
30*3bae07d4SMauro Carvalho Chehab 	struct gsc_dev *gsc = ctx->gsc_dev;
31*3bae07d4SMauro Carvalho Chehab 	int ret;
32*3bae07d4SMauro Carvalho Chehab 
33*3bae07d4SMauro Carvalho Chehab 	curr_ctx = v4l2_m2m_get_curr_priv(gsc->m2m.m2m_dev);
34*3bae07d4SMauro Carvalho Chehab 	if (!gsc_m2m_pending(gsc) || (curr_ctx != ctx))
35*3bae07d4SMauro Carvalho Chehab 		return 0;
36*3bae07d4SMauro Carvalho Chehab 
37*3bae07d4SMauro Carvalho Chehab 	gsc_ctx_state_lock_set(GSC_CTX_STOP_REQ, ctx);
38*3bae07d4SMauro Carvalho Chehab 	ret = wait_event_timeout(gsc->irq_queue,
39*3bae07d4SMauro Carvalho Chehab 			!gsc_ctx_state_is_set(GSC_CTX_STOP_REQ, ctx),
40*3bae07d4SMauro Carvalho Chehab 			GSC_SHUTDOWN_TIMEOUT);
41*3bae07d4SMauro Carvalho Chehab 
42*3bae07d4SMauro Carvalho Chehab 	return ret == 0 ? -ETIMEDOUT : ret;
43*3bae07d4SMauro Carvalho Chehab }
44*3bae07d4SMauro Carvalho Chehab 
__gsc_m2m_job_abort(struct gsc_ctx * ctx)45*3bae07d4SMauro Carvalho Chehab static void __gsc_m2m_job_abort(struct gsc_ctx *ctx)
46*3bae07d4SMauro Carvalho Chehab {
47*3bae07d4SMauro Carvalho Chehab 	int ret;
48*3bae07d4SMauro Carvalho Chehab 
49*3bae07d4SMauro Carvalho Chehab 	ret = gsc_m2m_ctx_stop_req(ctx);
50*3bae07d4SMauro Carvalho Chehab 	if ((ret == -ETIMEDOUT) || (ctx->state & GSC_CTX_ABORT)) {
51*3bae07d4SMauro Carvalho Chehab 		gsc_ctx_state_lock_clear(GSC_CTX_STOP_REQ | GSC_CTX_ABORT, ctx);
52*3bae07d4SMauro Carvalho Chehab 		gsc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR);
53*3bae07d4SMauro Carvalho Chehab 	}
54*3bae07d4SMauro Carvalho Chehab }
55*3bae07d4SMauro Carvalho Chehab 
gsc_m2m_start_streaming(struct vb2_queue * q,unsigned int count)56*3bae07d4SMauro Carvalho Chehab static int gsc_m2m_start_streaming(struct vb2_queue *q, unsigned int count)
57*3bae07d4SMauro Carvalho Chehab {
58*3bae07d4SMauro Carvalho Chehab 	struct gsc_ctx *ctx = q->drv_priv;
59*3bae07d4SMauro Carvalho Chehab 
60*3bae07d4SMauro Carvalho Chehab 	return pm_runtime_resume_and_get(&ctx->gsc_dev->pdev->dev);
61*3bae07d4SMauro Carvalho Chehab }
62*3bae07d4SMauro Carvalho Chehab 
__gsc_m2m_cleanup_queue(struct gsc_ctx * ctx)63*3bae07d4SMauro Carvalho Chehab static void __gsc_m2m_cleanup_queue(struct gsc_ctx *ctx)
64*3bae07d4SMauro Carvalho Chehab {
65*3bae07d4SMauro Carvalho Chehab 	struct vb2_v4l2_buffer *src_vb, *dst_vb;
66*3bae07d4SMauro Carvalho Chehab 
67*3bae07d4SMauro Carvalho Chehab 	while (v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) > 0) {
68*3bae07d4SMauro Carvalho Chehab 		src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
69*3bae07d4SMauro Carvalho Chehab 		v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_ERROR);
70*3bae07d4SMauro Carvalho Chehab 	}
71*3bae07d4SMauro Carvalho Chehab 
72*3bae07d4SMauro Carvalho Chehab 	while (v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx) > 0) {
73*3bae07d4SMauro Carvalho Chehab 		dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
74*3bae07d4SMauro Carvalho Chehab 		v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_ERROR);
75*3bae07d4SMauro Carvalho Chehab 	}
76*3bae07d4SMauro Carvalho Chehab }
77*3bae07d4SMauro Carvalho Chehab 
gsc_m2m_stop_streaming(struct vb2_queue * q)78*3bae07d4SMauro Carvalho Chehab static void gsc_m2m_stop_streaming(struct vb2_queue *q)
79*3bae07d4SMauro Carvalho Chehab {
80*3bae07d4SMauro Carvalho Chehab 	struct gsc_ctx *ctx = q->drv_priv;
81*3bae07d4SMauro Carvalho Chehab 
82*3bae07d4SMauro Carvalho Chehab 	__gsc_m2m_job_abort(ctx);
83*3bae07d4SMauro Carvalho Chehab 
84*3bae07d4SMauro Carvalho Chehab 	__gsc_m2m_cleanup_queue(ctx);
85*3bae07d4SMauro Carvalho Chehab 
86*3bae07d4SMauro Carvalho Chehab 	pm_runtime_put(&ctx->gsc_dev->pdev->dev);
87*3bae07d4SMauro Carvalho Chehab }
88*3bae07d4SMauro Carvalho Chehab 
gsc_m2m_job_finish(struct gsc_ctx * ctx,int vb_state)89*3bae07d4SMauro Carvalho Chehab void gsc_m2m_job_finish(struct gsc_ctx *ctx, int vb_state)
90*3bae07d4SMauro Carvalho Chehab {
91*3bae07d4SMauro Carvalho Chehab 	struct vb2_v4l2_buffer *src_vb, *dst_vb;
92*3bae07d4SMauro Carvalho Chehab 
93*3bae07d4SMauro Carvalho Chehab 	if (!ctx || !ctx->m2m_ctx)
94*3bae07d4SMauro Carvalho Chehab 		return;
95*3bae07d4SMauro Carvalho Chehab 
96*3bae07d4SMauro Carvalho Chehab 	src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
97*3bae07d4SMauro Carvalho Chehab 	dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
98*3bae07d4SMauro Carvalho Chehab 
99*3bae07d4SMauro Carvalho Chehab 	if (src_vb && dst_vb) {
100*3bae07d4SMauro Carvalho Chehab 		dst_vb->vb2_buf.timestamp = src_vb->vb2_buf.timestamp;
101*3bae07d4SMauro Carvalho Chehab 		dst_vb->timecode = src_vb->timecode;
102*3bae07d4SMauro Carvalho Chehab 		dst_vb->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
103*3bae07d4SMauro Carvalho Chehab 		dst_vb->flags |=
104*3bae07d4SMauro Carvalho Chehab 			src_vb->flags
105*3bae07d4SMauro Carvalho Chehab 			& V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
106*3bae07d4SMauro Carvalho Chehab 
107*3bae07d4SMauro Carvalho Chehab 		v4l2_m2m_buf_done(src_vb, vb_state);
108*3bae07d4SMauro Carvalho Chehab 		v4l2_m2m_buf_done(dst_vb, vb_state);
109*3bae07d4SMauro Carvalho Chehab 
110*3bae07d4SMauro Carvalho Chehab 		v4l2_m2m_job_finish(ctx->gsc_dev->m2m.m2m_dev,
111*3bae07d4SMauro Carvalho Chehab 				    ctx->m2m_ctx);
112*3bae07d4SMauro Carvalho Chehab 	}
113*3bae07d4SMauro Carvalho Chehab }
114*3bae07d4SMauro Carvalho Chehab 
gsc_m2m_job_abort(void * priv)115*3bae07d4SMauro Carvalho Chehab static void gsc_m2m_job_abort(void *priv)
116*3bae07d4SMauro Carvalho Chehab {
117*3bae07d4SMauro Carvalho Chehab 	__gsc_m2m_job_abort((struct gsc_ctx *)priv);
118*3bae07d4SMauro Carvalho Chehab }
119*3bae07d4SMauro Carvalho Chehab 
gsc_get_bufs(struct gsc_ctx * ctx)120*3bae07d4SMauro Carvalho Chehab static int gsc_get_bufs(struct gsc_ctx *ctx)
121*3bae07d4SMauro Carvalho Chehab {
122*3bae07d4SMauro Carvalho Chehab 	struct gsc_frame *s_frame, *d_frame;
123*3bae07d4SMauro Carvalho Chehab 	struct vb2_v4l2_buffer *src_vb, *dst_vb;
124*3bae07d4SMauro Carvalho Chehab 	int ret;
125*3bae07d4SMauro Carvalho Chehab 
126*3bae07d4SMauro Carvalho Chehab 	s_frame = &ctx->s_frame;
127*3bae07d4SMauro Carvalho Chehab 	d_frame = &ctx->d_frame;
128*3bae07d4SMauro Carvalho Chehab 
129*3bae07d4SMauro Carvalho Chehab 	src_vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
130*3bae07d4SMauro Carvalho Chehab 	ret = gsc_prepare_addr(ctx, &src_vb->vb2_buf, s_frame, &s_frame->addr);
131*3bae07d4SMauro Carvalho Chehab 	if (ret)
132*3bae07d4SMauro Carvalho Chehab 		return ret;
133*3bae07d4SMauro Carvalho Chehab 
134*3bae07d4SMauro Carvalho Chehab 	dst_vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
135*3bae07d4SMauro Carvalho Chehab 	ret = gsc_prepare_addr(ctx, &dst_vb->vb2_buf, d_frame, &d_frame->addr);
136*3bae07d4SMauro Carvalho Chehab 	if (ret)
137*3bae07d4SMauro Carvalho Chehab 		return ret;
138*3bae07d4SMauro Carvalho Chehab 
139*3bae07d4SMauro Carvalho Chehab 	dst_vb->vb2_buf.timestamp = src_vb->vb2_buf.timestamp;
140*3bae07d4SMauro Carvalho Chehab 
141*3bae07d4SMauro Carvalho Chehab 	return 0;
142*3bae07d4SMauro Carvalho Chehab }
143*3bae07d4SMauro Carvalho Chehab 
gsc_m2m_device_run(void * priv)144*3bae07d4SMauro Carvalho Chehab static void gsc_m2m_device_run(void *priv)
145*3bae07d4SMauro Carvalho Chehab {
146*3bae07d4SMauro Carvalho Chehab 	struct gsc_ctx *ctx = priv;
147*3bae07d4SMauro Carvalho Chehab 	struct gsc_dev *gsc;
148*3bae07d4SMauro Carvalho Chehab 	unsigned long flags;
149*3bae07d4SMauro Carvalho Chehab 	int ret;
150*3bae07d4SMauro Carvalho Chehab 	bool is_set = false;
151*3bae07d4SMauro Carvalho Chehab 
152*3bae07d4SMauro Carvalho Chehab 	if (WARN(!ctx, "null hardware context\n"))
153*3bae07d4SMauro Carvalho Chehab 		return;
154*3bae07d4SMauro Carvalho Chehab 
155*3bae07d4SMauro Carvalho Chehab 	gsc = ctx->gsc_dev;
156*3bae07d4SMauro Carvalho Chehab 	spin_lock_irqsave(&gsc->slock, flags);
157*3bae07d4SMauro Carvalho Chehab 
158*3bae07d4SMauro Carvalho Chehab 	set_bit(ST_M2M_PEND, &gsc->state);
159*3bae07d4SMauro Carvalho Chehab 
160*3bae07d4SMauro Carvalho Chehab 	/* Reconfigure hardware if the context has changed. */
161*3bae07d4SMauro Carvalho Chehab 	if (gsc->m2m.ctx != ctx) {
162*3bae07d4SMauro Carvalho Chehab 		pr_debug("gsc->m2m.ctx = 0x%p, current_ctx = 0x%p",
163*3bae07d4SMauro Carvalho Chehab 				gsc->m2m.ctx, ctx);
164*3bae07d4SMauro Carvalho Chehab 		ctx->state |= GSC_PARAMS;
165*3bae07d4SMauro Carvalho Chehab 		gsc->m2m.ctx = ctx;
166*3bae07d4SMauro Carvalho Chehab 	}
167*3bae07d4SMauro Carvalho Chehab 
168*3bae07d4SMauro Carvalho Chehab 	is_set = ctx->state & GSC_CTX_STOP_REQ;
169*3bae07d4SMauro Carvalho Chehab 	if (is_set) {
170*3bae07d4SMauro Carvalho Chehab 		ctx->state &= ~GSC_CTX_STOP_REQ;
171*3bae07d4SMauro Carvalho Chehab 		ctx->state |= GSC_CTX_ABORT;
172*3bae07d4SMauro Carvalho Chehab 		wake_up(&gsc->irq_queue);
173*3bae07d4SMauro Carvalho Chehab 		goto put_device;
174*3bae07d4SMauro Carvalho Chehab 	}
175*3bae07d4SMauro Carvalho Chehab 
176*3bae07d4SMauro Carvalho Chehab 	ret = gsc_get_bufs(ctx);
177*3bae07d4SMauro Carvalho Chehab 	if (ret) {
178*3bae07d4SMauro Carvalho Chehab 		pr_err("Wrong address");
179*3bae07d4SMauro Carvalho Chehab 		goto put_device;
180*3bae07d4SMauro Carvalho Chehab 	}
181*3bae07d4SMauro Carvalho Chehab 
182*3bae07d4SMauro Carvalho Chehab 	gsc_set_prefbuf(gsc, &ctx->s_frame);
183*3bae07d4SMauro Carvalho Chehab 	gsc_hw_set_input_addr(gsc, &ctx->s_frame.addr, GSC_M2M_BUF_NUM);
184*3bae07d4SMauro Carvalho Chehab 	gsc_hw_set_output_addr(gsc, &ctx->d_frame.addr, GSC_M2M_BUF_NUM);
185*3bae07d4SMauro Carvalho Chehab 
186*3bae07d4SMauro Carvalho Chehab 	if (ctx->state & GSC_PARAMS) {
187*3bae07d4SMauro Carvalho Chehab 		gsc_hw_set_input_buf_masking(gsc, GSC_M2M_BUF_NUM, false);
188*3bae07d4SMauro Carvalho Chehab 		gsc_hw_set_output_buf_masking(gsc, GSC_M2M_BUF_NUM, false);
189*3bae07d4SMauro Carvalho Chehab 		gsc_hw_set_frm_done_irq_mask(gsc, false);
190*3bae07d4SMauro Carvalho Chehab 		gsc_hw_set_gsc_irq_enable(gsc, true);
191*3bae07d4SMauro Carvalho Chehab 
192*3bae07d4SMauro Carvalho Chehab 		if (gsc_set_scaler_info(ctx)) {
193*3bae07d4SMauro Carvalho Chehab 			pr_err("Scaler setup error");
194*3bae07d4SMauro Carvalho Chehab 			goto put_device;
195*3bae07d4SMauro Carvalho Chehab 		}
196*3bae07d4SMauro Carvalho Chehab 
197*3bae07d4SMauro Carvalho Chehab 		gsc_hw_set_input_path(ctx);
198*3bae07d4SMauro Carvalho Chehab 		gsc_hw_set_in_size(ctx);
199*3bae07d4SMauro Carvalho Chehab 		gsc_hw_set_in_image_format(ctx);
200*3bae07d4SMauro Carvalho Chehab 
201*3bae07d4SMauro Carvalho Chehab 		gsc_hw_set_output_path(ctx);
202*3bae07d4SMauro Carvalho Chehab 		gsc_hw_set_out_size(ctx);
203*3bae07d4SMauro Carvalho Chehab 		gsc_hw_set_out_image_format(ctx);
204*3bae07d4SMauro Carvalho Chehab 
205*3bae07d4SMauro Carvalho Chehab 		gsc_hw_set_prescaler(ctx);
206*3bae07d4SMauro Carvalho Chehab 		gsc_hw_set_mainscaler(ctx);
207*3bae07d4SMauro Carvalho Chehab 		gsc_hw_set_rotation(ctx);
208*3bae07d4SMauro Carvalho Chehab 		gsc_hw_set_global_alpha(ctx);
209*3bae07d4SMauro Carvalho Chehab 	}
210*3bae07d4SMauro Carvalho Chehab 
211*3bae07d4SMauro Carvalho Chehab 	/* update shadow registers */
212*3bae07d4SMauro Carvalho Chehab 	gsc_hw_set_sfr_update(ctx);
213*3bae07d4SMauro Carvalho Chehab 
214*3bae07d4SMauro Carvalho Chehab 	ctx->state &= ~GSC_PARAMS;
215*3bae07d4SMauro Carvalho Chehab 	gsc_hw_enable_control(gsc, true);
216*3bae07d4SMauro Carvalho Chehab 
217*3bae07d4SMauro Carvalho Chehab 	spin_unlock_irqrestore(&gsc->slock, flags);
218*3bae07d4SMauro Carvalho Chehab 	return;
219*3bae07d4SMauro Carvalho Chehab 
220*3bae07d4SMauro Carvalho Chehab put_device:
221*3bae07d4SMauro Carvalho Chehab 	ctx->state &= ~GSC_PARAMS;
222*3bae07d4SMauro Carvalho Chehab 	spin_unlock_irqrestore(&gsc->slock, flags);
223*3bae07d4SMauro Carvalho Chehab }
224*3bae07d4SMauro Carvalho Chehab 
gsc_m2m_queue_setup(struct vb2_queue * vq,unsigned int * num_buffers,unsigned int * num_planes,unsigned int sizes[],struct device * alloc_devs[])225*3bae07d4SMauro Carvalho Chehab static int gsc_m2m_queue_setup(struct vb2_queue *vq,
226*3bae07d4SMauro Carvalho Chehab 			unsigned int *num_buffers, unsigned int *num_planes,
227*3bae07d4SMauro Carvalho Chehab 			unsigned int sizes[], struct device *alloc_devs[])
228*3bae07d4SMauro Carvalho Chehab {
229*3bae07d4SMauro Carvalho Chehab 	struct gsc_ctx *ctx = vb2_get_drv_priv(vq);
230*3bae07d4SMauro Carvalho Chehab 	struct gsc_frame *frame;
231*3bae07d4SMauro Carvalho Chehab 	int i;
232*3bae07d4SMauro Carvalho Chehab 
233*3bae07d4SMauro Carvalho Chehab 	frame = ctx_get_frame(ctx, vq->type);
234*3bae07d4SMauro Carvalho Chehab 	if (IS_ERR(frame))
235*3bae07d4SMauro Carvalho Chehab 		return PTR_ERR(frame);
236*3bae07d4SMauro Carvalho Chehab 
237*3bae07d4SMauro Carvalho Chehab 	if (!frame->fmt)
238*3bae07d4SMauro Carvalho Chehab 		return -EINVAL;
239*3bae07d4SMauro Carvalho Chehab 
240*3bae07d4SMauro Carvalho Chehab 	*num_planes = frame->fmt->num_planes;
241*3bae07d4SMauro Carvalho Chehab 	for (i = 0; i < frame->fmt->num_planes; i++)
242*3bae07d4SMauro Carvalho Chehab 		sizes[i] = frame->payload[i];
243*3bae07d4SMauro Carvalho Chehab 	return 0;
244*3bae07d4SMauro Carvalho Chehab }
245*3bae07d4SMauro Carvalho Chehab 
gsc_m2m_buf_prepare(struct vb2_buffer * vb)246*3bae07d4SMauro Carvalho Chehab static int gsc_m2m_buf_prepare(struct vb2_buffer *vb)
247*3bae07d4SMauro Carvalho Chehab {
248*3bae07d4SMauro Carvalho Chehab 	struct gsc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
249*3bae07d4SMauro Carvalho Chehab 	struct gsc_frame *frame;
250*3bae07d4SMauro Carvalho Chehab 	int i;
251*3bae07d4SMauro Carvalho Chehab 
252*3bae07d4SMauro Carvalho Chehab 	frame = ctx_get_frame(ctx, vb->vb2_queue->type);
253*3bae07d4SMauro Carvalho Chehab 	if (IS_ERR(frame))
254*3bae07d4SMauro Carvalho Chehab 		return PTR_ERR(frame);
255*3bae07d4SMauro Carvalho Chehab 
256*3bae07d4SMauro Carvalho Chehab 	if (V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type)) {
257*3bae07d4SMauro Carvalho Chehab 		for (i = 0; i < frame->fmt->num_planes; i++)
258*3bae07d4SMauro Carvalho Chehab 			vb2_set_plane_payload(vb, i, frame->payload[i]);
259*3bae07d4SMauro Carvalho Chehab 	}
260*3bae07d4SMauro Carvalho Chehab 
261*3bae07d4SMauro Carvalho Chehab 	return 0;
262*3bae07d4SMauro Carvalho Chehab }
263*3bae07d4SMauro Carvalho Chehab 
gsc_m2m_buf_queue(struct vb2_buffer * vb)264*3bae07d4SMauro Carvalho Chehab static void gsc_m2m_buf_queue(struct vb2_buffer *vb)
265*3bae07d4SMauro Carvalho Chehab {
266*3bae07d4SMauro Carvalho Chehab 	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
267*3bae07d4SMauro Carvalho Chehab 	struct gsc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
268*3bae07d4SMauro Carvalho Chehab 
269*3bae07d4SMauro Carvalho Chehab 	pr_debug("ctx: %p, ctx->state: 0x%x", ctx, ctx->state);
270*3bae07d4SMauro Carvalho Chehab 
271*3bae07d4SMauro Carvalho Chehab 	if (ctx->m2m_ctx)
272*3bae07d4SMauro Carvalho Chehab 		v4l2_m2m_buf_queue(ctx->m2m_ctx, vbuf);
273*3bae07d4SMauro Carvalho Chehab }
274*3bae07d4SMauro Carvalho Chehab 
275*3bae07d4SMauro Carvalho Chehab static const struct vb2_ops gsc_m2m_qops = {
276*3bae07d4SMauro Carvalho Chehab 	.queue_setup	 = gsc_m2m_queue_setup,
277*3bae07d4SMauro Carvalho Chehab 	.buf_prepare	 = gsc_m2m_buf_prepare,
278*3bae07d4SMauro Carvalho Chehab 	.buf_queue	 = gsc_m2m_buf_queue,
279*3bae07d4SMauro Carvalho Chehab 	.wait_prepare	 = vb2_ops_wait_prepare,
280*3bae07d4SMauro Carvalho Chehab 	.wait_finish	 = vb2_ops_wait_finish,
281*3bae07d4SMauro Carvalho Chehab 	.stop_streaming	 = gsc_m2m_stop_streaming,
282*3bae07d4SMauro Carvalho Chehab 	.start_streaming = gsc_m2m_start_streaming,
283*3bae07d4SMauro Carvalho Chehab };
284*3bae07d4SMauro Carvalho Chehab 
gsc_m2m_querycap(struct file * file,void * fh,struct v4l2_capability * cap)285*3bae07d4SMauro Carvalho Chehab static int gsc_m2m_querycap(struct file *file, void *fh,
286*3bae07d4SMauro Carvalho Chehab 			   struct v4l2_capability *cap)
287*3bae07d4SMauro Carvalho Chehab {
288*3bae07d4SMauro Carvalho Chehab 	strscpy(cap->driver, GSC_MODULE_NAME, sizeof(cap->driver));
289*3bae07d4SMauro Carvalho Chehab 	strscpy(cap->card, GSC_MODULE_NAME " gscaler", sizeof(cap->card));
290*3bae07d4SMauro Carvalho Chehab 	return 0;
291*3bae07d4SMauro Carvalho Chehab }
292*3bae07d4SMauro Carvalho Chehab 
gsc_m2m_enum_fmt(struct file * file,void * priv,struct v4l2_fmtdesc * f)293*3bae07d4SMauro Carvalho Chehab static int gsc_m2m_enum_fmt(struct file *file, void *priv,
294*3bae07d4SMauro Carvalho Chehab 			    struct v4l2_fmtdesc *f)
295*3bae07d4SMauro Carvalho Chehab {
296*3bae07d4SMauro Carvalho Chehab 	return gsc_enum_fmt(f);
297*3bae07d4SMauro Carvalho Chehab }
298*3bae07d4SMauro Carvalho Chehab 
gsc_m2m_g_fmt_mplane(struct file * file,void * fh,struct v4l2_format * f)299*3bae07d4SMauro Carvalho Chehab static int gsc_m2m_g_fmt_mplane(struct file *file, void *fh,
300*3bae07d4SMauro Carvalho Chehab 			     struct v4l2_format *f)
301*3bae07d4SMauro Carvalho Chehab {
302*3bae07d4SMauro Carvalho Chehab 	struct gsc_ctx *ctx = fh_to_ctx(fh);
303*3bae07d4SMauro Carvalho Chehab 
304*3bae07d4SMauro Carvalho Chehab 	return gsc_g_fmt_mplane(ctx, f);
305*3bae07d4SMauro Carvalho Chehab }
306*3bae07d4SMauro Carvalho Chehab 
gsc_m2m_try_fmt_mplane(struct file * file,void * fh,struct v4l2_format * f)307*3bae07d4SMauro Carvalho Chehab static int gsc_m2m_try_fmt_mplane(struct file *file, void *fh,
308*3bae07d4SMauro Carvalho Chehab 				  struct v4l2_format *f)
309*3bae07d4SMauro Carvalho Chehab {
310*3bae07d4SMauro Carvalho Chehab 	struct gsc_ctx *ctx = fh_to_ctx(fh);
311*3bae07d4SMauro Carvalho Chehab 
312*3bae07d4SMauro Carvalho Chehab 	return gsc_try_fmt_mplane(ctx, f);
313*3bae07d4SMauro Carvalho Chehab }
314*3bae07d4SMauro Carvalho Chehab 
gsc_m2m_s_fmt_mplane(struct file * file,void * fh,struct v4l2_format * f)315*3bae07d4SMauro Carvalho Chehab static int gsc_m2m_s_fmt_mplane(struct file *file, void *fh,
316*3bae07d4SMauro Carvalho Chehab 				 struct v4l2_format *f)
317*3bae07d4SMauro Carvalho Chehab {
318*3bae07d4SMauro Carvalho Chehab 	struct gsc_ctx *ctx = fh_to_ctx(fh);
319*3bae07d4SMauro Carvalho Chehab 	struct vb2_queue *vq;
320*3bae07d4SMauro Carvalho Chehab 	struct gsc_frame *frame;
321*3bae07d4SMauro Carvalho Chehab 	struct v4l2_pix_format_mplane *pix;
322*3bae07d4SMauro Carvalho Chehab 	int i, ret = 0;
323*3bae07d4SMauro Carvalho Chehab 
324*3bae07d4SMauro Carvalho Chehab 	ret = gsc_m2m_try_fmt_mplane(file, fh, f);
325*3bae07d4SMauro Carvalho Chehab 	if (ret)
326*3bae07d4SMauro Carvalho Chehab 		return ret;
327*3bae07d4SMauro Carvalho Chehab 
328*3bae07d4SMauro Carvalho Chehab 	vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
329*3bae07d4SMauro Carvalho Chehab 
330*3bae07d4SMauro Carvalho Chehab 	if (vb2_is_streaming(vq)) {
331*3bae07d4SMauro Carvalho Chehab 		pr_err("queue (%d) busy", f->type);
332*3bae07d4SMauro Carvalho Chehab 		return -EBUSY;
333*3bae07d4SMauro Carvalho Chehab 	}
334*3bae07d4SMauro Carvalho Chehab 
335*3bae07d4SMauro Carvalho Chehab 	if (V4L2_TYPE_IS_OUTPUT(f->type))
336*3bae07d4SMauro Carvalho Chehab 		frame = &ctx->s_frame;
337*3bae07d4SMauro Carvalho Chehab 	else
338*3bae07d4SMauro Carvalho Chehab 		frame = &ctx->d_frame;
339*3bae07d4SMauro Carvalho Chehab 
340*3bae07d4SMauro Carvalho Chehab 	pix = &f->fmt.pix_mp;
341*3bae07d4SMauro Carvalho Chehab 	frame->fmt = find_fmt(&pix->pixelformat, NULL, 0);
342*3bae07d4SMauro Carvalho Chehab 	frame->colorspace = pix->colorspace;
343*3bae07d4SMauro Carvalho Chehab 	if (!frame->fmt)
344*3bae07d4SMauro Carvalho Chehab 		return -EINVAL;
345*3bae07d4SMauro Carvalho Chehab 
346*3bae07d4SMauro Carvalho Chehab 	for (i = 0; i < frame->fmt->num_planes; i++)
347*3bae07d4SMauro Carvalho Chehab 		frame->payload[i] = pix->plane_fmt[i].sizeimage;
348*3bae07d4SMauro Carvalho Chehab 
349*3bae07d4SMauro Carvalho Chehab 	gsc_set_frame_size(frame, pix->width, pix->height);
350*3bae07d4SMauro Carvalho Chehab 
351*3bae07d4SMauro Carvalho Chehab 	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
352*3bae07d4SMauro Carvalho Chehab 		gsc_ctx_state_lock_set(GSC_PARAMS | GSC_DST_FMT, ctx);
353*3bae07d4SMauro Carvalho Chehab 	else
354*3bae07d4SMauro Carvalho Chehab 		gsc_ctx_state_lock_set(GSC_PARAMS | GSC_SRC_FMT, ctx);
355*3bae07d4SMauro Carvalho Chehab 
356*3bae07d4SMauro Carvalho Chehab 	pr_debug("f_w: %d, f_h: %d", frame->f_width, frame->f_height);
357*3bae07d4SMauro Carvalho Chehab 
358*3bae07d4SMauro Carvalho Chehab 	return 0;
359*3bae07d4SMauro Carvalho Chehab }
360*3bae07d4SMauro Carvalho Chehab 
gsc_m2m_reqbufs(struct file * file,void * fh,struct v4l2_requestbuffers * reqbufs)361*3bae07d4SMauro Carvalho Chehab static int gsc_m2m_reqbufs(struct file *file, void *fh,
362*3bae07d4SMauro Carvalho Chehab 			  struct v4l2_requestbuffers *reqbufs)
363*3bae07d4SMauro Carvalho Chehab {
364*3bae07d4SMauro Carvalho Chehab 	struct gsc_ctx *ctx = fh_to_ctx(fh);
365*3bae07d4SMauro Carvalho Chehab 	struct gsc_dev *gsc = ctx->gsc_dev;
366*3bae07d4SMauro Carvalho Chehab 	u32 max_cnt;
367*3bae07d4SMauro Carvalho Chehab 
368*3bae07d4SMauro Carvalho Chehab 	max_cnt = (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ?
369*3bae07d4SMauro Carvalho Chehab 		gsc->variant->in_buf_cnt : gsc->variant->out_buf_cnt;
370*3bae07d4SMauro Carvalho Chehab 	if (reqbufs->count > max_cnt)
371*3bae07d4SMauro Carvalho Chehab 		return -EINVAL;
372*3bae07d4SMauro Carvalho Chehab 
373*3bae07d4SMauro Carvalho Chehab 	return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
374*3bae07d4SMauro Carvalho Chehab }
375*3bae07d4SMauro Carvalho Chehab 
gsc_m2m_expbuf(struct file * file,void * fh,struct v4l2_exportbuffer * eb)376*3bae07d4SMauro Carvalho Chehab static int gsc_m2m_expbuf(struct file *file, void *fh,
377*3bae07d4SMauro Carvalho Chehab 				struct v4l2_exportbuffer *eb)
378*3bae07d4SMauro Carvalho Chehab {
379*3bae07d4SMauro Carvalho Chehab 	struct gsc_ctx *ctx = fh_to_ctx(fh);
380*3bae07d4SMauro Carvalho Chehab 	return v4l2_m2m_expbuf(file, ctx->m2m_ctx, eb);
381*3bae07d4SMauro Carvalho Chehab }
382*3bae07d4SMauro Carvalho Chehab 
gsc_m2m_querybuf(struct file * file,void * fh,struct v4l2_buffer * buf)383*3bae07d4SMauro Carvalho Chehab static int gsc_m2m_querybuf(struct file *file, void *fh,
384*3bae07d4SMauro Carvalho Chehab 					struct v4l2_buffer *buf)
385*3bae07d4SMauro Carvalho Chehab {
386*3bae07d4SMauro Carvalho Chehab 	struct gsc_ctx *ctx = fh_to_ctx(fh);
387*3bae07d4SMauro Carvalho Chehab 	return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
388*3bae07d4SMauro Carvalho Chehab }
389*3bae07d4SMauro Carvalho Chehab 
gsc_m2m_qbuf(struct file * file,void * fh,struct v4l2_buffer * buf)390*3bae07d4SMauro Carvalho Chehab static int gsc_m2m_qbuf(struct file *file, void *fh,
391*3bae07d4SMauro Carvalho Chehab 			  struct v4l2_buffer *buf)
392*3bae07d4SMauro Carvalho Chehab {
393*3bae07d4SMauro Carvalho Chehab 	struct gsc_ctx *ctx = fh_to_ctx(fh);
394*3bae07d4SMauro Carvalho Chehab 	return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
395*3bae07d4SMauro Carvalho Chehab }
396*3bae07d4SMauro Carvalho Chehab 
gsc_m2m_dqbuf(struct file * file,void * fh,struct v4l2_buffer * buf)397*3bae07d4SMauro Carvalho Chehab static int gsc_m2m_dqbuf(struct file *file, void *fh,
398*3bae07d4SMauro Carvalho Chehab 			   struct v4l2_buffer *buf)
399*3bae07d4SMauro Carvalho Chehab {
400*3bae07d4SMauro Carvalho Chehab 	struct gsc_ctx *ctx = fh_to_ctx(fh);
401*3bae07d4SMauro Carvalho Chehab 	return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
402*3bae07d4SMauro Carvalho Chehab }
403*3bae07d4SMauro Carvalho Chehab 
gsc_m2m_streamon(struct file * file,void * fh,enum v4l2_buf_type type)404*3bae07d4SMauro Carvalho Chehab static int gsc_m2m_streamon(struct file *file, void *fh,
405*3bae07d4SMauro Carvalho Chehab 			   enum v4l2_buf_type type)
406*3bae07d4SMauro Carvalho Chehab {
407*3bae07d4SMauro Carvalho Chehab 	struct gsc_ctx *ctx = fh_to_ctx(fh);
408*3bae07d4SMauro Carvalho Chehab 
409*3bae07d4SMauro Carvalho Chehab 	/* The source and target color format need to be set */
410*3bae07d4SMauro Carvalho Chehab 	if (V4L2_TYPE_IS_OUTPUT(type)) {
411*3bae07d4SMauro Carvalho Chehab 		if (!gsc_ctx_state_is_set(GSC_SRC_FMT, ctx))
412*3bae07d4SMauro Carvalho Chehab 			return -EINVAL;
413*3bae07d4SMauro Carvalho Chehab 	} else if (!gsc_ctx_state_is_set(GSC_DST_FMT, ctx)) {
414*3bae07d4SMauro Carvalho Chehab 		return -EINVAL;
415*3bae07d4SMauro Carvalho Chehab 	}
416*3bae07d4SMauro Carvalho Chehab 
417*3bae07d4SMauro Carvalho Chehab 	return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
418*3bae07d4SMauro Carvalho Chehab }
419*3bae07d4SMauro Carvalho Chehab 
gsc_m2m_streamoff(struct file * file,void * fh,enum v4l2_buf_type type)420*3bae07d4SMauro Carvalho Chehab static int gsc_m2m_streamoff(struct file *file, void *fh,
421*3bae07d4SMauro Carvalho Chehab 			    enum v4l2_buf_type type)
422*3bae07d4SMauro Carvalho Chehab {
423*3bae07d4SMauro Carvalho Chehab 	struct gsc_ctx *ctx = fh_to_ctx(fh);
424*3bae07d4SMauro Carvalho Chehab 	return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
425*3bae07d4SMauro Carvalho Chehab }
426*3bae07d4SMauro Carvalho Chehab 
427*3bae07d4SMauro Carvalho Chehab /* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */
is_rectangle_enclosed(struct v4l2_rect * a,struct v4l2_rect * b)428*3bae07d4SMauro Carvalho Chehab static int is_rectangle_enclosed(struct v4l2_rect *a, struct v4l2_rect *b)
429*3bae07d4SMauro Carvalho Chehab {
430*3bae07d4SMauro Carvalho Chehab 	if (a->left < b->left || a->top < b->top)
431*3bae07d4SMauro Carvalho Chehab 		return 0;
432*3bae07d4SMauro Carvalho Chehab 
433*3bae07d4SMauro Carvalho Chehab 	if (a->left + a->width > b->left + b->width)
434*3bae07d4SMauro Carvalho Chehab 		return 0;
435*3bae07d4SMauro Carvalho Chehab 
436*3bae07d4SMauro Carvalho Chehab 	if (a->top + a->height > b->top + b->height)
437*3bae07d4SMauro Carvalho Chehab 		return 0;
438*3bae07d4SMauro Carvalho Chehab 
439*3bae07d4SMauro Carvalho Chehab 	return 1;
440*3bae07d4SMauro Carvalho Chehab }
441*3bae07d4SMauro Carvalho Chehab 
gsc_m2m_g_selection(struct file * file,void * fh,struct v4l2_selection * s)442*3bae07d4SMauro Carvalho Chehab static int gsc_m2m_g_selection(struct file *file, void *fh,
443*3bae07d4SMauro Carvalho Chehab 			struct v4l2_selection *s)
444*3bae07d4SMauro Carvalho Chehab {
445*3bae07d4SMauro Carvalho Chehab 	struct gsc_frame *frame;
446*3bae07d4SMauro Carvalho Chehab 	struct gsc_ctx *ctx = fh_to_ctx(fh);
447*3bae07d4SMauro Carvalho Chehab 
448*3bae07d4SMauro Carvalho Chehab 	if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
449*3bae07d4SMauro Carvalho Chehab 	    (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT))
450*3bae07d4SMauro Carvalho Chehab 		return -EINVAL;
451*3bae07d4SMauro Carvalho Chehab 
452*3bae07d4SMauro Carvalho Chehab 	frame = ctx_get_frame(ctx, s->type);
453*3bae07d4SMauro Carvalho Chehab 	if (IS_ERR(frame))
454*3bae07d4SMauro Carvalho Chehab 		return PTR_ERR(frame);
455*3bae07d4SMauro Carvalho Chehab 
456*3bae07d4SMauro Carvalho Chehab 	switch (s->target) {
457*3bae07d4SMauro Carvalho Chehab 	case V4L2_SEL_TGT_COMPOSE_DEFAULT:
458*3bae07d4SMauro Carvalho Chehab 	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
459*3bae07d4SMauro Carvalho Chehab 	case V4L2_SEL_TGT_CROP_BOUNDS:
460*3bae07d4SMauro Carvalho Chehab 	case V4L2_SEL_TGT_CROP_DEFAULT:
461*3bae07d4SMauro Carvalho Chehab 		s->r.left = 0;
462*3bae07d4SMauro Carvalho Chehab 		s->r.top = 0;
463*3bae07d4SMauro Carvalho Chehab 		s->r.width = frame->f_width;
464*3bae07d4SMauro Carvalho Chehab 		s->r.height = frame->f_height;
465*3bae07d4SMauro Carvalho Chehab 		return 0;
466*3bae07d4SMauro Carvalho Chehab 
467*3bae07d4SMauro Carvalho Chehab 	case V4L2_SEL_TGT_COMPOSE:
468*3bae07d4SMauro Carvalho Chehab 	case V4L2_SEL_TGT_CROP:
469*3bae07d4SMauro Carvalho Chehab 		s->r.left = frame->crop.left;
470*3bae07d4SMauro Carvalho Chehab 		s->r.top = frame->crop.top;
471*3bae07d4SMauro Carvalho Chehab 		s->r.width = frame->crop.width;
472*3bae07d4SMauro Carvalho Chehab 		s->r.height = frame->crop.height;
473*3bae07d4SMauro Carvalho Chehab 		return 0;
474*3bae07d4SMauro Carvalho Chehab 	}
475*3bae07d4SMauro Carvalho Chehab 
476*3bae07d4SMauro Carvalho Chehab 	return -EINVAL;
477*3bae07d4SMauro Carvalho Chehab }
478*3bae07d4SMauro Carvalho Chehab 
gsc_m2m_s_selection(struct file * file,void * fh,struct v4l2_selection * s)479*3bae07d4SMauro Carvalho Chehab static int gsc_m2m_s_selection(struct file *file, void *fh,
480*3bae07d4SMauro Carvalho Chehab 				struct v4l2_selection *s)
481*3bae07d4SMauro Carvalho Chehab {
482*3bae07d4SMauro Carvalho Chehab 	struct gsc_frame *frame;
483*3bae07d4SMauro Carvalho Chehab 	struct gsc_ctx *ctx = fh_to_ctx(fh);
484*3bae07d4SMauro Carvalho Chehab 	struct gsc_variant *variant = ctx->gsc_dev->variant;
485*3bae07d4SMauro Carvalho Chehab 	struct v4l2_selection sel = *s;
486*3bae07d4SMauro Carvalho Chehab 	int ret;
487*3bae07d4SMauro Carvalho Chehab 
488*3bae07d4SMauro Carvalho Chehab 	if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
489*3bae07d4SMauro Carvalho Chehab 	    (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT))
490*3bae07d4SMauro Carvalho Chehab 		return -EINVAL;
491*3bae07d4SMauro Carvalho Chehab 
492*3bae07d4SMauro Carvalho Chehab 	ret = gsc_try_selection(ctx, &sel);
493*3bae07d4SMauro Carvalho Chehab 	if (ret)
494*3bae07d4SMauro Carvalho Chehab 		return ret;
495*3bae07d4SMauro Carvalho Chehab 
496*3bae07d4SMauro Carvalho Chehab 	if (s->flags & V4L2_SEL_FLAG_LE &&
497*3bae07d4SMauro Carvalho Chehab 	    !is_rectangle_enclosed(&sel.r, &s->r))
498*3bae07d4SMauro Carvalho Chehab 		return -ERANGE;
499*3bae07d4SMauro Carvalho Chehab 
500*3bae07d4SMauro Carvalho Chehab 	if (s->flags & V4L2_SEL_FLAG_GE &&
501*3bae07d4SMauro Carvalho Chehab 	    !is_rectangle_enclosed(&s->r, &sel.r))
502*3bae07d4SMauro Carvalho Chehab 		return -ERANGE;
503*3bae07d4SMauro Carvalho Chehab 
504*3bae07d4SMauro Carvalho Chehab 	s->r = sel.r;
505*3bae07d4SMauro Carvalho Chehab 
506*3bae07d4SMauro Carvalho Chehab 	switch (s->target) {
507*3bae07d4SMauro Carvalho Chehab 	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
508*3bae07d4SMauro Carvalho Chehab 	case V4L2_SEL_TGT_COMPOSE_DEFAULT:
509*3bae07d4SMauro Carvalho Chehab 	case V4L2_SEL_TGT_COMPOSE:
510*3bae07d4SMauro Carvalho Chehab 		frame = &ctx->s_frame;
511*3bae07d4SMauro Carvalho Chehab 		break;
512*3bae07d4SMauro Carvalho Chehab 
513*3bae07d4SMauro Carvalho Chehab 	case V4L2_SEL_TGT_CROP_BOUNDS:
514*3bae07d4SMauro Carvalho Chehab 	case V4L2_SEL_TGT_CROP:
515*3bae07d4SMauro Carvalho Chehab 	case V4L2_SEL_TGT_CROP_DEFAULT:
516*3bae07d4SMauro Carvalho Chehab 		frame = &ctx->d_frame;
517*3bae07d4SMauro Carvalho Chehab 		break;
518*3bae07d4SMauro Carvalho Chehab 
519*3bae07d4SMauro Carvalho Chehab 	default:
520*3bae07d4SMauro Carvalho Chehab 		return -EINVAL;
521*3bae07d4SMauro Carvalho Chehab 	}
522*3bae07d4SMauro Carvalho Chehab 
523*3bae07d4SMauro Carvalho Chehab 	/* Check to see if scaling ratio is within supported range */
524*3bae07d4SMauro Carvalho Chehab 	if (gsc_ctx_state_is_set(GSC_DST_FMT | GSC_SRC_FMT, ctx)) {
525*3bae07d4SMauro Carvalho Chehab 		if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
526*3bae07d4SMauro Carvalho Chehab 			ret = gsc_check_scaler_ratio(variant, sel.r.width,
527*3bae07d4SMauro Carvalho Chehab 				sel.r.height, ctx->d_frame.crop.width,
528*3bae07d4SMauro Carvalho Chehab 				ctx->d_frame.crop.height,
529*3bae07d4SMauro Carvalho Chehab 				ctx->gsc_ctrls.rotate->val, ctx->out_path);
530*3bae07d4SMauro Carvalho Chehab 		} else {
531*3bae07d4SMauro Carvalho Chehab 			ret = gsc_check_scaler_ratio(variant,
532*3bae07d4SMauro Carvalho Chehab 				ctx->s_frame.crop.width,
533*3bae07d4SMauro Carvalho Chehab 				ctx->s_frame.crop.height, sel.r.width,
534*3bae07d4SMauro Carvalho Chehab 				sel.r.height, ctx->gsc_ctrls.rotate->val,
535*3bae07d4SMauro Carvalho Chehab 				ctx->out_path);
536*3bae07d4SMauro Carvalho Chehab 		}
537*3bae07d4SMauro Carvalho Chehab 
538*3bae07d4SMauro Carvalho Chehab 		if (ret) {
539*3bae07d4SMauro Carvalho Chehab 			pr_err("Out of scaler range");
540*3bae07d4SMauro Carvalho Chehab 			return -EINVAL;
541*3bae07d4SMauro Carvalho Chehab 		}
542*3bae07d4SMauro Carvalho Chehab 	}
543*3bae07d4SMauro Carvalho Chehab 
544*3bae07d4SMauro Carvalho Chehab 	frame->crop = sel.r;
545*3bae07d4SMauro Carvalho Chehab 
546*3bae07d4SMauro Carvalho Chehab 	gsc_ctx_state_lock_set(GSC_PARAMS, ctx);
547*3bae07d4SMauro Carvalho Chehab 	return 0;
548*3bae07d4SMauro Carvalho Chehab }
549*3bae07d4SMauro Carvalho Chehab 
550*3bae07d4SMauro Carvalho Chehab static const struct v4l2_ioctl_ops gsc_m2m_ioctl_ops = {
551*3bae07d4SMauro Carvalho Chehab 	.vidioc_querycap		= gsc_m2m_querycap,
552*3bae07d4SMauro Carvalho Chehab 	.vidioc_enum_fmt_vid_cap	= gsc_m2m_enum_fmt,
553*3bae07d4SMauro Carvalho Chehab 	.vidioc_enum_fmt_vid_out	= gsc_m2m_enum_fmt,
554*3bae07d4SMauro Carvalho Chehab 	.vidioc_g_fmt_vid_cap_mplane	= gsc_m2m_g_fmt_mplane,
555*3bae07d4SMauro Carvalho Chehab 	.vidioc_g_fmt_vid_out_mplane	= gsc_m2m_g_fmt_mplane,
556*3bae07d4SMauro Carvalho Chehab 	.vidioc_try_fmt_vid_cap_mplane	= gsc_m2m_try_fmt_mplane,
557*3bae07d4SMauro Carvalho Chehab 	.vidioc_try_fmt_vid_out_mplane	= gsc_m2m_try_fmt_mplane,
558*3bae07d4SMauro Carvalho Chehab 	.vidioc_s_fmt_vid_cap_mplane	= gsc_m2m_s_fmt_mplane,
559*3bae07d4SMauro Carvalho Chehab 	.vidioc_s_fmt_vid_out_mplane	= gsc_m2m_s_fmt_mplane,
560*3bae07d4SMauro Carvalho Chehab 	.vidioc_reqbufs			= gsc_m2m_reqbufs,
561*3bae07d4SMauro Carvalho Chehab 	.vidioc_expbuf                  = gsc_m2m_expbuf,
562*3bae07d4SMauro Carvalho Chehab 	.vidioc_querybuf		= gsc_m2m_querybuf,
563*3bae07d4SMauro Carvalho Chehab 	.vidioc_qbuf			= gsc_m2m_qbuf,
564*3bae07d4SMauro Carvalho Chehab 	.vidioc_dqbuf			= gsc_m2m_dqbuf,
565*3bae07d4SMauro Carvalho Chehab 	.vidioc_streamon		= gsc_m2m_streamon,
566*3bae07d4SMauro Carvalho Chehab 	.vidioc_streamoff		= gsc_m2m_streamoff,
567*3bae07d4SMauro Carvalho Chehab 	.vidioc_g_selection		= gsc_m2m_g_selection,
568*3bae07d4SMauro Carvalho Chehab 	.vidioc_s_selection		= gsc_m2m_s_selection
569*3bae07d4SMauro Carvalho Chehab };
570*3bae07d4SMauro Carvalho Chehab 
queue_init(void * priv,struct vb2_queue * src_vq,struct vb2_queue * dst_vq)571*3bae07d4SMauro Carvalho Chehab static int queue_init(void *priv, struct vb2_queue *src_vq,
572*3bae07d4SMauro Carvalho Chehab 			struct vb2_queue *dst_vq)
573*3bae07d4SMauro Carvalho Chehab {
574*3bae07d4SMauro Carvalho Chehab 	struct gsc_ctx *ctx = priv;
575*3bae07d4SMauro Carvalho Chehab 	int ret;
576*3bae07d4SMauro Carvalho Chehab 
577*3bae07d4SMauro Carvalho Chehab 	memset(src_vq, 0, sizeof(*src_vq));
578*3bae07d4SMauro Carvalho Chehab 	src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
579*3bae07d4SMauro Carvalho Chehab 	src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
580*3bae07d4SMauro Carvalho Chehab 	src_vq->drv_priv = ctx;
581*3bae07d4SMauro Carvalho Chehab 	src_vq->ops = &gsc_m2m_qops;
582*3bae07d4SMauro Carvalho Chehab 	src_vq->mem_ops = &vb2_dma_contig_memops;
583*3bae07d4SMauro Carvalho Chehab 	src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
584*3bae07d4SMauro Carvalho Chehab 	src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
585*3bae07d4SMauro Carvalho Chehab 	src_vq->lock = &ctx->gsc_dev->lock;
586*3bae07d4SMauro Carvalho Chehab 	src_vq->dev = &ctx->gsc_dev->pdev->dev;
587*3bae07d4SMauro Carvalho Chehab 
588*3bae07d4SMauro Carvalho Chehab 	ret = vb2_queue_init(src_vq);
589*3bae07d4SMauro Carvalho Chehab 	if (ret)
590*3bae07d4SMauro Carvalho Chehab 		return ret;
591*3bae07d4SMauro Carvalho Chehab 
592*3bae07d4SMauro Carvalho Chehab 	memset(dst_vq, 0, sizeof(*dst_vq));
593*3bae07d4SMauro Carvalho Chehab 	dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
594*3bae07d4SMauro Carvalho Chehab 	dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
595*3bae07d4SMauro Carvalho Chehab 	dst_vq->drv_priv = ctx;
596*3bae07d4SMauro Carvalho Chehab 	dst_vq->ops = &gsc_m2m_qops;
597*3bae07d4SMauro Carvalho Chehab 	dst_vq->mem_ops = &vb2_dma_contig_memops;
598*3bae07d4SMauro Carvalho Chehab 	dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
599*3bae07d4SMauro Carvalho Chehab 	dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
600*3bae07d4SMauro Carvalho Chehab 	dst_vq->lock = &ctx->gsc_dev->lock;
601*3bae07d4SMauro Carvalho Chehab 	dst_vq->dev = &ctx->gsc_dev->pdev->dev;
602*3bae07d4SMauro Carvalho Chehab 
603*3bae07d4SMauro Carvalho Chehab 	return vb2_queue_init(dst_vq);
604*3bae07d4SMauro Carvalho Chehab }
605*3bae07d4SMauro Carvalho Chehab 
gsc_m2m_open(struct file * file)606*3bae07d4SMauro Carvalho Chehab static int gsc_m2m_open(struct file *file)
607*3bae07d4SMauro Carvalho Chehab {
608*3bae07d4SMauro Carvalho Chehab 	struct gsc_dev *gsc = video_drvdata(file);
609*3bae07d4SMauro Carvalho Chehab 	struct gsc_ctx *ctx = NULL;
610*3bae07d4SMauro Carvalho Chehab 	int ret;
611*3bae07d4SMauro Carvalho Chehab 
612*3bae07d4SMauro Carvalho Chehab 	pr_debug("pid: %d, state: 0x%lx", task_pid_nr(current), gsc->state);
613*3bae07d4SMauro Carvalho Chehab 
614*3bae07d4SMauro Carvalho Chehab 	if (mutex_lock_interruptible(&gsc->lock))
615*3bae07d4SMauro Carvalho Chehab 		return -ERESTARTSYS;
616*3bae07d4SMauro Carvalho Chehab 
617*3bae07d4SMauro Carvalho Chehab 	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
618*3bae07d4SMauro Carvalho Chehab 	if (!ctx) {
619*3bae07d4SMauro Carvalho Chehab 		ret = -ENOMEM;
620*3bae07d4SMauro Carvalho Chehab 		goto unlock;
621*3bae07d4SMauro Carvalho Chehab 	}
622*3bae07d4SMauro Carvalho Chehab 
623*3bae07d4SMauro Carvalho Chehab 	v4l2_fh_init(&ctx->fh, gsc->m2m.vfd);
624*3bae07d4SMauro Carvalho Chehab 	ret = gsc_ctrls_create(ctx);
625*3bae07d4SMauro Carvalho Chehab 	if (ret)
626*3bae07d4SMauro Carvalho Chehab 		goto error_fh;
627*3bae07d4SMauro Carvalho Chehab 
628*3bae07d4SMauro Carvalho Chehab 	/* Use separate control handler per file handle */
629*3bae07d4SMauro Carvalho Chehab 	ctx->fh.ctrl_handler = &ctx->ctrl_handler;
630*3bae07d4SMauro Carvalho Chehab 	file->private_data = &ctx->fh;
631*3bae07d4SMauro Carvalho Chehab 	v4l2_fh_add(&ctx->fh);
632*3bae07d4SMauro Carvalho Chehab 
633*3bae07d4SMauro Carvalho Chehab 	ctx->gsc_dev = gsc;
634*3bae07d4SMauro Carvalho Chehab 	/* Default color format */
635*3bae07d4SMauro Carvalho Chehab 	ctx->s_frame.fmt = get_format(0);
636*3bae07d4SMauro Carvalho Chehab 	ctx->d_frame.fmt = get_format(0);
637*3bae07d4SMauro Carvalho Chehab 	/* Setup the device context for mem2mem mode. */
638*3bae07d4SMauro Carvalho Chehab 	ctx->state = GSC_CTX_M2M;
639*3bae07d4SMauro Carvalho Chehab 	ctx->flags = 0;
640*3bae07d4SMauro Carvalho Chehab 	ctx->in_path = GSC_DMA;
641*3bae07d4SMauro Carvalho Chehab 	ctx->out_path = GSC_DMA;
642*3bae07d4SMauro Carvalho Chehab 
643*3bae07d4SMauro Carvalho Chehab 	ctx->m2m_ctx = v4l2_m2m_ctx_init(gsc->m2m.m2m_dev, ctx, queue_init);
644*3bae07d4SMauro Carvalho Chehab 	if (IS_ERR(ctx->m2m_ctx)) {
645*3bae07d4SMauro Carvalho Chehab 		pr_err("Failed to initialize m2m context");
646*3bae07d4SMauro Carvalho Chehab 		ret = PTR_ERR(ctx->m2m_ctx);
647*3bae07d4SMauro Carvalho Chehab 		goto error_ctrls;
648*3bae07d4SMauro Carvalho Chehab 	}
649*3bae07d4SMauro Carvalho Chehab 
650*3bae07d4SMauro Carvalho Chehab 	if (gsc->m2m.refcnt++ == 0)
651*3bae07d4SMauro Carvalho Chehab 		set_bit(ST_M2M_OPEN, &gsc->state);
652*3bae07d4SMauro Carvalho Chehab 
653*3bae07d4SMauro Carvalho Chehab 	pr_debug("gsc m2m driver is opened, ctx(0x%p)", ctx);
654*3bae07d4SMauro Carvalho Chehab 
655*3bae07d4SMauro Carvalho Chehab 	mutex_unlock(&gsc->lock);
656*3bae07d4SMauro Carvalho Chehab 	return 0;
657*3bae07d4SMauro Carvalho Chehab 
658*3bae07d4SMauro Carvalho Chehab error_ctrls:
659*3bae07d4SMauro Carvalho Chehab 	gsc_ctrls_delete(ctx);
660*3bae07d4SMauro Carvalho Chehab 	v4l2_fh_del(&ctx->fh);
661*3bae07d4SMauro Carvalho Chehab error_fh:
662*3bae07d4SMauro Carvalho Chehab 	v4l2_fh_exit(&ctx->fh);
663*3bae07d4SMauro Carvalho Chehab 	kfree(ctx);
664*3bae07d4SMauro Carvalho Chehab unlock:
665*3bae07d4SMauro Carvalho Chehab 	mutex_unlock(&gsc->lock);
666*3bae07d4SMauro Carvalho Chehab 	return ret;
667*3bae07d4SMauro Carvalho Chehab }
668*3bae07d4SMauro Carvalho Chehab 
gsc_m2m_release(struct file * file)669*3bae07d4SMauro Carvalho Chehab static int gsc_m2m_release(struct file *file)
670*3bae07d4SMauro Carvalho Chehab {
671*3bae07d4SMauro Carvalho Chehab 	struct gsc_ctx *ctx = fh_to_ctx(file->private_data);
672*3bae07d4SMauro Carvalho Chehab 	struct gsc_dev *gsc = ctx->gsc_dev;
673*3bae07d4SMauro Carvalho Chehab 
674*3bae07d4SMauro Carvalho Chehab 	pr_debug("pid: %d, state: 0x%lx, refcnt= %d",
675*3bae07d4SMauro Carvalho Chehab 		task_pid_nr(current), gsc->state, gsc->m2m.refcnt);
676*3bae07d4SMauro Carvalho Chehab 
677*3bae07d4SMauro Carvalho Chehab 	mutex_lock(&gsc->lock);
678*3bae07d4SMauro Carvalho Chehab 
679*3bae07d4SMauro Carvalho Chehab 	v4l2_m2m_ctx_release(ctx->m2m_ctx);
680*3bae07d4SMauro Carvalho Chehab 	gsc_ctrls_delete(ctx);
681*3bae07d4SMauro Carvalho Chehab 	v4l2_fh_del(&ctx->fh);
682*3bae07d4SMauro Carvalho Chehab 	v4l2_fh_exit(&ctx->fh);
683*3bae07d4SMauro Carvalho Chehab 
684*3bae07d4SMauro Carvalho Chehab 	if (--gsc->m2m.refcnt <= 0)
685*3bae07d4SMauro Carvalho Chehab 		clear_bit(ST_M2M_OPEN, &gsc->state);
686*3bae07d4SMauro Carvalho Chehab 	kfree(ctx);
687*3bae07d4SMauro Carvalho Chehab 
688*3bae07d4SMauro Carvalho Chehab 	mutex_unlock(&gsc->lock);
689*3bae07d4SMauro Carvalho Chehab 	return 0;
690*3bae07d4SMauro Carvalho Chehab }
691*3bae07d4SMauro Carvalho Chehab 
gsc_m2m_poll(struct file * file,struct poll_table_struct * wait)692*3bae07d4SMauro Carvalho Chehab static __poll_t gsc_m2m_poll(struct file *file,
693*3bae07d4SMauro Carvalho Chehab 					struct poll_table_struct *wait)
694*3bae07d4SMauro Carvalho Chehab {
695*3bae07d4SMauro Carvalho Chehab 	struct gsc_ctx *ctx = fh_to_ctx(file->private_data);
696*3bae07d4SMauro Carvalho Chehab 	struct gsc_dev *gsc = ctx->gsc_dev;
697*3bae07d4SMauro Carvalho Chehab 	__poll_t ret;
698*3bae07d4SMauro Carvalho Chehab 
699*3bae07d4SMauro Carvalho Chehab 	if (mutex_lock_interruptible(&gsc->lock))
700*3bae07d4SMauro Carvalho Chehab 		return EPOLLERR;
701*3bae07d4SMauro Carvalho Chehab 
702*3bae07d4SMauro Carvalho Chehab 	ret = v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
703*3bae07d4SMauro Carvalho Chehab 	mutex_unlock(&gsc->lock);
704*3bae07d4SMauro Carvalho Chehab 
705*3bae07d4SMauro Carvalho Chehab 	return ret;
706*3bae07d4SMauro Carvalho Chehab }
707*3bae07d4SMauro Carvalho Chehab 
gsc_m2m_mmap(struct file * file,struct vm_area_struct * vma)708*3bae07d4SMauro Carvalho Chehab static int gsc_m2m_mmap(struct file *file, struct vm_area_struct *vma)
709*3bae07d4SMauro Carvalho Chehab {
710*3bae07d4SMauro Carvalho Chehab 	struct gsc_ctx *ctx = fh_to_ctx(file->private_data);
711*3bae07d4SMauro Carvalho Chehab 	struct gsc_dev *gsc = ctx->gsc_dev;
712*3bae07d4SMauro Carvalho Chehab 	int ret;
713*3bae07d4SMauro Carvalho Chehab 
714*3bae07d4SMauro Carvalho Chehab 	if (mutex_lock_interruptible(&gsc->lock))
715*3bae07d4SMauro Carvalho Chehab 		return -ERESTARTSYS;
716*3bae07d4SMauro Carvalho Chehab 
717*3bae07d4SMauro Carvalho Chehab 	ret = v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
718*3bae07d4SMauro Carvalho Chehab 	mutex_unlock(&gsc->lock);
719*3bae07d4SMauro Carvalho Chehab 
720*3bae07d4SMauro Carvalho Chehab 	return ret;
721*3bae07d4SMauro Carvalho Chehab }
722*3bae07d4SMauro Carvalho Chehab 
723*3bae07d4SMauro Carvalho Chehab static const struct v4l2_file_operations gsc_m2m_fops = {
724*3bae07d4SMauro Carvalho Chehab 	.owner		= THIS_MODULE,
725*3bae07d4SMauro Carvalho Chehab 	.open		= gsc_m2m_open,
726*3bae07d4SMauro Carvalho Chehab 	.release	= gsc_m2m_release,
727*3bae07d4SMauro Carvalho Chehab 	.poll		= gsc_m2m_poll,
728*3bae07d4SMauro Carvalho Chehab 	.unlocked_ioctl	= video_ioctl2,
729*3bae07d4SMauro Carvalho Chehab 	.mmap		= gsc_m2m_mmap,
730*3bae07d4SMauro Carvalho Chehab };
731*3bae07d4SMauro Carvalho Chehab 
732*3bae07d4SMauro Carvalho Chehab static const struct v4l2_m2m_ops gsc_m2m_ops = {
733*3bae07d4SMauro Carvalho Chehab 	.device_run	= gsc_m2m_device_run,
734*3bae07d4SMauro Carvalho Chehab 	.job_abort	= gsc_m2m_job_abort,
735*3bae07d4SMauro Carvalho Chehab };
736*3bae07d4SMauro Carvalho Chehab 
gsc_register_m2m_device(struct gsc_dev * gsc)737*3bae07d4SMauro Carvalho Chehab int gsc_register_m2m_device(struct gsc_dev *gsc)
738*3bae07d4SMauro Carvalho Chehab {
739*3bae07d4SMauro Carvalho Chehab 	struct platform_device *pdev;
740*3bae07d4SMauro Carvalho Chehab 	int ret;
741*3bae07d4SMauro Carvalho Chehab 
742*3bae07d4SMauro Carvalho Chehab 	if (!gsc)
743*3bae07d4SMauro Carvalho Chehab 		return -ENODEV;
744*3bae07d4SMauro Carvalho Chehab 
745*3bae07d4SMauro Carvalho Chehab 	pdev = gsc->pdev;
746*3bae07d4SMauro Carvalho Chehab 
747*3bae07d4SMauro Carvalho Chehab 	gsc->vdev.fops		= &gsc_m2m_fops;
748*3bae07d4SMauro Carvalho Chehab 	gsc->vdev.ioctl_ops	= &gsc_m2m_ioctl_ops;
749*3bae07d4SMauro Carvalho Chehab 	gsc->vdev.release	= video_device_release_empty;
750*3bae07d4SMauro Carvalho Chehab 	gsc->vdev.lock		= &gsc->lock;
751*3bae07d4SMauro Carvalho Chehab 	gsc->vdev.vfl_dir	= VFL_DIR_M2M;
752*3bae07d4SMauro Carvalho Chehab 	gsc->vdev.v4l2_dev	= &gsc->v4l2_dev;
753*3bae07d4SMauro Carvalho Chehab 	gsc->vdev.device_caps	= V4L2_CAP_STREAMING |
754*3bae07d4SMauro Carvalho Chehab 				  V4L2_CAP_VIDEO_M2M_MPLANE;
755*3bae07d4SMauro Carvalho Chehab 	snprintf(gsc->vdev.name, sizeof(gsc->vdev.name), "%s.%d:m2m",
756*3bae07d4SMauro Carvalho Chehab 					GSC_MODULE_NAME, gsc->id);
757*3bae07d4SMauro Carvalho Chehab 
758*3bae07d4SMauro Carvalho Chehab 	video_set_drvdata(&gsc->vdev, gsc);
759*3bae07d4SMauro Carvalho Chehab 
760*3bae07d4SMauro Carvalho Chehab 	gsc->m2m.vfd = &gsc->vdev;
761*3bae07d4SMauro Carvalho Chehab 	gsc->m2m.m2m_dev = v4l2_m2m_init(&gsc_m2m_ops);
762*3bae07d4SMauro Carvalho Chehab 	if (IS_ERR(gsc->m2m.m2m_dev)) {
763*3bae07d4SMauro Carvalho Chehab 		dev_err(&pdev->dev, "failed to initialize v4l2-m2m device\n");
764*3bae07d4SMauro Carvalho Chehab 		return PTR_ERR(gsc->m2m.m2m_dev);
765*3bae07d4SMauro Carvalho Chehab 	}
766*3bae07d4SMauro Carvalho Chehab 
767*3bae07d4SMauro Carvalho Chehab 	ret = video_register_device(&gsc->vdev, VFL_TYPE_VIDEO, -1);
768*3bae07d4SMauro Carvalho Chehab 	if (ret) {
769*3bae07d4SMauro Carvalho Chehab 		dev_err(&pdev->dev,
770*3bae07d4SMauro Carvalho Chehab 			 "%s(): failed to register video device\n", __func__);
771*3bae07d4SMauro Carvalho Chehab 		goto err_m2m_release;
772*3bae07d4SMauro Carvalho Chehab 	}
773*3bae07d4SMauro Carvalho Chehab 
774*3bae07d4SMauro Carvalho Chehab 	pr_debug("gsc m2m driver registered as /dev/video%d", gsc->vdev.num);
775*3bae07d4SMauro Carvalho Chehab 	return 0;
776*3bae07d4SMauro Carvalho Chehab 
777*3bae07d4SMauro Carvalho Chehab err_m2m_release:
778*3bae07d4SMauro Carvalho Chehab 	v4l2_m2m_release(gsc->m2m.m2m_dev);
779*3bae07d4SMauro Carvalho Chehab 
780*3bae07d4SMauro Carvalho Chehab 	return ret;
781*3bae07d4SMauro Carvalho Chehab }
782*3bae07d4SMauro Carvalho Chehab 
gsc_unregister_m2m_device(struct gsc_dev * gsc)783*3bae07d4SMauro Carvalho Chehab void gsc_unregister_m2m_device(struct gsc_dev *gsc)
784*3bae07d4SMauro Carvalho Chehab {
785*3bae07d4SMauro Carvalho Chehab 	if (gsc) {
786*3bae07d4SMauro Carvalho Chehab 		v4l2_m2m_release(gsc->m2m.m2m_dev);
787*3bae07d4SMauro Carvalho Chehab 		video_unregister_device(&gsc->vdev);
788*3bae07d4SMauro Carvalho Chehab 	}
789*3bae07d4SMauro Carvalho Chehab }
790