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