| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Samsung S5P G2D - 2D Graphics Accelerator Driver |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright (c) 2011 Samsung Electronics Co., Ltd. |
|---|
| 5 | 6 | * Kamil Debski, <k.debski@samsung.com> |
|---|
| 6 | | - * |
|---|
| 7 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 8 | | - * it under the terms of the GNU General Public License as published by the |
|---|
| 9 | | - * Free Software Foundation; either version 2 of the |
|---|
| 10 | | - * License, or (at your option) any later version |
|---|
| 11 | 7 | */ |
|---|
| 12 | 8 | |
|---|
| 13 | 9 | #include <linux/module.h> |
|---|
| .. | .. |
|---|
| 33 | 29 | |
|---|
| 34 | 30 | static struct g2d_fmt formats[] = { |
|---|
| 35 | 31 | { |
|---|
| 36 | | - .name = "XRGB_8888", |
|---|
| 37 | 32 | .fourcc = V4L2_PIX_FMT_RGB32, |
|---|
| 38 | 33 | .depth = 32, |
|---|
| 39 | 34 | .hw = COLOR_MODE(ORDER_XRGB, MODE_XRGB_8888), |
|---|
| 40 | 35 | }, |
|---|
| 41 | 36 | { |
|---|
| 42 | | - .name = "RGB_565", |
|---|
| 43 | 37 | .fourcc = V4L2_PIX_FMT_RGB565X, |
|---|
| 44 | 38 | .depth = 16, |
|---|
| 45 | 39 | .hw = COLOR_MODE(ORDER_XRGB, MODE_RGB_565), |
|---|
| 46 | 40 | }, |
|---|
| 47 | 41 | { |
|---|
| 48 | | - .name = "XRGB_1555", |
|---|
| 49 | 42 | .fourcc = V4L2_PIX_FMT_RGB555X, |
|---|
| 50 | 43 | .depth = 16, |
|---|
| 51 | 44 | .hw = COLOR_MODE(ORDER_XRGB, MODE_XRGB_1555), |
|---|
| 52 | 45 | }, |
|---|
| 53 | 46 | { |
|---|
| 54 | | - .name = "XRGB_4444", |
|---|
| 55 | 47 | .fourcc = V4L2_PIX_FMT_RGB444, |
|---|
| 56 | 48 | .depth = 16, |
|---|
| 57 | 49 | .hw = COLOR_MODE(ORDER_XRGB, MODE_XRGB_4444), |
|---|
| 58 | 50 | }, |
|---|
| 59 | 51 | { |
|---|
| 60 | | - .name = "PACKED_RGB_888", |
|---|
| 61 | 52 | .fourcc = V4L2_PIX_FMT_RGB24, |
|---|
| 62 | 53 | .depth = 24, |
|---|
| 63 | 54 | .hw = COLOR_MODE(ORDER_XRGB, MODE_PACKED_RGB_888), |
|---|
| .. | .. |
|---|
| 89 | 80 | |
|---|
| 90 | 81 | |
|---|
| 91 | 82 | static struct g2d_frame *get_frame(struct g2d_ctx *ctx, |
|---|
| 92 | | - enum v4l2_buf_type type) |
|---|
| 83 | + enum v4l2_buf_type type) |
|---|
| 93 | 84 | { |
|---|
| 94 | 85 | switch (type) { |
|---|
| 95 | 86 | case V4L2_BUF_TYPE_VIDEO_OUTPUT: |
|---|
| .. | .. |
|---|
| 300 | 291 | static int vidioc_querycap(struct file *file, void *priv, |
|---|
| 301 | 292 | struct v4l2_capability *cap) |
|---|
| 302 | 293 | { |
|---|
| 303 | | - strncpy(cap->driver, G2D_NAME, sizeof(cap->driver) - 1); |
|---|
| 304 | | - strncpy(cap->card, G2D_NAME, sizeof(cap->card) - 1); |
|---|
| 294 | + strscpy(cap->driver, G2D_NAME, sizeof(cap->driver)); |
|---|
| 295 | + strscpy(cap->card, G2D_NAME, sizeof(cap->card)); |
|---|
| 305 | 296 | cap->bus_info[0] = 0; |
|---|
| 306 | | - cap->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; |
|---|
| 307 | | - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; |
|---|
| 308 | 297 | return 0; |
|---|
| 309 | 298 | } |
|---|
| 310 | 299 | |
|---|
| 311 | 300 | static int vidioc_enum_fmt(struct file *file, void *prv, struct v4l2_fmtdesc *f) |
|---|
| 312 | 301 | { |
|---|
| 313 | | - struct g2d_fmt *fmt; |
|---|
| 314 | 302 | if (f->index >= NUM_FORMATS) |
|---|
| 315 | 303 | return -EINVAL; |
|---|
| 316 | | - fmt = &formats[f->index]; |
|---|
| 317 | | - f->pixelformat = fmt->fourcc; |
|---|
| 318 | | - strncpy(f->description, fmt->name, sizeof(f->description) - 1); |
|---|
| 304 | + f->pixelformat = formats[f->index].fourcc; |
|---|
| 319 | 305 | return 0; |
|---|
| 320 | 306 | } |
|---|
| 321 | 307 | |
|---|
| .. | .. |
|---|
| 411 | 397 | return 0; |
|---|
| 412 | 398 | } |
|---|
| 413 | 399 | |
|---|
| 414 | | -static int vidioc_cropcap(struct file *file, void *priv, |
|---|
| 415 | | - struct v4l2_cropcap *cr) |
|---|
| 416 | | -{ |
|---|
| 417 | | - struct g2d_ctx *ctx = priv; |
|---|
| 418 | | - struct g2d_frame *f; |
|---|
| 419 | | - |
|---|
| 420 | | - f = get_frame(ctx, cr->type); |
|---|
| 421 | | - if (IS_ERR(f)) |
|---|
| 422 | | - return PTR_ERR(f); |
|---|
| 423 | | - |
|---|
| 424 | | - cr->bounds.left = 0; |
|---|
| 425 | | - cr->bounds.top = 0; |
|---|
| 426 | | - cr->bounds.width = f->width; |
|---|
| 427 | | - cr->bounds.height = f->height; |
|---|
| 428 | | - cr->defrect = cr->bounds; |
|---|
| 429 | | - return 0; |
|---|
| 430 | | -} |
|---|
| 431 | | - |
|---|
| 432 | | -static int vidioc_g_crop(struct file *file, void *prv, struct v4l2_crop *cr) |
|---|
| 400 | +static int vidioc_g_selection(struct file *file, void *prv, |
|---|
| 401 | + struct v4l2_selection *s) |
|---|
| 433 | 402 | { |
|---|
| 434 | 403 | struct g2d_ctx *ctx = prv; |
|---|
| 435 | 404 | struct g2d_frame *f; |
|---|
| 436 | 405 | |
|---|
| 437 | | - f = get_frame(ctx, cr->type); |
|---|
| 406 | + f = get_frame(ctx, s->type); |
|---|
| 438 | 407 | if (IS_ERR(f)) |
|---|
| 439 | 408 | return PTR_ERR(f); |
|---|
| 440 | 409 | |
|---|
| 441 | | - cr->c.left = f->o_height; |
|---|
| 442 | | - cr->c.top = f->o_width; |
|---|
| 443 | | - cr->c.width = f->c_width; |
|---|
| 444 | | - cr->c.height = f->c_height; |
|---|
| 410 | + switch (s->target) { |
|---|
| 411 | + case V4L2_SEL_TGT_CROP: |
|---|
| 412 | + case V4L2_SEL_TGT_CROP_DEFAULT: |
|---|
| 413 | + case V4L2_SEL_TGT_CROP_BOUNDS: |
|---|
| 414 | + if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) |
|---|
| 415 | + return -EINVAL; |
|---|
| 416 | + break; |
|---|
| 417 | + case V4L2_SEL_TGT_COMPOSE: |
|---|
| 418 | + case V4L2_SEL_TGT_COMPOSE_DEFAULT: |
|---|
| 419 | + case V4L2_SEL_TGT_COMPOSE_BOUNDS: |
|---|
| 420 | + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) |
|---|
| 421 | + return -EINVAL; |
|---|
| 422 | + break; |
|---|
| 423 | + default: |
|---|
| 424 | + return -EINVAL; |
|---|
| 425 | + } |
|---|
| 426 | + |
|---|
| 427 | + switch (s->target) { |
|---|
| 428 | + case V4L2_SEL_TGT_CROP: |
|---|
| 429 | + case V4L2_SEL_TGT_COMPOSE: |
|---|
| 430 | + s->r.left = f->o_height; |
|---|
| 431 | + s->r.top = f->o_width; |
|---|
| 432 | + s->r.width = f->c_width; |
|---|
| 433 | + s->r.height = f->c_height; |
|---|
| 434 | + break; |
|---|
| 435 | + case V4L2_SEL_TGT_CROP_DEFAULT: |
|---|
| 436 | + case V4L2_SEL_TGT_CROP_BOUNDS: |
|---|
| 437 | + case V4L2_SEL_TGT_COMPOSE_DEFAULT: |
|---|
| 438 | + case V4L2_SEL_TGT_COMPOSE_BOUNDS: |
|---|
| 439 | + s->r.left = 0; |
|---|
| 440 | + s->r.top = 0; |
|---|
| 441 | + s->r.width = f->width; |
|---|
| 442 | + s->r.height = f->height; |
|---|
| 443 | + break; |
|---|
| 444 | + default: |
|---|
| 445 | + return -EINVAL; |
|---|
| 446 | + } |
|---|
| 445 | 447 | return 0; |
|---|
| 446 | 448 | } |
|---|
| 447 | 449 | |
|---|
| 448 | | -static int vidioc_try_crop(struct file *file, void *prv, const struct v4l2_crop *cr) |
|---|
| 450 | +static int vidioc_try_selection(struct file *file, void *prv, |
|---|
| 451 | + const struct v4l2_selection *s) |
|---|
| 449 | 452 | { |
|---|
| 450 | 453 | struct g2d_ctx *ctx = prv; |
|---|
| 451 | 454 | struct g2d_dev *dev = ctx->dev; |
|---|
| 452 | 455 | struct g2d_frame *f; |
|---|
| 453 | 456 | |
|---|
| 454 | | - f = get_frame(ctx, cr->type); |
|---|
| 457 | + f = get_frame(ctx, s->type); |
|---|
| 455 | 458 | if (IS_ERR(f)) |
|---|
| 456 | 459 | return PTR_ERR(f); |
|---|
| 457 | 460 | |
|---|
| 458 | | - if (cr->c.top < 0 || cr->c.left < 0) { |
|---|
| 461 | + if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { |
|---|
| 462 | + if (s->target != V4L2_SEL_TGT_COMPOSE) |
|---|
| 463 | + return -EINVAL; |
|---|
| 464 | + } else if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { |
|---|
| 465 | + if (s->target != V4L2_SEL_TGT_CROP) |
|---|
| 466 | + return -EINVAL; |
|---|
| 467 | + } |
|---|
| 468 | + |
|---|
| 469 | + if (s->r.top < 0 || s->r.left < 0) { |
|---|
| 459 | 470 | v4l2_err(&dev->v4l2_dev, |
|---|
| 460 | 471 | "doesn't support negative values for top & left\n"); |
|---|
| 461 | 472 | return -EINVAL; |
|---|
| .. | .. |
|---|
| 464 | 475 | return 0; |
|---|
| 465 | 476 | } |
|---|
| 466 | 477 | |
|---|
| 467 | | -static int vidioc_s_crop(struct file *file, void *prv, const struct v4l2_crop *cr) |
|---|
| 478 | +static int vidioc_s_selection(struct file *file, void *prv, |
|---|
| 479 | + struct v4l2_selection *s) |
|---|
| 468 | 480 | { |
|---|
| 469 | 481 | struct g2d_ctx *ctx = prv; |
|---|
| 470 | 482 | struct g2d_frame *f; |
|---|
| 471 | 483 | int ret; |
|---|
| 472 | 484 | |
|---|
| 473 | | - ret = vidioc_try_crop(file, prv, cr); |
|---|
| 485 | + ret = vidioc_try_selection(file, prv, s); |
|---|
| 474 | 486 | if (ret) |
|---|
| 475 | 487 | return ret; |
|---|
| 476 | | - f = get_frame(ctx, cr->type); |
|---|
| 488 | + f = get_frame(ctx, s->type); |
|---|
| 477 | 489 | if (IS_ERR(f)) |
|---|
| 478 | 490 | return PTR_ERR(f); |
|---|
| 479 | 491 | |
|---|
| 480 | | - f->c_width = cr->c.width; |
|---|
| 481 | | - f->c_height = cr->c.height; |
|---|
| 482 | | - f->o_width = cr->c.left; |
|---|
| 483 | | - f->o_height = cr->c.top; |
|---|
| 492 | + f->c_width = s->r.width; |
|---|
| 493 | + f->c_height = s->r.height; |
|---|
| 494 | + f->o_width = s->r.left; |
|---|
| 495 | + f->o_height = s->r.top; |
|---|
| 484 | 496 | f->bottom = f->o_height + f->c_height; |
|---|
| 485 | 497 | f->right = f->o_width + f->c_width; |
|---|
| 486 | 498 | return 0; |
|---|
| .. | .. |
|---|
| 588 | 600 | .vidioc_streamon = v4l2_m2m_ioctl_streamon, |
|---|
| 589 | 601 | .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, |
|---|
| 590 | 602 | |
|---|
| 591 | | - .vidioc_g_crop = vidioc_g_crop, |
|---|
| 592 | | - .vidioc_s_crop = vidioc_s_crop, |
|---|
| 593 | | - .vidioc_cropcap = vidioc_cropcap, |
|---|
| 603 | + .vidioc_g_selection = vidioc_g_selection, |
|---|
| 604 | + .vidioc_s_selection = vidioc_s_selection, |
|---|
| 594 | 605 | }; |
|---|
| 595 | 606 | |
|---|
| 596 | 607 | static const struct video_device g2d_videodev = { |
|---|
| .. | .. |
|---|
| 683 | 694 | goto unreg_v4l2_dev; |
|---|
| 684 | 695 | } |
|---|
| 685 | 696 | *vfd = g2d_videodev; |
|---|
| 697 | + set_bit(V4L2_FL_QUIRK_INVERTED_CROP, &vfd->flags); |
|---|
| 686 | 698 | vfd->lock = &dev->mutex; |
|---|
| 687 | 699 | vfd->v4l2_dev = &dev->v4l2_dev; |
|---|
| 688 | | - ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); |
|---|
| 689 | | - if (ret) { |
|---|
| 690 | | - v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); |
|---|
| 691 | | - goto rel_vdev; |
|---|
| 692 | | - } |
|---|
| 693 | | - video_set_drvdata(vfd, dev); |
|---|
| 694 | | - dev->vfd = vfd; |
|---|
| 695 | | - v4l2_info(&dev->v4l2_dev, "device registered as /dev/video%d\n", |
|---|
| 696 | | - vfd->num); |
|---|
| 700 | + vfd->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; |
|---|
| 701 | + |
|---|
| 697 | 702 | platform_set_drvdata(pdev, dev); |
|---|
| 698 | 703 | dev->m2m_dev = v4l2_m2m_init(&g2d_m2m_ops); |
|---|
| 699 | 704 | if (IS_ERR(dev->m2m_dev)) { |
|---|
| 700 | 705 | v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n"); |
|---|
| 701 | 706 | ret = PTR_ERR(dev->m2m_dev); |
|---|
| 702 | | - goto unreg_video_dev; |
|---|
| 707 | + goto rel_vdev; |
|---|
| 703 | 708 | } |
|---|
| 704 | 709 | |
|---|
| 705 | 710 | def_frame.stride = (def_frame.width * def_frame.fmt->depth) >> 3; |
|---|
| .. | .. |
|---|
| 707 | 712 | of_id = of_match_node(exynos_g2d_match, pdev->dev.of_node); |
|---|
| 708 | 713 | if (!of_id) { |
|---|
| 709 | 714 | ret = -ENODEV; |
|---|
| 710 | | - goto unreg_video_dev; |
|---|
| 715 | + goto free_m2m; |
|---|
| 711 | 716 | } |
|---|
| 712 | 717 | dev->variant = (struct g2d_variant *)of_id->data; |
|---|
| 713 | 718 | |
|---|
| 719 | + ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); |
|---|
| 720 | + if (ret) { |
|---|
| 721 | + v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); |
|---|
| 722 | + goto free_m2m; |
|---|
| 723 | + } |
|---|
| 724 | + video_set_drvdata(vfd, dev); |
|---|
| 725 | + dev->vfd = vfd; |
|---|
| 726 | + v4l2_info(&dev->v4l2_dev, "device registered as /dev/video%d\n", |
|---|
| 727 | + vfd->num); |
|---|
| 728 | + |
|---|
| 714 | 729 | return 0; |
|---|
| 715 | 730 | |
|---|
| 716 | | -unreg_video_dev: |
|---|
| 717 | | - video_unregister_device(dev->vfd); |
|---|
| 731 | +free_m2m: |
|---|
| 732 | + v4l2_m2m_release(dev->m2m_dev); |
|---|
| 718 | 733 | rel_vdev: |
|---|
| 719 | 734 | video_device_release(vfd); |
|---|
| 720 | 735 | unreg_v4l2_dev: |
|---|