FILE
rzv2h-ivc-video.c
File preview with syntax highlighting.
linux-6.19.14/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-video.c
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Renesas RZ/V2H(P) Input Video Control Block driver 4 * 5 * Copyright (C) 2025 Ideas on Board Oy 6 */ 7 8#include "rzv2h-ivc.h" 9 10#include <linux/cleanup.h> 11#include <linux/iopoll.h> 12#include <linux/lockdep.h> 13#include <linux/media-bus-format.h> 14#include <linux/minmax.h> 15#include <linux/mutex.h> 16#include <linux/pm_runtime.h> 17 18#include <media/mipi-csi2.h> 19#include <media/v4l2-ctrls.h> 20#include <media/v4l2-dev.h> 21#include <media/v4l2-event.h> 22#include <media/v4l2-fh.h> 23#include <media/v4l2-ioctl.h> 24#include <media/videobuf2-dma-contig.h> 25 26#define RZV2H_IVC_FIXED_HBLANK 0x20 27#define RZV2H_IVC_MIN_VBLANK(hts) max(0x1b, 15 + (120501 / (hts))) 28 29struct rzv2h_ivc_buf { 30 struct vb2_v4l2_buffer vb; 31 struct list_head queue; 32 dma_addr_t addr; 33}; 34 35#define to_rzv2h_ivc_buf(vbuf) \ 36 container_of(vbuf, struct rzv2h_ivc_buf, vb) 37 38static const struct rzv2h_ivc_format rzv2h_ivc_formats[] = { 39 { 40 .fourcc = V4L2_PIX_FMT_SBGGR8, 41 .mbus_codes = { 42 MEDIA_BUS_FMT_SBGGR8_1X8, 43 }, 44 .dtype = MIPI_CSI2_DT_RAW8, 45 }, 46 { 47 .fourcc = V4L2_PIX_FMT_SGBRG8, 48 .mbus_codes = { 49 MEDIA_BUS_FMT_SGBRG8_1X8, 50 }, 51 .dtype = MIPI_CSI2_DT_RAW8, 52 }, 53 { 54 .fourcc = V4L2_PIX_FMT_SGRBG8, 55 .mbus_codes = { 56 MEDIA_BUS_FMT_SGRBG8_1X8, 57 }, 58 .dtype = MIPI_CSI2_DT_RAW8, 59 }, 60 { 61 .fourcc = V4L2_PIX_FMT_SRGGB8, 62 .mbus_codes = { 63 MEDIA_BUS_FMT_SRGGB8_1X8, 64 }, 65 .dtype = MIPI_CSI2_DT_RAW8, 66 }, 67 { 68 .fourcc = V4L2_PIX_FMT_RAW_CRU10, 69 .mbus_codes = { 70 MEDIA_BUS_FMT_SBGGR10_1X10, 71 MEDIA_BUS_FMT_SGBRG10_1X10, 72 MEDIA_BUS_FMT_SGRBG10_1X10, 73 MEDIA_BUS_FMT_SRGGB10_1X10 74 }, 75 .dtype = MIPI_CSI2_DT_RAW10, 76 }, 77 { 78 .fourcc = V4L2_PIX_FMT_RAW_CRU12, 79 .mbus_codes = { 80 MEDIA_BUS_FMT_SBGGR12_1X12, 81 MEDIA_BUS_FMT_SGBRG12_1X12, 82 MEDIA_BUS_FMT_SGRBG12_1X12, 83 MEDIA_BUS_FMT_SRGGB12_1X12 84 }, 85 .dtype = MIPI_CSI2_DT_RAW12, 86 }, 87 { 88 .fourcc = V4L2_PIX_FMT_RAW_CRU14, 89 .mbus_codes = { 90 MEDIA_BUS_FMT_SBGGR14_1X14, 91 MEDIA_BUS_FMT_SGBRG14_1X14, 92 MEDIA_BUS_FMT_SGRBG14_1X14, 93 MEDIA_BUS_FMT_SRGGB14_1X14 94 }, 95 .dtype = MIPI_CSI2_DT_RAW14, 96 }, 97 { 98 .fourcc = V4L2_PIX_FMT_SBGGR16, 99 .mbus_codes = { 100 MEDIA_BUS_FMT_SBGGR16_1X16, 101 }, 102 .dtype = MIPI_CSI2_DT_RAW16, 103 }, 104 { 105 .fourcc = V4L2_PIX_FMT_SGBRG16, 106 .mbus_codes = { 107 MEDIA_BUS_FMT_SGBRG16_1X16, 108 }, 109 .dtype = MIPI_CSI2_DT_RAW16, 110 }, 111 { 112 .fourcc = V4L2_PIX_FMT_SGRBG16, 113 .mbus_codes = { 114 MEDIA_BUS_FMT_SGRBG16_1X16, 115 }, 116 .dtype = MIPI_CSI2_DT_RAW16, 117 }, 118 { 119 .fourcc = V4L2_PIX_FMT_SRGGB16, 120 .mbus_codes = { 121 MEDIA_BUS_FMT_SRGGB16_1X16, 122 }, 123 .dtype = MIPI_CSI2_DT_RAW16, 124 }, 125}; 126 127void rzv2h_ivc_buffer_done(struct rzv2h_ivc *ivc) 128{ 129 struct rzv2h_ivc_buf *buf; 130 131 lockdep_assert_in_irq(); 132 133 scoped_guard(spinlock, &ivc->buffers.lock) { 134 if (!ivc->buffers.curr) 135 return; 136 137 buf = ivc->buffers.curr; 138 ivc->buffers.curr = NULL; 139 } 140 141 buf->vb.sequence = ivc->buffers.sequence++; 142 vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); 143} 144 145static void rzv2h_ivc_transfer_buffer(struct work_struct *work) 146{ 147 struct rzv2h_ivc *ivc = container_of(work, struct rzv2h_ivc, 148 buffers.work); 149 struct rzv2h_ivc_buf *buf; 150 151 /* Setup buffers */ 152 scoped_guard(spinlock_irqsave, &ivc->buffers.lock) { 153 buf = list_first_entry_or_null(&ivc->buffers.queue, 154 struct rzv2h_ivc_buf, queue); 155 } 156 157 if (!buf) 158 return; 159 160 list_del(&buf->queue); 161 162 ivc->buffers.curr = buf; 163 buf->addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); 164 rzv2h_ivc_write(ivc, RZV2H_IVC_REG_AXIRX_SADDL_P0, buf->addr); 165 166 scoped_guard(spinlock_irqsave, &ivc->spinlock) { 167 ivc->vvalid_ifp = 2; 168 } 169 rzv2h_ivc_write(ivc, RZV2H_IVC_REG_FM_FRCON, 0x1); 170} 171 172static int rzv2h_ivc_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, 173 unsigned int *num_planes, unsigned int sizes[], 174 struct device *alloc_devs[]) 175{ 176 struct rzv2h_ivc *ivc = vb2_get_drv_priv(q); 177 178 if (*num_planes && *num_planes > 1) 179 return -EINVAL; 180 181 if (sizes[0] && sizes[0] < ivc->format.pix.plane_fmt[0].sizeimage) 182 return -EINVAL; 183 184 *num_planes = 1; 185 186 if (!sizes[0]) 187 sizes[0] = ivc->format.pix.plane_fmt[0].sizeimage; 188 189 return 0; 190} 191 192static void rzv2h_ivc_buf_queue(struct vb2_buffer *vb) 193{ 194 struct rzv2h_ivc *ivc = vb2_get_drv_priv(vb->vb2_queue); 195 struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 196 struct rzv2h_ivc_buf *buf = to_rzv2h_ivc_buf(vbuf); 197 198 scoped_guard(spinlock_irq, &ivc->buffers.lock) { 199 list_add_tail(&buf->queue, &ivc->buffers.queue); 200 } 201 202 scoped_guard(spinlock_irq, &ivc->spinlock) { 203 if (vb2_is_streaming(vb->vb2_queue) && !ivc->vvalid_ifp) 204 queue_work(ivc->buffers.async_wq, &ivc->buffers.work); 205 } 206} 207 208static void rzv2h_ivc_format_configure(struct rzv2h_ivc *ivc) 209{ 210 const struct rzv2h_ivc_format *fmt = ivc->format.fmt; 211 struct v4l2_pix_format_mplane *pix = &ivc->format.pix; 212 unsigned int vblank; 213 unsigned int hts; 214 215 /* Currently only CRU packed pixel formats are supported */ 216 rzv2h_ivc_write(ivc, RZV2H_IVC_REG_AXIRX_PXFMT, 217 RZV2H_IVC_INPUT_FMT_CRU_PACKED); 218 219 rzv2h_ivc_update_bits(ivc, RZV2H_IVC_REG_AXIRX_PXFMT, 220 RZV2H_IVC_PXFMT_DTYPE, fmt->dtype); 221 222 rzv2h_ivc_write(ivc, RZV2H_IVC_REG_AXIRX_HSIZE, pix->width); 223 rzv2h_ivc_write(ivc, RZV2H_IVC_REG_AXIRX_VSIZE, pix->height); 224 rzv2h_ivc_write(ivc, RZV2H_IVC_REG_AXIRX_STRD, 225 pix->plane_fmt[0].bytesperline); 226 227 /* 228 * The ISP has minimum vertical blanking requirements that must be 229 * adhered to by the IVC. The minimum is a function of the Iridix blocks 230 * clocking requirements and the width of the image and horizontal 231 * blanking, but if we assume the worst case then it boils down to the 232 * below (plus one to the numerator to ensure the answer is rounded up) 233 */ 234 235 hts = pix->width + RZV2H_IVC_FIXED_HBLANK; 236 vblank = RZV2H_IVC_MIN_VBLANK(hts); 237 238 rzv2h_ivc_write(ivc, RZV2H_IVC_REG_AXIRX_BLANK, 239 RZV2H_IVC_VBLANK(vblank)); 240} 241 242static void rzv2h_ivc_return_buffers(struct rzv2h_ivc *ivc, 243 enum vb2_buffer_state state) 244{ 245 struct rzv2h_ivc_buf *buf, *tmp; 246 247 guard(spinlock_irqsave)(&ivc->buffers.lock); 248 249 if (ivc->buffers.curr) { 250 vb2_buffer_done(&ivc->buffers.curr->vb.vb2_buf, state); 251 ivc->buffers.curr = NULL; 252 } 253 254 list_for_each_entry_safe(buf, tmp, &ivc->buffers.queue, queue) { 255 list_del(&buf->queue); 256 vb2_buffer_done(&buf->vb.vb2_buf, state); 257 } 258} 259 260static int rzv2h_ivc_start_streaming(struct vb2_queue *q, unsigned int count) 261{ 262 struct rzv2h_ivc *ivc = vb2_get_drv_priv(q); 263 int ret; 264 265 ivc->buffers.sequence = 0; 266 ivc->vvalid_ifp = 0; 267 268 ret = pm_runtime_resume_and_get(ivc->dev); 269 if (ret) 270 goto err_return_buffers; 271 272 ret = video_device_pipeline_alloc_start(&ivc->vdev.dev); 273 if (ret) { 274 dev_err(ivc->dev, "failed to start media pipeline\n"); 275 goto err_pm_runtime_put; 276 } 277 278 rzv2h_ivc_format_configure(ivc); 279 280 queue_work(ivc->buffers.async_wq, &ivc->buffers.work); 281 282 return 0; 283 284err_pm_runtime_put: 285 pm_runtime_put(ivc->dev); 286err_return_buffers: 287 rzv2h_ivc_return_buffers(ivc, VB2_BUF_STATE_QUEUED); 288 289 return ret; 290} 291 292static void rzv2h_ivc_stop_streaming(struct vb2_queue *q) 293{ 294 struct rzv2h_ivc *ivc = vb2_get_drv_priv(q); 295 u32 val = 0; 296 297 rzv2h_ivc_write(ivc, RZV2H_IVC_REG_FM_STOP, 0x1); 298 readl_poll_timeout(ivc->base + RZV2H_IVC_REG_FM_STOP, 299 val, !val, 10 * USEC_PER_MSEC, 250 * USEC_PER_MSEC); 300 301 rzv2h_ivc_return_buffers(ivc, VB2_BUF_STATE_ERROR); 302 video_device_pipeline_stop(&ivc->vdev.dev); 303 pm_runtime_put_autosuspend(ivc->dev); 304} 305 306static const struct vb2_ops rzv2h_ivc_vb2_ops = { 307 .queue_setup = &rzv2h_ivc_queue_setup, 308 .buf_queue = &rzv2h_ivc_buf_queue, 309 .start_streaming = &rzv2h_ivc_start_streaming, 310 .stop_streaming = &rzv2h_ivc_stop_streaming, 311}; 312 313static const struct rzv2h_ivc_format * 314rzv2h_ivc_format_from_pixelformat(u32 fourcc) 315{ 316 for (unsigned int i = 0; i < ARRAY_SIZE(rzv2h_ivc_formats); i++) 317 if (fourcc == rzv2h_ivc_formats[i].fourcc) 318 return &rzv2h_ivc_formats[i]; 319 320 return &rzv2h_ivc_formats[0]; 321} 322 323static int rzv2h_ivc_enum_fmt_vid_out(struct file *file, void *fh, 324 struct v4l2_fmtdesc *f) 325{ 326 if (f->index >= ARRAY_SIZE(rzv2h_ivc_formats)) 327 return -EINVAL; 328 329 f->pixelformat = rzv2h_ivc_formats[f->index].fourcc; 330 return 0; 331} 332 333static int rzv2h_ivc_g_fmt_vid_out(struct file *file, void *fh, 334 struct v4l2_format *f) 335{ 336 struct rzv2h_ivc *ivc = video_drvdata(file); 337 338 f->fmt.pix_mp = ivc->format.pix; 339 340 return 0; 341} 342 343static void rzv2h_ivc_try_fmt(struct v4l2_pix_format_mplane *pix, 344 const struct rzv2h_ivc_format *fmt) 345{ 346 pix->pixelformat = fmt->fourcc; 347 348 pix->width = clamp(pix->width, RZV2H_IVC_MIN_WIDTH, 349 RZV2H_IVC_MAX_WIDTH); 350 pix->height = clamp(pix->height, RZV2H_IVC_MIN_HEIGHT, 351 RZV2H_IVC_MAX_HEIGHT); 352 353 pix->field = V4L2_FIELD_NONE; 354 pix->colorspace = V4L2_COLORSPACE_RAW; 355 pix->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(pix->colorspace); 356 pix->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, 357 pix->colorspace, 358 pix->ycbcr_enc); 359 360 v4l2_fill_pixfmt_mp(pix, pix->pixelformat, pix->width, pix->height); 361} 362 363static void rzv2h_ivc_set_format(struct rzv2h_ivc *ivc, 364 struct v4l2_pix_format_mplane *pix) 365{ 366 const struct rzv2h_ivc_format *fmt; 367 368 fmt = rzv2h_ivc_format_from_pixelformat(pix->pixelformat); 369 370 rzv2h_ivc_try_fmt(pix, fmt); 371 ivc->format.pix = *pix; 372 ivc->format.fmt = fmt; 373} 374 375static int rzv2h_ivc_s_fmt_vid_out(struct file *file, void *fh, 376 struct v4l2_format *f) 377{ 378 struct rzv2h_ivc *ivc = video_drvdata(file); 379 struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; 380 381 if (vb2_is_busy(&ivc->vdev.vb2q)) 382 return -EBUSY; 383 384 rzv2h_ivc_set_format(ivc, pix); 385 386 return 0; 387} 388 389static int rzv2h_ivc_try_fmt_vid_out(struct file *file, void *fh, 390 struct v4l2_format *f) 391{ 392 const struct rzv2h_ivc_format *fmt; 393 394 fmt = rzv2h_ivc_format_from_pixelformat(f->fmt.pix.pixelformat); 395 rzv2h_ivc_try_fmt(&f->fmt.pix_mp, fmt); 396 397 return 0; 398} 399 400static int rzv2h_ivc_querycap(struct file *file, void *fh, 401 struct v4l2_capability *cap) 402{ 403 strscpy(cap->driver, "rzv2h-ivc", sizeof(cap->driver)); 404 strscpy(cap->card, "Renesas Input Video Control", sizeof(cap->card)); 405 406 return 0; 407} 408 409static const struct v4l2_ioctl_ops rzv2h_ivc_v4l2_ioctl_ops = { 410 .vidioc_reqbufs = vb2_ioctl_reqbufs, 411 .vidioc_querybuf = vb2_ioctl_querybuf, 412 .vidioc_create_bufs = vb2_ioctl_create_bufs, 413 .vidioc_qbuf = vb2_ioctl_qbuf, 414 .vidioc_expbuf = vb2_ioctl_expbuf, 415 .vidioc_dqbuf = vb2_ioctl_dqbuf, 416 .vidioc_prepare_buf = vb2_ioctl_prepare_buf, 417 .vidioc_streamon = vb2_ioctl_streamon, 418 .vidioc_streamoff = vb2_ioctl_streamoff, 419 .vidioc_enum_fmt_vid_out = rzv2h_ivc_enum_fmt_vid_out, 420 .vidioc_g_fmt_vid_out_mplane = rzv2h_ivc_g_fmt_vid_out, 421 .vidioc_s_fmt_vid_out_mplane = rzv2h_ivc_s_fmt_vid_out, 422 .vidioc_try_fmt_vid_out_mplane = rzv2h_ivc_try_fmt_vid_out, 423 .vidioc_querycap = rzv2h_ivc_querycap, 424 .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 425 .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 426}; 427 428static const struct v4l2_file_operations rzv2h_ivc_v4l2_fops = { 429 .owner = THIS_MODULE, 430 .unlocked_ioctl = video_ioctl2, 431 .open = v4l2_fh_open, 432 .release = vb2_fop_release, 433 .poll = vb2_fop_poll, 434 .mmap = vb2_fop_mmap, 435}; 436 437int rzv2h_ivc_init_vdev(struct rzv2h_ivc *ivc, struct v4l2_device *v4l2_dev) 438{ 439 struct v4l2_pix_format_mplane pix = { }; 440 struct video_device *vdev; 441 struct vb2_queue *vb2q; 442 int ret; 443 444 spin_lock_init(&ivc->buffers.lock); 445 INIT_LIST_HEAD(&ivc->buffers.queue); 446 INIT_WORK(&ivc->buffers.work, rzv2h_ivc_transfer_buffer); 447 448 ivc->buffers.async_wq = alloc_workqueue("rzv2h-ivc", 0, 0); 449 if (!ivc->buffers.async_wq) 450 return -EINVAL; 451 452 /* Initialise vb2 queue */ 453 vb2q = &ivc->vdev.vb2q; 454 vb2q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; 455 vb2q->io_modes = VB2_MMAP | VB2_DMABUF; 456 vb2q->drv_priv = ivc; 457 vb2q->mem_ops = &vb2_dma_contig_memops; 458 vb2q->ops = &rzv2h_ivc_vb2_ops; 459 vb2q->buf_struct_size = sizeof(struct rzv2h_ivc_buf); 460 vb2q->min_queued_buffers = 0; 461 vb2q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 462 vb2q->lock = &ivc->lock; 463 vb2q->dev = ivc->dev; 464 465 ret = vb2_queue_init(vb2q); 466 if (ret) { 467 dev_err(ivc->dev, "vb2 queue init failed\n"); 468 goto err_destroy_workqueue; 469 } 470 471 /* Initialise Video Device */ 472 vdev = &ivc->vdev.dev; 473 strscpy(vdev->name, "rzv2h-ivc", sizeof(vdev->name)); 474 vdev->release = video_device_release_empty; 475 vdev->fops = &rzv2h_ivc_v4l2_fops; 476 vdev->ioctl_ops = &rzv2h_ivc_v4l2_ioctl_ops; 477 vdev->lock = &ivc->lock; 478 vdev->v4l2_dev = v4l2_dev; 479 vdev->queue = vb2q; 480 vdev->device_caps = V4L2_CAP_VIDEO_OUTPUT_MPLANE | V4L2_CAP_STREAMING; 481 vdev->vfl_dir = VFL_DIR_TX; 482 video_set_drvdata(vdev, ivc); 483 484 pix.pixelformat = V4L2_PIX_FMT_SRGGB16; 485 pix.width = RZV2H_IVC_DEFAULT_WIDTH; 486 pix.height = RZV2H_IVC_DEFAULT_HEIGHT; 487 rzv2h_ivc_set_format(ivc, &pix); 488 489 ivc->vdev.pad.flags = MEDIA_PAD_FL_SOURCE; 490 ret = media_entity_pads_init(&ivc->vdev.dev.entity, 1, &ivc->vdev.pad); 491 if (ret) 492 goto err_release_vb2q; 493 494 ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); 495 if (ret) { 496 dev_err(ivc->dev, "failed to register IVC video device\n"); 497 goto err_cleanup_vdev_entity; 498 } 499 500 ret = media_create_pad_link(&vdev->entity, 0, &ivc->subdev.sd.entity, 501 RZV2H_IVC_SUBDEV_SINK_PAD, 502 MEDIA_LNK_FL_ENABLED | 503 MEDIA_LNK_FL_IMMUTABLE); 504 if (ret) { 505 dev_err(ivc->dev, "failed to create media link\n"); 506 goto err_unregister_vdev; 507 } 508 509 return 0; 510 511err_unregister_vdev: 512 video_unregister_device(vdev); 513err_cleanup_vdev_entity: 514 media_entity_cleanup(&vdev->entity); 515err_release_vb2q: 516 vb2_queue_release(vb2q); 517err_destroy_workqueue: 518 destroy_workqueue(ivc->buffers.async_wq); 519 520 return ret; 521} 522 523void rzv2h_deinit_video_dev_and_queue(struct rzv2h_ivc *ivc) 524{ 525 struct video_device *vdev = &ivc->vdev.dev; 526 struct vb2_queue *vb2q = &ivc->vdev.vb2q; 527 528 vb2_video_unregister_device(vdev); 529 media_entity_cleanup(&vdev->entity); 530 vb2_queue_release(vb2q); 531}
~/
Read-only file preview.