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