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