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