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