FILE
stf-video.c
File preview with syntax highlighting.
linux-6.19.14/drivers/staging/media/starfive/camss/stf-video.c
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * stf_video.c 4 * 5 * StarFive Camera Subsystem - V4L2 device node 6 * 7 * Copyright (C) 2021-2023 StarFive Technology Co., Ltd. 8 */ 9 10#include <linux/pm_runtime.h> 11#include <media/v4l2-ctrls.h> 12#include <media/v4l2-event.h> 13#include <media/v4l2-mc.h> 14#include <media/videobuf2-dma-contig.h> 15 16#include "stf-camss.h" 17#include "stf-video.h" 18 19/* ----------------------------------------------------------------------------- 20 * Helper functions 21 */ 22 23static inline struct stfcamss_buffer * 24to_stfcamss_buffer(struct vb2_v4l2_buffer *vbuf) 25{ 26 return container_of(vbuf, struct stfcamss_buffer, vb); 27} 28 29static const struct stfcamss_format_info * 30video_g_fi_by_index(struct stfcamss_video *video, int index) 31{ 32 if (index >= video->nformats) 33 return NULL; 34 35 return &video->formats[index]; 36} 37 38static const struct stfcamss_format_info * 39video_g_fi_by_mcode(struct stfcamss_video *video, u32 mcode) 40{ 41 unsigned int i; 42 43 for (i = 0; i < video->nformats; i++) { 44 if (video->formats[i].code == mcode) 45 return &video->formats[i]; 46 } 47 48 return NULL; 49} 50 51static const struct stfcamss_format_info * 52video_g_fi_by_pfmt(struct stfcamss_video *video, u32 pixelformat) 53{ 54 unsigned int i; 55 56 for (i = 0; i < video->nformats; i++) { 57 if (video->formats[i].pixelformat == pixelformat) 58 return &video->formats[i]; 59 } 60 61 return NULL; 62} 63 64static int __video_try_fmt(struct stfcamss_video *video, struct v4l2_format *f) 65{ 66 struct v4l2_pix_format *pix = &f->fmt.pix; 67 const struct stfcamss_format_info *fi; 68 u32 width, height; 69 u32 bpl; 70 unsigned int i; 71 72 fi = video_g_fi_by_pfmt(video, pix->pixelformat); 73 if (!fi) 74 fi = &video->formats[0]; /* default format */ 75 76 width = pix->width; 77 height = pix->height; 78 79 memset(pix, 0, sizeof(*pix)); 80 81 pix->pixelformat = fi->pixelformat; 82 pix->width = clamp_t(u32, width, STFCAMSS_FRAME_MIN_WIDTH, 83 STFCAMSS_FRAME_MAX_WIDTH); 84 pix->height = clamp_t(u32, height, STFCAMSS_FRAME_MIN_HEIGHT, 85 STFCAMSS_FRAME_MAX_HEIGHT); 86 bpl = pix->width * fi->bpp / 8; 87 bpl = ALIGN(bpl, video->bpl_alignment); 88 pix->bytesperline = bpl; 89 90 for (i = 0; i < fi->planes; ++i) 91 pix->sizeimage += bpl * pix->height / fi->vsub[i]; 92 93 pix->field = V4L2_FIELD_NONE; 94 pix->colorspace = V4L2_COLORSPACE_SRGB; 95 pix->flags = 0; 96 pix->ycbcr_enc = 97 V4L2_MAP_YCBCR_ENC_DEFAULT(pix->colorspace); 98 pix->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, 99 pix->colorspace, 100 pix->ycbcr_enc); 101 pix->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(pix->colorspace); 102 103 return 0; 104} 105 106static int stf_video_init_format(struct stfcamss_video *video) 107{ 108 int ret; 109 struct v4l2_format format = { 110 .type = video->type, 111 .fmt.pix = { 112 .width = 1920, 113 .height = 1080, 114 .pixelformat = V4L2_PIX_FMT_NV12, 115 }, 116 }; 117 118 ret = __video_try_fmt(video, &format); 119 120 if (ret < 0) 121 return ret; 122 123 video->active_fmt = format; 124 125 return 0; 126} 127 128/* ----------------------------------------------------------------------------- 129 * Video queue operations 130 */ 131 132static int video_queue_setup(struct vb2_queue *q, 133 unsigned int *num_buffers, 134 unsigned int *num_planes, 135 unsigned int sizes[], 136 struct device *alloc_devs[]) 137{ 138 struct stfcamss_video *video = vb2_get_drv_priv(q); 139 const struct v4l2_pix_format *format = &video->active_fmt.fmt.pix; 140 141 if (*num_planes) { 142 if (*num_planes != 1) 143 return -EINVAL; 144 145 if (sizes[0] < format->sizeimage) 146 return -EINVAL; 147 } else { 148 *num_planes = 1; 149 sizes[0] = format->sizeimage; 150 } 151 152 if (!sizes[0]) { 153 dev_dbg(video->stfcamss->dev, 154 "%s: error size is zero.\n", __func__); 155 return -EINVAL; 156 } 157 158 dev_dbg(video->stfcamss->dev, "planes = %d, size = %d\n", 159 *num_planes, sizes[0]); 160 161 return 0; 162} 163 164static int video_buf_init(struct vb2_buffer *vb) 165{ 166 struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 167 struct stfcamss_video *video = vb2_get_drv_priv(vb->vb2_queue); 168 struct stfcamss_buffer *buffer = to_stfcamss_buffer(vbuf); 169 const struct v4l2_pix_format *fmt = &video->active_fmt.fmt.pix; 170 dma_addr_t *paddr; 171 172 paddr = vb2_plane_cookie(vb, 0); 173 buffer->addr[0] = *paddr; 174 175 if (fmt->pixelformat == V4L2_PIX_FMT_NV12) 176 buffer->addr[1] = 177 buffer->addr[0] + fmt->bytesperline * fmt->height; 178 179 return 0; 180} 181 182static int video_buf_prepare(struct vb2_buffer *vb) 183{ 184 struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 185 struct stfcamss_video *video = vb2_get_drv_priv(vb->vb2_queue); 186 const struct v4l2_pix_format *fmt = &video->active_fmt.fmt.pix; 187 188 if (fmt->sizeimage > vb2_plane_size(vb, 0)) { 189 dev_dbg(video->stfcamss->dev, 190 "sizeimage = %u, plane size = %u\n", 191 fmt->sizeimage, (unsigned int)vb2_plane_size(vb, 0)); 192 return -EINVAL; 193 } 194 vb2_set_plane_payload(vb, 0, fmt->sizeimage); 195 196 vbuf->field = V4L2_FIELD_NONE; 197 198 return 0; 199} 200 201static void video_buf_queue(struct vb2_buffer *vb) 202{ 203 struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 204 struct stfcamss_video *video = vb2_get_drv_priv(vb->vb2_queue); 205 struct stfcamss_buffer *buffer = to_stfcamss_buffer(vbuf); 206 207 video->ops->queue_buffer(video, buffer); 208} 209 210static int video_get_subdev_format(struct stfcamss_video *video, 211 struct v4l2_subdev_format *fmt) 212{ 213 struct v4l2_subdev *subdev; 214 struct media_pad *pad; 215 struct media_entity *entity; 216 int ret; 217 218 entity = &video->vdev.entity; 219 while (1) { 220 pad = &entity->pads[0]; 221 if (!(pad->flags & MEDIA_PAD_FL_SINK)) 222 break; 223 224 pad = media_pad_remote_pad_first(pad); 225 if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) 226 break; 227 228 entity = pad->entity; 229 subdev = media_entity_to_v4l2_subdev(entity); 230 231 fmt->pad = pad->index; 232 233 ret = v4l2_subdev_call_state_active(subdev, pad, get_fmt, fmt); 234 if (ret < 0 && ret != -ENOIOCTLCMD) 235 return ret; 236 else if (!ret) 237 break; 238 } 239 240 return 0; 241} 242 243static int stf_video_check_format(struct stfcamss_video *video) 244{ 245 struct v4l2_pix_format *pix = &video->active_fmt.fmt.pix; 246 const struct stfcamss_format_info *fi; 247 int ret; 248 struct v4l2_subdev_format sd_fmt = { 249 .which = V4L2_SUBDEV_FORMAT_ACTIVE, 250 }; 251 252 ret = video_get_subdev_format(video, &sd_fmt); 253 if (ret < 0) 254 return ret; 255 256 fi = video_g_fi_by_mcode(video, sd_fmt.format.code); 257 if (!fi) 258 return -EINVAL; 259 260 if (pix->pixelformat != fi->pixelformat || 261 pix->height != sd_fmt.format.height || 262 pix->width != sd_fmt.format.width || 263 pix->field != sd_fmt.format.field) 264 return -EPIPE; 265 266 return 0; 267} 268 269static int video_start_streaming(struct vb2_queue *q, unsigned int count) 270{ 271 struct stfcamss_video *video = vb2_get_drv_priv(q); 272 struct video_device *vdev = &video->vdev; 273 int ret; 274 275 ret = video_device_pipeline_start(vdev, &video->stfcamss->pipe); 276 if (ret < 0) { 277 dev_err(video->stfcamss->dev, 278 "Failed to media_pipeline_start: %d\n", ret); 279 goto err_ret_buffers; 280 } 281 282 ret = pm_runtime_resume_and_get(video->stfcamss->dev); 283 if (ret < 0) { 284 dev_err(video->stfcamss->dev, "power up failed %d\n", ret); 285 goto err_pipeline_stop; 286 } 287 288 video->ops->start_streaming(video); 289 290 ret = v4l2_subdev_call(video->source_subdev, video, s_stream, true); 291 if (ret) { 292 dev_err(video->stfcamss->dev, "stream on failed\n"); 293 goto err_pm_put; 294 } 295 296 return 0; 297 298err_pm_put: 299 pm_runtime_put(video->stfcamss->dev); 300err_pipeline_stop: 301 video_device_pipeline_stop(vdev); 302err_ret_buffers: 303 video->ops->flush_buffers(video, VB2_BUF_STATE_QUEUED); 304 return ret; 305} 306 307static void video_stop_streaming(struct vb2_queue *q) 308{ 309 struct stfcamss_video *video = vb2_get_drv_priv(q); 310 struct video_device *vdev = &video->vdev; 311 312 video->ops->stop_streaming(video); 313 314 v4l2_subdev_call(video->source_subdev, video, s_stream, false); 315 316 pm_runtime_put(video->stfcamss->dev); 317 318 video_device_pipeline_stop(vdev); 319 video->ops->flush_buffers(video, VB2_BUF_STATE_ERROR); 320} 321 322static const struct vb2_ops stf_video_vb2_q_ops = { 323 .queue_setup = video_queue_setup, 324 .buf_init = video_buf_init, 325 .buf_prepare = video_buf_prepare, 326 .buf_queue = video_buf_queue, 327 .start_streaming = video_start_streaming, 328 .stop_streaming = video_stop_streaming, 329}; 330 331/* ----------------------------------------------------------------------------- 332 * V4L2 ioctls 333 */ 334 335static int video_querycap(struct file *file, void *fh, 336 struct v4l2_capability *cap) 337{ 338 strscpy(cap->driver, "starfive-camss", sizeof(cap->driver)); 339 strscpy(cap->card, "Starfive Camera Subsystem", sizeof(cap->card)); 340 341 return 0; 342} 343 344static int video_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f) 345{ 346 struct stfcamss_video *video = video_drvdata(file); 347 const struct stfcamss_format_info *fi; 348 349 if (f->index >= video->nformats) 350 return -EINVAL; 351 352 if (f->mbus_code) { 353 /* Each entry in formats[] table has unique mbus_code */ 354 if (f->index > 0) 355 return -EINVAL; 356 357 fi = video_g_fi_by_mcode(video, f->mbus_code); 358 } else { 359 fi = video_g_fi_by_index(video, f->index); 360 } 361 362 if (!fi) 363 return -EINVAL; 364 365 f->pixelformat = fi->pixelformat; 366 367 return 0; 368} 369 370static int video_enum_framesizes(struct file *file, void *fh, 371 struct v4l2_frmsizeenum *fsize) 372{ 373 struct stfcamss_video *video = video_drvdata(file); 374 unsigned int i; 375 376 if (fsize->index) 377 return -EINVAL; 378 379 for (i = 0; i < video->nformats; i++) { 380 if (video->formats[i].pixelformat == fsize->pixel_format) 381 break; 382 } 383 384 if (i == video->nformats) 385 return -EINVAL; 386 387 fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; 388 fsize->stepwise.min_width = STFCAMSS_FRAME_MIN_WIDTH; 389 fsize->stepwise.max_width = STFCAMSS_FRAME_MAX_WIDTH; 390 fsize->stepwise.min_height = STFCAMSS_FRAME_MIN_HEIGHT; 391 fsize->stepwise.max_height = STFCAMSS_FRAME_MAX_HEIGHT; 392 fsize->stepwise.step_width = 1; 393 fsize->stepwise.step_height = 1; 394 395 return 0; 396} 397 398static int video_g_fmt(struct file *file, void *fh, struct v4l2_format *f) 399{ 400 struct stfcamss_video *video = video_drvdata(file); 401 402 *f = video->active_fmt; 403 404 return 0; 405} 406 407static int video_s_fmt(struct file *file, void *fh, struct v4l2_format *f) 408{ 409 struct stfcamss_video *video = video_drvdata(file); 410 int ret; 411 412 if (vb2_is_busy(&video->vb2_q)) 413 return -EBUSY; 414 415 ret = __video_try_fmt(video, f); 416 if (ret < 0) 417 return ret; 418 419 video->active_fmt = *f; 420 421 return 0; 422} 423 424static int video_try_fmt(struct file *file, void *fh, struct v4l2_format *f) 425{ 426 struct stfcamss_video *video = video_drvdata(file); 427 428 return __video_try_fmt(video, f); 429} 430 431static const struct v4l2_ioctl_ops stf_vid_ioctl_ops = { 432 .vidioc_querycap = video_querycap, 433 .vidioc_enum_fmt_vid_cap = video_enum_fmt, 434 .vidioc_enum_framesizes = video_enum_framesizes, 435 .vidioc_g_fmt_vid_cap = video_g_fmt, 436 .vidioc_s_fmt_vid_cap = video_s_fmt, 437 .vidioc_try_fmt_vid_cap = video_try_fmt, 438 .vidioc_reqbufs = vb2_ioctl_reqbufs, 439 .vidioc_querybuf = vb2_ioctl_querybuf, 440 .vidioc_qbuf = vb2_ioctl_qbuf, 441 .vidioc_expbuf = vb2_ioctl_expbuf, 442 .vidioc_dqbuf = vb2_ioctl_dqbuf, 443 .vidioc_create_bufs = vb2_ioctl_create_bufs, 444 .vidioc_prepare_buf = vb2_ioctl_prepare_buf, 445 .vidioc_streamon = vb2_ioctl_streamon, 446 .vidioc_streamoff = vb2_ioctl_streamoff, 447}; 448 449/* ----------------------------------------------------------------------------- 450 * V4L2 file operations 451 */ 452 453static const struct v4l2_file_operations stf_vid_fops = { 454 .owner = THIS_MODULE, 455 .unlocked_ioctl = video_ioctl2, 456 .open = v4l2_fh_open, 457 .release = vb2_fop_release, 458 .poll = vb2_fop_poll, 459 .mmap = vb2_fop_mmap, 460 .read = vb2_fop_read, 461}; 462 463/* ----------------------------------------------------------------------------- 464 * STFCAMSS video core 465 */ 466 467static int stf_link_validate(struct media_link *link) 468{ 469 struct video_device *vdev = 470 media_entity_to_video_device(link->sink->entity); 471 struct stfcamss_video *video = video_get_drvdata(vdev); 472 int ret; 473 474 ret = stf_video_check_format(video); 475 476 return ret; 477} 478 479static const struct media_entity_operations stf_media_ops = { 480 .link_validate = stf_link_validate, 481}; 482 483static void stf_video_release(struct video_device *vdev) 484{ 485 struct stfcamss_video *video = video_get_drvdata(vdev); 486 487 media_entity_cleanup(&vdev->entity); 488 489 mutex_destroy(&video->q_lock); 490 mutex_destroy(&video->lock); 491} 492 493int stf_video_register(struct stfcamss_video *video, 494 struct v4l2_device *v4l2_dev, const char *name) 495{ 496 struct video_device *vdev = &video->vdev; 497 struct vb2_queue *q; 498 struct media_pad *pad = &video->pad; 499 int ret; 500 501 mutex_init(&video->q_lock); 502 mutex_init(&video->lock); 503 504 q = &video->vb2_q; 505 q->drv_priv = video; 506 q->mem_ops = &vb2_dma_contig_memops; 507 q->ops = &stf_video_vb2_q_ops; 508 q->type = video->type; 509 q->io_modes = VB2_DMABUF | VB2_MMAP; 510 q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 511 q->buf_struct_size = sizeof(struct stfcamss_buffer); 512 q->dev = video->stfcamss->dev; 513 q->lock = &video->q_lock; 514 q->min_queued_buffers = STFCAMSS_MIN_BUFFERS; 515 ret = vb2_queue_init(q); 516 if (ret < 0) { 517 dev_err(video->stfcamss->dev, 518 "Failed to init vb2 queue: %d\n", ret); 519 goto err_mutex_destroy; 520 } 521 522 pad->flags = MEDIA_PAD_FL_SINK; 523 ret = media_entity_pads_init(&vdev->entity, 1, pad); 524 if (ret < 0) { 525 dev_err(video->stfcamss->dev, 526 "Failed to init video entity: %d\n", ret); 527 goto err_mutex_destroy; 528 } 529 530 ret = stf_video_init_format(video); 531 if (ret < 0) { 532 dev_err(video->stfcamss->dev, 533 "Failed to init format: %d\n", ret); 534 goto err_media_cleanup; 535 } 536 537 vdev->fops = &stf_vid_fops; 538 vdev->ioctl_ops = &stf_vid_ioctl_ops; 539 vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; 540 vdev->entity.ops = &stf_media_ops; 541 vdev->vfl_dir = VFL_DIR_RX; 542 vdev->release = stf_video_release; 543 vdev->v4l2_dev = v4l2_dev; 544 vdev->queue = &video->vb2_q; 545 vdev->lock = &video->lock; 546 strscpy(vdev->name, name, sizeof(vdev->name)); 547 548 video_set_drvdata(vdev, video); 549 550 ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); 551 if (ret < 0) { 552 dev_err(video->stfcamss->dev, 553 "Failed to register video device: %d\n", ret); 554 goto err_media_cleanup; 555 } 556 557 return 0; 558 559err_media_cleanup: 560 media_entity_cleanup(&vdev->entity); 561err_mutex_destroy: 562 mutex_destroy(&video->lock); 563 mutex_destroy(&video->q_lock); 564 return ret; 565} 566 567void stf_video_unregister(struct stfcamss_video *video) 568{ 569 vb2_video_unregister_device(&video->vdev); 570}
~/
Read-only file preview.