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/platform_device.h> 17 #include <linux/module.h> 18 #include <linux/of.h> 19 #include <linux/pm.h> 20 21 #include <media/v4l2-device.h> 22 #include <media/v4l2-ioctl.h> 23 #include <media/v4l2-ctrls.h> 24 #include <media/v4l2-mem2mem.h> 25 26 #include "cedrus.h" 27 #include "cedrus_video.h" 28 #include "cedrus_dec.h" 29 #include "cedrus_hw.h" 30 31 static const struct cedrus_control cedrus_controls[] = { 32 { 33 .cfg = { 34 .id = V4L2_CID_MPEG_VIDEO_MPEG2_SLICE_PARAMS, 35 }, 36 .codec = CEDRUS_CODEC_MPEG2, 37 }, 38 { 39 .cfg = { 40 .id = V4L2_CID_MPEG_VIDEO_MPEG2_QUANTIZATION, 41 }, 42 .codec = CEDRUS_CODEC_MPEG2, 43 }, 44 { 45 .cfg = { 46 .id = V4L2_CID_STATELESS_H264_DECODE_PARAMS, 47 }, 48 .codec = CEDRUS_CODEC_H264, 49 }, 50 { 51 .cfg = { 52 .id = V4L2_CID_STATELESS_H264_SLICE_PARAMS, 53 }, 54 .codec = CEDRUS_CODEC_H264, 55 }, 56 { 57 .cfg = { 58 .id = V4L2_CID_STATELESS_H264_SPS, 59 }, 60 .codec = CEDRUS_CODEC_H264, 61 }, 62 { 63 .cfg = { 64 .id = V4L2_CID_STATELESS_H264_PPS, 65 }, 66 .codec = CEDRUS_CODEC_H264, 67 }, 68 { 69 .cfg = { 70 .id = V4L2_CID_STATELESS_H264_SCALING_MATRIX, 71 }, 72 .codec = CEDRUS_CODEC_H264, 73 }, 74 { 75 .cfg = { 76 .id = V4L2_CID_STATELESS_H264_PRED_WEIGHTS, 77 }, 78 .codec = CEDRUS_CODEC_H264, 79 }, 80 { 81 .cfg = { 82 .id = V4L2_CID_STATELESS_H264_DECODE_MODE, 83 .max = V4L2_STATELESS_H264_DECODE_MODE_SLICE_BASED, 84 .def = V4L2_STATELESS_H264_DECODE_MODE_SLICE_BASED, 85 }, 86 .codec = CEDRUS_CODEC_H264, 87 }, 88 { 89 .cfg = { 90 .id = V4L2_CID_STATELESS_H264_START_CODE, 91 .max = V4L2_STATELESS_H264_START_CODE_NONE, 92 .def = V4L2_STATELESS_H264_START_CODE_NONE, 93 }, 94 .codec = CEDRUS_CODEC_H264, 95 }, 96 /* 97 * We only expose supported profiles information, 98 * and not levels as it's not clear what is supported 99 * for each hardware/core version. 100 * In any case, TRY/S_FMT will clamp the format resolution 101 * to the maximum supported. 102 */ 103 { 104 .cfg = { 105 .id = V4L2_CID_MPEG_VIDEO_H264_PROFILE, 106 .min = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, 107 .def = V4L2_MPEG_VIDEO_H264_PROFILE_MAIN, 108 .max = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH, 109 .menu_skip_mask = 110 BIT(V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED), 111 }, 112 .codec = CEDRUS_CODEC_H264, 113 }, 114 { 115 .cfg = { 116 .id = V4L2_CID_MPEG_VIDEO_HEVC_SPS, 117 }, 118 .codec = CEDRUS_CODEC_H265, 119 }, 120 { 121 .cfg = { 122 .id = V4L2_CID_MPEG_VIDEO_HEVC_PPS, 123 }, 124 .codec = CEDRUS_CODEC_H265, 125 }, 126 { 127 .cfg = { 128 .id = V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS, 129 }, 130 .codec = CEDRUS_CODEC_H265, 131 }, 132 { 133 .cfg = { 134 .id = V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE, 135 .max = V4L2_MPEG_VIDEO_HEVC_DECODE_MODE_SLICE_BASED, 136 .def = V4L2_MPEG_VIDEO_HEVC_DECODE_MODE_SLICE_BASED, 137 }, 138 .codec = CEDRUS_CODEC_H265, 139 }, 140 { 141 .cfg = { 142 .id = V4L2_CID_MPEG_VIDEO_HEVC_START_CODE, 143 .max = V4L2_MPEG_VIDEO_HEVC_START_CODE_NONE, 144 .def = V4L2_MPEG_VIDEO_HEVC_START_CODE_NONE, 145 }, 146 .codec = CEDRUS_CODEC_H265, 147 }, 148 { 149 .cfg = { 150 .id = V4L2_CID_STATELESS_VP8_FRAME, 151 }, 152 .codec = CEDRUS_CODEC_VP8, 153 }, 154 }; 155 156 #define CEDRUS_CONTROLS_COUNT ARRAY_SIZE(cedrus_controls) 157 158 void *cedrus_find_control_data(struct cedrus_ctx *ctx, u32 id) 159 { 160 unsigned int i; 161 162 for (i = 0; ctx->ctrls[i]; i++) 163 if (ctx->ctrls[i]->id == id) 164 return ctx->ctrls[i]->p_cur.p; 165 166 return NULL; 167 } 168 169 static int cedrus_init_ctrls(struct cedrus_dev *dev, struct cedrus_ctx *ctx) 170 { 171 struct v4l2_ctrl_handler *hdl = &ctx->hdl; 172 struct v4l2_ctrl *ctrl; 173 unsigned int ctrl_size; 174 unsigned int i; 175 176 v4l2_ctrl_handler_init(hdl, CEDRUS_CONTROLS_COUNT); 177 if (hdl->error) { 178 v4l2_err(&dev->v4l2_dev, 179 "Failed to initialize control handler\n"); 180 return hdl->error; 181 } 182 183 ctrl_size = sizeof(ctrl) * CEDRUS_CONTROLS_COUNT + 1; 184 185 ctx->ctrls = kzalloc(ctrl_size, GFP_KERNEL); 186 if (!ctx->ctrls) 187 return -ENOMEM; 188 189 for (i = 0; i < CEDRUS_CONTROLS_COUNT; i++) { 190 ctrl = v4l2_ctrl_new_custom(hdl, &cedrus_controls[i].cfg, 191 NULL); 192 if (hdl->error) { 193 v4l2_err(&dev->v4l2_dev, 194 "Failed to create new custom control\n"); 195 196 v4l2_ctrl_handler_free(hdl); 197 kfree(ctx->ctrls); 198 return hdl->error; 199 } 200 201 ctx->ctrls[i] = ctrl; 202 } 203 204 ctx->fh.ctrl_handler = hdl; 205 v4l2_ctrl_handler_setup(hdl); 206 207 return 0; 208 } 209 210 static int cedrus_request_validate(struct media_request *req) 211 { 212 struct media_request_object *obj; 213 struct cedrus_ctx *ctx = NULL; 214 unsigned int count; 215 216 list_for_each_entry(obj, &req->objects, list) { 217 struct vb2_buffer *vb; 218 219 if (vb2_request_object_is_buffer(obj)) { 220 vb = container_of(obj, struct vb2_buffer, req_obj); 221 ctx = vb2_get_drv_priv(vb->vb2_queue); 222 223 break; 224 } 225 } 226 227 if (!ctx) 228 return -ENOENT; 229 230 count = vb2_request_buffer_cnt(req); 231 if (!count) { 232 v4l2_info(&ctx->dev->v4l2_dev, 233 "No buffer was provided with the request\n"); 234 return -ENOENT; 235 } else if (count > 1) { 236 v4l2_info(&ctx->dev->v4l2_dev, 237 "More than one buffer was provided with the request\n"); 238 return -EINVAL; 239 } 240 241 return vb2_request_validate(req); 242 } 243 244 static int cedrus_open(struct file *file) 245 { 246 struct cedrus_dev *dev = video_drvdata(file); 247 struct cedrus_ctx *ctx = NULL; 248 int ret; 249 250 if (mutex_lock_interruptible(&dev->dev_mutex)) 251 return -ERESTARTSYS; 252 253 ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 254 if (!ctx) { 255 mutex_unlock(&dev->dev_mutex); 256 return -ENOMEM; 257 } 258 259 v4l2_fh_init(&ctx->fh, video_devdata(file)); 260 file->private_data = &ctx->fh; 261 ctx->dev = dev; 262 263 ret = cedrus_init_ctrls(dev, ctx); 264 if (ret) 265 goto err_free; 266 267 ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, 268 &cedrus_queue_init); 269 if (IS_ERR(ctx->fh.m2m_ctx)) { 270 ret = PTR_ERR(ctx->fh.m2m_ctx); 271 goto err_ctrls; 272 } 273 ctx->dst_fmt.pixelformat = V4L2_PIX_FMT_SUNXI_TILED_NV12; 274 cedrus_prepare_format(&ctx->dst_fmt); 275 ctx->src_fmt.pixelformat = V4L2_PIX_FMT_MPEG2_SLICE; 276 /* 277 * TILED_NV12 has more strict requirements, so copy the width and 278 * height to src_fmt to ensure that is matches the dst_fmt resolution. 279 */ 280 ctx->src_fmt.width = ctx->dst_fmt.width; 281 ctx->src_fmt.height = ctx->dst_fmt.height; 282 cedrus_prepare_format(&ctx->src_fmt); 283 284 v4l2_fh_add(&ctx->fh); 285 286 mutex_unlock(&dev->dev_mutex); 287 288 return 0; 289 290 err_ctrls: 291 v4l2_ctrl_handler_free(&ctx->hdl); 292 err_free: 293 kfree(ctx); 294 mutex_unlock(&dev->dev_mutex); 295 296 return ret; 297 } 298 299 static int cedrus_release(struct file *file) 300 { 301 struct cedrus_dev *dev = video_drvdata(file); 302 struct cedrus_ctx *ctx = container_of(file->private_data, 303 struct cedrus_ctx, fh); 304 305 mutex_lock(&dev->dev_mutex); 306 307 v4l2_fh_del(&ctx->fh); 308 v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); 309 310 v4l2_ctrl_handler_free(&ctx->hdl); 311 kfree(ctx->ctrls); 312 313 v4l2_fh_exit(&ctx->fh); 314 315 kfree(ctx); 316 317 mutex_unlock(&dev->dev_mutex); 318 319 return 0; 320 } 321 322 static const struct v4l2_file_operations cedrus_fops = { 323 .owner = THIS_MODULE, 324 .open = cedrus_open, 325 .release = cedrus_release, 326 .poll = v4l2_m2m_fop_poll, 327 .unlocked_ioctl = video_ioctl2, 328 .mmap = v4l2_m2m_fop_mmap, 329 }; 330 331 static const struct video_device cedrus_video_device = { 332 .name = CEDRUS_NAME, 333 .vfl_dir = VFL_DIR_M2M, 334 .fops = &cedrus_fops, 335 .ioctl_ops = &cedrus_ioctl_ops, 336 .minor = -1, 337 .release = video_device_release_empty, 338 .device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING, 339 }; 340 341 static const struct v4l2_m2m_ops cedrus_m2m_ops = { 342 .device_run = cedrus_device_run, 343 }; 344 345 static const struct media_device_ops cedrus_m2m_media_ops = { 346 .req_validate = cedrus_request_validate, 347 .req_queue = v4l2_m2m_request_queue, 348 }; 349 350 static int cedrus_probe(struct platform_device *pdev) 351 { 352 struct cedrus_dev *dev; 353 struct video_device *vfd; 354 int ret; 355 356 dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); 357 if (!dev) 358 return -ENOMEM; 359 360 dev->vfd = cedrus_video_device; 361 dev->dev = &pdev->dev; 362 dev->pdev = pdev; 363 364 ret = cedrus_hw_probe(dev); 365 if (ret) { 366 dev_err(&pdev->dev, "Failed to probe hardware\n"); 367 return ret; 368 } 369 370 dev->dec_ops[CEDRUS_CODEC_MPEG2] = &cedrus_dec_ops_mpeg2; 371 dev->dec_ops[CEDRUS_CODEC_H264] = &cedrus_dec_ops_h264; 372 dev->dec_ops[CEDRUS_CODEC_H265] = &cedrus_dec_ops_h265; 373 dev->dec_ops[CEDRUS_CODEC_VP8] = &cedrus_dec_ops_vp8; 374 375 mutex_init(&dev->dev_mutex); 376 377 ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); 378 if (ret) { 379 dev_err(&pdev->dev, "Failed to register V4L2 device\n"); 380 return ret; 381 } 382 383 vfd = &dev->vfd; 384 vfd->lock = &dev->dev_mutex; 385 vfd->v4l2_dev = &dev->v4l2_dev; 386 387 snprintf(vfd->name, sizeof(vfd->name), "%s", cedrus_video_device.name); 388 video_set_drvdata(vfd, dev); 389 390 dev->m2m_dev = v4l2_m2m_init(&cedrus_m2m_ops); 391 if (IS_ERR(dev->m2m_dev)) { 392 v4l2_err(&dev->v4l2_dev, 393 "Failed to initialize V4L2 M2M device\n"); 394 ret = PTR_ERR(dev->m2m_dev); 395 396 goto err_v4l2; 397 } 398 399 dev->mdev.dev = &pdev->dev; 400 strscpy(dev->mdev.model, CEDRUS_NAME, sizeof(dev->mdev.model)); 401 strscpy(dev->mdev.bus_info, "platform:" CEDRUS_NAME, 402 sizeof(dev->mdev.bus_info)); 403 404 media_device_init(&dev->mdev); 405 dev->mdev.ops = &cedrus_m2m_media_ops; 406 dev->v4l2_dev.mdev = &dev->mdev; 407 408 ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); 409 if (ret) { 410 v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); 411 goto err_m2m; 412 } 413 414 v4l2_info(&dev->v4l2_dev, 415 "Device registered as /dev/video%d\n", vfd->num); 416 417 ret = v4l2_m2m_register_media_controller(dev->m2m_dev, vfd, 418 MEDIA_ENT_F_PROC_VIDEO_DECODER); 419 if (ret) { 420 v4l2_err(&dev->v4l2_dev, 421 "Failed to initialize V4L2 M2M media controller\n"); 422 goto err_video; 423 } 424 425 ret = media_device_register(&dev->mdev); 426 if (ret) { 427 v4l2_err(&dev->v4l2_dev, "Failed to register media device\n"); 428 goto err_m2m_mc; 429 } 430 431 platform_set_drvdata(pdev, dev); 432 433 return 0; 434 435 err_m2m_mc: 436 v4l2_m2m_unregister_media_controller(dev->m2m_dev); 437 err_video: 438 video_unregister_device(&dev->vfd); 439 err_m2m: 440 v4l2_m2m_release(dev->m2m_dev); 441 err_v4l2: 442 v4l2_device_unregister(&dev->v4l2_dev); 443 444 return ret; 445 } 446 447 static int cedrus_remove(struct platform_device *pdev) 448 { 449 struct cedrus_dev *dev = platform_get_drvdata(pdev); 450 451 if (media_devnode_is_registered(dev->mdev.devnode)) { 452 media_device_unregister(&dev->mdev); 453 v4l2_m2m_unregister_media_controller(dev->m2m_dev); 454 media_device_cleanup(&dev->mdev); 455 } 456 457 v4l2_m2m_release(dev->m2m_dev); 458 video_unregister_device(&dev->vfd); 459 v4l2_device_unregister(&dev->v4l2_dev); 460 461 cedrus_hw_remove(dev); 462 463 return 0; 464 } 465 466 static const struct cedrus_variant sun4i_a10_cedrus_variant = { 467 .capabilities = CEDRUS_CAPABILITY_MPEG2_DEC | 468 CEDRUS_CAPABILITY_H264_DEC | 469 CEDRUS_CAPABILITY_VP8_DEC, 470 .mod_rate = 320000000, 471 }; 472 473 static const struct cedrus_variant sun5i_a13_cedrus_variant = { 474 .capabilities = CEDRUS_CAPABILITY_MPEG2_DEC | 475 CEDRUS_CAPABILITY_H264_DEC | 476 CEDRUS_CAPABILITY_VP8_DEC, 477 .mod_rate = 320000000, 478 }; 479 480 static const struct cedrus_variant sun7i_a20_cedrus_variant = { 481 .capabilities = CEDRUS_CAPABILITY_MPEG2_DEC | 482 CEDRUS_CAPABILITY_H264_DEC | 483 CEDRUS_CAPABILITY_VP8_DEC, 484 .mod_rate = 320000000, 485 }; 486 487 static const struct cedrus_variant sun8i_a33_cedrus_variant = { 488 .capabilities = CEDRUS_CAPABILITY_UNTILED | 489 CEDRUS_CAPABILITY_MPEG2_DEC | 490 CEDRUS_CAPABILITY_H264_DEC | 491 CEDRUS_CAPABILITY_VP8_DEC, 492 .mod_rate = 320000000, 493 }; 494 495 static const struct cedrus_variant sun8i_h3_cedrus_variant = { 496 .capabilities = CEDRUS_CAPABILITY_UNTILED | 497 CEDRUS_CAPABILITY_MPEG2_DEC | 498 CEDRUS_CAPABILITY_H264_DEC | 499 CEDRUS_CAPABILITY_H265_DEC | 500 CEDRUS_CAPABILITY_VP8_DEC, 501 .mod_rate = 402000000, 502 }; 503 504 static const struct cedrus_variant sun8i_v3s_cedrus_variant = { 505 .capabilities = CEDRUS_CAPABILITY_UNTILED | 506 CEDRUS_CAPABILITY_H264_DEC, 507 .mod_rate = 297000000, 508 }; 509 510 static const struct cedrus_variant sun8i_r40_cedrus_variant = { 511 .capabilities = CEDRUS_CAPABILITY_UNTILED | 512 CEDRUS_CAPABILITY_MPEG2_DEC | 513 CEDRUS_CAPABILITY_H264_DEC | 514 CEDRUS_CAPABILITY_VP8_DEC, 515 .mod_rate = 297000000, 516 }; 517 518 static const struct cedrus_variant sun50i_a64_cedrus_variant = { 519 .capabilities = CEDRUS_CAPABILITY_UNTILED | 520 CEDRUS_CAPABILITY_MPEG2_DEC | 521 CEDRUS_CAPABILITY_H264_DEC | 522 CEDRUS_CAPABILITY_H265_DEC | 523 CEDRUS_CAPABILITY_VP8_DEC, 524 .mod_rate = 402000000, 525 }; 526 527 static const struct cedrus_variant sun50i_h5_cedrus_variant = { 528 .capabilities = CEDRUS_CAPABILITY_UNTILED | 529 CEDRUS_CAPABILITY_MPEG2_DEC | 530 CEDRUS_CAPABILITY_H264_DEC | 531 CEDRUS_CAPABILITY_H265_DEC | 532 CEDRUS_CAPABILITY_VP8_DEC, 533 .mod_rate = 402000000, 534 }; 535 536 static const struct cedrus_variant sun50i_h6_cedrus_variant = { 537 .capabilities = CEDRUS_CAPABILITY_UNTILED | 538 CEDRUS_CAPABILITY_MPEG2_DEC | 539 CEDRUS_CAPABILITY_H264_DEC | 540 CEDRUS_CAPABILITY_H265_DEC | 541 CEDRUS_CAPABILITY_VP8_DEC, 542 .mod_rate = 600000000, 543 }; 544 545 static const struct of_device_id cedrus_dt_match[] = { 546 { 547 .compatible = "allwinner,sun4i-a10-video-engine", 548 .data = &sun4i_a10_cedrus_variant, 549 }, 550 { 551 .compatible = "allwinner,sun5i-a13-video-engine", 552 .data = &sun5i_a13_cedrus_variant, 553 }, 554 { 555 .compatible = "allwinner,sun7i-a20-video-engine", 556 .data = &sun7i_a20_cedrus_variant, 557 }, 558 { 559 .compatible = "allwinner,sun8i-a33-video-engine", 560 .data = &sun8i_a33_cedrus_variant, 561 }, 562 { 563 .compatible = "allwinner,sun8i-h3-video-engine", 564 .data = &sun8i_h3_cedrus_variant, 565 }, 566 { 567 .compatible = "allwinner,sun8i-v3s-video-engine", 568 .data = &sun8i_v3s_cedrus_variant, 569 }, 570 { 571 .compatible = "allwinner,sun8i-r40-video-engine", 572 .data = &sun8i_r40_cedrus_variant, 573 }, 574 { 575 .compatible = "allwinner,sun50i-a64-video-engine", 576 .data = &sun50i_a64_cedrus_variant, 577 }, 578 { 579 .compatible = "allwinner,sun50i-h5-video-engine", 580 .data = &sun50i_h5_cedrus_variant, 581 }, 582 { 583 .compatible = "allwinner,sun50i-h6-video-engine", 584 .data = &sun50i_h6_cedrus_variant, 585 }, 586 { /* sentinel */ } 587 }; 588 MODULE_DEVICE_TABLE(of, cedrus_dt_match); 589 590 static const struct dev_pm_ops cedrus_dev_pm_ops = { 591 SET_RUNTIME_PM_OPS(cedrus_hw_suspend, 592 cedrus_hw_resume, NULL) 593 }; 594 595 static struct platform_driver cedrus_driver = { 596 .probe = cedrus_probe, 597 .remove = cedrus_remove, 598 .driver = { 599 .name = CEDRUS_NAME, 600 .of_match_table = of_match_ptr(cedrus_dt_match), 601 .pm = &cedrus_dev_pm_ops, 602 }, 603 }; 604 module_platform_driver(cedrus_driver); 605 606 MODULE_LICENSE("GPL v2"); 607 MODULE_AUTHOR("Florent Revest <florent.revest@free-electrons.com>"); 608 MODULE_AUTHOR("Paul Kocialkowski <paul.kocialkowski@bootlin.com>"); 609 MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin.com>"); 610 MODULE_DESCRIPTION("Cedrus VPU driver"); 611