File Index
Files and directories
Sign in
FILE

imx-media-csc-scaler.c

File preview with syntax highlighting.

linux-6.19.14/drivers/staging/media/imx/imx-media-csc-scaler.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * i.MX IPUv3 IC PP mem2mem CSC/Scaler driver
  4 *
  5 * Copyright (C) 2011 Pengutronix, Sascha Hauer
  6 * Copyright (C) 2018 Pengutronix, Philipp Zabel
  7 */
  8#include <linux/module.h>
  9#include <linux/delay.h>
 10#include <linux/fs.h>
 11#include <linux/sched.h>
 12#include <linux/slab.h>
 13#include <video/imx-ipu-v3.h>
 14#include <video/imx-ipu-image-convert.h>
 15
 16#include <media/media-device.h>
 17#include <media/v4l2-ctrls.h>
 18#include <media/v4l2-event.h>
 19#include <media/v4l2-mem2mem.h>
 20#include <media/v4l2-device.h>
 21#include <media/v4l2-ioctl.h>
 22#include <media/videobuf2-dma-contig.h>
 23
 24#include "imx-media.h"
 25
 26#define IMX_CSC_SCALER_NAME "imx-csc-scaler"
 27
 28enum {
 29	V4L2_M2M_SRC = 0,
 30	V4L2_M2M_DST = 1,
 31};
 32
 33struct ipu_csc_scaler_priv {
 34	struct imx_media_video_dev	vdev;
 35
 36	struct v4l2_m2m_dev		*m2m_dev;
 37	struct device			*dev;
 38
 39	struct imx_media_dev		*md;
 40
 41	struct mutex			mutex;	/* mem2mem device mutex */
 42};
 43
 44#define vdev_to_priv(v) container_of(v, struct ipu_csc_scaler_priv, vdev)
 45
 46/* Per-queue, driver-specific private data */
 47struct ipu_csc_scaler_q_data {
 48	struct v4l2_pix_format		cur_fmt;
 49	struct v4l2_rect		rect;
 50};
 51
 52struct ipu_csc_scaler_ctx {
 53	struct ipu_csc_scaler_priv	*priv;
 54
 55	struct v4l2_fh			fh;
 56	struct ipu_csc_scaler_q_data	q_data[2];
 57	struct ipu_image_convert_ctx	*icc;
 58
 59	struct v4l2_ctrl_handler	ctrl_hdlr;
 60	int				rotate;
 61	bool				hflip;
 62	bool				vflip;
 63	enum ipu_rotate_mode		rot_mode;
 64	unsigned int			sequence;
 65};
 66
 67static inline struct ipu_csc_scaler_ctx *file_to_ctx(struct file *filp)
 68{
 69	return container_of(file_to_v4l2_fh(filp), struct ipu_csc_scaler_ctx, fh);
 70}
 71
 72static struct ipu_csc_scaler_q_data *get_q_data(struct ipu_csc_scaler_ctx *ctx,
 73						enum v4l2_buf_type type)
 74{
 75	if (V4L2_TYPE_IS_OUTPUT(type))
 76		return &ctx->q_data[V4L2_M2M_SRC];
 77	else
 78		return &ctx->q_data[V4L2_M2M_DST];
 79}
 80
 81/*
 82 * mem2mem callbacks
 83 */
 84
 85static void job_abort(void *_ctx)
 86{
 87	struct ipu_csc_scaler_ctx *ctx = _ctx;
 88
 89	if (ctx->icc)
 90		ipu_image_convert_abort(ctx->icc);
 91}
 92
 93static void ipu_ic_pp_complete(struct ipu_image_convert_run *run, void *_ctx)
 94{
 95	struct ipu_csc_scaler_ctx *ctx = _ctx;
 96	struct ipu_csc_scaler_priv *priv = ctx->priv;
 97	struct vb2_v4l2_buffer *src_buf, *dst_buf;
 98
 99	src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
100	dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
101
102	v4l2_m2m_buf_copy_metadata(src_buf, dst_buf);
103
104	src_buf->sequence = ctx->sequence++;
105	dst_buf->sequence = src_buf->sequence;
106
107	v4l2_m2m_buf_done(src_buf, run->status ? VB2_BUF_STATE_ERROR :
108						 VB2_BUF_STATE_DONE);
109	v4l2_m2m_buf_done(dst_buf, run->status ? VB2_BUF_STATE_ERROR :
110						 VB2_BUF_STATE_DONE);
111
112	v4l2_m2m_job_finish(priv->m2m_dev, ctx->fh.m2m_ctx);
113	kfree(run);
114}
115
116static void device_run(void *_ctx)
117{
118	struct ipu_csc_scaler_ctx *ctx = _ctx;
119	struct ipu_csc_scaler_priv *priv = ctx->priv;
120	struct vb2_v4l2_buffer *src_buf, *dst_buf;
121	struct ipu_image_convert_run *run;
122	int ret;
123
124	src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
125	dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
126
127	run = kzalloc(sizeof(*run), GFP_KERNEL);
128	if (!run)
129		goto err;
130
131	run->ctx = ctx->icc;
132	run->in_phys = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
133	run->out_phys = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0);
134
135	ret = ipu_image_convert_queue(run);
136	if (ret < 0) {
137		v4l2_err(ctx->priv->vdev.vfd->v4l2_dev,
138			 "%s: failed to queue: %d\n", __func__, ret);
139		goto err;
140	}
141
142	return;
143
144err:
145	v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
146	v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
147	v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR);
148	v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR);
149	v4l2_m2m_job_finish(priv->m2m_dev, ctx->fh.m2m_ctx);
150}
151
152/*
153 * Video ioctls
154 */
155static int ipu_csc_scaler_querycap(struct file *file, void *priv,
156				   struct v4l2_capability *cap)
157{
158	strscpy(cap->driver, IMX_CSC_SCALER_NAME, sizeof(cap->driver));
159	strscpy(cap->card, IMX_CSC_SCALER_NAME, sizeof(cap->card));
160	snprintf(cap->bus_info, sizeof(cap->bus_info),
161		 "platform:%s", IMX_CSC_SCALER_NAME);
162
163	return 0;
164}
165
166static int ipu_csc_scaler_enum_fmt(struct file *file, void *fh,
167				   struct v4l2_fmtdesc *f)
168{
169	u32 fourcc;
170	int ret;
171
172	ret = imx_media_enum_pixel_formats(&fourcc, f->index,
173					   PIXFMT_SEL_YUV_RGB, 0);
174	if (ret)
175		return ret;
176
177	f->pixelformat = fourcc;
178
179	return 0;
180}
181
182static int ipu_csc_scaler_g_fmt(struct file *file, void *priv,
183				struct v4l2_format *f)
184{
185	struct ipu_csc_scaler_ctx *ctx = file_to_ctx(file);
186	struct ipu_csc_scaler_q_data *q_data;
187
188	q_data = get_q_data(ctx, f->type);
189
190	f->fmt.pix = q_data->cur_fmt;
191
192	return 0;
193}
194
195static int ipu_csc_scaler_try_fmt(struct file *file, void *priv,
196				  struct v4l2_format *f)
197{
198	struct ipu_csc_scaler_ctx *ctx = file_to_ctx(file);
199	struct ipu_csc_scaler_q_data *q_data = get_q_data(ctx, f->type);
200	struct ipu_image test_in, test_out;
201	enum v4l2_field field;
202
203	field = f->fmt.pix.field;
204	if (field == V4L2_FIELD_ANY)
205		field = V4L2_FIELD_NONE;
206	else if (field != V4L2_FIELD_NONE)
207		return -EINVAL;
208
209	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
210		struct ipu_csc_scaler_q_data *q_data_in =
211			get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
212
213		test_out.pix = f->fmt.pix;
214		test_in.pix = q_data_in->cur_fmt;
215	} else {
216		struct ipu_csc_scaler_q_data *q_data_out =
217			get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
218
219		test_in.pix = f->fmt.pix;
220		test_out.pix = q_data_out->cur_fmt;
221	}
222
223	ipu_image_convert_adjust(&test_in, &test_out, ctx->rot_mode);
224
225	f->fmt.pix = (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) ?
226		test_out.pix : test_in.pix;
227
228	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
229		f->fmt.pix.colorspace = q_data->cur_fmt.colorspace;
230		f->fmt.pix.ycbcr_enc = q_data->cur_fmt.ycbcr_enc;
231		f->fmt.pix.xfer_func = q_data->cur_fmt.xfer_func;
232		f->fmt.pix.quantization = q_data->cur_fmt.quantization;
233	} else if (f->fmt.pix.colorspace == V4L2_COLORSPACE_DEFAULT) {
234		f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
235		f->fmt.pix.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
236		f->fmt.pix.xfer_func = V4L2_XFER_FUNC_DEFAULT;
237		f->fmt.pix.quantization = V4L2_QUANTIZATION_DEFAULT;
238	}
239
240	return 0;
241}
242
243static int ipu_csc_scaler_s_fmt(struct file *file, void *priv,
244				struct v4l2_format *f)
245{
246	struct ipu_csc_scaler_ctx *ctx = file_to_ctx(file);
247	struct ipu_csc_scaler_q_data *q_data;
248	struct vb2_queue *vq;
249	int ret;
250
251	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
252	if (vb2_is_busy(vq)) {
253		v4l2_err(ctx->priv->vdev.vfd->v4l2_dev, "%s: queue busy\n",
254			 __func__);
255		return -EBUSY;
256	}
257
258	q_data = get_q_data(ctx, f->type);
259
260	ret = ipu_csc_scaler_try_fmt(file, priv, f);
261	if (ret < 0)
262		return ret;
263
264	q_data->cur_fmt.width = f->fmt.pix.width;
265	q_data->cur_fmt.height = f->fmt.pix.height;
266	q_data->cur_fmt.pixelformat = f->fmt.pix.pixelformat;
267	q_data->cur_fmt.field = f->fmt.pix.field;
268	q_data->cur_fmt.bytesperline = f->fmt.pix.bytesperline;
269	q_data->cur_fmt.sizeimage = f->fmt.pix.sizeimage;
270
271	/* Reset cropping/composing rectangle */
272	q_data->rect.left = 0;
273	q_data->rect.top = 0;
274	q_data->rect.width = q_data->cur_fmt.width;
275	q_data->rect.height = q_data->cur_fmt.height;
276
277	if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
278		/* Set colorimetry on the output queue */
279		q_data->cur_fmt.colorspace = f->fmt.pix.colorspace;
280		q_data->cur_fmt.ycbcr_enc = f->fmt.pix.ycbcr_enc;
281		q_data->cur_fmt.xfer_func = f->fmt.pix.xfer_func;
282		q_data->cur_fmt.quantization = f->fmt.pix.quantization;
283		/* Propagate colorimetry to the capture queue */
284		q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
285		q_data->cur_fmt.colorspace = f->fmt.pix.colorspace;
286		q_data->cur_fmt.ycbcr_enc = f->fmt.pix.ycbcr_enc;
287		q_data->cur_fmt.xfer_func = f->fmt.pix.xfer_func;
288		q_data->cur_fmt.quantization = f->fmt.pix.quantization;
289	}
290
291	/*
292	 * TODO: Setting colorimetry on the capture queue is currently not
293	 * supported by the V4L2 API
294	 */
295
296	return 0;
297}
298
299static int ipu_csc_scaler_g_selection(struct file *file, void *priv,
300				      struct v4l2_selection *s)
301{
302	struct ipu_csc_scaler_ctx *ctx = file_to_ctx(file);
303	struct ipu_csc_scaler_q_data *q_data;
304
305	switch (s->target) {
306	case V4L2_SEL_TGT_CROP:
307	case V4L2_SEL_TGT_CROP_DEFAULT:
308	case V4L2_SEL_TGT_CROP_BOUNDS:
309		if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
310			return -EINVAL;
311		q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
312		break;
313	case V4L2_SEL_TGT_COMPOSE:
314	case V4L2_SEL_TGT_COMPOSE_DEFAULT:
315	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
316		if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
317			return -EINVAL;
318		q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
319		break;
320	default:
321		return -EINVAL;
322	}
323
324	if (s->target == V4L2_SEL_TGT_CROP ||
325	    s->target == V4L2_SEL_TGT_COMPOSE) {
326		s->r = q_data->rect;
327	} else {
328		s->r.left = 0;
329		s->r.top = 0;
330		s->r.width = q_data->cur_fmt.width;
331		s->r.height = q_data->cur_fmt.height;
332	}
333
334	return 0;
335}
336
337static int ipu_csc_scaler_s_selection(struct file *file, void *priv,
338				      struct v4l2_selection *s)
339{
340	struct ipu_csc_scaler_ctx *ctx = file_to_ctx(file);
341	struct ipu_csc_scaler_q_data *q_data;
342
343	switch (s->target) {
344	case V4L2_SEL_TGT_CROP:
345		if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
346			return -EINVAL;
347		break;
348	case V4L2_SEL_TGT_COMPOSE:
349		if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
350			return -EINVAL;
351		break;
352	default:
353		return -EINVAL;
354	}
355
356	if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
357	    s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
358		return -EINVAL;
359
360	q_data = get_q_data(ctx, s->type);
361
362	/* The input's frame width to the IC must be a multiple of 8 pixels
363	 * When performing resizing the frame width must be multiple of burst
364	 * size - 8 or 16 pixels as defined by CB#_BURST_16 parameter.
365	 */
366	if (s->flags & V4L2_SEL_FLAG_GE)
367		s->r.width = round_up(s->r.width, 8);
368	if (s->flags & V4L2_SEL_FLAG_LE)
369		s->r.width = round_down(s->r.width, 8);
370	s->r.width = clamp_t(unsigned int, s->r.width, 8,
371			     round_down(q_data->cur_fmt.width, 8));
372	s->r.height = clamp_t(unsigned int, s->r.height, 1,
373			      q_data->cur_fmt.height);
374	s->r.left = clamp_t(unsigned int, s->r.left, 0,
375			    q_data->cur_fmt.width - s->r.width);
376	s->r.top = clamp_t(unsigned int, s->r.top, 0,
377			   q_data->cur_fmt.height - s->r.height);
378
379	/* V4L2_SEL_FLAG_KEEP_CONFIG is only valid for subdevices */
380	q_data->rect = s->r;
381
382	return 0;
383}
384
385static const struct v4l2_ioctl_ops ipu_csc_scaler_ioctl_ops = {
386	.vidioc_querycap		= ipu_csc_scaler_querycap,
387
388	.vidioc_enum_fmt_vid_cap	= ipu_csc_scaler_enum_fmt,
389	.vidioc_g_fmt_vid_cap		= ipu_csc_scaler_g_fmt,
390	.vidioc_try_fmt_vid_cap		= ipu_csc_scaler_try_fmt,
391	.vidioc_s_fmt_vid_cap		= ipu_csc_scaler_s_fmt,
392
393	.vidioc_enum_fmt_vid_out	= ipu_csc_scaler_enum_fmt,
394	.vidioc_g_fmt_vid_out		= ipu_csc_scaler_g_fmt,
395	.vidioc_try_fmt_vid_out		= ipu_csc_scaler_try_fmt,
396	.vidioc_s_fmt_vid_out		= ipu_csc_scaler_s_fmt,
397
398	.vidioc_g_selection		= ipu_csc_scaler_g_selection,
399	.vidioc_s_selection		= ipu_csc_scaler_s_selection,
400
401	.vidioc_reqbufs			= v4l2_m2m_ioctl_reqbufs,
402	.vidioc_querybuf		= v4l2_m2m_ioctl_querybuf,
403
404	.vidioc_qbuf			= v4l2_m2m_ioctl_qbuf,
405	.vidioc_expbuf			= v4l2_m2m_ioctl_expbuf,
406	.vidioc_dqbuf			= v4l2_m2m_ioctl_dqbuf,
407	.vidioc_create_bufs		= v4l2_m2m_ioctl_create_bufs,
408	.vidioc_prepare_buf		= v4l2_m2m_ioctl_prepare_buf,
409
410	.vidioc_streamon		= v4l2_m2m_ioctl_streamon,
411	.vidioc_streamoff		= v4l2_m2m_ioctl_streamoff,
412
413	.vidioc_subscribe_event		= v4l2_ctrl_subscribe_event,
414	.vidioc_unsubscribe_event	= v4l2_event_unsubscribe,
415};
416
417/*
418 * Queue operations
419 */
420
421static int ipu_csc_scaler_queue_setup(struct vb2_queue *vq,
422				      unsigned int *nbuffers,
423				      unsigned int *nplanes,
424				      unsigned int sizes[],
425				      struct device *alloc_devs[])
426{
427	struct ipu_csc_scaler_ctx *ctx = vb2_get_drv_priv(vq);
428	struct ipu_csc_scaler_q_data *q_data;
429	unsigned int size, count = *nbuffers;
430
431	q_data = get_q_data(ctx, vq->type);
432
433	size = q_data->cur_fmt.sizeimage;
434
435	*nbuffers = count;
436
437	if (*nplanes)
438		return sizes[0] < size ? -EINVAL : 0;
439
440	*nplanes = 1;
441	sizes[0] = size;
442
443	dev_dbg(ctx->priv->dev, "get %d buffer(s) of size %d each.\n",
444		count, size);
445
446	return 0;
447}
448
449static int ipu_csc_scaler_buf_prepare(struct vb2_buffer *vb)
450{
451	struct vb2_queue *vq = vb->vb2_queue;
452	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
453	struct ipu_csc_scaler_ctx *ctx = vb2_get_drv_priv(vq);
454	struct ipu_csc_scaler_q_data *q_data;
455	unsigned long size;
456
457	dev_dbg(ctx->priv->dev, "type: %d\n", vq->type);
458
459	if (V4L2_TYPE_IS_OUTPUT(vq->type)) {
460		if (vbuf->field == V4L2_FIELD_ANY)
461			vbuf->field = V4L2_FIELD_NONE;
462		if (vbuf->field != V4L2_FIELD_NONE) {
463			dev_dbg(ctx->priv->dev, "%s: field isn't supported\n",
464				__func__);
465			return -EINVAL;
466		}
467	}
468
469	q_data = get_q_data(ctx, vq->type);
470	size = q_data->cur_fmt.sizeimage;
471
472	if (vb2_plane_size(vb, 0) < size) {
473		dev_dbg(ctx->priv->dev,
474			"%s: data will not fit into plane (%lu < %lu)\n",
475			__func__, vb2_plane_size(vb, 0), size);
476		return -EINVAL;
477	}
478
479	vb2_set_plane_payload(vb, 0, size);
480
481	return 0;
482}
483
484static void ipu_csc_scaler_buf_queue(struct vb2_buffer *vb)
485{
486	struct ipu_csc_scaler_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
487
488	v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, to_vb2_v4l2_buffer(vb));
489}
490
491static void ipu_image_from_q_data(struct ipu_image *im,
492				  struct ipu_csc_scaler_q_data *q_data)
493{
494	struct v4l2_pix_format *fmt = &q_data->cur_fmt;
495
496	im->pix = *fmt;
497	if (fmt->ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT)
498		im->pix.ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
499	if (fmt->quantization == V4L2_QUANTIZATION_DEFAULT)
500		im->pix.ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
501	im->rect = q_data->rect;
502}
503
504static int ipu_csc_scaler_start_streaming(struct vb2_queue *q,
505					  unsigned int count)
506{
507	const enum ipu_ic_task ic_task = IC_TASK_POST_PROCESSOR;
508	struct ipu_csc_scaler_ctx *ctx = vb2_get_drv_priv(q);
509	struct ipu_csc_scaler_priv *priv = ctx->priv;
510	struct ipu_soc *ipu = priv->md->ipu[0];
511	struct ipu_csc_scaler_q_data *q_data;
512	struct vb2_queue *other_q;
513	struct ipu_image in, out;
514
515	other_q = v4l2_m2m_get_vq(ctx->fh.m2m_ctx,
516				  (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) ?
517				  V4L2_BUF_TYPE_VIDEO_OUTPUT :
518				  V4L2_BUF_TYPE_VIDEO_CAPTURE);
519	if (!vb2_is_streaming(other_q))
520		return 0;
521
522	if (ctx->icc) {
523		v4l2_warn(ctx->priv->vdev.vfd->v4l2_dev, "removing old ICC\n");
524		ipu_image_convert_unprepare(ctx->icc);
525	}
526
527	q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
528	ipu_image_from_q_data(&in, q_data);
529
530	q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
531	ipu_image_from_q_data(&out, q_data);
532
533	ctx->icc = ipu_image_convert_prepare(ipu, ic_task, &in, &out,
534					     ctx->rot_mode,
535					     ipu_ic_pp_complete, ctx);
536	if (IS_ERR(ctx->icc)) {
537		struct vb2_v4l2_buffer *buf;
538		int ret = PTR_ERR(ctx->icc);
539
540		ctx->icc = NULL;
541		v4l2_err(ctx->priv->vdev.vfd->v4l2_dev, "%s: error %d\n",
542			 __func__, ret);
543		while ((buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx)))
544			v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED);
545		while ((buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx)))
546			v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED);
547		return ret;
548	}
549
550	return 0;
551}
552
553static void ipu_csc_scaler_stop_streaming(struct vb2_queue *q)
554{
555	struct ipu_csc_scaler_ctx *ctx = vb2_get_drv_priv(q);
556	struct vb2_v4l2_buffer *buf;
557
558	if (ctx->icc) {
559		ipu_image_convert_unprepare(ctx->icc);
560		ctx->icc = NULL;
561	}
562
563	ctx->sequence = 0;
564
565	if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
566		while ((buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx)))
567			v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR);
568	} else {
569		while ((buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx)))
570			v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR);
571	}
572}
573
574static const struct vb2_ops ipu_csc_scaler_qops = {
575	.queue_setup		= ipu_csc_scaler_queue_setup,
576	.buf_prepare		= ipu_csc_scaler_buf_prepare,
577	.buf_queue		= ipu_csc_scaler_buf_queue,
578	.start_streaming	= ipu_csc_scaler_start_streaming,
579	.stop_streaming		= ipu_csc_scaler_stop_streaming,
580};
581
582static int ipu_csc_scaler_queue_init(void *priv, struct vb2_queue *src_vq,
583				     struct vb2_queue *dst_vq)
584{
585	struct ipu_csc_scaler_ctx *ctx = priv;
586	int ret;
587
588	memset(src_vq, 0, sizeof(*src_vq));
589	src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
590	src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
591	src_vq->drv_priv = ctx;
592	src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
593	src_vq->ops = &ipu_csc_scaler_qops;
594	src_vq->mem_ops = &vb2_dma_contig_memops;
595	src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
596	src_vq->lock = &ctx->priv->mutex;
597	src_vq->dev = ctx->priv->dev;
598
599	ret = vb2_queue_init(src_vq);
600	if (ret)
601		return ret;
602
603	memset(dst_vq, 0, sizeof(*dst_vq));
604	dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
605	dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
606	dst_vq->drv_priv = ctx;
607	dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
608	dst_vq->ops = &ipu_csc_scaler_qops;
609	dst_vq->mem_ops = &vb2_dma_contig_memops;
610	dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
611	dst_vq->lock = &ctx->priv->mutex;
612	dst_vq->dev = ctx->priv->dev;
613
614	return vb2_queue_init(dst_vq);
615}
616
617static int ipu_csc_scaler_s_ctrl(struct v4l2_ctrl *ctrl)
618{
619	struct ipu_csc_scaler_ctx *ctx = container_of(ctrl->handler,
620						      struct ipu_csc_scaler_ctx,
621						      ctrl_hdlr);
622	enum ipu_rotate_mode rot_mode;
623	int rotate;
624	bool hflip, vflip;
625	int ret = 0;
626
627	rotate = ctx->rotate;
628	hflip = ctx->hflip;
629	vflip = ctx->vflip;
630
631	switch (ctrl->id) {
632	case V4L2_CID_HFLIP:
633		hflip = ctrl->val;
634		break;
635	case V4L2_CID_VFLIP:
636		vflip = ctrl->val;
637		break;
638	case V4L2_CID_ROTATE:
639		rotate = ctrl->val;
640		break;
641	default:
642		return -EINVAL;
643	}
644
645	ret = ipu_degrees_to_rot_mode(&rot_mode, rotate, hflip, vflip);
646	if (ret)
647		return ret;
648
649	if (rot_mode != ctx->rot_mode) {
650		struct v4l2_pix_format *in_fmt, *out_fmt;
651		struct ipu_image test_in, test_out;
652
653		in_fmt = &ctx->q_data[V4L2_M2M_SRC].cur_fmt;
654		out_fmt = &ctx->q_data[V4L2_M2M_DST].cur_fmt;
655
656		test_in.pix = *in_fmt;
657		test_out.pix = *out_fmt;
658
659		if (ipu_rot_mode_is_irt(rot_mode) !=
660		    ipu_rot_mode_is_irt(ctx->rot_mode)) {
661			/* Switch width & height to keep aspect ratio intact */
662			test_out.pix.width = out_fmt->height;
663			test_out.pix.height = out_fmt->width;
664		}
665
666		ipu_image_convert_adjust(&test_in, &test_out, ctx->rot_mode);
667
668		/* Check if output format needs to be changed */
669		if (test_in.pix.width != in_fmt->width ||
670		    test_in.pix.height != in_fmt->height ||
671		    test_in.pix.bytesperline != in_fmt->bytesperline ||
672		    test_in.pix.sizeimage != in_fmt->sizeimage) {
673			struct vb2_queue *out_q;
674
675			out_q = v4l2_m2m_get_vq(ctx->fh.m2m_ctx,
676						V4L2_BUF_TYPE_VIDEO_OUTPUT);
677			if (vb2_is_busy(out_q))
678				return -EBUSY;
679		}
680
681		/* Check if capture format needs to be changed */
682		if (test_out.pix.width != out_fmt->width ||
683		    test_out.pix.height != out_fmt->height ||
684		    test_out.pix.bytesperline != out_fmt->bytesperline ||
685		    test_out.pix.sizeimage != out_fmt->sizeimage) {
686			struct vb2_queue *cap_q;
687
688			cap_q = v4l2_m2m_get_vq(ctx->fh.m2m_ctx,
689						V4L2_BUF_TYPE_VIDEO_CAPTURE);
690			if (vb2_is_busy(cap_q))
691				return -EBUSY;
692		}
693
694		*in_fmt = test_in.pix;
695		*out_fmt = test_out.pix;
696
697		ctx->rot_mode = rot_mode;
698		ctx->rotate = rotate;
699		ctx->hflip = hflip;
700		ctx->vflip = vflip;
701	}
702
703	return 0;
704}
705
706static const struct v4l2_ctrl_ops ipu_csc_scaler_ctrl_ops = {
707	.s_ctrl = ipu_csc_scaler_s_ctrl,
708};
709
710static int ipu_csc_scaler_init_controls(struct ipu_csc_scaler_ctx *ctx)
711{
712	struct v4l2_ctrl_handler *hdlr = &ctx->ctrl_hdlr;
713
714	v4l2_ctrl_handler_init(hdlr, 3);
715
716	v4l2_ctrl_new_std(hdlr, &ipu_csc_scaler_ctrl_ops, V4L2_CID_HFLIP,
717			  0, 1, 1, 0);
718	v4l2_ctrl_new_std(hdlr, &ipu_csc_scaler_ctrl_ops, V4L2_CID_VFLIP,
719			  0, 1, 1, 0);
720	v4l2_ctrl_new_std(hdlr, &ipu_csc_scaler_ctrl_ops, V4L2_CID_ROTATE,
721			  0, 270, 90, 0);
722
723	if (hdlr->error) {
724		v4l2_ctrl_handler_free(hdlr);
725		return hdlr->error;
726	}
727
728	v4l2_ctrl_handler_setup(hdlr);
729	return 0;
730}
731
732#define DEFAULT_WIDTH	720
733#define DEFAULT_HEIGHT	576
734static const struct ipu_csc_scaler_q_data ipu_csc_scaler_q_data_default = {
735	.cur_fmt = {
736		.width = DEFAULT_WIDTH,
737		.height = DEFAULT_HEIGHT,
738		.pixelformat = V4L2_PIX_FMT_YUV420,
739		.field = V4L2_FIELD_NONE,
740		.bytesperline = DEFAULT_WIDTH,
741		.sizeimage = DEFAULT_WIDTH * DEFAULT_HEIGHT * 3 / 2,
742		.colorspace = V4L2_COLORSPACE_SRGB,
743	},
744	.rect = {
745		.width = DEFAULT_WIDTH,
746		.height = DEFAULT_HEIGHT,
747	},
748};
749
750/*
751 * File operations
752 */
753static int ipu_csc_scaler_open(struct file *file)
754{
755	struct ipu_csc_scaler_priv *priv = video_drvdata(file);
756	struct ipu_csc_scaler_ctx *ctx = NULL;
757	int ret;
758
759	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
760	if (!ctx)
761		return -ENOMEM;
762
763	ctx->rot_mode = IPU_ROTATE_NONE;
764
765	v4l2_fh_init(&ctx->fh, video_devdata(file));
766	v4l2_fh_add(&ctx->fh, file);
767	ctx->priv = priv;
768
769	ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(priv->m2m_dev, ctx,
770					    &ipu_csc_scaler_queue_init);
771	if (IS_ERR(ctx->fh.m2m_ctx)) {
772		ret = PTR_ERR(ctx->fh.m2m_ctx);
773		goto err_ctx;
774	}
775
776	ret = ipu_csc_scaler_init_controls(ctx);
777	if (ret)
778		goto err_ctrls;
779
780	ctx->fh.ctrl_handler = &ctx->ctrl_hdlr;
781
782	ctx->q_data[V4L2_M2M_SRC] = ipu_csc_scaler_q_data_default;
783	ctx->q_data[V4L2_M2M_DST] = ipu_csc_scaler_q_data_default;
784
785	dev_dbg(priv->dev, "Created instance %p, m2m_ctx: %p\n", ctx,
786		ctx->fh.m2m_ctx);
787
788	return 0;
789
790err_ctrls:
791	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
792err_ctx:
793	v4l2_fh_del(&ctx->fh, file);
794	v4l2_fh_exit(&ctx->fh);
795	kfree(ctx);
796	return ret;
797}
798
799static int ipu_csc_scaler_release(struct file *file)
800{
801	struct ipu_csc_scaler_priv *priv = video_drvdata(file);
802	struct ipu_csc_scaler_ctx *ctx = file_to_ctx(file);
803
804	dev_dbg(priv->dev, "Releasing instance %p\n", ctx);
805
806	v4l2_ctrl_handler_free(&ctx->ctrl_hdlr);
807	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
808	v4l2_fh_del(&ctx->fh, file);
809	v4l2_fh_exit(&ctx->fh);
810	kfree(ctx);
811
812	return 0;
813}
814
815static const struct v4l2_file_operations ipu_csc_scaler_fops = {
816	.owner		= THIS_MODULE,
817	.open		= ipu_csc_scaler_open,
818	.release	= ipu_csc_scaler_release,
819	.poll		= v4l2_m2m_fop_poll,
820	.unlocked_ioctl	= video_ioctl2,
821	.mmap		= v4l2_m2m_fop_mmap,
822};
823
824static const struct v4l2_m2m_ops m2m_ops = {
825	.device_run	= device_run,
826	.job_abort	= job_abort,
827};
828
829static void ipu_csc_scaler_video_device_release(struct video_device *vdev)
830{
831	struct ipu_csc_scaler_priv *priv = video_get_drvdata(vdev);
832
833	v4l2_m2m_release(priv->m2m_dev);
834	video_device_release(vdev);
835	kfree(priv);
836}
837
838static const struct video_device ipu_csc_scaler_videodev_template = {
839	.name		= "ipu_ic_pp csc/scaler",
840	.fops		= &ipu_csc_scaler_fops,
841	.ioctl_ops	= &ipu_csc_scaler_ioctl_ops,
842	.minor		= -1,
843	.release	= ipu_csc_scaler_video_device_release,
844	.vfl_dir	= VFL_DIR_M2M,
845	.device_caps	= V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING,
846};
847
848int imx_media_csc_scaler_device_register(struct imx_media_video_dev *vdev)
849{
850	struct ipu_csc_scaler_priv *priv = vdev_to_priv(vdev);
851	struct video_device *vfd = vdev->vfd;
852	int ret;
853
854	vfd->v4l2_dev = &priv->md->v4l2_dev;
855
856	ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1);
857	if (ret) {
858		v4l2_err(vfd->v4l2_dev, "Failed to register video device\n");
859		return ret;
860	}
861
862	v4l2_info(vfd->v4l2_dev, "Registered %s as /dev/%s\n", vfd->name,
863		  video_device_node_name(vfd));
864
865	return 0;
866}
867
868void imx_media_csc_scaler_device_unregister(struct imx_media_video_dev *vdev)
869{
870	struct ipu_csc_scaler_priv *priv = vdev_to_priv(vdev);
871	struct video_device *vfd = priv->vdev.vfd;
872
873	video_unregister_device(vfd);
874}
875
876struct imx_media_video_dev *
877imx_media_csc_scaler_device_init(struct imx_media_dev *md)
878{
879	struct ipu_csc_scaler_priv *priv;
880	struct video_device *vfd;
881	int ret;
882
883	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
884	if (!priv)
885		return ERR_PTR(-ENOMEM);
886
887	priv->md = md;
888	priv->dev = md->md.dev;
889
890	mutex_init(&priv->mutex);
891
892	vfd = video_device_alloc();
893	if (!vfd) {
894		ret = -ENOMEM;
895		goto err_vfd;
896	}
897
898	*vfd = ipu_csc_scaler_videodev_template;
899	vfd->lock = &priv->mutex;
900	priv->vdev.vfd = vfd;
901
902	INIT_LIST_HEAD(&priv->vdev.list);
903
904	video_set_drvdata(vfd, priv);
905
906	priv->m2m_dev = v4l2_m2m_init(&m2m_ops);
907	if (IS_ERR(priv->m2m_dev)) {
908		ret = PTR_ERR(priv->m2m_dev);
909		v4l2_err(&md->v4l2_dev, "Failed to init mem2mem device: %d\n",
910			 ret);
911		goto err_m2m;
912	}
913
914	return &priv->vdev;
915
916err_m2m:
917	video_device_release(vfd);
918err_vfd:
919	kfree(priv);
920	return ERR_PTR(ret);
921}
922
923MODULE_DESCRIPTION("i.MX IPUv3 mem2mem scaler/CSC driver");
924MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
925MODULE_LICENSE("GPL");
~/ Read-only file preview.