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 203 list_for_each_entry(obj, &req->objects, list) { 204 struct vb2_buffer *vb; 205 206 if (vb2_request_object_is_buffer(obj)) { 207 vb = container_of(obj, struct vb2_buffer, req_obj); 208 ctx = vb2_get_drv_priv(vb->vb2_queue); 209 210 break; 211 } 212 } 213 214 if (!ctx) 215 return -ENOENT; 216 217 count = vb2_request_buffer_cnt(req); 218 if (!count) { 219 v4l2_info(&ctx->dev->v4l2_dev, 220 "No buffer was provided with the request\n"); 221 return -ENOENT; 222 } else if (count > 1) { 223 v4l2_info(&ctx->dev->v4l2_dev, 224 "More than one buffer was provided with the request\n"); 225 return -EINVAL; 226 } 227 228 parent_hdl = &ctx->hdl; 229 230 hdl = v4l2_ctrl_request_hdl_find(req, parent_hdl); 231 if (!hdl) { 232 v4l2_info(&ctx->dev->v4l2_dev, "Missing codec control(s)\n"); 233 return -ENOENT; 234 } 235 236 for (i = 0; i < CEDRUS_CONTROLS_COUNT; i++) { 237 if (cedrus_controls[i].codec != ctx->current_codec || 238 !cedrus_controls[i].required) 239 continue; 240 241 ctrl_test = v4l2_ctrl_request_hdl_ctrl_find(hdl, 242 cedrus_controls[i].cfg.id); 243 if (!ctrl_test) { 244 v4l2_info(&ctx->dev->v4l2_dev, 245 "Missing required codec control\n"); 246 return -ENOENT; 247 } 248 } 249 250 v4l2_ctrl_request_hdl_put(hdl); 251 252 return vb2_request_validate(req); 253 } 254 255 static int cedrus_open(struct file *file) 256 { 257 struct cedrus_dev *dev = video_drvdata(file); 258 struct cedrus_ctx *ctx = NULL; 259 int ret; 260 261 if (mutex_lock_interruptible(&dev->dev_mutex)) 262 return -ERESTARTSYS; 263 264 ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 265 if (!ctx) { 266 mutex_unlock(&dev->dev_mutex); 267 return -ENOMEM; 268 } 269 270 v4l2_fh_init(&ctx->fh, video_devdata(file)); 271 file->private_data = &ctx->fh; 272 ctx->dev = dev; 273 274 ret = cedrus_init_ctrls(dev, ctx); 275 if (ret) 276 goto err_free; 277 278 ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, 279 &cedrus_queue_init); 280 if (IS_ERR(ctx->fh.m2m_ctx)) { 281 ret = PTR_ERR(ctx->fh.m2m_ctx); 282 goto err_ctrls; 283 } 284 ctx->dst_fmt.pixelformat = V4L2_PIX_FMT_SUNXI_TILED_NV12; 285 cedrus_prepare_format(&ctx->dst_fmt); 286 ctx->src_fmt.pixelformat = V4L2_PIX_FMT_MPEG2_SLICE; 287 /* 288 * TILED_NV12 has more strict requirements, so copy the width and 289 * height to src_fmt to ensure that is matches the dst_fmt resolution. 290 */ 291 ctx->src_fmt.width = ctx->dst_fmt.width; 292 ctx->src_fmt.height = ctx->dst_fmt.height; 293 cedrus_prepare_format(&ctx->src_fmt); 294 295 v4l2_fh_add(&ctx->fh); 296 297 mutex_unlock(&dev->dev_mutex); 298 299 return 0; 300 301 err_ctrls: 302 v4l2_ctrl_handler_free(&ctx->hdl); 303 err_free: 304 kfree(ctx); 305 mutex_unlock(&dev->dev_mutex); 306 307 return ret; 308 } 309 310 static int cedrus_release(struct file *file) 311 { 312 struct cedrus_dev *dev = video_drvdata(file); 313 struct cedrus_ctx *ctx = container_of(file->private_data, 314 struct cedrus_ctx, fh); 315 316 mutex_lock(&dev->dev_mutex); 317 318 v4l2_fh_del(&ctx->fh); 319 v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); 320 321 v4l2_ctrl_handler_free(&ctx->hdl); 322 kfree(ctx->ctrls); 323 324 v4l2_fh_exit(&ctx->fh); 325 326 kfree(ctx); 327 328 mutex_unlock(&dev->dev_mutex); 329 330 return 0; 331 } 332 333 static const struct v4l2_file_operations cedrus_fops = { 334 .owner = THIS_MODULE, 335 .open = cedrus_open, 336 .release = cedrus_release, 337 .poll = v4l2_m2m_fop_poll, 338 .unlocked_ioctl = video_ioctl2, 339 .mmap = v4l2_m2m_fop_mmap, 340 }; 341 342 static const struct video_device cedrus_video_device = { 343 .name = CEDRUS_NAME, 344 .vfl_dir = VFL_DIR_M2M, 345 .fops = &cedrus_fops, 346 .ioctl_ops = &cedrus_ioctl_ops, 347 .minor = -1, 348 .release = video_device_release_empty, 349 .device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING, 350 }; 351 352 static const struct v4l2_m2m_ops cedrus_m2m_ops = { 353 .device_run = cedrus_device_run, 354 }; 355 356 static const struct media_device_ops cedrus_m2m_media_ops = { 357 .req_validate = cedrus_request_validate, 358 .req_queue = v4l2_m2m_request_queue, 359 }; 360 361 static int cedrus_probe(struct platform_device *pdev) 362 { 363 struct cedrus_dev *dev; 364 struct video_device *vfd; 365 int ret; 366 367 dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); 368 if (!dev) 369 return -ENOMEM; 370 371 dev->vfd = cedrus_video_device; 372 dev->dev = &pdev->dev; 373 dev->pdev = pdev; 374 375 ret = cedrus_hw_probe(dev); 376 if (ret) { 377 dev_err(&pdev->dev, "Failed to probe hardware\n"); 378 return ret; 379 } 380 381 dev->dec_ops[CEDRUS_CODEC_MPEG2] = &cedrus_dec_ops_mpeg2; 382 dev->dec_ops[CEDRUS_CODEC_H264] = &cedrus_dec_ops_h264; 383 dev->dec_ops[CEDRUS_CODEC_H265] = &cedrus_dec_ops_h265; 384 385 mutex_init(&dev->dev_mutex); 386 387 ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); 388 if (ret) { 389 dev_err(&pdev->dev, "Failed to register V4L2 device\n"); 390 return ret; 391 } 392 393 vfd = &dev->vfd; 394 vfd->lock = &dev->dev_mutex; 395 vfd->v4l2_dev = &dev->v4l2_dev; 396 397 snprintf(vfd->name, sizeof(vfd->name), "%s", cedrus_video_device.name); 398 video_set_drvdata(vfd, dev); 399 400 dev->m2m_dev = v4l2_m2m_init(&cedrus_m2m_ops); 401 if (IS_ERR(dev->m2m_dev)) { 402 v4l2_err(&dev->v4l2_dev, 403 "Failed to initialize V4L2 M2M device\n"); 404 ret = PTR_ERR(dev->m2m_dev); 405 406 goto err_v4l2; 407 } 408 409 dev->mdev.dev = &pdev->dev; 410 strscpy(dev->mdev.model, CEDRUS_NAME, sizeof(dev->mdev.model)); 411 strscpy(dev->mdev.bus_info, "platform:" CEDRUS_NAME, 412 sizeof(dev->mdev.bus_info)); 413 414 media_device_init(&dev->mdev); 415 dev->mdev.ops = &cedrus_m2m_media_ops; 416 dev->v4l2_dev.mdev = &dev->mdev; 417 418 ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); 419 if (ret) { 420 v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); 421 goto err_m2m; 422 } 423 424 v4l2_info(&dev->v4l2_dev, 425 "Device registered as /dev/video%d\n", vfd->num); 426 427 ret = v4l2_m2m_register_media_controller(dev->m2m_dev, vfd, 428 MEDIA_ENT_F_PROC_VIDEO_DECODER); 429 if (ret) { 430 v4l2_err(&dev->v4l2_dev, 431 "Failed to initialize V4L2 M2M media controller\n"); 432 goto err_video; 433 } 434 435 ret = media_device_register(&dev->mdev); 436 if (ret) { 437 v4l2_err(&dev->v4l2_dev, "Failed to register media device\n"); 438 goto err_m2m_mc; 439 } 440 441 platform_set_drvdata(pdev, dev); 442 443 return 0; 444 445 err_m2m_mc: 446 v4l2_m2m_unregister_media_controller(dev->m2m_dev); 447 err_video: 448 video_unregister_device(&dev->vfd); 449 err_m2m: 450 v4l2_m2m_release(dev->m2m_dev); 451 err_v4l2: 452 v4l2_device_unregister(&dev->v4l2_dev); 453 454 return ret; 455 } 456 457 static int cedrus_remove(struct platform_device *pdev) 458 { 459 struct cedrus_dev *dev = platform_get_drvdata(pdev); 460 461 if (media_devnode_is_registered(dev->mdev.devnode)) { 462 media_device_unregister(&dev->mdev); 463 v4l2_m2m_unregister_media_controller(dev->m2m_dev); 464 media_device_cleanup(&dev->mdev); 465 } 466 467 v4l2_m2m_release(dev->m2m_dev); 468 video_unregister_device(&dev->vfd); 469 v4l2_device_unregister(&dev->v4l2_dev); 470 471 cedrus_hw_remove(dev); 472 473 return 0; 474 } 475 476 static const struct cedrus_variant sun4i_a10_cedrus_variant = { 477 .mod_rate = 320000000, 478 }; 479 480 static const struct cedrus_variant sun5i_a13_cedrus_variant = { 481 .mod_rate = 320000000, 482 }; 483 484 static const struct cedrus_variant sun7i_a20_cedrus_variant = { 485 .mod_rate = 320000000, 486 }; 487 488 static const struct cedrus_variant sun8i_a33_cedrus_variant = { 489 .capabilities = CEDRUS_CAPABILITY_UNTILED, 490 .mod_rate = 320000000, 491 }; 492 493 static const struct cedrus_variant sun8i_h3_cedrus_variant = { 494 .capabilities = CEDRUS_CAPABILITY_UNTILED | 495 CEDRUS_CAPABILITY_H265_DEC, 496 .mod_rate = 402000000, 497 }; 498 499 static const struct cedrus_variant sun50i_a64_cedrus_variant = { 500 .capabilities = CEDRUS_CAPABILITY_UNTILED | 501 CEDRUS_CAPABILITY_H265_DEC, 502 .mod_rate = 402000000, 503 }; 504 505 static const struct cedrus_variant sun50i_h5_cedrus_variant = { 506 .capabilities = CEDRUS_CAPABILITY_UNTILED | 507 CEDRUS_CAPABILITY_H265_DEC, 508 .mod_rate = 402000000, 509 }; 510 511 static const struct cedrus_variant sun50i_h6_cedrus_variant = { 512 .capabilities = CEDRUS_CAPABILITY_UNTILED | 513 CEDRUS_CAPABILITY_H265_DEC, 514 .quirks = CEDRUS_QUIRK_NO_DMA_OFFSET, 515 .mod_rate = 600000000, 516 }; 517 518 static const struct of_device_id cedrus_dt_match[] = { 519 { 520 .compatible = "allwinner,sun4i-a10-video-engine", 521 .data = &sun4i_a10_cedrus_variant, 522 }, 523 { 524 .compatible = "allwinner,sun5i-a13-video-engine", 525 .data = &sun5i_a13_cedrus_variant, 526 }, 527 { 528 .compatible = "allwinner,sun7i-a20-video-engine", 529 .data = &sun7i_a20_cedrus_variant, 530 }, 531 { 532 .compatible = "allwinner,sun8i-a33-video-engine", 533 .data = &sun8i_a33_cedrus_variant, 534 }, 535 { 536 .compatible = "allwinner,sun8i-h3-video-engine", 537 .data = &sun8i_h3_cedrus_variant, 538 }, 539 { 540 .compatible = "allwinner,sun50i-a64-video-engine", 541 .data = &sun50i_a64_cedrus_variant, 542 }, 543 { 544 .compatible = "allwinner,sun50i-h5-video-engine", 545 .data = &sun50i_h5_cedrus_variant, 546 }, 547 { 548 .compatible = "allwinner,sun50i-h6-video-engine", 549 .data = &sun50i_h6_cedrus_variant, 550 }, 551 { /* sentinel */ } 552 }; 553 MODULE_DEVICE_TABLE(of, cedrus_dt_match); 554 555 static const struct dev_pm_ops cedrus_dev_pm_ops = { 556 SET_RUNTIME_PM_OPS(cedrus_hw_suspend, 557 cedrus_hw_resume, NULL) 558 }; 559 560 static struct platform_driver cedrus_driver = { 561 .probe = cedrus_probe, 562 .remove = cedrus_remove, 563 .driver = { 564 .name = CEDRUS_NAME, 565 .of_match_table = of_match_ptr(cedrus_dt_match), 566 .pm = &cedrus_dev_pm_ops, 567 }, 568 }; 569 module_platform_driver(cedrus_driver); 570 571 MODULE_LICENSE("GPL v2"); 572 MODULE_AUTHOR("Florent Revest <florent.revest@free-electrons.com>"); 573 MODULE_AUTHOR("Paul Kocialkowski <paul.kocialkowski@bootlin.com>"); 574 MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin.com>"); 575 MODULE_DESCRIPTION("Cedrus VPU driver"); 576