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
cedrus_try_ctrl(struct v4l2_ctrl * ctrl)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
cedrus_find_control_data(struct cedrus_ctx * ctx,u32 id)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
cedrus_init_ctrls(struct cedrus_dev * dev,struct cedrus_ctx * ctx)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
cedrus_request_validate(struct media_request * req)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
cedrus_open(struct file * file)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
cedrus_release(struct file * file)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
cedrus_probe(struct platform_device * pdev)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 INIT_DELAYED_WORK(&dev->watchdog_work, cedrus_watchdog);
443
444 ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
445 if (ret) {
446 dev_err(&pdev->dev, "Failed to register V4L2 device\n");
447 return ret;
448 }
449
450 vfd = &dev->vfd;
451 vfd->lock = &dev->dev_mutex;
452 vfd->v4l2_dev = &dev->v4l2_dev;
453
454 snprintf(vfd->name, sizeof(vfd->name), "%s", cedrus_video_device.name);
455 video_set_drvdata(vfd, dev);
456
457 dev->m2m_dev = v4l2_m2m_init(&cedrus_m2m_ops);
458 if (IS_ERR(dev->m2m_dev)) {
459 v4l2_err(&dev->v4l2_dev,
460 "Failed to initialize V4L2 M2M device\n");
461 ret = PTR_ERR(dev->m2m_dev);
462
463 goto err_v4l2;
464 }
465
466 dev->mdev.dev = &pdev->dev;
467 strscpy(dev->mdev.model, CEDRUS_NAME, sizeof(dev->mdev.model));
468 strscpy(dev->mdev.bus_info, "platform:" CEDRUS_NAME,
469 sizeof(dev->mdev.bus_info));
470
471 media_device_init(&dev->mdev);
472 dev->mdev.ops = &cedrus_m2m_media_ops;
473 dev->v4l2_dev.mdev = &dev->mdev;
474
475 ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0);
476 if (ret) {
477 v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
478 goto err_m2m;
479 }
480
481 v4l2_info(&dev->v4l2_dev,
482 "Device registered as /dev/video%d\n", vfd->num);
483
484 ret = v4l2_m2m_register_media_controller(dev->m2m_dev, vfd,
485 MEDIA_ENT_F_PROC_VIDEO_DECODER);
486 if (ret) {
487 v4l2_err(&dev->v4l2_dev,
488 "Failed to initialize V4L2 M2M media controller\n");
489 goto err_video;
490 }
491
492 ret = media_device_register(&dev->mdev);
493 if (ret) {
494 v4l2_err(&dev->v4l2_dev, "Failed to register media device\n");
495 goto err_m2m_mc;
496 }
497
498 platform_set_drvdata(pdev, dev);
499
500 return 0;
501
502 err_m2m_mc:
503 v4l2_m2m_unregister_media_controller(dev->m2m_dev);
504 err_video:
505 video_unregister_device(&dev->vfd);
506 err_m2m:
507 v4l2_m2m_release(dev->m2m_dev);
508 err_v4l2:
509 v4l2_device_unregister(&dev->v4l2_dev);
510
511 return ret;
512 }
513
cedrus_remove(struct platform_device * pdev)514 static int cedrus_remove(struct platform_device *pdev)
515 {
516 struct cedrus_dev *dev = platform_get_drvdata(pdev);
517
518 if (media_devnode_is_registered(dev->mdev.devnode)) {
519 media_device_unregister(&dev->mdev);
520 v4l2_m2m_unregister_media_controller(dev->m2m_dev);
521 media_device_cleanup(&dev->mdev);
522 }
523
524 v4l2_m2m_release(dev->m2m_dev);
525 video_unregister_device(&dev->vfd);
526 v4l2_device_unregister(&dev->v4l2_dev);
527
528 cedrus_hw_remove(dev);
529
530 return 0;
531 }
532
533 static const struct cedrus_variant sun4i_a10_cedrus_variant = {
534 .capabilities = CEDRUS_CAPABILITY_MPEG2_DEC |
535 CEDRUS_CAPABILITY_H264_DEC |
536 CEDRUS_CAPABILITY_VP8_DEC,
537 .mod_rate = 320000000,
538 };
539
540 static const struct cedrus_variant sun5i_a13_cedrus_variant = {
541 .capabilities = CEDRUS_CAPABILITY_MPEG2_DEC |
542 CEDRUS_CAPABILITY_H264_DEC |
543 CEDRUS_CAPABILITY_VP8_DEC,
544 .mod_rate = 320000000,
545 };
546
547 static const struct cedrus_variant sun7i_a20_cedrus_variant = {
548 .capabilities = CEDRUS_CAPABILITY_MPEG2_DEC |
549 CEDRUS_CAPABILITY_H264_DEC |
550 CEDRUS_CAPABILITY_VP8_DEC,
551 .mod_rate = 320000000,
552 };
553
554 static const struct cedrus_variant sun8i_a33_cedrus_variant = {
555 .capabilities = CEDRUS_CAPABILITY_UNTILED |
556 CEDRUS_CAPABILITY_MPEG2_DEC |
557 CEDRUS_CAPABILITY_H264_DEC |
558 CEDRUS_CAPABILITY_VP8_DEC,
559 .mod_rate = 320000000,
560 };
561
562 static const struct cedrus_variant sun8i_h3_cedrus_variant = {
563 .capabilities = CEDRUS_CAPABILITY_UNTILED |
564 CEDRUS_CAPABILITY_MPEG2_DEC |
565 CEDRUS_CAPABILITY_H264_DEC |
566 CEDRUS_CAPABILITY_H265_DEC |
567 CEDRUS_CAPABILITY_VP8_DEC,
568 .mod_rate = 402000000,
569 };
570
571 static const struct cedrus_variant sun8i_v3s_cedrus_variant = {
572 .capabilities = CEDRUS_CAPABILITY_UNTILED |
573 CEDRUS_CAPABILITY_H264_DEC,
574 .mod_rate = 297000000,
575 };
576
577 static const struct cedrus_variant sun8i_r40_cedrus_variant = {
578 .capabilities = CEDRUS_CAPABILITY_UNTILED |
579 CEDRUS_CAPABILITY_MPEG2_DEC |
580 CEDRUS_CAPABILITY_H264_DEC |
581 CEDRUS_CAPABILITY_VP8_DEC,
582 .mod_rate = 297000000,
583 };
584
585 static const struct cedrus_variant sun20i_d1_cedrus_variant = {
586 .capabilities = CEDRUS_CAPABILITY_UNTILED |
587 CEDRUS_CAPABILITY_MPEG2_DEC |
588 CEDRUS_CAPABILITY_H264_DEC |
589 CEDRUS_CAPABILITY_H265_DEC,
590 .mod_rate = 432000000,
591 };
592
593 static const struct cedrus_variant sun50i_a64_cedrus_variant = {
594 .capabilities = CEDRUS_CAPABILITY_UNTILED |
595 CEDRUS_CAPABILITY_MPEG2_DEC |
596 CEDRUS_CAPABILITY_H264_DEC |
597 CEDRUS_CAPABILITY_H265_DEC |
598 CEDRUS_CAPABILITY_VP8_DEC,
599 .mod_rate = 402000000,
600 };
601
602 static const struct cedrus_variant sun50i_h5_cedrus_variant = {
603 .capabilities = CEDRUS_CAPABILITY_UNTILED |
604 CEDRUS_CAPABILITY_MPEG2_DEC |
605 CEDRUS_CAPABILITY_H264_DEC |
606 CEDRUS_CAPABILITY_H265_DEC |
607 CEDRUS_CAPABILITY_VP8_DEC,
608 .mod_rate = 402000000,
609 };
610
611 static const struct cedrus_variant sun50i_h6_cedrus_variant = {
612 .capabilities = CEDRUS_CAPABILITY_UNTILED |
613 CEDRUS_CAPABILITY_MPEG2_DEC |
614 CEDRUS_CAPABILITY_H264_DEC |
615 CEDRUS_CAPABILITY_H265_DEC |
616 CEDRUS_CAPABILITY_H265_10_DEC |
617 CEDRUS_CAPABILITY_VP8_DEC,
618 .mod_rate = 600000000,
619 };
620
621 static const struct of_device_id cedrus_dt_match[] = {
622 {
623 .compatible = "allwinner,sun4i-a10-video-engine",
624 .data = &sun4i_a10_cedrus_variant,
625 },
626 {
627 .compatible = "allwinner,sun5i-a13-video-engine",
628 .data = &sun5i_a13_cedrus_variant,
629 },
630 {
631 .compatible = "allwinner,sun7i-a20-video-engine",
632 .data = &sun7i_a20_cedrus_variant,
633 },
634 {
635 .compatible = "allwinner,sun8i-a33-video-engine",
636 .data = &sun8i_a33_cedrus_variant,
637 },
638 {
639 .compatible = "allwinner,sun8i-h3-video-engine",
640 .data = &sun8i_h3_cedrus_variant,
641 },
642 {
643 .compatible = "allwinner,sun8i-v3s-video-engine",
644 .data = &sun8i_v3s_cedrus_variant,
645 },
646 {
647 .compatible = "allwinner,sun8i-r40-video-engine",
648 .data = &sun8i_r40_cedrus_variant,
649 },
650 {
651 .compatible = "allwinner,sun20i-d1-video-engine",
652 .data = &sun20i_d1_cedrus_variant,
653 },
654 {
655 .compatible = "allwinner,sun50i-a64-video-engine",
656 .data = &sun50i_a64_cedrus_variant,
657 },
658 {
659 .compatible = "allwinner,sun50i-h5-video-engine",
660 .data = &sun50i_h5_cedrus_variant,
661 },
662 {
663 .compatible = "allwinner,sun50i-h6-video-engine",
664 .data = &sun50i_h6_cedrus_variant,
665 },
666 { /* sentinel */ }
667 };
668 MODULE_DEVICE_TABLE(of, cedrus_dt_match);
669
670 static const struct dev_pm_ops cedrus_dev_pm_ops = {
671 SET_RUNTIME_PM_OPS(cedrus_hw_suspend,
672 cedrus_hw_resume, NULL)
673 };
674
675 static struct platform_driver cedrus_driver = {
676 .probe = cedrus_probe,
677 .remove = cedrus_remove,
678 .driver = {
679 .name = CEDRUS_NAME,
680 .of_match_table = of_match_ptr(cedrus_dt_match),
681 .pm = &cedrus_dev_pm_ops,
682 },
683 };
684 module_platform_driver(cedrus_driver);
685
686 MODULE_LICENSE("GPL v2");
687 MODULE_AUTHOR("Florent Revest <florent.revest@free-electrons.com>");
688 MODULE_AUTHOR("Paul Kocialkowski <paul.kocialkowski@bootlin.com>");
689 MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin.com>");
690 MODULE_DESCRIPTION("Cedrus VPU driver");
691