566 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * A virtual stateless decoder device for stateless uAPI development purposes.4 *5 * This tool's objective is to help the development and testing of userspace6 * applications that use the V4L2 stateless API to decode media.7 *8 * A userspace implementation can use visl to run a decoding loop even when no9 * hardware is available or when the kernel uAPI for the codec has not been10 * upstreamed yet. This can reveal bugs at an early stage.11 *12 * This driver can also trace the contents of the V4L2 controls submitted to it.13 * It can also dump the contents of the vb2 buffers through a debugfs14 * interface. This is in many ways similar to the tracing infrastructure15 * available for other popular encode/decode APIs out there and can help develop16 * a userspace application by using another (working) one as a reference.17 *18 * Note that no actual decoding of video frames is performed by visl. The V4L219 * test pattern generator is used to write various debug information to the20 * capture buffers instead.21 *22 * Copyright (C) 2022 Collabora, Ltd.23 *24 * Based on the vim2m driver, that is:25 *26 * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd.27 * Pawel Osciak, <pawel@osciak.com>28 * Marek Szyprowski, <m.szyprowski@samsung.com>29 *30 * Based on the vicodec driver, that is:31 *32 * Copyright 2018 Cisco Systems, Inc. and/or its affiliates. All rights reserved.33 *34 * Based on the Cedrus VPU driver, that is:35 *36 * Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com>37 * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com>38 * Copyright (C) 2018 Bootlin39 */40 41#include <linux/debugfs.h>42#include <linux/module.h>43#include <linux/platform_device.h>44#include <media/v4l2-ctrls.h>45#include <media/v4l2-device.h>46#include <media/v4l2-ioctl.h>47#include <media/v4l2-mem2mem.h>48 49#include "visl.h"50#include "visl-dec.h"51#include "visl-debugfs.h"52#include "visl-video.h"53 54unsigned int visl_debug;55module_param(visl_debug, uint, 0644);56MODULE_PARM_DESC(visl_debug, " activates debug info");57 58unsigned int visl_transtime_ms;59module_param(visl_transtime_ms, uint, 0644);60MODULE_PARM_DESC(visl_transtime_ms, " simulated process time in milliseconds.");61 62/*63 * dprintk can be slow through serial. This lets one limit the tracing to a64 * particular number of frames65 */66int visl_dprintk_frame_start = -1;67module_param(visl_dprintk_frame_start, int, 0444);68MODULE_PARM_DESC(visl_dprintk_frame_start,69 " a frame number to start tracing with dprintk");70 71unsigned int visl_dprintk_nframes;72module_param(visl_dprintk_nframes, uint, 0444);73MODULE_PARM_DESC(visl_dprintk_nframes,74 " the number of frames to trace with dprintk");75 76bool keep_bitstream_buffers;77module_param(keep_bitstream_buffers, bool, 0444);78MODULE_PARM_DESC(keep_bitstream_buffers,79 " keep bitstream buffers in debugfs after streaming is stopped");80 81int bitstream_trace_frame_start = -1;82module_param(bitstream_trace_frame_start, int, 0444);83MODULE_PARM_DESC(bitstream_trace_frame_start,84 " a frame number to start dumping the bitstream through debugfs");85 86unsigned int bitstream_trace_nframes;87module_param(bitstream_trace_nframes, uint, 0444);88MODULE_PARM_DESC(bitstream_trace_nframes,89 " the number of frames to dump the bitstream through debugfs");90 91bool tpg_verbose;92module_param(tpg_verbose, bool, 0644);93MODULE_PARM_DESC(tpg_verbose,94 " add more verbose information on the generated output frames");95 96static const struct visl_ctrl_desc visl_fwht_ctrl_descs[] = {97 {98 .cfg.id = V4L2_CID_STATELESS_FWHT_PARAMS,99 },100};101 102const struct visl_ctrls visl_fwht_ctrls = {103 .ctrls = visl_fwht_ctrl_descs,104 .num_ctrls = ARRAY_SIZE(visl_fwht_ctrl_descs)105};106 107static const struct visl_ctrl_desc visl_mpeg2_ctrl_descs[] = {108 {109 .cfg.id = V4L2_CID_STATELESS_MPEG2_SEQUENCE,110 },111 {112 .cfg.id = V4L2_CID_STATELESS_MPEG2_PICTURE,113 },114 {115 .cfg.id = V4L2_CID_STATELESS_MPEG2_QUANTISATION,116 },117};118 119const struct visl_ctrls visl_mpeg2_ctrls = {120 .ctrls = visl_mpeg2_ctrl_descs,121 .num_ctrls = ARRAY_SIZE(visl_mpeg2_ctrl_descs),122};123 124static const struct visl_ctrl_desc visl_vp8_ctrl_descs[] = {125 {126 .cfg.id = V4L2_CID_STATELESS_VP8_FRAME,127 },128};129 130const struct visl_ctrls visl_vp8_ctrls = {131 .ctrls = visl_vp8_ctrl_descs,132 .num_ctrls = ARRAY_SIZE(visl_vp8_ctrl_descs),133};134 135static const struct visl_ctrl_desc visl_vp9_ctrl_descs[] = {136 {137 .cfg.id = V4L2_CID_STATELESS_VP9_FRAME,138 },139 {140 .cfg.id = V4L2_CID_STATELESS_VP9_COMPRESSED_HDR,141 },142};143 144const struct visl_ctrls visl_vp9_ctrls = {145 .ctrls = visl_vp9_ctrl_descs,146 .num_ctrls = ARRAY_SIZE(visl_vp9_ctrl_descs),147};148 149static const struct visl_ctrl_desc visl_h264_ctrl_descs[] = {150 {151 .cfg.id = V4L2_CID_STATELESS_H264_DECODE_PARAMS,152 },153 {154 .cfg.id = V4L2_CID_STATELESS_H264_SPS,155 },156 {157 .cfg.id = V4L2_CID_STATELESS_H264_PPS,158 },159 {160 .cfg.id = V4L2_CID_STATELESS_H264_SCALING_MATRIX,161 },162 {163 .cfg.id = V4L2_CID_STATELESS_H264_DECODE_MODE,164 },165 {166 .cfg.id = V4L2_CID_STATELESS_H264_START_CODE,167 },168 {169 .cfg.id = V4L2_CID_STATELESS_H264_SLICE_PARAMS,170 },171 {172 .cfg.id = V4L2_CID_STATELESS_H264_PRED_WEIGHTS,173 },174};175 176const struct visl_ctrls visl_h264_ctrls = {177 .ctrls = visl_h264_ctrl_descs,178 .num_ctrls = ARRAY_SIZE(visl_h264_ctrl_descs),179};180 181static const struct visl_ctrl_desc visl_hevc_ctrl_descs[] = {182 {183 .cfg.id = V4L2_CID_STATELESS_HEVC_SPS,184 },185 {186 .cfg.id = V4L2_CID_STATELESS_HEVC_PPS,187 },188 {189 .cfg.id = V4L2_CID_STATELESS_HEVC_SLICE_PARAMS,190 /* The absolute maximum for level > 6 */191 .cfg.dims = { 600 },192 },193 {194 .cfg.id = V4L2_CID_STATELESS_HEVC_SCALING_MATRIX,195 },196 {197 .cfg.id = V4L2_CID_STATELESS_HEVC_DECODE_PARAMS,198 },199 {200 .cfg.id = V4L2_CID_STATELESS_HEVC_DECODE_MODE,201 },202 {203 .cfg.id = V4L2_CID_STATELESS_HEVC_START_CODE,204 },205 {206 .cfg.id = V4L2_CID_STATELESS_HEVC_ENTRY_POINT_OFFSETS,207 .cfg.dims = { 256 },208 .cfg.max = 0xffffffff,209 .cfg.step = 1,210 },211 212};213 214const struct visl_ctrls visl_hevc_ctrls = {215 .ctrls = visl_hevc_ctrl_descs,216 .num_ctrls = ARRAY_SIZE(visl_hevc_ctrl_descs),217};218 219static const struct visl_ctrl_desc visl_av1_ctrl_descs[] = {220 {221 .cfg.id = V4L2_CID_STATELESS_AV1_FRAME,222 },223 {224 .cfg.id = V4L2_CID_STATELESS_AV1_TILE_GROUP_ENTRY,225 .cfg.dims = { V4L2_AV1_MAX_TILE_COUNT },226 },227 {228 .cfg.id = V4L2_CID_STATELESS_AV1_SEQUENCE,229 },230 {231 .cfg.id = V4L2_CID_STATELESS_AV1_FILM_GRAIN,232 },233};234 235const struct visl_ctrls visl_av1_ctrls = {236 .ctrls = visl_av1_ctrl_descs,237 .num_ctrls = ARRAY_SIZE(visl_av1_ctrl_descs),238};239 240struct v4l2_ctrl *visl_find_control(struct visl_ctx *ctx, u32 id)241{242 struct v4l2_ctrl_handler *hdl = &ctx->hdl;243 244 return v4l2_ctrl_find(hdl, id);245}246 247void *visl_find_control_data(struct visl_ctx *ctx, u32 id)248{249 struct v4l2_ctrl *ctrl;250 251 ctrl = visl_find_control(ctx, id);252 if (ctrl)253 return ctrl->p_cur.p;254 255 return NULL;256}257 258u32 visl_control_num_elems(struct visl_ctx *ctx, u32 id)259{260 struct v4l2_ctrl *ctrl;261 262 ctrl = visl_find_control(ctx, id);263 if (ctrl)264 return ctrl->elems;265 266 return 0;267}268 269static void visl_device_release(struct video_device *vdev)270{271 struct visl_dev *dev = container_of(vdev, struct visl_dev, vfd);272 273 v4l2_device_unregister(&dev->v4l2_dev);274 v4l2_m2m_release(dev->m2m_dev);275 media_device_cleanup(&dev->mdev);276 visl_debugfs_deinit(dev);277 kfree(dev);278}279 280#define VISL_CONTROLS_COUNT ARRAY_SIZE(visl_controls)281 282static int visl_init_ctrls(struct visl_ctx *ctx)283{284 struct visl_dev *dev = ctx->dev;285 struct v4l2_ctrl_handler *hdl = &ctx->hdl;286 unsigned int ctrl_cnt = 0;287 unsigned int i;288 unsigned int j;289 const struct visl_ctrls *ctrls;290 291 for (i = 0; i < num_coded_fmts; i++)292 ctrl_cnt += visl_coded_fmts[i].ctrls->num_ctrls;293 294 v4l2_ctrl_handler_init(hdl, ctrl_cnt);295 296 for (i = 0; i < num_coded_fmts; i++) {297 ctrls = visl_coded_fmts[i].ctrls;298 for (j = 0; j < ctrls->num_ctrls; j++)299 v4l2_ctrl_new_custom(hdl, &ctrls->ctrls[j].cfg, NULL);300 }301 302 if (hdl->error) {303 v4l2_err(&dev->v4l2_dev,304 "Failed to initialize control handler\n");305 v4l2_ctrl_handler_free(hdl);306 return hdl->error;307 }308 309 ctx->fh.ctrl_handler = hdl;310 v4l2_ctrl_handler_setup(hdl);311 312 return 0;313}314 315static int visl_open(struct file *file)316{317 struct visl_dev *dev = video_drvdata(file);318 struct visl_ctx *ctx = NULL;319 int rc = 0;320 321 if (mutex_lock_interruptible(&dev->dev_mutex))322 return -ERESTARTSYS;323 ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);324 if (!ctx) {325 rc = -ENOMEM;326 goto unlock;327 }328 329 ctx->tpg_str_buf = kzalloc(TPG_STR_BUF_SZ, GFP_KERNEL);330 331 v4l2_fh_init(&ctx->fh, video_devdata(file));332 file->private_data = &ctx->fh;333 ctx->dev = dev;334 335 rc = visl_init_ctrls(ctx);336 if (rc)337 goto free_ctx;338 339 ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &visl_queue_init);340 341 mutex_init(&ctx->vb_mutex);342 343 if (IS_ERR(ctx->fh.m2m_ctx)) {344 rc = PTR_ERR(ctx->fh.m2m_ctx);345 goto free_hdl;346 }347 348 rc = visl_set_default_format(ctx);349 if (rc)350 goto free_m2m_ctx;351 352 v4l2_fh_add(&ctx->fh);353 354 dprintk(dev, "Created instance: %p, m2m_ctx: %p\n",355 ctx, ctx->fh.m2m_ctx);356 357 mutex_unlock(&dev->dev_mutex);358 return rc;359 360free_m2m_ctx:361 v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);362free_hdl:363 v4l2_ctrl_handler_free(&ctx->hdl);364 v4l2_fh_exit(&ctx->fh);365free_ctx:366 kfree(ctx->tpg_str_buf);367 kfree(ctx);368unlock:369 mutex_unlock(&dev->dev_mutex);370 return rc;371}372 373static int visl_release(struct file *file)374{375 struct visl_dev *dev = video_drvdata(file);376 struct visl_ctx *ctx = visl_file_to_ctx(file);377 378 dprintk(dev, "Releasing instance %p\n", ctx);379 380 tpg_free(&ctx->tpg);381 v4l2_fh_del(&ctx->fh);382 v4l2_fh_exit(&ctx->fh);383 v4l2_ctrl_handler_free(&ctx->hdl);384 mutex_lock(&dev->dev_mutex);385 v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);386 mutex_unlock(&dev->dev_mutex);387 388 kfree(ctx->tpg_str_buf);389 kfree(ctx);390 391 return 0;392}393 394static const struct v4l2_file_operations visl_fops = {395 .owner = THIS_MODULE,396 .open = visl_open,397 .release = visl_release,398 .poll = v4l2_m2m_fop_poll,399 .unlocked_ioctl = video_ioctl2,400 .mmap = v4l2_m2m_fop_mmap,401};402 403static const struct video_device visl_videodev = {404 .name = VISL_NAME,405 .vfl_dir = VFL_DIR_M2M,406 .fops = &visl_fops,407 .ioctl_ops = &visl_ioctl_ops,408 .minor = -1,409 .release = visl_device_release,410 .device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING,411};412 413static const struct v4l2_m2m_ops visl_m2m_ops = {414 .device_run = visl_device_run,415};416 417static const struct media_device_ops visl_m2m_media_ops = {418 .req_validate = visl_request_validate,419 .req_queue = v4l2_m2m_request_queue,420};421 422static int visl_probe(struct platform_device *pdev)423{424 struct visl_dev *dev;425 struct video_device *vfd;426 int ret;427 int rc;428 429 dev = kzalloc(sizeof(*dev), GFP_KERNEL);430 if (!dev)431 return -ENOMEM;432 433 ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);434 if (ret)435 goto error_visl_dev;436 437 mutex_init(&dev->dev_mutex);438 439 dev->vfd = visl_videodev;440 vfd = &dev->vfd;441 vfd->lock = &dev->dev_mutex;442 vfd->v4l2_dev = &dev->v4l2_dev;443 444 video_set_drvdata(vfd, dev);445 446 platform_set_drvdata(pdev, dev);447 448 dev->m2m_dev = v4l2_m2m_init(&visl_m2m_ops);449 if (IS_ERR(dev->m2m_dev)) {450 v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n");451 ret = PTR_ERR(dev->m2m_dev);452 dev->m2m_dev = NULL;453 goto error_dev;454 }455 456 dev->mdev.dev = &pdev->dev;457 strscpy(dev->mdev.model, "visl", sizeof(dev->mdev.model));458 strscpy(dev->mdev.bus_info, "platform:visl",459 sizeof(dev->mdev.bus_info));460 media_device_init(&dev->mdev);461 dev->mdev.ops = &visl_m2m_media_ops;462 dev->v4l2_dev.mdev = &dev->mdev;463 464 ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1);465 if (ret) {466 v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");467 goto error_m2m;468 }469 470 v4l2_info(&dev->v4l2_dev,471 "Device registered as /dev/video%d\n", vfd->num);472 473 ret = v4l2_m2m_register_media_controller(dev->m2m_dev, vfd,474 MEDIA_ENT_F_PROC_VIDEO_DECODER);475 if (ret) {476 v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem media controller\n");477 goto error_v4l2;478 }479 480 ret = media_device_register(&dev->mdev);481 if (ret) {482 v4l2_err(&dev->v4l2_dev, "Failed to register mem2mem media device\n");483 goto error_m2m_mc;484 }485 486 rc = visl_debugfs_init(dev);487 if (rc)488 dprintk(dev, "visl_debugfs_init failed: %d\n"489 "Continuing without debugfs support\n", rc);490 491 return 0;492 493error_m2m_mc:494 v4l2_m2m_unregister_media_controller(dev->m2m_dev);495error_v4l2:496 video_unregister_device(&dev->vfd);497 /* visl_device_release called by video_unregister_device to release various objects */498 return ret;499error_m2m:500 v4l2_m2m_release(dev->m2m_dev);501error_dev:502 v4l2_device_unregister(&dev->v4l2_dev);503error_visl_dev:504 kfree(dev);505 506 return ret;507}508 509static void visl_remove(struct platform_device *pdev)510{511 struct visl_dev *dev = platform_get_drvdata(pdev);512 513 v4l2_info(&dev->v4l2_dev, "Removing " VISL_NAME);514 515#ifdef CONFIG_MEDIA_CONTROLLER516 if (media_devnode_is_registered(dev->mdev.devnode)) {517 media_device_unregister(&dev->mdev);518 v4l2_m2m_unregister_media_controller(dev->m2m_dev);519 }520#endif521 video_unregister_device(&dev->vfd);522}523 524static struct platform_driver visl_pdrv = {525 .probe = visl_probe,526 .remove_new = visl_remove,527 .driver = {528 .name = VISL_NAME,529 },530};531 532static void visl_dev_release(struct device *dev) {}533 534static struct platform_device visl_pdev = {535 .name = VISL_NAME,536 .dev.release = visl_dev_release,537};538 539static void __exit visl_exit(void)540{541 platform_driver_unregister(&visl_pdrv);542 platform_device_unregister(&visl_pdev);543}544 545static int __init visl_init(void)546{547 int ret;548 549 ret = platform_device_register(&visl_pdev);550 if (ret)551 return ret;552 553 ret = platform_driver_register(&visl_pdrv);554 if (ret)555 platform_device_unregister(&visl_pdev);556 557 return ret;558}559 560MODULE_DESCRIPTION("Virtual stateless decoder device");561MODULE_AUTHOR("Daniel Almeida <daniel.almeida@collabora.com>");562MODULE_LICENSE("GPL");563 564module_init(visl_init);565module_exit(visl_exit);566