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 
20 #include <media/v4l2-device.h>
21 #include <media/v4l2-ioctl.h>
22 #include <media/v4l2-ctrls.h>
23 #include <media/v4l2-mem2mem.h>
24 
25 #include "cedrus.h"
26 #include "cedrus_video.h"
27 #include "cedrus_dec.h"
28 #include "cedrus_hw.h"
29 
30 static const struct cedrus_control cedrus_controls[] = {
31 	{
32 		.id		= V4L2_CID_MPEG_VIDEO_MPEG2_SLICE_PARAMS,
33 		.elem_size	= sizeof(struct v4l2_ctrl_mpeg2_slice_params),
34 		.codec		= CEDRUS_CODEC_MPEG2,
35 		.required	= true,
36 	},
37 	{
38 		.id		= V4L2_CID_MPEG_VIDEO_MPEG2_QUANTIZATION,
39 		.elem_size	= sizeof(struct v4l2_ctrl_mpeg2_quantization),
40 		.codec		= CEDRUS_CODEC_MPEG2,
41 		.required	= false,
42 	},
43 };
44 
45 #define CEDRUS_CONTROLS_COUNT	ARRAY_SIZE(cedrus_controls)
46 
47 void *cedrus_find_control_data(struct cedrus_ctx *ctx, u32 id)
48 {
49 	unsigned int i;
50 
51 	for (i = 0; ctx->ctrls[i]; i++)
52 		if (ctx->ctrls[i]->id == id)
53 			return ctx->ctrls[i]->p_cur.p;
54 
55 	return NULL;
56 }
57 
58 static int cedrus_init_ctrls(struct cedrus_dev *dev, struct cedrus_ctx *ctx)
59 {
60 	struct v4l2_ctrl_handler *hdl = &ctx->hdl;
61 	struct v4l2_ctrl *ctrl;
62 	unsigned int ctrl_size;
63 	unsigned int i;
64 
65 	v4l2_ctrl_handler_init(hdl, CEDRUS_CONTROLS_COUNT);
66 	if (hdl->error) {
67 		v4l2_err(&dev->v4l2_dev,
68 			 "Failed to initialize control handler\n");
69 		return hdl->error;
70 	}
71 
72 	ctrl_size = sizeof(ctrl) * CEDRUS_CONTROLS_COUNT + 1;
73 
74 	ctx->ctrls = kzalloc(ctrl_size, GFP_KERNEL);
75 	if (!ctx->ctrls)
76 		return -ENOMEM;
77 
78 	for (i = 0; i < CEDRUS_CONTROLS_COUNT; i++) {
79 		struct v4l2_ctrl_config cfg = {};
80 
81 		cfg.elem_size = cedrus_controls[i].elem_size;
82 		cfg.id = cedrus_controls[i].id;
83 
84 		ctrl = v4l2_ctrl_new_custom(hdl, &cfg, NULL);
85 		if (hdl->error) {
86 			v4l2_err(&dev->v4l2_dev,
87 				 "Failed to create new custom control\n");
88 
89 			v4l2_ctrl_handler_free(hdl);
90 			kfree(ctx->ctrls);
91 			return hdl->error;
92 		}
93 
94 		ctx->ctrls[i] = ctrl;
95 	}
96 
97 	ctx->fh.ctrl_handler = hdl;
98 	v4l2_ctrl_handler_setup(hdl);
99 
100 	return 0;
101 }
102 
103 static int cedrus_request_validate(struct media_request *req)
104 {
105 	struct media_request_object *obj;
106 	struct v4l2_ctrl_handler *parent_hdl, *hdl;
107 	struct cedrus_ctx *ctx = NULL;
108 	struct v4l2_ctrl *ctrl_test;
109 	unsigned int count;
110 	unsigned int i;
111 
112 	list_for_each_entry(obj, &req->objects, list) {
113 		struct vb2_buffer *vb;
114 
115 		if (vb2_request_object_is_buffer(obj)) {
116 			vb = container_of(obj, struct vb2_buffer, req_obj);
117 			ctx = vb2_get_drv_priv(vb->vb2_queue);
118 
119 			break;
120 		}
121 	}
122 
123 	if (!ctx)
124 		return -ENOENT;
125 
126 	count = vb2_request_buffer_cnt(req);
127 	if (!count) {
128 		v4l2_info(&ctx->dev->v4l2_dev,
129 			  "No buffer was provided with the request\n");
130 		return -ENOENT;
131 	} else if (count > 1) {
132 		v4l2_info(&ctx->dev->v4l2_dev,
133 			  "More than one buffer was provided with the request\n");
134 		return -EINVAL;
135 	}
136 
137 	parent_hdl = &ctx->hdl;
138 
139 	hdl = v4l2_ctrl_request_hdl_find(req, parent_hdl);
140 	if (!hdl) {
141 		v4l2_info(&ctx->dev->v4l2_dev, "Missing codec control(s)\n");
142 		return -ENOENT;
143 	}
144 
145 	for (i = 0; i < CEDRUS_CONTROLS_COUNT; i++) {
146 		if (cedrus_controls[i].codec != ctx->current_codec ||
147 		    !cedrus_controls[i].required)
148 			continue;
149 
150 		ctrl_test = v4l2_ctrl_request_hdl_ctrl_find(hdl,
151 							    cedrus_controls[i].id);
152 		if (!ctrl_test) {
153 			v4l2_info(&ctx->dev->v4l2_dev,
154 				  "Missing required codec control\n");
155 			return -ENOENT;
156 		}
157 	}
158 
159 	v4l2_ctrl_request_hdl_put(hdl);
160 
161 	return vb2_request_validate(req);
162 }
163 
164 static int cedrus_open(struct file *file)
165 {
166 	struct cedrus_dev *dev = video_drvdata(file);
167 	struct cedrus_ctx *ctx = NULL;
168 	int ret;
169 
170 	if (mutex_lock_interruptible(&dev->dev_mutex))
171 		return -ERESTARTSYS;
172 
173 	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
174 	if (!ctx) {
175 		mutex_unlock(&dev->dev_mutex);
176 		return -ENOMEM;
177 	}
178 
179 	v4l2_fh_init(&ctx->fh, video_devdata(file));
180 	file->private_data = &ctx->fh;
181 	ctx->dev = dev;
182 
183 	ret = cedrus_init_ctrls(dev, ctx);
184 	if (ret)
185 		goto err_free;
186 
187 	ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx,
188 					    &cedrus_queue_init);
189 	if (IS_ERR(ctx->fh.m2m_ctx)) {
190 		ret = PTR_ERR(ctx->fh.m2m_ctx);
191 		goto err_ctrls;
192 	}
193 
194 	v4l2_fh_add(&ctx->fh);
195 
196 	mutex_unlock(&dev->dev_mutex);
197 
198 	return 0;
199 
200 err_ctrls:
201 	v4l2_ctrl_handler_free(&ctx->hdl);
202 err_free:
203 	kfree(ctx);
204 	mutex_unlock(&dev->dev_mutex);
205 
206 	return ret;
207 }
208 
209 static int cedrus_release(struct file *file)
210 {
211 	struct cedrus_dev *dev = video_drvdata(file);
212 	struct cedrus_ctx *ctx = container_of(file->private_data,
213 					      struct cedrus_ctx, fh);
214 
215 	mutex_lock(&dev->dev_mutex);
216 
217 	v4l2_fh_del(&ctx->fh);
218 	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
219 
220 	v4l2_ctrl_handler_free(&ctx->hdl);
221 	kfree(ctx->ctrls);
222 
223 	v4l2_fh_exit(&ctx->fh);
224 
225 	kfree(ctx);
226 
227 	mutex_unlock(&dev->dev_mutex);
228 
229 	return 0;
230 }
231 
232 static const struct v4l2_file_operations cedrus_fops = {
233 	.owner		= THIS_MODULE,
234 	.open		= cedrus_open,
235 	.release	= cedrus_release,
236 	.poll		= v4l2_m2m_fop_poll,
237 	.unlocked_ioctl	= video_ioctl2,
238 	.mmap		= v4l2_m2m_fop_mmap,
239 };
240 
241 static const struct video_device cedrus_video_device = {
242 	.name		= CEDRUS_NAME,
243 	.vfl_dir	= VFL_DIR_M2M,
244 	.fops		= &cedrus_fops,
245 	.ioctl_ops	= &cedrus_ioctl_ops,
246 	.minor		= -1,
247 	.release	= video_device_release_empty,
248 	.device_caps	= V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING,
249 };
250 
251 static const struct v4l2_m2m_ops cedrus_m2m_ops = {
252 	.device_run	= cedrus_device_run,
253 };
254 
255 static const struct media_device_ops cedrus_m2m_media_ops = {
256 	.req_validate	= cedrus_request_validate,
257 	.req_queue	= v4l2_m2m_request_queue,
258 };
259 
260 static int cedrus_probe(struct platform_device *pdev)
261 {
262 	struct cedrus_dev *dev;
263 	struct video_device *vfd;
264 	int ret;
265 
266 	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
267 	if (!dev)
268 		return -ENOMEM;
269 
270 	dev->vfd = cedrus_video_device;
271 	dev->dev = &pdev->dev;
272 	dev->pdev = pdev;
273 
274 	ret = cedrus_hw_probe(dev);
275 	if (ret) {
276 		dev_err(&pdev->dev, "Failed to probe hardware\n");
277 		return ret;
278 	}
279 
280 	dev->dec_ops[CEDRUS_CODEC_MPEG2] = &cedrus_dec_ops_mpeg2;
281 
282 	mutex_init(&dev->dev_mutex);
283 
284 	ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
285 	if (ret) {
286 		dev_err(&pdev->dev, "Failed to register V4L2 device\n");
287 		return ret;
288 	}
289 
290 	vfd = &dev->vfd;
291 	vfd->lock = &dev->dev_mutex;
292 	vfd->v4l2_dev = &dev->v4l2_dev;
293 
294 	snprintf(vfd->name, sizeof(vfd->name), "%s", cedrus_video_device.name);
295 	video_set_drvdata(vfd, dev);
296 
297 	dev->m2m_dev = v4l2_m2m_init(&cedrus_m2m_ops);
298 	if (IS_ERR(dev->m2m_dev)) {
299 		v4l2_err(&dev->v4l2_dev,
300 			 "Failed to initialize V4L2 M2M device\n");
301 		ret = PTR_ERR(dev->m2m_dev);
302 
303 		goto err_v4l2;
304 	}
305 
306 	dev->mdev.dev = &pdev->dev;
307 	strscpy(dev->mdev.model, CEDRUS_NAME, sizeof(dev->mdev.model));
308 
309 	media_device_init(&dev->mdev);
310 	dev->mdev.ops = &cedrus_m2m_media_ops;
311 	dev->v4l2_dev.mdev = &dev->mdev;
312 
313 	ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
314 	if (ret) {
315 		v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
316 		goto err_m2m;
317 	}
318 
319 	v4l2_info(&dev->v4l2_dev,
320 		  "Device registered as /dev/video%d\n", vfd->num);
321 
322 	ret = v4l2_m2m_register_media_controller(dev->m2m_dev, vfd,
323 						 MEDIA_ENT_F_PROC_VIDEO_DECODER);
324 	if (ret) {
325 		v4l2_err(&dev->v4l2_dev,
326 			 "Failed to initialize V4L2 M2M media controller\n");
327 		goto err_video;
328 	}
329 
330 	ret = media_device_register(&dev->mdev);
331 	if (ret) {
332 		v4l2_err(&dev->v4l2_dev, "Failed to register media device\n");
333 		goto err_m2m_mc;
334 	}
335 
336 	platform_set_drvdata(pdev, dev);
337 
338 	return 0;
339 
340 err_m2m_mc:
341 	v4l2_m2m_unregister_media_controller(dev->m2m_dev);
342 err_video:
343 	video_unregister_device(&dev->vfd);
344 err_m2m:
345 	v4l2_m2m_release(dev->m2m_dev);
346 err_v4l2:
347 	v4l2_device_unregister(&dev->v4l2_dev);
348 
349 	return ret;
350 }
351 
352 static int cedrus_remove(struct platform_device *pdev)
353 {
354 	struct cedrus_dev *dev = platform_get_drvdata(pdev);
355 
356 	if (media_devnode_is_registered(dev->mdev.devnode)) {
357 		media_device_unregister(&dev->mdev);
358 		v4l2_m2m_unregister_media_controller(dev->m2m_dev);
359 		media_device_cleanup(&dev->mdev);
360 	}
361 
362 	v4l2_m2m_release(dev->m2m_dev);
363 	video_unregister_device(&dev->vfd);
364 	v4l2_device_unregister(&dev->v4l2_dev);
365 
366 	cedrus_hw_remove(dev);
367 
368 	return 0;
369 }
370 
371 static const struct cedrus_variant sun4i_a10_cedrus_variant = {
372 	/* No particular capability. */
373 };
374 
375 static const struct cedrus_variant sun5i_a13_cedrus_variant = {
376 	/* No particular capability. */
377 };
378 
379 static const struct cedrus_variant sun7i_a20_cedrus_variant = {
380 	/* No particular capability. */
381 };
382 
383 static const struct cedrus_variant sun8i_a33_cedrus_variant = {
384 	.capabilities	= CEDRUS_CAPABILITY_UNTILED,
385 };
386 
387 static const struct cedrus_variant sun8i_h3_cedrus_variant = {
388 	.capabilities	= CEDRUS_CAPABILITY_UNTILED,
389 };
390 
391 static const struct cedrus_variant sun50i_a64_cedrus_variant = {
392 	.capabilities	= CEDRUS_CAPABILITY_UNTILED,
393 };
394 
395 static const struct cedrus_variant sun50i_h5_cedrus_variant = {
396 	.capabilities	= CEDRUS_CAPABILITY_UNTILED,
397 };
398 
399 static const struct cedrus_variant sun50i_h6_cedrus_variant = {
400 	.capabilities	= CEDRUS_CAPABILITY_UNTILED,
401 	.quirks		= CEDRUS_QUIRK_NO_DMA_OFFSET,
402 };
403 
404 static const struct of_device_id cedrus_dt_match[] = {
405 	{
406 		.compatible = "allwinner,sun4i-a10-video-engine",
407 		.data = &sun4i_a10_cedrus_variant,
408 	},
409 	{
410 		.compatible = "allwinner,sun5i-a13-video-engine",
411 		.data = &sun5i_a13_cedrus_variant,
412 	},
413 	{
414 		.compatible = "allwinner,sun7i-a20-video-engine",
415 		.data = &sun7i_a20_cedrus_variant,
416 	},
417 	{
418 		.compatible = "allwinner,sun8i-a33-video-engine",
419 		.data = &sun8i_a33_cedrus_variant,
420 	},
421 	{
422 		.compatible = "allwinner,sun8i-h3-video-engine",
423 		.data = &sun8i_h3_cedrus_variant,
424 	},
425 	{
426 		.compatible = "allwinner,sun50i-a64-video-engine",
427 		.data = &sun50i_a64_cedrus_variant,
428 	},
429 	{
430 		.compatible = "allwinner,sun50i-h5-video-engine",
431 		.data = &sun50i_h5_cedrus_variant,
432 	},
433 	{
434 		.compatible = "allwinner,sun50i-h6-video-engine",
435 		.data = &sun50i_h6_cedrus_variant,
436 	},
437 	{ /* sentinel */ }
438 };
439 MODULE_DEVICE_TABLE(of, cedrus_dt_match);
440 
441 static struct platform_driver cedrus_driver = {
442 	.probe		= cedrus_probe,
443 	.remove		= cedrus_remove,
444 	.driver		= {
445 		.name		= CEDRUS_NAME,
446 		.of_match_table	= of_match_ptr(cedrus_dt_match),
447 	},
448 };
449 module_platform_driver(cedrus_driver);
450 
451 MODULE_LICENSE("GPL v2");
452 MODULE_AUTHOR("Florent Revest <florent.revest@free-electrons.com>");
453 MODULE_AUTHOR("Paul Kocialkowski <paul.kocialkowski@bootlin.com>");
454 MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin.com>");
455 MODULE_DESCRIPTION("Cedrus VPU driver");
456