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