| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Samsung S5P/EXYNOS4 SoC series FIMC (video postprocessor) driver |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright (C) 2012 - 2013 Samsung Electronics Co., Ltd. |
|---|
| 5 | 6 | * Sylwester Nawrocki <s.nawrocki@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 |
|---|
| 9 | | - * by the Free Software Foundation, either version 2 of the License, |
|---|
| 10 | | - * or (at your option) any later version. |
|---|
| 11 | 7 | */ |
|---|
| 12 | 8 | |
|---|
| 13 | 9 | #include <linux/module.h> |
|---|
| .. | .. |
|---|
| 77 | 73 | static int start_streaming(struct vb2_queue *q, unsigned int count) |
|---|
| 78 | 74 | { |
|---|
| 79 | 75 | struct fimc_ctx *ctx = q->drv_priv; |
|---|
| 80 | | - int ret; |
|---|
| 81 | 76 | |
|---|
| 82 | | - ret = pm_runtime_get_sync(&ctx->fimc_dev->pdev->dev); |
|---|
| 83 | | - return ret > 0 ? 0 : ret; |
|---|
| 77 | + return pm_runtime_resume_and_get(&ctx->fimc_dev->pdev->dev); |
|---|
| 84 | 78 | } |
|---|
| 85 | 79 | |
|---|
| 86 | 80 | static void stop_streaming(struct vb2_queue *q) |
|---|
| 87 | 81 | { |
|---|
| 88 | 82 | struct fimc_ctx *ctx = q->drv_priv; |
|---|
| 89 | | - |
|---|
| 90 | 83 | |
|---|
| 91 | 84 | fimc_m2m_shutdown(ctx); |
|---|
| 92 | 85 | fimc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR); |
|---|
| .. | .. |
|---|
| 236 | 229 | struct v4l2_capability *cap) |
|---|
| 237 | 230 | { |
|---|
| 238 | 231 | struct fimc_dev *fimc = video_drvdata(file); |
|---|
| 239 | | - unsigned int caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE; |
|---|
| 240 | 232 | |
|---|
| 241 | | - __fimc_vidioc_querycap(&fimc->pdev->dev, cap, caps); |
|---|
| 233 | + __fimc_vidioc_querycap(&fimc->pdev->dev, cap); |
|---|
| 242 | 234 | return 0; |
|---|
| 243 | 235 | } |
|---|
| 244 | 236 | |
|---|
| 245 | | -static int fimc_m2m_enum_fmt_mplane(struct file *file, void *priv, |
|---|
| 246 | | - struct v4l2_fmtdesc *f) |
|---|
| 237 | +static int fimc_m2m_enum_fmt(struct file *file, void *priv, |
|---|
| 238 | + struct v4l2_fmtdesc *f) |
|---|
| 247 | 239 | { |
|---|
| 248 | 240 | struct fimc_fmt *fmt; |
|---|
| 249 | 241 | |
|---|
| .. | .. |
|---|
| 252 | 244 | if (!fmt) |
|---|
| 253 | 245 | return -EINVAL; |
|---|
| 254 | 246 | |
|---|
| 255 | | - strncpy(f->description, fmt->name, sizeof(f->description) - 1); |
|---|
| 256 | 247 | f->pixelformat = fmt->fourcc; |
|---|
| 257 | 248 | return 0; |
|---|
| 258 | 249 | } |
|---|
| .. | .. |
|---|
| 383 | 374 | return 0; |
|---|
| 384 | 375 | } |
|---|
| 385 | 376 | |
|---|
| 386 | | -static int fimc_m2m_cropcap(struct file *file, void *fh, |
|---|
| 387 | | - struct v4l2_cropcap *cr) |
|---|
| 377 | +static int fimc_m2m_g_selection(struct file *file, void *fh, |
|---|
| 378 | + struct v4l2_selection *s) |
|---|
| 388 | 379 | { |
|---|
| 389 | 380 | struct fimc_ctx *ctx = fh_to_ctx(fh); |
|---|
| 390 | 381 | struct fimc_frame *frame; |
|---|
| 391 | 382 | |
|---|
| 392 | | - frame = ctx_get_frame(ctx, cr->type); |
|---|
| 383 | + frame = ctx_get_frame(ctx, s->type); |
|---|
| 393 | 384 | if (IS_ERR(frame)) |
|---|
| 394 | 385 | return PTR_ERR(frame); |
|---|
| 395 | 386 | |
|---|
| 396 | | - cr->bounds.left = 0; |
|---|
| 397 | | - cr->bounds.top = 0; |
|---|
| 398 | | - cr->bounds.width = frame->o_width; |
|---|
| 399 | | - cr->bounds.height = frame->o_height; |
|---|
| 400 | | - cr->defrect = cr->bounds; |
|---|
| 387 | + switch (s->target) { |
|---|
| 388 | + case V4L2_SEL_TGT_CROP: |
|---|
| 389 | + case V4L2_SEL_TGT_CROP_DEFAULT: |
|---|
| 390 | + case V4L2_SEL_TGT_CROP_BOUNDS: |
|---|
| 391 | + if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) |
|---|
| 392 | + return -EINVAL; |
|---|
| 393 | + break; |
|---|
| 394 | + case V4L2_SEL_TGT_COMPOSE: |
|---|
| 395 | + case V4L2_SEL_TGT_COMPOSE_DEFAULT: |
|---|
| 396 | + case V4L2_SEL_TGT_COMPOSE_BOUNDS: |
|---|
| 397 | + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) |
|---|
| 398 | + return -EINVAL; |
|---|
| 399 | + break; |
|---|
| 400 | + default: |
|---|
| 401 | + return -EINVAL; |
|---|
| 402 | + } |
|---|
| 401 | 403 | |
|---|
| 404 | + switch (s->target) { |
|---|
| 405 | + case V4L2_SEL_TGT_CROP: |
|---|
| 406 | + case V4L2_SEL_TGT_COMPOSE: |
|---|
| 407 | + s->r.left = frame->offs_h; |
|---|
| 408 | + s->r.top = frame->offs_v; |
|---|
| 409 | + s->r.width = frame->width; |
|---|
| 410 | + s->r.height = frame->height; |
|---|
| 411 | + break; |
|---|
| 412 | + case V4L2_SEL_TGT_CROP_DEFAULT: |
|---|
| 413 | + case V4L2_SEL_TGT_CROP_BOUNDS: |
|---|
| 414 | + case V4L2_SEL_TGT_COMPOSE_DEFAULT: |
|---|
| 415 | + case V4L2_SEL_TGT_COMPOSE_BOUNDS: |
|---|
| 416 | + s->r.left = 0; |
|---|
| 417 | + s->r.top = 0; |
|---|
| 418 | + s->r.width = frame->o_width; |
|---|
| 419 | + s->r.height = frame->o_height; |
|---|
| 420 | + break; |
|---|
| 421 | + default: |
|---|
| 422 | + return -EINVAL; |
|---|
| 423 | + } |
|---|
| 402 | 424 | return 0; |
|---|
| 403 | 425 | } |
|---|
| 404 | 426 | |
|---|
| 405 | | -static int fimc_m2m_g_crop(struct file *file, void *fh, struct v4l2_crop *cr) |
|---|
| 406 | | -{ |
|---|
| 407 | | - struct fimc_ctx *ctx = fh_to_ctx(fh); |
|---|
| 408 | | - struct fimc_frame *frame; |
|---|
| 409 | | - |
|---|
| 410 | | - frame = ctx_get_frame(ctx, cr->type); |
|---|
| 411 | | - if (IS_ERR(frame)) |
|---|
| 412 | | - return PTR_ERR(frame); |
|---|
| 413 | | - |
|---|
| 414 | | - cr->c.left = frame->offs_h; |
|---|
| 415 | | - cr->c.top = frame->offs_v; |
|---|
| 416 | | - cr->c.width = frame->width; |
|---|
| 417 | | - cr->c.height = frame->height; |
|---|
| 418 | | - |
|---|
| 419 | | - return 0; |
|---|
| 420 | | -} |
|---|
| 421 | | - |
|---|
| 422 | | -static int fimc_m2m_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr) |
|---|
| 427 | +static int fimc_m2m_try_selection(struct fimc_ctx *ctx, |
|---|
| 428 | + struct v4l2_selection *s) |
|---|
| 423 | 429 | { |
|---|
| 424 | 430 | struct fimc_dev *fimc = ctx->fimc_dev; |
|---|
| 425 | 431 | struct fimc_frame *f; |
|---|
| 426 | 432 | u32 min_size, halign, depth = 0; |
|---|
| 427 | 433 | int i; |
|---|
| 428 | 434 | |
|---|
| 429 | | - if (cr->c.top < 0 || cr->c.left < 0) { |
|---|
| 435 | + if (s->r.top < 0 || s->r.left < 0) { |
|---|
| 430 | 436 | v4l2_err(&fimc->m2m.vfd, |
|---|
| 431 | 437 | "doesn't support negative values for top & left\n"); |
|---|
| 432 | 438 | return -EINVAL; |
|---|
| 433 | 439 | } |
|---|
| 434 | | - if (cr->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) |
|---|
| 440 | + if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { |
|---|
| 435 | 441 | f = &ctx->d_frame; |
|---|
| 436 | | - else if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) |
|---|
| 442 | + if (s->target != V4L2_SEL_TGT_COMPOSE) |
|---|
| 443 | + return -EINVAL; |
|---|
| 444 | + } else if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { |
|---|
| 437 | 445 | f = &ctx->s_frame; |
|---|
| 438 | | - else |
|---|
| 446 | + if (s->target != V4L2_SEL_TGT_CROP) |
|---|
| 447 | + return -EINVAL; |
|---|
| 448 | + } else { |
|---|
| 439 | 449 | return -EINVAL; |
|---|
| 450 | + } |
|---|
| 440 | 451 | |
|---|
| 441 | 452 | min_size = (f == &ctx->s_frame) ? |
|---|
| 442 | 453 | fimc->variant->min_inp_pixsize : fimc->variant->min_out_pixsize; |
|---|
| .. | .. |
|---|
| 450 | 461 | for (i = 0; i < f->fmt->memplanes; i++) |
|---|
| 451 | 462 | depth += f->fmt->depth[i]; |
|---|
| 452 | 463 | |
|---|
| 453 | | - v4l_bound_align_image(&cr->c.width, min_size, f->o_width, |
|---|
| 464 | + v4l_bound_align_image(&s->r.width, min_size, f->o_width, |
|---|
| 454 | 465 | ffs(min_size) - 1, |
|---|
| 455 | | - &cr->c.height, min_size, f->o_height, |
|---|
| 466 | + &s->r.height, min_size, f->o_height, |
|---|
| 456 | 467 | halign, 64/(ALIGN(depth, 8))); |
|---|
| 457 | 468 | |
|---|
| 458 | 469 | /* adjust left/top if cropping rectangle is out of bounds */ |
|---|
| 459 | | - if (cr->c.left + cr->c.width > f->o_width) |
|---|
| 460 | | - cr->c.left = f->o_width - cr->c.width; |
|---|
| 461 | | - if (cr->c.top + cr->c.height > f->o_height) |
|---|
| 462 | | - cr->c.top = f->o_height - cr->c.height; |
|---|
| 470 | + if (s->r.left + s->r.width > f->o_width) |
|---|
| 471 | + s->r.left = f->o_width - s->r.width; |
|---|
| 472 | + if (s->r.top + s->r.height > f->o_height) |
|---|
| 473 | + s->r.top = f->o_height - s->r.height; |
|---|
| 463 | 474 | |
|---|
| 464 | | - cr->c.left = round_down(cr->c.left, min_size); |
|---|
| 465 | | - cr->c.top = round_down(cr->c.top, fimc->variant->hor_offs_align); |
|---|
| 475 | + s->r.left = round_down(s->r.left, min_size); |
|---|
| 476 | + s->r.top = round_down(s->r.top, fimc->variant->hor_offs_align); |
|---|
| 466 | 477 | |
|---|
| 467 | 478 | dbg("l:%d, t:%d, w:%d, h:%d, f_w: %d, f_h: %d", |
|---|
| 468 | | - cr->c.left, cr->c.top, cr->c.width, cr->c.height, |
|---|
| 479 | + s->r.left, s->r.top, s->r.width, s->r.height, |
|---|
| 469 | 480 | f->f_width, f->f_height); |
|---|
| 470 | 481 | |
|---|
| 471 | 482 | return 0; |
|---|
| 472 | 483 | } |
|---|
| 473 | 484 | |
|---|
| 474 | | -static int fimc_m2m_s_crop(struct file *file, void *fh, const struct v4l2_crop *crop) |
|---|
| 485 | +static int fimc_m2m_s_selection(struct file *file, void *fh, |
|---|
| 486 | + struct v4l2_selection *s) |
|---|
| 475 | 487 | { |
|---|
| 476 | 488 | struct fimc_ctx *ctx = fh_to_ctx(fh); |
|---|
| 477 | 489 | struct fimc_dev *fimc = ctx->fimc_dev; |
|---|
| 478 | | - struct v4l2_crop cr = *crop; |
|---|
| 479 | 490 | struct fimc_frame *f; |
|---|
| 480 | 491 | int ret; |
|---|
| 481 | 492 | |
|---|
| 482 | | - ret = fimc_m2m_try_crop(ctx, &cr); |
|---|
| 493 | + ret = fimc_m2m_try_selection(ctx, s); |
|---|
| 483 | 494 | if (ret) |
|---|
| 484 | 495 | return ret; |
|---|
| 485 | 496 | |
|---|
| 486 | | - f = (cr.type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ? |
|---|
| 497 | + f = (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) ? |
|---|
| 487 | 498 | &ctx->s_frame : &ctx->d_frame; |
|---|
| 488 | 499 | |
|---|
| 489 | 500 | /* Check to see if scaling ratio is within supported range */ |
|---|
| 490 | | - if (cr.type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { |
|---|
| 491 | | - ret = fimc_check_scaler_ratio(ctx, cr.c.width, |
|---|
| 492 | | - cr.c.height, ctx->d_frame.width, |
|---|
| 501 | + if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { |
|---|
| 502 | + ret = fimc_check_scaler_ratio(ctx, s->r.width, |
|---|
| 503 | + s->r.height, ctx->d_frame.width, |
|---|
| 493 | 504 | ctx->d_frame.height, ctx->rotation); |
|---|
| 494 | 505 | } else { |
|---|
| 495 | 506 | ret = fimc_check_scaler_ratio(ctx, ctx->s_frame.width, |
|---|
| 496 | | - ctx->s_frame.height, cr.c.width, |
|---|
| 497 | | - cr.c.height, ctx->rotation); |
|---|
| 507 | + ctx->s_frame.height, s->r.width, |
|---|
| 508 | + s->r.height, ctx->rotation); |
|---|
| 498 | 509 | } |
|---|
| 499 | 510 | if (ret) { |
|---|
| 500 | 511 | v4l2_err(&fimc->m2m.vfd, "Out of scaler range\n"); |
|---|
| 501 | 512 | return -EINVAL; |
|---|
| 502 | 513 | } |
|---|
| 503 | 514 | |
|---|
| 504 | | - f->offs_h = cr.c.left; |
|---|
| 505 | | - f->offs_v = cr.c.top; |
|---|
| 506 | | - f->width = cr.c.width; |
|---|
| 507 | | - f->height = cr.c.height; |
|---|
| 515 | + f->offs_h = s->r.left; |
|---|
| 516 | + f->offs_v = s->r.top; |
|---|
| 517 | + f->width = s->r.width; |
|---|
| 518 | + f->height = s->r.height; |
|---|
| 508 | 519 | |
|---|
| 509 | 520 | fimc_ctx_state_set(FIMC_PARAMS, ctx); |
|---|
| 510 | 521 | |
|---|
| .. | .. |
|---|
| 513 | 524 | |
|---|
| 514 | 525 | static const struct v4l2_ioctl_ops fimc_m2m_ioctl_ops = { |
|---|
| 515 | 526 | .vidioc_querycap = fimc_m2m_querycap, |
|---|
| 516 | | - .vidioc_enum_fmt_vid_cap_mplane = fimc_m2m_enum_fmt_mplane, |
|---|
| 517 | | - .vidioc_enum_fmt_vid_out_mplane = fimc_m2m_enum_fmt_mplane, |
|---|
| 527 | + .vidioc_enum_fmt_vid_cap = fimc_m2m_enum_fmt, |
|---|
| 528 | + .vidioc_enum_fmt_vid_out = fimc_m2m_enum_fmt, |
|---|
| 518 | 529 | .vidioc_g_fmt_vid_cap_mplane = fimc_m2m_g_fmt_mplane, |
|---|
| 519 | 530 | .vidioc_g_fmt_vid_out_mplane = fimc_m2m_g_fmt_mplane, |
|---|
| 520 | 531 | .vidioc_try_fmt_vid_cap_mplane = fimc_m2m_try_fmt_mplane, |
|---|
| .. | .. |
|---|
| 528 | 539 | .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, |
|---|
| 529 | 540 | .vidioc_streamon = v4l2_m2m_ioctl_streamon, |
|---|
| 530 | 541 | .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, |
|---|
| 531 | | - .vidioc_g_crop = fimc_m2m_g_crop, |
|---|
| 532 | | - .vidioc_s_crop = fimc_m2m_s_crop, |
|---|
| 533 | | - .vidioc_cropcap = fimc_m2m_cropcap |
|---|
| 542 | + .vidioc_g_selection = fimc_m2m_g_selection, |
|---|
| 543 | + .vidioc_s_selection = fimc_m2m_s_selection, |
|---|
| 534 | 544 | |
|---|
| 535 | 545 | }; |
|---|
| 536 | 546 | |
|---|
| .. | .. |
|---|
| 717 | 727 | vfd->release = video_device_release_empty; |
|---|
| 718 | 728 | vfd->lock = &fimc->lock; |
|---|
| 719 | 729 | vfd->vfl_dir = VFL_DIR_M2M; |
|---|
| 730 | + vfd->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE; |
|---|
| 731 | + set_bit(V4L2_FL_QUIRK_INVERTED_CROP, &vfd->flags); |
|---|
| 720 | 732 | |
|---|
| 721 | 733 | snprintf(vfd->name, sizeof(vfd->name), "fimc.%d.m2m", fimc->id); |
|---|
| 722 | 734 | video_set_drvdata(vfd, fimc); |
|---|
| .. | .. |
|---|
| 731 | 743 | if (ret) |
|---|
| 732 | 744 | goto err_me; |
|---|
| 733 | 745 | |
|---|
| 734 | | - ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); |
|---|
| 746 | + ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1); |
|---|
| 735 | 747 | if (ret) |
|---|
| 736 | 748 | goto err_vd; |
|---|
| 737 | 749 | |
|---|