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_STATELESS_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_STATELESS_HEVC_SPS,
168 .ops = &cedrus_ctrl_ops,
169 },
170 .codec = CEDRUS_CODEC_H265,
171 },
172 {
173 .cfg = {
174 .id = V4L2_CID_STATELESS_HEVC_PPS,
175 },
176 .codec = CEDRUS_CODEC_H265,
177 },
178 {
179 .cfg = {
180 .id = V4L2_CID_STATELESS_HEVC_SLICE_PARAMS,
181 /* The driver can only handle 1 entry per slice for now */
182 .dims = { 1 },
183 },
184 .codec = CEDRUS_CODEC_H265,
185 },
186 {
187 .cfg = {
188 .id = V4L2_CID_STATELESS_HEVC_SCALING_MATRIX,
189 },
190 .codec = CEDRUS_CODEC_H265,
191 },
192 {
193 .cfg = {
194 .id = V4L2_CID_STATELESS_HEVC_ENTRY_POINT_OFFSETS,
195 /* maximum 256 entry point offsets per slice */
196 .dims = { 256 },
197 .max = 0xffffffff,
198 .step = 1,
199 },
200 .codec = CEDRUS_CODEC_H265,
201 },
202 {
203 .cfg = {
204 .id = V4L2_CID_STATELESS_HEVC_DECODE_MODE,
205 .max = V4L2_STATELESS_HEVC_DECODE_MODE_SLICE_BASED,
206 .def = V4L2_STATELESS_HEVC_DECODE_MODE_SLICE_BASED,
207 },
208 .codec = CEDRUS_CODEC_H265,
209 },
210 {
211 .cfg = {
212 .id = V4L2_CID_STATELESS_HEVC_START_CODE,
213 .max = V4L2_STATELESS_HEVC_START_CODE_NONE,
214 .def = V4L2_STATELESS_HEVC_START_CODE_NONE,
215 },
216 .codec = CEDRUS_CODEC_H265,
217 },
218 {
219 .cfg = {
220 .id = V4L2_CID_STATELESS_VP8_FRAME,
221 },
222 .codec = CEDRUS_CODEC_VP8,
223 },
224 {
225 .cfg = {
226 .id = V4L2_CID_STATELESS_HEVC_DECODE_PARAMS,
227 },
228 .codec = CEDRUS_CODEC_H265,
229 },
230 };
231
232 #define CEDRUS_CONTROLS_COUNT ARRAY_SIZE(cedrus_controls)
233
cedrus_find_control_data(struct cedrus_ctx * ctx,u32 id)234 void *cedrus_find_control_data(struct cedrus_ctx *ctx, u32 id)
235 {
236 unsigned int i;
237
238 for (i = 0; ctx->ctrls[i]; i++)
239 if (ctx->ctrls[i]->id == id)
240 return ctx->ctrls[i]->p_cur.p;
241
242 return NULL;
243 }
244
cedrus_get_num_of_controls(struct cedrus_ctx * ctx,u32 id)245 u32 cedrus_get_num_of_controls(struct cedrus_ctx *ctx, u32 id)
246 {
247 unsigned int i;
248
249 for (i = 0; ctx->ctrls[i]; i++)
250 if (ctx->ctrls[i]->id == id)
251 return ctx->ctrls[i]->elems;
252
253 return 0;
254 }
255
cedrus_init_ctrls(struct cedrus_dev * dev,struct cedrus_ctx * ctx)256 static int cedrus_init_ctrls(struct cedrus_dev *dev, struct cedrus_ctx *ctx)
257 {
258 struct v4l2_ctrl_handler *hdl = &ctx->hdl;
259 struct v4l2_ctrl *ctrl;
260 unsigned int ctrl_size;
261 unsigned int i;
262
263 v4l2_ctrl_handler_init(hdl, CEDRUS_CONTROLS_COUNT);
264 if (hdl->error) {
265 v4l2_err(&dev->v4l2_dev,
266 "Failed to initialize control handler: %d\n",
267 hdl->error);
268 return hdl->error;
269 }
270
271 ctrl_size = sizeof(ctrl) * CEDRUS_CONTROLS_COUNT + 1;
272
273 ctx->ctrls = kzalloc(ctrl_size, GFP_KERNEL);
274 if (!ctx->ctrls)
275 return -ENOMEM;
276
277 for (i = 0; i < CEDRUS_CONTROLS_COUNT; i++) {
278 ctrl = v4l2_ctrl_new_custom(hdl, &cedrus_controls[i].cfg,
279 NULL);
280 if (hdl->error) {
281 v4l2_err(&dev->v4l2_dev,
282 "Failed to create %s control: %d\n",
283 v4l2_ctrl_get_name(cedrus_controls[i].cfg.id),
284 hdl->error);
285
286 v4l2_ctrl_handler_free(hdl);
287 kfree(ctx->ctrls);
288 ctx->ctrls = NULL;
289 return hdl->error;
290 }
291
292 ctx->ctrls[i] = ctrl;
293 }
294
295 ctx->fh.ctrl_handler = hdl;
296 v4l2_ctrl_handler_setup(hdl);
297
298 return 0;
299 }
300
cedrus_request_validate(struct media_request * req)301 static int cedrus_request_validate(struct media_request *req)
302 {
303 struct media_request_object *obj;
304 struct cedrus_ctx *ctx = NULL;
305 unsigned int count;
306
307 list_for_each_entry(obj, &req->objects, list) {
308 struct vb2_buffer *vb;
309
310 if (vb2_request_object_is_buffer(obj)) {
311 vb = container_of(obj, struct vb2_buffer, req_obj);
312 ctx = vb2_get_drv_priv(vb->vb2_queue);
313
314 break;
315 }
316 }
317
318 if (!ctx)
319 return -ENOENT;
320
321 count = vb2_request_buffer_cnt(req);
322 if (!count) {
323 v4l2_info(&ctx->dev->v4l2_dev,
324 "No buffer was provided with the request\n");
325 return -ENOENT;
326 } else if (count > 1) {
327 v4l2_info(&ctx->dev->v4l2_dev,
328 "More than one buffer was provided with the request\n");
329 return -EINVAL;
330 }
331
332 return vb2_request_validate(req);
333 }
334
cedrus_open(struct file * file)335 static int cedrus_open(struct file *file)
336 {
337 struct cedrus_dev *dev = video_drvdata(file);
338 struct cedrus_ctx *ctx = NULL;
339 int ret;
340
341 if (mutex_lock_interruptible(&dev->dev_mutex))
342 return -ERESTARTSYS;
343
344 ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
345 if (!ctx) {
346 mutex_unlock(&dev->dev_mutex);
347 return -ENOMEM;
348 }
349
350 v4l2_fh_init(&ctx->fh, video_devdata(file));
351 file->private_data = &ctx->fh;
352 ctx->dev = dev;
353
354 ret = cedrus_init_ctrls(dev, ctx);
355 if (ret)
356 goto err_free;
357
358 ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx,
359 &cedrus_queue_init);
360 if (IS_ERR(ctx->fh.m2m_ctx)) {
361 ret = PTR_ERR(ctx->fh.m2m_ctx);
362 goto err_ctrls;
363 }
364 ctx->dst_fmt.pixelformat = V4L2_PIX_FMT_NV12_32L32;
365 cedrus_prepare_format(&ctx->dst_fmt);
366 ctx->src_fmt.pixelformat = V4L2_PIX_FMT_MPEG2_SLICE;
367 /*
368 * TILED_NV12 has more strict requirements, so copy the width and
369 * height to src_fmt to ensure that is matches the dst_fmt resolution.
370 */
371 ctx->src_fmt.width = ctx->dst_fmt.width;
372 ctx->src_fmt.height = ctx->dst_fmt.height;
373 cedrus_prepare_format(&ctx->src_fmt);
374
375 v4l2_fh_add(&ctx->fh);
376
377 mutex_unlock(&dev->dev_mutex);
378
379 return 0;
380
381 err_ctrls:
382 v4l2_ctrl_handler_free(&ctx->hdl);
383 err_free:
384 kfree(ctx);
385 mutex_unlock(&dev->dev_mutex);
386
387 return ret;
388 }
389
cedrus_release(struct file * file)390 static int cedrus_release(struct file *file)
391 {
392 struct cedrus_dev *dev = video_drvdata(file);
393 struct cedrus_ctx *ctx = container_of(file->private_data,
394 struct cedrus_ctx, fh);
395
396 mutex_lock(&dev->dev_mutex);
397
398 v4l2_fh_del(&ctx->fh);
399 v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
400
401 v4l2_ctrl_handler_free(&ctx->hdl);
402 kfree(ctx->ctrls);
403
404 v4l2_fh_exit(&ctx->fh);
405
406 kfree(ctx);
407
408 mutex_unlock(&dev->dev_mutex);
409
410 return 0;
411 }
412
413 static const struct v4l2_file_operations cedrus_fops = {
414 .owner = THIS_MODULE,
415 .open = cedrus_open,
416 .release = cedrus_release,
417 .poll = v4l2_m2m_fop_poll,
418 .unlocked_ioctl = video_ioctl2,
419 .mmap = v4l2_m2m_fop_mmap,
420 };
421
422 static const struct video_device cedrus_video_device = {
423 .name = CEDRUS_NAME,
424 .vfl_dir = VFL_DIR_M2M,
425 .fops = &cedrus_fops,
426 .ioctl_ops = &cedrus_ioctl_ops,
427 .minor = -1,
428 .release = video_device_release_empty,
429 .device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING,
430 };
431
432 static const struct v4l2_m2m_ops cedrus_m2m_ops = {
433 .device_run = cedrus_device_run,
434 };
435
436 static const struct media_device_ops cedrus_m2m_media_ops = {
437 .req_validate = cedrus_request_validate,
438 .req_queue = v4l2_m2m_request_queue,
439 };
440
cedrus_probe(struct platform_device * pdev)441 static int cedrus_probe(struct platform_device *pdev)
442 {
443 struct cedrus_dev *dev;
444 struct video_device *vfd;
445 int ret;
446
447 dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
448 if (!dev)
449 return -ENOMEM;
450
451 platform_set_drvdata(pdev, dev);
452
453 dev->vfd = cedrus_video_device;
454 dev->dev = &pdev->dev;
455 dev->pdev = pdev;
456
457 ret = cedrus_hw_probe(dev);
458 if (ret) {
459 dev_err(&pdev->dev, "Failed to probe hardware\n");
460 return ret;
461 }
462
463 dev->dec_ops[CEDRUS_CODEC_MPEG2] = &cedrus_dec_ops_mpeg2;
464 dev->dec_ops[CEDRUS_CODEC_H264] = &cedrus_dec_ops_h264;
465 dev->dec_ops[CEDRUS_CODEC_H265] = &cedrus_dec_ops_h265;
466 dev->dec_ops[CEDRUS_CODEC_VP8] = &cedrus_dec_ops_vp8;
467
468 mutex_init(&dev->dev_mutex);
469
470 INIT_DELAYED_WORK(&dev->watchdog_work, cedrus_watchdog);
471
472 ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
473 if (ret) {
474 dev_err(&pdev->dev, "Failed to register V4L2 device\n");
475 return ret;
476 }
477
478 vfd = &dev->vfd;
479 vfd->lock = &dev->dev_mutex;
480 vfd->v4l2_dev = &dev->v4l2_dev;
481
482 snprintf(vfd->name, sizeof(vfd->name), "%s", cedrus_video_device.name);
483 video_set_drvdata(vfd, dev);
484
485 dev->m2m_dev = v4l2_m2m_init(&cedrus_m2m_ops);
486 if (IS_ERR(dev->m2m_dev)) {
487 v4l2_err(&dev->v4l2_dev,
488 "Failed to initialize V4L2 M2M device\n");
489 ret = PTR_ERR(dev->m2m_dev);
490
491 goto err_v4l2;
492 }
493
494 dev->mdev.dev = &pdev->dev;
495 strscpy(dev->mdev.model, CEDRUS_NAME, sizeof(dev->mdev.model));
496 strscpy(dev->mdev.bus_info, "platform:" CEDRUS_NAME,
497 sizeof(dev->mdev.bus_info));
498
499 media_device_init(&dev->mdev);
500 dev->mdev.ops = &cedrus_m2m_media_ops;
501 dev->v4l2_dev.mdev = &dev->mdev;
502
503 ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0);
504 if (ret) {
505 v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
506 goto err_m2m;
507 }
508
509 v4l2_info(&dev->v4l2_dev,
510 "Device registered as /dev/video%d\n", vfd->num);
511
512 ret = v4l2_m2m_register_media_controller(dev->m2m_dev, vfd,
513 MEDIA_ENT_F_PROC_VIDEO_DECODER);
514 if (ret) {
515 v4l2_err(&dev->v4l2_dev,
516 "Failed to initialize V4L2 M2M media controller\n");
517 goto err_video;
518 }
519
520 ret = media_device_register(&dev->mdev);
521 if (ret) {
522 v4l2_err(&dev->v4l2_dev, "Failed to register media device\n");
523 goto err_m2m_mc;
524 }
525
526 return 0;
527
528 err_m2m_mc:
529 v4l2_m2m_unregister_media_controller(dev->m2m_dev);
530 err_video:
531 video_unregister_device(&dev->vfd);
532 err_m2m:
533 v4l2_m2m_release(dev->m2m_dev);
534 err_v4l2:
535 v4l2_device_unregister(&dev->v4l2_dev);
536
537 return ret;
538 }
539
cedrus_remove(struct platform_device * pdev)540 static int cedrus_remove(struct platform_device *pdev)
541 {
542 struct cedrus_dev *dev = platform_get_drvdata(pdev);
543
544 if (media_devnode_is_registered(dev->mdev.devnode)) {
545 media_device_unregister(&dev->mdev);
546 v4l2_m2m_unregister_media_controller(dev->m2m_dev);
547 media_device_cleanup(&dev->mdev);
548 }
549
550 v4l2_m2m_release(dev->m2m_dev);
551 video_unregister_device(&dev->vfd);
552 v4l2_device_unregister(&dev->v4l2_dev);
553
554 cedrus_hw_remove(dev);
555
556 return 0;
557 }
558
559 static const struct cedrus_variant sun4i_a10_cedrus_variant = {
560 .capabilities = CEDRUS_CAPABILITY_MPEG2_DEC |
561 CEDRUS_CAPABILITY_H264_DEC |
562 CEDRUS_CAPABILITY_VP8_DEC,
563 .mod_rate = 320000000,
564 };
565
566 static const struct cedrus_variant sun5i_a13_cedrus_variant = {
567 .capabilities = CEDRUS_CAPABILITY_MPEG2_DEC |
568 CEDRUS_CAPABILITY_H264_DEC |
569 CEDRUS_CAPABILITY_VP8_DEC,
570 .mod_rate = 320000000,
571 };
572
573 static const struct cedrus_variant sun7i_a20_cedrus_variant = {
574 .capabilities = CEDRUS_CAPABILITY_MPEG2_DEC |
575 CEDRUS_CAPABILITY_H264_DEC |
576 CEDRUS_CAPABILITY_VP8_DEC,
577 .mod_rate = 320000000,
578 };
579
580 static const struct cedrus_variant sun8i_a33_cedrus_variant = {
581 .capabilities = CEDRUS_CAPABILITY_UNTILED |
582 CEDRUS_CAPABILITY_MPEG2_DEC |
583 CEDRUS_CAPABILITY_H264_DEC |
584 CEDRUS_CAPABILITY_VP8_DEC,
585 .mod_rate = 320000000,
586 };
587
588 static const struct cedrus_variant sun8i_h3_cedrus_variant = {
589 .capabilities = CEDRUS_CAPABILITY_UNTILED |
590 CEDRUS_CAPABILITY_MPEG2_DEC |
591 CEDRUS_CAPABILITY_H264_DEC |
592 CEDRUS_CAPABILITY_H265_DEC |
593 CEDRUS_CAPABILITY_VP8_DEC,
594 .mod_rate = 402000000,
595 };
596
597 static const struct cedrus_variant sun8i_v3s_cedrus_variant = {
598 .capabilities = CEDRUS_CAPABILITY_UNTILED |
599 CEDRUS_CAPABILITY_H264_DEC,
600 .mod_rate = 297000000,
601 };
602
603 static const struct cedrus_variant sun8i_r40_cedrus_variant = {
604 .capabilities = CEDRUS_CAPABILITY_UNTILED |
605 CEDRUS_CAPABILITY_MPEG2_DEC |
606 CEDRUS_CAPABILITY_H264_DEC |
607 CEDRUS_CAPABILITY_VP8_DEC,
608 .mod_rate = 297000000,
609 };
610
611 static const struct cedrus_variant sun20i_d1_cedrus_variant = {
612 .capabilities = CEDRUS_CAPABILITY_UNTILED |
613 CEDRUS_CAPABILITY_MPEG2_DEC |
614 CEDRUS_CAPABILITY_H264_DEC |
615 CEDRUS_CAPABILITY_H265_DEC,
616 .mod_rate = 432000000,
617 };
618
619 static const struct cedrus_variant sun50i_a64_cedrus_variant = {
620 .capabilities = CEDRUS_CAPABILITY_UNTILED |
621 CEDRUS_CAPABILITY_MPEG2_DEC |
622 CEDRUS_CAPABILITY_H264_DEC |
623 CEDRUS_CAPABILITY_H265_DEC |
624 CEDRUS_CAPABILITY_VP8_DEC,
625 .mod_rate = 402000000,
626 };
627
628 static const struct cedrus_variant sun50i_h5_cedrus_variant = {
629 .capabilities = CEDRUS_CAPABILITY_UNTILED |
630 CEDRUS_CAPABILITY_MPEG2_DEC |
631 CEDRUS_CAPABILITY_H264_DEC |
632 CEDRUS_CAPABILITY_H265_DEC |
633 CEDRUS_CAPABILITY_VP8_DEC,
634 .mod_rate = 402000000,
635 };
636
637 static const struct cedrus_variant sun50i_h6_cedrus_variant = {
638 .capabilities = CEDRUS_CAPABILITY_UNTILED |
639 CEDRUS_CAPABILITY_MPEG2_DEC |
640 CEDRUS_CAPABILITY_H264_DEC |
641 CEDRUS_CAPABILITY_H265_DEC |
642 CEDRUS_CAPABILITY_H265_10_DEC |
643 CEDRUS_CAPABILITY_VP8_DEC,
644 .mod_rate = 600000000,
645 };
646
647 static const struct of_device_id cedrus_dt_match[] = {
648 {
649 .compatible = "allwinner,sun4i-a10-video-engine",
650 .data = &sun4i_a10_cedrus_variant,
651 },
652 {
653 .compatible = "allwinner,sun5i-a13-video-engine",
654 .data = &sun5i_a13_cedrus_variant,
655 },
656 {
657 .compatible = "allwinner,sun7i-a20-video-engine",
658 .data = &sun7i_a20_cedrus_variant,
659 },
660 {
661 .compatible = "allwinner,sun8i-a33-video-engine",
662 .data = &sun8i_a33_cedrus_variant,
663 },
664 {
665 .compatible = "allwinner,sun8i-h3-video-engine",
666 .data = &sun8i_h3_cedrus_variant,
667 },
668 {
669 .compatible = "allwinner,sun8i-v3s-video-engine",
670 .data = &sun8i_v3s_cedrus_variant,
671 },
672 {
673 .compatible = "allwinner,sun8i-r40-video-engine",
674 .data = &sun8i_r40_cedrus_variant,
675 },
676 {
677 .compatible = "allwinner,sun20i-d1-video-engine",
678 .data = &sun20i_d1_cedrus_variant,
679 },
680 {
681 .compatible = "allwinner,sun50i-a64-video-engine",
682 .data = &sun50i_a64_cedrus_variant,
683 },
684 {
685 .compatible = "allwinner,sun50i-h5-video-engine",
686 .data = &sun50i_h5_cedrus_variant,
687 },
688 {
689 .compatible = "allwinner,sun50i-h6-video-engine",
690 .data = &sun50i_h6_cedrus_variant,
691 },
692 { /* sentinel */ }
693 };
694 MODULE_DEVICE_TABLE(of, cedrus_dt_match);
695
696 static const struct dev_pm_ops cedrus_dev_pm_ops = {
697 SET_RUNTIME_PM_OPS(cedrus_hw_suspend,
698 cedrus_hw_resume, NULL)
699 };
700
701 static struct platform_driver cedrus_driver = {
702 .probe = cedrus_probe,
703 .remove = cedrus_remove,
704 .driver = {
705 .name = CEDRUS_NAME,
706 .of_match_table = of_match_ptr(cedrus_dt_match),
707 .pm = &cedrus_dev_pm_ops,
708 },
709 };
710 module_platform_driver(cedrus_driver);
711
712 MODULE_LICENSE("GPL v2");
713 MODULE_AUTHOR("Florent Revest <florent.revest@free-electrons.com>");
714 MODULE_AUTHOR("Paul Kocialkowski <paul.kocialkowski@bootlin.com>");
715 MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin.com>");
716 MODULE_DESCRIPTION("Cedrus VPU driver");
717