.. | .. |
---|
| 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: |
---|