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