FILE
imx-media-csc-scaler.c
File preview with syntax highlighting.
linux-6.19.14/drivers/staging/media/imx/imx-media-csc-scaler.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.