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