Merge tag 'soundwire-5.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul...
[linux-2.6-microblaze.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 #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");