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 
203 	list_for_each_entry(obj, &req->objects, list) {
204 		struct vb2_buffer *vb;
205 
206 		if (vb2_request_object_is_buffer(obj)) {
207 			vb = container_of(obj, struct vb2_buffer, req_obj);
208 			ctx = vb2_get_drv_priv(vb->vb2_queue);
209 
210 			break;
211 		}
212 	}
213 
214 	if (!ctx)
215 		return -ENOENT;
216 
217 	count = vb2_request_buffer_cnt(req);
218 	if (!count) {
219 		v4l2_info(&ctx->dev->v4l2_dev,
220 			  "No buffer was provided with the request\n");
221 		return -ENOENT;
222 	} else if (count > 1) {
223 		v4l2_info(&ctx->dev->v4l2_dev,
224 			  "More than one buffer was provided with the request\n");
225 		return -EINVAL;
226 	}
227 
228 	parent_hdl = &ctx->hdl;
229 
230 	hdl = v4l2_ctrl_request_hdl_find(req, parent_hdl);
231 	if (!hdl) {
232 		v4l2_info(&ctx->dev->v4l2_dev, "Missing codec control(s)\n");
233 		return -ENOENT;
234 	}
235 
236 	for (i = 0; i < CEDRUS_CONTROLS_COUNT; i++) {
237 		if (cedrus_controls[i].codec != ctx->current_codec ||
238 		    !cedrus_controls[i].required)
239 			continue;
240 
241 		ctrl_test = v4l2_ctrl_request_hdl_ctrl_find(hdl,
242 							    cedrus_controls[i].cfg.id);
243 		if (!ctrl_test) {
244 			v4l2_info(&ctx->dev->v4l2_dev,
245 				  "Missing required codec control\n");
246 			return -ENOENT;
247 		}
248 	}
249 
250 	v4l2_ctrl_request_hdl_put(hdl);
251 
252 	return vb2_request_validate(req);
253 }
254 
255 static int cedrus_open(struct file *file)
256 {
257 	struct cedrus_dev *dev = video_drvdata(file);
258 	struct cedrus_ctx *ctx = NULL;
259 	int ret;
260 
261 	if (mutex_lock_interruptible(&dev->dev_mutex))
262 		return -ERESTARTSYS;
263 
264 	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
265 	if (!ctx) {
266 		mutex_unlock(&dev->dev_mutex);
267 		return -ENOMEM;
268 	}
269 
270 	v4l2_fh_init(&ctx->fh, video_devdata(file));
271 	file->private_data = &ctx->fh;
272 	ctx->dev = dev;
273 
274 	ret = cedrus_init_ctrls(dev, ctx);
275 	if (ret)
276 		goto err_free;
277 
278 	ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx,
279 					    &cedrus_queue_init);
280 	if (IS_ERR(ctx->fh.m2m_ctx)) {
281 		ret = PTR_ERR(ctx->fh.m2m_ctx);
282 		goto err_ctrls;
283 	}
284 	ctx->dst_fmt.pixelformat = V4L2_PIX_FMT_SUNXI_TILED_NV12;
285 	cedrus_prepare_format(&ctx->dst_fmt);
286 	ctx->src_fmt.pixelformat = V4L2_PIX_FMT_MPEG2_SLICE;
287 	/*
288 	 * TILED_NV12 has more strict requirements, so copy the width and
289 	 * height to src_fmt to ensure that is matches the dst_fmt resolution.
290 	 */
291 	ctx->src_fmt.width = ctx->dst_fmt.width;
292 	ctx->src_fmt.height = ctx->dst_fmt.height;
293 	cedrus_prepare_format(&ctx->src_fmt);
294 
295 	v4l2_fh_add(&ctx->fh);
296 
297 	mutex_unlock(&dev->dev_mutex);
298 
299 	return 0;
300 
301 err_ctrls:
302 	v4l2_ctrl_handler_free(&ctx->hdl);
303 err_free:
304 	kfree(ctx);
305 	mutex_unlock(&dev->dev_mutex);
306 
307 	return ret;
308 }
309 
310 static int cedrus_release(struct file *file)
311 {
312 	struct cedrus_dev *dev = video_drvdata(file);
313 	struct cedrus_ctx *ctx = container_of(file->private_data,
314 					      struct cedrus_ctx, fh);
315 
316 	mutex_lock(&dev->dev_mutex);
317 
318 	v4l2_fh_del(&ctx->fh);
319 	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
320 
321 	v4l2_ctrl_handler_free(&ctx->hdl);
322 	kfree(ctx->ctrls);
323 
324 	v4l2_fh_exit(&ctx->fh);
325 
326 	kfree(ctx);
327 
328 	mutex_unlock(&dev->dev_mutex);
329 
330 	return 0;
331 }
332 
333 static const struct v4l2_file_operations cedrus_fops = {
334 	.owner		= THIS_MODULE,
335 	.open		= cedrus_open,
336 	.release	= cedrus_release,
337 	.poll		= v4l2_m2m_fop_poll,
338 	.unlocked_ioctl	= video_ioctl2,
339 	.mmap		= v4l2_m2m_fop_mmap,
340 };
341 
342 static const struct video_device cedrus_video_device = {
343 	.name		= CEDRUS_NAME,
344 	.vfl_dir	= VFL_DIR_M2M,
345 	.fops		= &cedrus_fops,
346 	.ioctl_ops	= &cedrus_ioctl_ops,
347 	.minor		= -1,
348 	.release	= video_device_release_empty,
349 	.device_caps	= V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING,
350 };
351 
352 static const struct v4l2_m2m_ops cedrus_m2m_ops = {
353 	.device_run	= cedrus_device_run,
354 };
355 
356 static const struct media_device_ops cedrus_m2m_media_ops = {
357 	.req_validate	= cedrus_request_validate,
358 	.req_queue	= v4l2_m2m_request_queue,
359 };
360 
361 static int cedrus_probe(struct platform_device *pdev)
362 {
363 	struct cedrus_dev *dev;
364 	struct video_device *vfd;
365 	int ret;
366 
367 	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
368 	if (!dev)
369 		return -ENOMEM;
370 
371 	dev->vfd = cedrus_video_device;
372 	dev->dev = &pdev->dev;
373 	dev->pdev = pdev;
374 
375 	ret = cedrus_hw_probe(dev);
376 	if (ret) {
377 		dev_err(&pdev->dev, "Failed to probe hardware\n");
378 		return ret;
379 	}
380 
381 	dev->dec_ops[CEDRUS_CODEC_MPEG2] = &cedrus_dec_ops_mpeg2;
382 	dev->dec_ops[CEDRUS_CODEC_H264] = &cedrus_dec_ops_h264;
383 	dev->dec_ops[CEDRUS_CODEC_H265] = &cedrus_dec_ops_h265;
384 
385 	mutex_init(&dev->dev_mutex);
386 
387 	ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
388 	if (ret) {
389 		dev_err(&pdev->dev, "Failed to register V4L2 device\n");
390 		return ret;
391 	}
392 
393 	vfd = &dev->vfd;
394 	vfd->lock = &dev->dev_mutex;
395 	vfd->v4l2_dev = &dev->v4l2_dev;
396 
397 	snprintf(vfd->name, sizeof(vfd->name), "%s", cedrus_video_device.name);
398 	video_set_drvdata(vfd, dev);
399 
400 	dev->m2m_dev = v4l2_m2m_init(&cedrus_m2m_ops);
401 	if (IS_ERR(dev->m2m_dev)) {
402 		v4l2_err(&dev->v4l2_dev,
403 			 "Failed to initialize V4L2 M2M device\n");
404 		ret = PTR_ERR(dev->m2m_dev);
405 
406 		goto err_v4l2;
407 	}
408 
409 	dev->mdev.dev = &pdev->dev;
410 	strscpy(dev->mdev.model, CEDRUS_NAME, sizeof(dev->mdev.model));
411 	strscpy(dev->mdev.bus_info, "platform:" CEDRUS_NAME,
412 		sizeof(dev->mdev.bus_info));
413 
414 	media_device_init(&dev->mdev);
415 	dev->mdev.ops = &cedrus_m2m_media_ops;
416 	dev->v4l2_dev.mdev = &dev->mdev;
417 
418 	ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0);
419 	if (ret) {
420 		v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
421 		goto err_m2m;
422 	}
423 
424 	v4l2_info(&dev->v4l2_dev,
425 		  "Device registered as /dev/video%d\n", vfd->num);
426 
427 	ret = v4l2_m2m_register_media_controller(dev->m2m_dev, vfd,
428 						 MEDIA_ENT_F_PROC_VIDEO_DECODER);
429 	if (ret) {
430 		v4l2_err(&dev->v4l2_dev,
431 			 "Failed to initialize V4L2 M2M media controller\n");
432 		goto err_video;
433 	}
434 
435 	ret = media_device_register(&dev->mdev);
436 	if (ret) {
437 		v4l2_err(&dev->v4l2_dev, "Failed to register media device\n");
438 		goto err_m2m_mc;
439 	}
440 
441 	platform_set_drvdata(pdev, dev);
442 
443 	return 0;
444 
445 err_m2m_mc:
446 	v4l2_m2m_unregister_media_controller(dev->m2m_dev);
447 err_video:
448 	video_unregister_device(&dev->vfd);
449 err_m2m:
450 	v4l2_m2m_release(dev->m2m_dev);
451 err_v4l2:
452 	v4l2_device_unregister(&dev->v4l2_dev);
453 
454 	return ret;
455 }
456 
457 static int cedrus_remove(struct platform_device *pdev)
458 {
459 	struct cedrus_dev *dev = platform_get_drvdata(pdev);
460 
461 	if (media_devnode_is_registered(dev->mdev.devnode)) {
462 		media_device_unregister(&dev->mdev);
463 		v4l2_m2m_unregister_media_controller(dev->m2m_dev);
464 		media_device_cleanup(&dev->mdev);
465 	}
466 
467 	v4l2_m2m_release(dev->m2m_dev);
468 	video_unregister_device(&dev->vfd);
469 	v4l2_device_unregister(&dev->v4l2_dev);
470 
471 	cedrus_hw_remove(dev);
472 
473 	return 0;
474 }
475 
476 static const struct cedrus_variant sun4i_a10_cedrus_variant = {
477 	.mod_rate	= 320000000,
478 };
479 
480 static const struct cedrus_variant sun5i_a13_cedrus_variant = {
481 	.mod_rate	= 320000000,
482 };
483 
484 static const struct cedrus_variant sun7i_a20_cedrus_variant = {
485 	.mod_rate	= 320000000,
486 };
487 
488 static const struct cedrus_variant sun8i_a33_cedrus_variant = {
489 	.capabilities	= CEDRUS_CAPABILITY_UNTILED,
490 	.mod_rate	= 320000000,
491 };
492 
493 static const struct cedrus_variant sun8i_h3_cedrus_variant = {
494 	.capabilities	= CEDRUS_CAPABILITY_UNTILED |
495 			  CEDRUS_CAPABILITY_H265_DEC,
496 	.mod_rate	= 402000000,
497 };
498 
499 static const struct cedrus_variant sun50i_a64_cedrus_variant = {
500 	.capabilities	= CEDRUS_CAPABILITY_UNTILED |
501 			  CEDRUS_CAPABILITY_H265_DEC,
502 	.mod_rate	= 402000000,
503 };
504 
505 static const struct cedrus_variant sun50i_h5_cedrus_variant = {
506 	.capabilities	= CEDRUS_CAPABILITY_UNTILED |
507 			  CEDRUS_CAPABILITY_H265_DEC,
508 	.mod_rate	= 402000000,
509 };
510 
511 static const struct cedrus_variant sun50i_h6_cedrus_variant = {
512 	.capabilities	= CEDRUS_CAPABILITY_UNTILED |
513 			  CEDRUS_CAPABILITY_H265_DEC,
514 	.quirks		= CEDRUS_QUIRK_NO_DMA_OFFSET,
515 	.mod_rate	= 600000000,
516 };
517 
518 static const struct of_device_id cedrus_dt_match[] = {
519 	{
520 		.compatible = "allwinner,sun4i-a10-video-engine",
521 		.data = &sun4i_a10_cedrus_variant,
522 	},
523 	{
524 		.compatible = "allwinner,sun5i-a13-video-engine",
525 		.data = &sun5i_a13_cedrus_variant,
526 	},
527 	{
528 		.compatible = "allwinner,sun7i-a20-video-engine",
529 		.data = &sun7i_a20_cedrus_variant,
530 	},
531 	{
532 		.compatible = "allwinner,sun8i-a33-video-engine",
533 		.data = &sun8i_a33_cedrus_variant,
534 	},
535 	{
536 		.compatible = "allwinner,sun8i-h3-video-engine",
537 		.data = &sun8i_h3_cedrus_variant,
538 	},
539 	{
540 		.compatible = "allwinner,sun50i-a64-video-engine",
541 		.data = &sun50i_a64_cedrus_variant,
542 	},
543 	{
544 		.compatible = "allwinner,sun50i-h5-video-engine",
545 		.data = &sun50i_h5_cedrus_variant,
546 	},
547 	{
548 		.compatible = "allwinner,sun50i-h6-video-engine",
549 		.data = &sun50i_h6_cedrus_variant,
550 	},
551 	{ /* sentinel */ }
552 };
553 MODULE_DEVICE_TABLE(of, cedrus_dt_match);
554 
555 static const struct dev_pm_ops cedrus_dev_pm_ops = {
556 	SET_RUNTIME_PM_OPS(cedrus_hw_suspend,
557 			   cedrus_hw_resume, NULL)
558 };
559 
560 static struct platform_driver cedrus_driver = {
561 	.probe		= cedrus_probe,
562 	.remove		= cedrus_remove,
563 	.driver		= {
564 		.name		= CEDRUS_NAME,
565 		.of_match_table	= of_match_ptr(cedrus_dt_match),
566 		.pm		= &cedrus_dev_pm_ops,
567 	},
568 };
569 module_platform_driver(cedrus_driver);
570 
571 MODULE_LICENSE("GPL v2");
572 MODULE_AUTHOR("Florent Revest <florent.revest@free-electrons.com>");
573 MODULE_AUTHOR("Paul Kocialkowski <paul.kocialkowski@bootlin.com>");
574 MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin.com>");
575 MODULE_DESCRIPTION("Cedrus VPU driver");
576