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