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