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_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
 142void *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
 153static 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
 194static 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
 260static 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
 306err_ctrls:
 307        v4l2_ctrl_handler_free(&ctx->hdl);
 308err_free:
 309        kfree(ctx);
 310        mutex_unlock(&dev->dev_mutex);
 311
 312        return ret;
 313}
 314
 315static 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
 338static 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
 347static 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
 357static const struct v4l2_m2m_ops cedrus_m2m_ops = {
 358        .device_run     = cedrus_device_run,
 359};
 360
 361static const struct media_device_ops cedrus_m2m_media_ops = {
 362        .req_validate   = cedrus_request_validate,
 363        .req_queue      = v4l2_m2m_request_queue,
 364};
 365
 366static 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
 450err_m2m_mc:
 451        v4l2_m2m_unregister_media_controller(dev->m2m_dev);
 452err_video:
 453        video_unregister_device(&dev->vfd);
 454err_m2m:
 455        v4l2_m2m_release(dev->m2m_dev);
 456err_v4l2:
 457        v4l2_device_unregister(&dev->v4l2_dev);
 458
 459        return ret;
 460}
 461
 462static 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
 481static const struct cedrus_variant sun4i_a10_cedrus_variant = {
 482        .mod_rate       = 320000000,
 483};
 484
 485static const struct cedrus_variant sun5i_a13_cedrus_variant = {
 486        .mod_rate       = 320000000,
 487};
 488
 489static const struct cedrus_variant sun7i_a20_cedrus_variant = {
 490        .mod_rate       = 320000000,
 491};
 492
 493static const struct cedrus_variant sun8i_a33_cedrus_variant = {
 494        .capabilities   = CEDRUS_CAPABILITY_UNTILED,
 495        .mod_rate       = 320000000,
 496};
 497
 498static const struct cedrus_variant sun8i_h3_cedrus_variant = {
 499        .capabilities   = CEDRUS_CAPABILITY_UNTILED |
 500                          CEDRUS_CAPABILITY_H265_DEC,
 501        .mod_rate       = 402000000,
 502};
 503
 504static const struct cedrus_variant sun50i_a64_cedrus_variant = {
 505        .capabilities   = CEDRUS_CAPABILITY_UNTILED |
 506                          CEDRUS_CAPABILITY_H265_DEC,
 507        .mod_rate       = 402000000,
 508};
 509
 510static const struct cedrus_variant sun50i_h5_cedrus_variant = {
 511        .capabilities   = CEDRUS_CAPABILITY_UNTILED |
 512                          CEDRUS_CAPABILITY_H265_DEC,
 513        .mod_rate       = 402000000,
 514};
 515
 516static 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
 523static 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};
 558MODULE_DEVICE_TABLE(of, cedrus_dt_match);
 559
 560static const struct dev_pm_ops cedrus_dev_pm_ops = {
 561        SET_RUNTIME_PM_OPS(cedrus_hw_suspend,
 562                           cedrus_hw_resume, NULL)
 563};
 564
 565static 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};
 574module_platform_driver(cedrus_driver);
 575
 576MODULE_LICENSE("GPL v2");
 577MODULE_AUTHOR("Florent Revest <florent.revest@free-electrons.com>");
 578MODULE_AUTHOR("Paul Kocialkowski <paul.kocialkowski@bootlin.com>");
 579MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin.com>");
 580MODULE_DESCRIPTION("Cedrus VPU driver");
 581