Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/sparc
[sfrench/cifs-2.6.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
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         memset(ctx->ctrls, 0, ctrl_size);
76
77         for (i = 0; i < CEDRUS_CONTROLS_COUNT; i++) {
78                 struct v4l2_ctrl_config cfg = { 0 };
79
80                 cfg.elem_size = cedrus_controls[i].elem_size;
81                 cfg.id = cedrus_controls[i].id;
82
83                 ctrl = v4l2_ctrl_new_custom(hdl, &cfg, NULL);
84                 if (hdl->error) {
85                         v4l2_err(&dev->v4l2_dev,
86                                  "Failed to create new custom control\n");
87
88                         v4l2_ctrl_handler_free(hdl);
89                         kfree(ctx->ctrls);
90                         return hdl->error;
91                 }
92
93                 ctx->ctrls[i] = ctrl;
94         }
95
96         ctx->fh.ctrl_handler = hdl;
97         v4l2_ctrl_handler_setup(hdl);
98
99         return 0;
100 }
101
102 static int cedrus_request_validate(struct media_request *req)
103 {
104         struct media_request_object *obj;
105         struct v4l2_ctrl_handler *parent_hdl, *hdl;
106         struct cedrus_ctx *ctx = NULL;
107         struct v4l2_ctrl *ctrl_test;
108         unsigned int count;
109         unsigned int i;
110
111         count = vb2_request_buffer_cnt(req);
112         if (!count) {
113                 v4l2_info(&ctx->dev->v4l2_dev,
114                           "No buffer was provided with the request\n");
115                 return -ENOENT;
116         } else if (count > 1) {
117                 v4l2_info(&ctx->dev->v4l2_dev,
118                           "More than one buffer was provided with the request\n");
119                 return -EINVAL;
120         }
121
122         list_for_each_entry(obj, &req->objects, list) {
123                 struct vb2_buffer *vb;
124
125                 if (vb2_request_object_is_buffer(obj)) {
126                         vb = container_of(obj, struct vb2_buffer, req_obj);
127                         ctx = vb2_get_drv_priv(vb->vb2_queue);
128
129                         break;
130                 }
131         }
132
133         if (!ctx)
134                 return -ENOENT;
135
136         parent_hdl = &ctx->hdl;
137
138         hdl = v4l2_ctrl_request_hdl_find(req, parent_hdl);
139         if (!hdl) {
140                 v4l2_info(&ctx->dev->v4l2_dev, "Missing codec control(s)\n");
141                 return -ENOENT;
142         }
143
144         for (i = 0; i < CEDRUS_CONTROLS_COUNT; i++) {
145                 if (cedrus_controls[i].codec != ctx->current_codec ||
146                     !cedrus_controls[i].required)
147                         continue;
148
149                 ctrl_test = v4l2_ctrl_request_hdl_ctrl_find(hdl,
150                                                             cedrus_controls[i].id);
151                 if (!ctrl_test) {
152                         v4l2_info(&ctx->dev->v4l2_dev,
153                                   "Missing required codec control\n");
154                         return -ENOENT;
155                 }
156         }
157
158         v4l2_ctrl_request_hdl_put(hdl);
159
160         return vb2_request_validate(req);
161 }
162
163 static int cedrus_open(struct file *file)
164 {
165         struct cedrus_dev *dev = video_drvdata(file);
166         struct cedrus_ctx *ctx = NULL;
167         int ret;
168
169         if (mutex_lock_interruptible(&dev->dev_mutex))
170                 return -ERESTARTSYS;
171
172         ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
173         if (!ctx) {
174                 mutex_unlock(&dev->dev_mutex);
175                 return -ENOMEM;
176         }
177
178         v4l2_fh_init(&ctx->fh, video_devdata(file));
179         file->private_data = &ctx->fh;
180         ctx->dev = dev;
181
182         ret = cedrus_init_ctrls(dev, ctx);
183         if (ret)
184                 goto err_free;
185
186         ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx,
187                                             &cedrus_queue_init);
188         if (IS_ERR(ctx->fh.m2m_ctx)) {
189                 ret = PTR_ERR(ctx->fh.m2m_ctx);
190                 goto err_ctrls;
191         }
192
193         v4l2_fh_add(&ctx->fh);
194
195         mutex_unlock(&dev->dev_mutex);
196
197         return 0;
198
199 err_ctrls:
200         v4l2_ctrl_handler_free(&ctx->hdl);
201 err_free:
202         kfree(ctx);
203         mutex_unlock(&dev->dev_mutex);
204
205         return ret;
206 }
207
208 static int cedrus_release(struct file *file)
209 {
210         struct cedrus_dev *dev = video_drvdata(file);
211         struct cedrus_ctx *ctx = container_of(file->private_data,
212                                               struct cedrus_ctx, fh);
213
214         mutex_lock(&dev->dev_mutex);
215
216         v4l2_fh_del(&ctx->fh);
217         v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
218
219         v4l2_ctrl_handler_free(&ctx->hdl);
220         kfree(ctx->ctrls);
221
222         v4l2_fh_exit(&ctx->fh);
223
224         kfree(ctx);
225
226         mutex_unlock(&dev->dev_mutex);
227
228         return 0;
229 }
230
231 static const struct v4l2_file_operations cedrus_fops = {
232         .owner          = THIS_MODULE,
233         .open           = cedrus_open,
234         .release        = cedrus_release,
235         .poll           = v4l2_m2m_fop_poll,
236         .unlocked_ioctl = video_ioctl2,
237         .mmap           = v4l2_m2m_fop_mmap,
238 };
239
240 static const struct video_device cedrus_video_device = {
241         .name           = CEDRUS_NAME,
242         .vfl_dir        = VFL_DIR_M2M,
243         .fops           = &cedrus_fops,
244         .ioctl_ops      = &cedrus_ioctl_ops,
245         .minor          = -1,
246         .release        = video_device_release_empty,
247         .device_caps    = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING,
248 };
249
250 static const struct v4l2_m2m_ops cedrus_m2m_ops = {
251         .device_run     = cedrus_device_run,
252 };
253
254 static const struct media_device_ops cedrus_m2m_media_ops = {
255         .req_validate   = cedrus_request_validate,
256         .req_queue      = vb2_m2m_request_queue,
257 };
258
259 static int cedrus_probe(struct platform_device *pdev)
260 {
261         struct cedrus_dev *dev;
262         struct video_device *vfd;
263         int ret;
264
265         dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
266         if (!dev)
267                 return -ENOMEM;
268
269         dev->vfd = cedrus_video_device;
270         dev->dev = &pdev->dev;
271         dev->pdev = pdev;
272
273         ret = cedrus_hw_probe(dev);
274         if (ret) {
275                 dev_err(&pdev->dev, "Failed to probe hardware\n");
276                 return ret;
277         }
278
279         dev->dec_ops[CEDRUS_CODEC_MPEG2] = &cedrus_dec_ops_mpeg2;
280
281         mutex_init(&dev->dev_mutex);
282         spin_lock_init(&dev->irq_lock);
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_video;
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 = v4l2_m2m_register_media_controller(dev->m2m_dev, vfd,
314                                                  MEDIA_ENT_F_PROC_VIDEO_DECODER);
315         if (ret) {
316                 v4l2_err(&dev->v4l2_dev,
317                          "Failed to initialize V4L2 M2M media controller\n");
318                 goto err_m2m;
319         }
320
321         ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
322         if (ret) {
323                 v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
324                 goto err_v4l2;
325         }
326
327         v4l2_info(&dev->v4l2_dev,
328                   "Device registered as /dev/video%d\n", vfd->num);
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_m2m:
343         v4l2_m2m_release(dev->m2m_dev);
344 err_video:
345         video_unregister_device(&dev->vfd);
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 of_device_id cedrus_dt_match[] = {
392         {
393                 .compatible = "allwinner,sun4i-a10-video-engine",
394                 .data = &sun4i_a10_cedrus_variant,
395         },
396         {
397                 .compatible = "allwinner,sun5i-a13-video-engine",
398                 .data = &sun5i_a13_cedrus_variant,
399         },
400         {
401                 .compatible = "allwinner,sun7i-a20-video-engine",
402                 .data = &sun7i_a20_cedrus_variant,
403         },
404         {
405                 .compatible = "allwinner,sun8i-a33-video-engine",
406                 .data = &sun8i_a33_cedrus_variant,
407         },
408         {
409                 .compatible = "allwinner,sun8i-h3-video-engine",
410                 .data = &sun8i_h3_cedrus_variant,
411         },
412         { /* sentinel */ }
413 };
414 MODULE_DEVICE_TABLE(of, cedrus_dt_match);
415
416 static struct platform_driver cedrus_driver = {
417         .probe          = cedrus_probe,
418         .remove         = cedrus_remove,
419         .driver         = {
420                 .name           = CEDRUS_NAME,
421                 .owner          = THIS_MODULE,
422                 .of_match_table = of_match_ptr(cedrus_dt_match),
423         },
424 };
425 module_platform_driver(cedrus_driver);
426
427 MODULE_LICENSE("GPL v2");
428 MODULE_AUTHOR("Florent Revest <florent.revest@free-electrons.com>");
429 MODULE_AUTHOR("Paul Kocialkowski <paul.kocialkowski@bootlin.com>");
430 MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin.com>");
431 MODULE_DESCRIPTION("Cedrus VPU driver");