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