FILE
cedrus_video.c
File preview with syntax highlighting.
linux-6.19.14/drivers/staging/media/sunxi/cedrus/cedrus_video.c
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Cedrus VPU driver 4 * 5 * Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com> 6 * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com> 7 * Copyright (C) 2018 Bootlin 8 * 9 * Based on the vim2m driver, that is: 10 * 11 * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd. 12 * Pawel Osciak, <pawel@osciak.com> 13 * Marek Szyprowski, <m.szyprowski@samsung.com> 14 */ 15 16#include <linux/pm_runtime.h> 17 18#include <media/videobuf2-dma-contig.h> 19#include <media/v4l2-device.h> 20#include <media/v4l2-ioctl.h> 21#include <media/v4l2-event.h> 22#include <media/v4l2-mem2mem.h> 23 24#include "cedrus.h" 25#include "cedrus_video.h" 26#include "cedrus_dec.h" 27#include "cedrus_hw.h" 28 29#define CEDRUS_DECODE_SRC BIT(0) 30#define CEDRUS_DECODE_DST BIT(1) 31 32#define CEDRUS_MIN_WIDTH 16U 33#define CEDRUS_MIN_HEIGHT 16U 34#define CEDRUS_MAX_WIDTH 4096U 35#define CEDRUS_MAX_HEIGHT 2304U 36 37static struct cedrus_format cedrus_formats[] = { 38 { 39 .pixelformat = V4L2_PIX_FMT_MPEG2_SLICE, 40 .directions = CEDRUS_DECODE_SRC, 41 .capabilities = CEDRUS_CAPABILITY_MPEG2_DEC, 42 }, 43 { 44 .pixelformat = V4L2_PIX_FMT_H264_SLICE, 45 .directions = CEDRUS_DECODE_SRC, 46 .capabilities = CEDRUS_CAPABILITY_H264_DEC, 47 }, 48 { 49 .pixelformat = V4L2_PIX_FMT_HEVC_SLICE, 50 .directions = CEDRUS_DECODE_SRC, 51 .capabilities = CEDRUS_CAPABILITY_H265_DEC, 52 }, 53 { 54 .pixelformat = V4L2_PIX_FMT_VP8_FRAME, 55 .directions = CEDRUS_DECODE_SRC, 56 .capabilities = CEDRUS_CAPABILITY_VP8_DEC, 57 }, 58 { 59 .pixelformat = V4L2_PIX_FMT_NV12, 60 .directions = CEDRUS_DECODE_DST, 61 .capabilities = CEDRUS_CAPABILITY_UNTILED, 62 }, 63 { 64 .pixelformat = V4L2_PIX_FMT_NV12_32L32, 65 .directions = CEDRUS_DECODE_DST, 66 }, 67 { 68 .pixelformat = V4L2_PIX_FMT_NV21, 69 .directions = CEDRUS_DECODE_DST, 70 .capabilities = CEDRUS_CAPABILITY_UNTILED, 71 }, 72 { 73 .pixelformat = V4L2_PIX_FMT_YUV420, 74 .directions = CEDRUS_DECODE_DST, 75 .capabilities = CEDRUS_CAPABILITY_UNTILED, 76 }, 77 { 78 .pixelformat = V4L2_PIX_FMT_YVU420, 79 .directions = CEDRUS_DECODE_DST, 80 .capabilities = CEDRUS_CAPABILITY_UNTILED, 81 }, 82}; 83 84#define CEDRUS_FORMATS_COUNT ARRAY_SIZE(cedrus_formats) 85 86static struct cedrus_format *cedrus_find_format(struct cedrus_ctx *ctx, 87 u32 pixelformat, u32 directions) 88{ 89 struct cedrus_format *first_valid_fmt = NULL; 90 struct cedrus_format *fmt; 91 unsigned int i; 92 93 for (i = 0; i < CEDRUS_FORMATS_COUNT; i++) { 94 fmt = &cedrus_formats[i]; 95 96 if (!cedrus_is_capable(ctx, fmt->capabilities) || 97 !(fmt->directions & directions)) 98 continue; 99 100 if (fmt->pixelformat == pixelformat) 101 break; 102 103 if (!first_valid_fmt) 104 first_valid_fmt = fmt; 105 } 106 107 if (i == CEDRUS_FORMATS_COUNT) 108 return first_valid_fmt; 109 110 return &cedrus_formats[i]; 111} 112 113void cedrus_prepare_format(struct v4l2_pix_format *pix_fmt) 114{ 115 unsigned int width = pix_fmt->width; 116 unsigned int height = pix_fmt->height; 117 unsigned int sizeimage = pix_fmt->sizeimage; 118 unsigned int bytesperline = pix_fmt->bytesperline; 119 120 pix_fmt->field = V4L2_FIELD_NONE; 121 122 /* Limit to hardware min/max. */ 123 width = clamp(width, CEDRUS_MIN_WIDTH, CEDRUS_MAX_WIDTH); 124 height = clamp(height, CEDRUS_MIN_HEIGHT, CEDRUS_MAX_HEIGHT); 125 126 switch (pix_fmt->pixelformat) { 127 case V4L2_PIX_FMT_MPEG2_SLICE: 128 case V4L2_PIX_FMT_H264_SLICE: 129 case V4L2_PIX_FMT_HEVC_SLICE: 130 case V4L2_PIX_FMT_VP8_FRAME: 131 /* Zero bytes per line for encoded source. */ 132 bytesperline = 0; 133 /* Choose some minimum size since this can't be 0 */ 134 sizeimage = max_t(u32, SZ_1K, sizeimage); 135 break; 136 137 case V4L2_PIX_FMT_NV12_32L32: 138 /* 32-aligned stride. */ 139 bytesperline = ALIGN(width, 32); 140 141 /* 32-aligned height. */ 142 height = ALIGN(height, 32); 143 144 /* Luma plane size. */ 145 sizeimage = bytesperline * height; 146 147 /* Chroma plane size. */ 148 sizeimage += bytesperline * ALIGN(height, 64) / 2; 149 150 break; 151 152 case V4L2_PIX_FMT_NV12: 153 case V4L2_PIX_FMT_NV21: 154 case V4L2_PIX_FMT_YUV420: 155 case V4L2_PIX_FMT_YVU420: 156 /* 16-aligned stride. */ 157 bytesperline = ALIGN(width, 16); 158 159 /* 16-aligned height. */ 160 height = ALIGN(height, 16); 161 162 /* Luma plane size. */ 163 sizeimage = bytesperline * height; 164 165 /* Chroma plane size. */ 166 sizeimage += bytesperline * height / 2; 167 168 break; 169 } 170 171 pix_fmt->width = width; 172 pix_fmt->height = height; 173 174 pix_fmt->bytesperline = bytesperline; 175 pix_fmt->sizeimage = sizeimage; 176} 177 178static int cedrus_querycap(struct file *file, void *priv, 179 struct v4l2_capability *cap) 180{ 181 strscpy(cap->driver, CEDRUS_NAME, sizeof(cap->driver)); 182 strscpy(cap->card, CEDRUS_NAME, sizeof(cap->card)); 183 snprintf(cap->bus_info, sizeof(cap->bus_info), 184 "platform:%s", CEDRUS_NAME); 185 186 return 0; 187} 188 189static int cedrus_enum_fmt(struct file *file, struct v4l2_fmtdesc *f, 190 u32 direction) 191{ 192 struct cedrus_ctx *ctx = cedrus_file2ctx(file); 193 unsigned int i, index; 194 195 /* Index among formats that match the requested direction. */ 196 index = 0; 197 198 for (i = 0; i < CEDRUS_FORMATS_COUNT; i++) { 199 if (!cedrus_is_capable(ctx, cedrus_formats[i].capabilities)) 200 continue; 201 202 if (!(cedrus_formats[i].directions & direction)) 203 continue; 204 205 if (index == f->index) 206 break; 207 208 index++; 209 } 210 211 /* Matched format. */ 212 if (i < CEDRUS_FORMATS_COUNT) { 213 f->pixelformat = cedrus_formats[i].pixelformat; 214 215 return 0; 216 } 217 218 return -EINVAL; 219} 220 221static int cedrus_enum_fmt_vid_cap(struct file *file, void *priv, 222 struct v4l2_fmtdesc *f) 223{ 224 return cedrus_enum_fmt(file, f, CEDRUS_DECODE_DST); 225} 226 227static int cedrus_enum_fmt_vid_out(struct file *file, void *priv, 228 struct v4l2_fmtdesc *f) 229{ 230 return cedrus_enum_fmt(file, f, CEDRUS_DECODE_SRC); 231} 232 233static int cedrus_g_fmt_vid_cap(struct file *file, void *priv, 234 struct v4l2_format *f) 235{ 236 struct cedrus_ctx *ctx = cedrus_file2ctx(file); 237 238 f->fmt.pix = ctx->dst_fmt; 239 return 0; 240} 241 242static int cedrus_g_fmt_vid_out(struct file *file, void *priv, 243 struct v4l2_format *f) 244{ 245 struct cedrus_ctx *ctx = cedrus_file2ctx(file); 246 247 f->fmt.pix = ctx->src_fmt; 248 return 0; 249} 250 251static int cedrus_try_fmt_vid_cap_p(struct cedrus_ctx *ctx, 252 struct v4l2_pix_format *pix_fmt) 253{ 254 struct cedrus_format *fmt = 255 cedrus_find_format(ctx, pix_fmt->pixelformat, 256 CEDRUS_DECODE_DST); 257 258 if (!fmt) 259 return -EINVAL; 260 261 pix_fmt->pixelformat = fmt->pixelformat; 262 pix_fmt->width = ctx->src_fmt.width; 263 pix_fmt->height = ctx->src_fmt.height; 264 cedrus_prepare_format(pix_fmt); 265 266 if (ctx->current_codec->extra_cap_size) 267 pix_fmt->sizeimage += 268 ctx->current_codec->extra_cap_size(ctx, pix_fmt); 269 270 return 0; 271} 272 273static int cedrus_try_fmt_vid_cap(struct file *file, void *priv, 274 struct v4l2_format *f) 275{ 276 return cedrus_try_fmt_vid_cap_p(cedrus_file2ctx(file), &f->fmt.pix); 277} 278 279static int cedrus_try_fmt_vid_out_p(struct cedrus_ctx *ctx, 280 struct v4l2_pix_format *pix_fmt) 281{ 282 struct cedrus_format *fmt = 283 cedrus_find_format(ctx, pix_fmt->pixelformat, 284 CEDRUS_DECODE_SRC); 285 286 if (!fmt) 287 return -EINVAL; 288 289 pix_fmt->pixelformat = fmt->pixelformat; 290 cedrus_prepare_format(pix_fmt); 291 292 return 0; 293} 294 295static int cedrus_try_fmt_vid_out(struct file *file, void *priv, 296 struct v4l2_format *f) 297{ 298 return cedrus_try_fmt_vid_out_p(cedrus_file2ctx(file), &f->fmt.pix); 299} 300 301static int cedrus_s_fmt_vid_cap(struct file *file, void *priv, 302 struct v4l2_format *f) 303{ 304 struct cedrus_ctx *ctx = cedrus_file2ctx(file); 305 struct vb2_queue *vq; 306 int ret; 307 308 vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); 309 if (vb2_is_busy(vq)) 310 return -EBUSY; 311 312 ret = cedrus_try_fmt_vid_cap(file, priv, f); 313 if (ret) 314 return ret; 315 316 ctx->dst_fmt = f->fmt.pix; 317 318 return 0; 319} 320 321void cedrus_reset_cap_format(struct cedrus_ctx *ctx) 322{ 323 ctx->dst_fmt.pixelformat = 0; 324 cedrus_try_fmt_vid_cap_p(ctx, &ctx->dst_fmt); 325} 326 327static int cedrus_s_fmt_vid_out_p(struct cedrus_ctx *ctx, 328 struct v4l2_pix_format *pix_fmt) 329{ 330 struct vb2_queue *vq; 331 int ret; 332 333 ret = cedrus_try_fmt_vid_out_p(ctx, pix_fmt); 334 if (ret) 335 return ret; 336 337 ctx->src_fmt = *pix_fmt; 338 339 vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); 340 341 switch (ctx->src_fmt.pixelformat) { 342 case V4L2_PIX_FMT_H264_SLICE: 343 case V4L2_PIX_FMT_HEVC_SLICE: 344 vq->subsystem_flags |= 345 VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF; 346 break; 347 default: 348 vq->subsystem_flags &= 349 ~VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF; 350 break; 351 } 352 353 switch (ctx->src_fmt.pixelformat) { 354 case V4L2_PIX_FMT_MPEG2_SLICE: 355 ctx->current_codec = &cedrus_dec_ops_mpeg2; 356 break; 357 case V4L2_PIX_FMT_H264_SLICE: 358 ctx->current_codec = &cedrus_dec_ops_h264; 359 break; 360 case V4L2_PIX_FMT_HEVC_SLICE: 361 ctx->current_codec = &cedrus_dec_ops_h265; 362 break; 363 case V4L2_PIX_FMT_VP8_FRAME: 364 ctx->current_codec = &cedrus_dec_ops_vp8; 365 break; 366 } 367 368 /* Propagate format information to capture. */ 369 ctx->dst_fmt.colorspace = pix_fmt->colorspace; 370 ctx->dst_fmt.xfer_func = pix_fmt->xfer_func; 371 ctx->dst_fmt.ycbcr_enc = pix_fmt->ycbcr_enc; 372 ctx->dst_fmt.quantization = pix_fmt->quantization; 373 cedrus_reset_cap_format(ctx); 374 375 return 0; 376} 377 378void cedrus_reset_out_format(struct cedrus_ctx *ctx) 379{ 380 ctx->src_fmt.pixelformat = 0; 381 cedrus_s_fmt_vid_out_p(ctx, &ctx->src_fmt); 382} 383 384static int cedrus_s_fmt_vid_out(struct file *file, void *priv, 385 struct v4l2_format *f) 386{ 387 struct cedrus_ctx *ctx = cedrus_file2ctx(file); 388 struct vb2_queue *vq; 389 struct vb2_queue *peer_vq; 390 391 vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); 392 /* 393 * In order to support dynamic resolution change, 394 * the decoder admits a resolution change, as long 395 * as the pixelformat remains. Can't be done if streaming. 396 */ 397 if (vb2_is_streaming(vq) || (vb2_is_busy(vq) && 398 f->fmt.pix.pixelformat != ctx->src_fmt.pixelformat)) 399 return -EBUSY; 400 /* 401 * Since format change on the OUTPUT queue will reset 402 * the CAPTURE queue, we can't allow doing so 403 * when the CAPTURE queue has buffers allocated. 404 */ 405 peer_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, 406 V4L2_BUF_TYPE_VIDEO_CAPTURE); 407 if (vb2_is_busy(peer_vq)) 408 return -EBUSY; 409 410 return cedrus_s_fmt_vid_out_p(cedrus_file2ctx(file), &f->fmt.pix); 411} 412 413const struct v4l2_ioctl_ops cedrus_ioctl_ops = { 414 .vidioc_querycap = cedrus_querycap, 415 416 .vidioc_enum_fmt_vid_cap = cedrus_enum_fmt_vid_cap, 417 .vidioc_g_fmt_vid_cap = cedrus_g_fmt_vid_cap, 418 .vidioc_try_fmt_vid_cap = cedrus_try_fmt_vid_cap, 419 .vidioc_s_fmt_vid_cap = cedrus_s_fmt_vid_cap, 420 421 .vidioc_enum_fmt_vid_out = cedrus_enum_fmt_vid_out, 422 .vidioc_g_fmt_vid_out = cedrus_g_fmt_vid_out, 423 .vidioc_try_fmt_vid_out = cedrus_try_fmt_vid_out, 424 .vidioc_s_fmt_vid_out = cedrus_s_fmt_vid_out, 425 426 .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, 427 .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, 428 .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, 429 .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, 430 .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, 431 .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, 432 .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, 433 434 .vidioc_streamon = v4l2_m2m_ioctl_streamon, 435 .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, 436 437 .vidioc_try_decoder_cmd = v4l2_m2m_ioctl_stateless_try_decoder_cmd, 438 .vidioc_decoder_cmd = v4l2_m2m_ioctl_stateless_decoder_cmd, 439 440 .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 441 .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 442}; 443 444static int cedrus_queue_setup(struct vb2_queue *vq, unsigned int *nbufs, 445 unsigned int *nplanes, unsigned int sizes[], 446 struct device *alloc_devs[]) 447{ 448 struct cedrus_ctx *ctx = vb2_get_drv_priv(vq); 449 struct v4l2_pix_format *pix_fmt; 450 451 if (V4L2_TYPE_IS_OUTPUT(vq->type)) 452 pix_fmt = &ctx->src_fmt; 453 else 454 pix_fmt = &ctx->dst_fmt; 455 456 if (*nplanes) { 457 if (sizes[0] < pix_fmt->sizeimage) 458 return -EINVAL; 459 } else { 460 sizes[0] = pix_fmt->sizeimage; 461 *nplanes = 1; 462 } 463 464 return 0; 465} 466 467static void cedrus_queue_cleanup(struct vb2_queue *vq, u32 state) 468{ 469 struct cedrus_ctx *ctx = vb2_get_drv_priv(vq); 470 struct vb2_v4l2_buffer *vbuf; 471 472 for (;;) { 473 if (V4L2_TYPE_IS_OUTPUT(vq->type)) 474 vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); 475 else 476 vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); 477 478 if (!vbuf) 479 return; 480 481 v4l2_ctrl_request_complete(vbuf->vb2_buf.req_obj.req, 482 &ctx->hdl); 483 v4l2_m2m_buf_done(vbuf, state); 484 } 485} 486 487static int cedrus_buf_out_validate(struct vb2_buffer *vb) 488{ 489 struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 490 491 vbuf->field = V4L2_FIELD_NONE; 492 return 0; 493} 494 495static int cedrus_buf_prepare(struct vb2_buffer *vb) 496{ 497 struct vb2_queue *vq = vb->vb2_queue; 498 struct cedrus_ctx *ctx = vb2_get_drv_priv(vq); 499 struct v4l2_pix_format *pix_fmt; 500 501 if (V4L2_TYPE_IS_OUTPUT(vq->type)) 502 pix_fmt = &ctx->src_fmt; 503 else 504 pix_fmt = &ctx->dst_fmt; 505 506 if (vb2_plane_size(vb, 0) < pix_fmt->sizeimage) 507 return -EINVAL; 508 509 /* 510 * Buffer's bytesused must be written by driver for CAPTURE buffers. 511 * (for OUTPUT buffers, if userspace passes 0 bytesused, v4l2-core sets 512 * it to buffer length). 513 */ 514 if (V4L2_TYPE_IS_CAPTURE(vq->type)) 515 vb2_set_plane_payload(vb, 0, pix_fmt->sizeimage); 516 517 return 0; 518} 519 520static int cedrus_start_streaming(struct vb2_queue *vq, unsigned int count) 521{ 522 struct cedrus_ctx *ctx = vb2_get_drv_priv(vq); 523 struct cedrus_dev *dev = ctx->dev; 524 int ret = 0; 525 526 if (V4L2_TYPE_IS_OUTPUT(vq->type)) { 527 ret = pm_runtime_resume_and_get(dev->dev); 528 if (ret < 0) 529 goto err_cleanup; 530 531 if (ctx->current_codec->start) { 532 ret = ctx->current_codec->start(ctx); 533 if (ret) 534 goto err_pm; 535 } 536 } 537 538 return 0; 539 540err_pm: 541 pm_runtime_put(dev->dev); 542err_cleanup: 543 cedrus_queue_cleanup(vq, VB2_BUF_STATE_QUEUED); 544 545 return ret; 546} 547 548static void cedrus_stop_streaming(struct vb2_queue *vq) 549{ 550 struct cedrus_ctx *ctx = vb2_get_drv_priv(vq); 551 struct cedrus_dev *dev = ctx->dev; 552 553 if (V4L2_TYPE_IS_OUTPUT(vq->type)) { 554 if (ctx->current_codec->stop) 555 ctx->current_codec->stop(ctx); 556 557 pm_runtime_put(dev->dev); 558 } 559 560 cedrus_queue_cleanup(vq, VB2_BUF_STATE_ERROR); 561} 562 563static void cedrus_buf_queue(struct vb2_buffer *vb) 564{ 565 struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 566 struct cedrus_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); 567 568 v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); 569} 570 571static void cedrus_buf_request_complete(struct vb2_buffer *vb) 572{ 573 struct cedrus_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); 574 575 v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->hdl); 576} 577 578static const struct vb2_ops cedrus_qops = { 579 .queue_setup = cedrus_queue_setup, 580 .buf_prepare = cedrus_buf_prepare, 581 .buf_queue = cedrus_buf_queue, 582 .buf_out_validate = cedrus_buf_out_validate, 583 .buf_request_complete = cedrus_buf_request_complete, 584 .start_streaming = cedrus_start_streaming, 585 .stop_streaming = cedrus_stop_streaming, 586}; 587 588int cedrus_queue_init(void *priv, struct vb2_queue *src_vq, 589 struct vb2_queue *dst_vq) 590{ 591 struct cedrus_ctx *ctx = priv; 592 int ret; 593 594 src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; 595 src_vq->io_modes = VB2_MMAP | VB2_DMABUF; 596 src_vq->drv_priv = ctx; 597 src_vq->buf_struct_size = sizeof(struct cedrus_buffer); 598 src_vq->ops = &cedrus_qops; 599 src_vq->mem_ops = &vb2_dma_contig_memops; 600 src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; 601 src_vq->lock = &ctx->dev->dev_mutex; 602 src_vq->dev = ctx->dev->dev; 603 src_vq->supports_requests = true; 604 src_vq->requires_requests = true; 605 606 ret = vb2_queue_init(src_vq); 607 if (ret) 608 return ret; 609 610 dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 611 dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; 612 dst_vq->drv_priv = ctx; 613 dst_vq->buf_struct_size = sizeof(struct cedrus_buffer); 614 dst_vq->ops = &cedrus_qops; 615 dst_vq->mem_ops = &vb2_dma_contig_memops; 616 dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; 617 dst_vq->lock = &ctx->dev->dev_mutex; 618 dst_vq->dev = ctx->dev->dev; 619 620 return vb2_queue_init(dst_vq); 621}
~/
Read-only file preview.