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