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 .cfg = { 33 .id = V4L2_CID_MPEG_VIDEO_MPEG2_SLICE_PARAMS, 34 }, 35 .codec = CEDRUS_CODEC_MPEG2, 36 .required = true, 37 }, 38 { 39 .cfg = { 40 .id = V4L2_CID_MPEG_VIDEO_MPEG2_QUANTIZATION, 41 }, 42 .codec = CEDRUS_CODEC_MPEG2, 43 .required = false, 44 }, 45 { 46 .cfg = { 47 .id = V4L2_CID_MPEG_VIDEO_H264_DECODE_PARAMS, 48 }, 49 .codec = CEDRUS_CODEC_H264, 50 .required = true, 51 }, 52 { 53 .cfg = { 54 .id = V4L2_CID_MPEG_VIDEO_H264_SLICE_PARAMS, 55 }, 56 .codec = CEDRUS_CODEC_H264, 57 .required = true, 58 }, 59 { 60 .cfg = { 61 .id = V4L2_CID_MPEG_VIDEO_H264_SPS, 62 }, 63 .codec = CEDRUS_CODEC_H264, 64 .required = true, 65 }, 66 { 67 .cfg = { 68 .id = V4L2_CID_MPEG_VIDEO_H264_PPS, 69 }, 70 .codec = CEDRUS_CODEC_H264, 71 .required = true, 72 }, 73 { 74 .cfg = { 75 .id = V4L2_CID_MPEG_VIDEO_H264_SCALING_MATRIX, 76 }, 77 .codec = CEDRUS_CODEC_H264, 78 .required = true, 79 }, 80 { 81 .cfg = { 82 .id = V4L2_CID_MPEG_VIDEO_H264_DECODE_MODE, 83 .max = V4L2_MPEG_VIDEO_H264_DECODE_MODE_SLICE_BASED, 84 .def = V4L2_MPEG_VIDEO_H264_DECODE_MODE_SLICE_BASED, 85 }, 86 .codec = CEDRUS_CODEC_H264, 87 .required = false, 88 }, 89 { 90 .cfg = { 91 .id = V4L2_CID_MPEG_VIDEO_H264_START_CODE, 92 .max = V4L2_MPEG_VIDEO_H264_START_CODE_NONE, 93 .def = V4L2_MPEG_VIDEO_H264_START_CODE_NONE, 94 }, 95 .codec = CEDRUS_CODEC_H264, 96 .required = false, 97 }, 98 }; 99 100 #define CEDRUS_CONTROLS_COUNT ARRAY_SIZE(cedrus_controls) 101 102 void *cedrus_find_control_data(struct cedrus_ctx *ctx, u32 id) 103 { 104 unsigned int i; 105 106 for (i = 0; ctx->ctrls[i]; i++) 107 if (ctx->ctrls[i]->id == id) 108 return ctx->ctrls[i]->p_cur.p; 109 110 return NULL; 111 } 112 113 static int cedrus_init_ctrls(struct cedrus_dev *dev, struct cedrus_ctx *ctx) 114 { 115 struct v4l2_ctrl_handler *hdl = &ctx->hdl; 116 struct v4l2_ctrl *ctrl; 117 unsigned int ctrl_size; 118 unsigned int i; 119 120 v4l2_ctrl_handler_init(hdl, CEDRUS_CONTROLS_COUNT); 121 if (hdl->error) { 122 v4l2_err(&dev->v4l2_dev, 123 "Failed to initialize control handler\n"); 124 return hdl->error; 125 } 126 127 ctrl_size = sizeof(ctrl) * CEDRUS_CONTROLS_COUNT + 1; 128 129 ctx->ctrls = kzalloc(ctrl_size, GFP_KERNEL); 130 if (!ctx->ctrls) 131 return -ENOMEM; 132 133 for (i = 0; i < CEDRUS_CONTROLS_COUNT; i++) { 134 ctrl = v4l2_ctrl_new_custom(hdl, &cedrus_controls[i].cfg, 135 NULL); 136 if (hdl->error) { 137 v4l2_err(&dev->v4l2_dev, 138 "Failed to create new custom control\n"); 139 140 v4l2_ctrl_handler_free(hdl); 141 kfree(ctx->ctrls); 142 return hdl->error; 143 } 144 145 ctx->ctrls[i] = ctrl; 146 } 147 148 ctx->fh.ctrl_handler = hdl; 149 v4l2_ctrl_handler_setup(hdl); 150 151 return 0; 152 } 153 154 static int cedrus_request_validate(struct media_request *req) 155 { 156 struct media_request_object *obj; 157 struct v4l2_ctrl_handler *parent_hdl, *hdl; 158 struct cedrus_ctx *ctx = NULL; 159 struct v4l2_ctrl *ctrl_test; 160 unsigned int count; 161 unsigned int i; 162 163 list_for_each_entry(obj, &req->objects, list) { 164 struct vb2_buffer *vb; 165 166 if (vb2_request_object_is_buffer(obj)) { 167 vb = container_of(obj, struct vb2_buffer, req_obj); 168 ctx = vb2_get_drv_priv(vb->vb2_queue); 169 170 break; 171 } 172 } 173 174 if (!ctx) 175 return -ENOENT; 176 177 count = vb2_request_buffer_cnt(req); 178 if (!count) { 179 v4l2_info(&ctx->dev->v4l2_dev, 180 "No buffer was provided with the request\n"); 181 return -ENOENT; 182 } else if (count > 1) { 183 v4l2_info(&ctx->dev->v4l2_dev, 184 "More than one buffer was provided with the request\n"); 185 return -EINVAL; 186 } 187 188 parent_hdl = &ctx->hdl; 189 190 hdl = v4l2_ctrl_request_hdl_find(req, parent_hdl); 191 if (!hdl) { 192 v4l2_info(&ctx->dev->v4l2_dev, "Missing codec control(s)\n"); 193 return -ENOENT; 194 } 195 196 for (i = 0; i < CEDRUS_CONTROLS_COUNT; i++) { 197 if (cedrus_controls[i].codec != ctx->current_codec || 198 !cedrus_controls[i].required) 199 continue; 200 201 ctrl_test = v4l2_ctrl_request_hdl_ctrl_find(hdl, 202 cedrus_controls[i].cfg.id); 203 if (!ctrl_test) { 204 v4l2_info(&ctx->dev->v4l2_dev, 205 "Missing required codec control\n"); 206 return -ENOENT; 207 } 208 } 209 210 v4l2_ctrl_request_hdl_put(hdl); 211 212 return vb2_request_validate(req); 213 } 214 215 static int cedrus_open(struct file *file) 216 { 217 struct cedrus_dev *dev = video_drvdata(file); 218 struct cedrus_ctx *ctx = NULL; 219 int ret; 220 221 if (mutex_lock_interruptible(&dev->dev_mutex)) 222 return -ERESTARTSYS; 223 224 ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 225 if (!ctx) { 226 mutex_unlock(&dev->dev_mutex); 227 return -ENOMEM; 228 } 229 230 v4l2_fh_init(&ctx->fh, video_devdata(file)); 231 file->private_data = &ctx->fh; 232 ctx->dev = dev; 233 234 ret = cedrus_init_ctrls(dev, ctx); 235 if (ret) 236 goto err_free; 237 238 ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, 239 &cedrus_queue_init); 240 if (IS_ERR(ctx->fh.m2m_ctx)) { 241 ret = PTR_ERR(ctx->fh.m2m_ctx); 242 goto err_ctrls; 243 } 244 245 v4l2_fh_add(&ctx->fh); 246 247 mutex_unlock(&dev->dev_mutex); 248 249 return 0; 250 251 err_ctrls: 252 v4l2_ctrl_handler_free(&ctx->hdl); 253 err_free: 254 kfree(ctx); 255 mutex_unlock(&dev->dev_mutex); 256 257 return ret; 258 } 259 260 static int cedrus_release(struct file *file) 261 { 262 struct cedrus_dev *dev = video_drvdata(file); 263 struct cedrus_ctx *ctx = container_of(file->private_data, 264 struct cedrus_ctx, fh); 265 266 mutex_lock(&dev->dev_mutex); 267 268 v4l2_fh_del(&ctx->fh); 269 v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); 270 271 v4l2_ctrl_handler_free(&ctx->hdl); 272 kfree(ctx->ctrls); 273 274 v4l2_fh_exit(&ctx->fh); 275 276 kfree(ctx); 277 278 mutex_unlock(&dev->dev_mutex); 279 280 return 0; 281 } 282 283 static const struct v4l2_file_operations cedrus_fops = { 284 .owner = THIS_MODULE, 285 .open = cedrus_open, 286 .release = cedrus_release, 287 .poll = v4l2_m2m_fop_poll, 288 .unlocked_ioctl = video_ioctl2, 289 .mmap = v4l2_m2m_fop_mmap, 290 }; 291 292 static const struct video_device cedrus_video_device = { 293 .name = CEDRUS_NAME, 294 .vfl_dir = VFL_DIR_M2M, 295 .fops = &cedrus_fops, 296 .ioctl_ops = &cedrus_ioctl_ops, 297 .minor = -1, 298 .release = video_device_release_empty, 299 .device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING, 300 }; 301 302 static const struct v4l2_m2m_ops cedrus_m2m_ops = { 303 .device_run = cedrus_device_run, 304 }; 305 306 static const struct media_device_ops cedrus_m2m_media_ops = { 307 .req_validate = cedrus_request_validate, 308 .req_queue = v4l2_m2m_request_queue, 309 }; 310 311 static int cedrus_probe(struct platform_device *pdev) 312 { 313 struct cedrus_dev *dev; 314 struct video_device *vfd; 315 int ret; 316 317 dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); 318 if (!dev) 319 return -ENOMEM; 320 321 dev->vfd = cedrus_video_device; 322 dev->dev = &pdev->dev; 323 dev->pdev = pdev; 324 325 ret = cedrus_hw_probe(dev); 326 if (ret) { 327 dev_err(&pdev->dev, "Failed to probe hardware\n"); 328 return ret; 329 } 330 331 dev->dec_ops[CEDRUS_CODEC_MPEG2] = &cedrus_dec_ops_mpeg2; 332 dev->dec_ops[CEDRUS_CODEC_H264] = &cedrus_dec_ops_h264; 333 334 mutex_init(&dev->dev_mutex); 335 336 ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); 337 if (ret) { 338 dev_err(&pdev->dev, "Failed to register V4L2 device\n"); 339 return ret; 340 } 341 342 vfd = &dev->vfd; 343 vfd->lock = &dev->dev_mutex; 344 vfd->v4l2_dev = &dev->v4l2_dev; 345 346 snprintf(vfd->name, sizeof(vfd->name), "%s", cedrus_video_device.name); 347 video_set_drvdata(vfd, dev); 348 349 dev->m2m_dev = v4l2_m2m_init(&cedrus_m2m_ops); 350 if (IS_ERR(dev->m2m_dev)) { 351 v4l2_err(&dev->v4l2_dev, 352 "Failed to initialize V4L2 M2M device\n"); 353 ret = PTR_ERR(dev->m2m_dev); 354 355 goto err_v4l2; 356 } 357 358 dev->mdev.dev = &pdev->dev; 359 strscpy(dev->mdev.model, CEDRUS_NAME, sizeof(dev->mdev.model)); 360 361 media_device_init(&dev->mdev); 362 dev->mdev.ops = &cedrus_m2m_media_ops; 363 dev->v4l2_dev.mdev = &dev->mdev; 364 365 ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); 366 if (ret) { 367 v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); 368 goto err_m2m; 369 } 370 371 v4l2_info(&dev->v4l2_dev, 372 "Device registered as /dev/video%d\n", vfd->num); 373 374 ret = v4l2_m2m_register_media_controller(dev->m2m_dev, vfd, 375 MEDIA_ENT_F_PROC_VIDEO_DECODER); 376 if (ret) { 377 v4l2_err(&dev->v4l2_dev, 378 "Failed to initialize V4L2 M2M media controller\n"); 379 goto err_video; 380 } 381 382 ret = media_device_register(&dev->mdev); 383 if (ret) { 384 v4l2_err(&dev->v4l2_dev, "Failed to register media device\n"); 385 goto err_m2m_mc; 386 } 387 388 platform_set_drvdata(pdev, dev); 389 390 return 0; 391 392 err_m2m_mc: 393 v4l2_m2m_unregister_media_controller(dev->m2m_dev); 394 err_video: 395 video_unregister_device(&dev->vfd); 396 err_m2m: 397 v4l2_m2m_release(dev->m2m_dev); 398 err_v4l2: 399 v4l2_device_unregister(&dev->v4l2_dev); 400 401 return ret; 402 } 403 404 static int cedrus_remove(struct platform_device *pdev) 405 { 406 struct cedrus_dev *dev = platform_get_drvdata(pdev); 407 408 if (media_devnode_is_registered(dev->mdev.devnode)) { 409 media_device_unregister(&dev->mdev); 410 v4l2_m2m_unregister_media_controller(dev->m2m_dev); 411 media_device_cleanup(&dev->mdev); 412 } 413 414 v4l2_m2m_release(dev->m2m_dev); 415 video_unregister_device(&dev->vfd); 416 v4l2_device_unregister(&dev->v4l2_dev); 417 418 cedrus_hw_remove(dev); 419 420 return 0; 421 } 422 423 static const struct cedrus_variant sun4i_a10_cedrus_variant = { 424 .mod_rate = 320000000, 425 }; 426 427 static const struct cedrus_variant sun5i_a13_cedrus_variant = { 428 .mod_rate = 320000000, 429 }; 430 431 static const struct cedrus_variant sun7i_a20_cedrus_variant = { 432 .mod_rate = 320000000, 433 }; 434 435 static const struct cedrus_variant sun8i_a33_cedrus_variant = { 436 .capabilities = CEDRUS_CAPABILITY_UNTILED, 437 .mod_rate = 320000000, 438 }; 439 440 static const struct cedrus_variant sun8i_h3_cedrus_variant = { 441 .capabilities = CEDRUS_CAPABILITY_UNTILED, 442 .mod_rate = 402000000, 443 }; 444 445 static const struct cedrus_variant sun50i_a64_cedrus_variant = { 446 .capabilities = CEDRUS_CAPABILITY_UNTILED, 447 .mod_rate = 402000000, 448 }; 449 450 static const struct cedrus_variant sun50i_h5_cedrus_variant = { 451 .capabilities = CEDRUS_CAPABILITY_UNTILED, 452 .mod_rate = 402000000, 453 }; 454 455 static const struct cedrus_variant sun50i_h6_cedrus_variant = { 456 .capabilities = CEDRUS_CAPABILITY_UNTILED, 457 .quirks = CEDRUS_QUIRK_NO_DMA_OFFSET, 458 .mod_rate = 600000000, 459 }; 460 461 static const struct of_device_id cedrus_dt_match[] = { 462 { 463 .compatible = "allwinner,sun4i-a10-video-engine", 464 .data = &sun4i_a10_cedrus_variant, 465 }, 466 { 467 .compatible = "allwinner,sun5i-a13-video-engine", 468 .data = &sun5i_a13_cedrus_variant, 469 }, 470 { 471 .compatible = "allwinner,sun7i-a20-video-engine", 472 .data = &sun7i_a20_cedrus_variant, 473 }, 474 { 475 .compatible = "allwinner,sun8i-a33-video-engine", 476 .data = &sun8i_a33_cedrus_variant, 477 }, 478 { 479 .compatible = "allwinner,sun8i-h3-video-engine", 480 .data = &sun8i_h3_cedrus_variant, 481 }, 482 { 483 .compatible = "allwinner,sun50i-a64-video-engine", 484 .data = &sun50i_a64_cedrus_variant, 485 }, 486 { 487 .compatible = "allwinner,sun50i-h5-video-engine", 488 .data = &sun50i_h5_cedrus_variant, 489 }, 490 { 491 .compatible = "allwinner,sun50i-h6-video-engine", 492 .data = &sun50i_h6_cedrus_variant, 493 }, 494 { /* sentinel */ } 495 }; 496 MODULE_DEVICE_TABLE(of, cedrus_dt_match); 497 498 static struct platform_driver cedrus_driver = { 499 .probe = cedrus_probe, 500 .remove = cedrus_remove, 501 .driver = { 502 .name = CEDRUS_NAME, 503 .of_match_table = of_match_ptr(cedrus_dt_match), 504 }, 505 }; 506 module_platform_driver(cedrus_driver); 507 508 MODULE_LICENSE("GPL v2"); 509 MODULE_AUTHOR("Florent Revest <florent.revest@free-electrons.com>"); 510 MODULE_AUTHOR("Paul Kocialkowski <paul.kocialkowski@bootlin.com>"); 511 MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin.com>"); 512 MODULE_DESCRIPTION("Cedrus VPU driver"); 513