18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * vivid-vid-cap.c - video capture support functions. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/errno.h> 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/sched.h> 118c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 128c2ecf20Sopenharmony_ci#include <linux/videodev2.h> 138c2ecf20Sopenharmony_ci#include <linux/v4l2-dv-timings.h> 148c2ecf20Sopenharmony_ci#include <media/v4l2-common.h> 158c2ecf20Sopenharmony_ci#include <media/v4l2-event.h> 168c2ecf20Sopenharmony_ci#include <media/v4l2-dv-timings.h> 178c2ecf20Sopenharmony_ci#include <media/v4l2-rect.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include "vivid-core.h" 208c2ecf20Sopenharmony_ci#include "vivid-vid-common.h" 218c2ecf20Sopenharmony_ci#include "vivid-kthread-cap.h" 228c2ecf20Sopenharmony_ci#include "vivid-vid-cap.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic const struct vivid_fmt formats_ovl[] = { 258c2ecf20Sopenharmony_ci { 268c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */ 278c2ecf20Sopenharmony_ci .vdownsampling = { 1 }, 288c2ecf20Sopenharmony_ci .bit_depth = { 16 }, 298c2ecf20Sopenharmony_ci .planes = 1, 308c2ecf20Sopenharmony_ci .buffers = 1, 318c2ecf20Sopenharmony_ci }, 328c2ecf20Sopenharmony_ci { 338c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_XRGB555, /* gggbbbbb arrrrrgg */ 348c2ecf20Sopenharmony_ci .vdownsampling = { 1 }, 358c2ecf20Sopenharmony_ci .bit_depth = { 16 }, 368c2ecf20Sopenharmony_ci .planes = 1, 378c2ecf20Sopenharmony_ci .buffers = 1, 388c2ecf20Sopenharmony_ci }, 398c2ecf20Sopenharmony_ci { 408c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_ARGB555, /* gggbbbbb arrrrrgg */ 418c2ecf20Sopenharmony_ci .vdownsampling = { 1 }, 428c2ecf20Sopenharmony_ci .bit_depth = { 16 }, 438c2ecf20Sopenharmony_ci .planes = 1, 448c2ecf20Sopenharmony_ci .buffers = 1, 458c2ecf20Sopenharmony_ci }, 468c2ecf20Sopenharmony_ci}; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/* The number of discrete webcam framesizes */ 498c2ecf20Sopenharmony_ci#define VIVID_WEBCAM_SIZES 6 508c2ecf20Sopenharmony_ci/* The number of discrete webcam frameintervals */ 518c2ecf20Sopenharmony_ci#define VIVID_WEBCAM_IVALS (VIVID_WEBCAM_SIZES * 2) 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci/* Sizes must be in increasing order */ 548c2ecf20Sopenharmony_cistatic const struct v4l2_frmsize_discrete webcam_sizes[VIVID_WEBCAM_SIZES] = { 558c2ecf20Sopenharmony_ci { 320, 180 }, 568c2ecf20Sopenharmony_ci { 640, 360 }, 578c2ecf20Sopenharmony_ci { 640, 480 }, 588c2ecf20Sopenharmony_ci { 1280, 720 }, 598c2ecf20Sopenharmony_ci { 1920, 1080 }, 608c2ecf20Sopenharmony_ci { 3840, 2160 }, 618c2ecf20Sopenharmony_ci}; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci/* 648c2ecf20Sopenharmony_ci * Intervals must be in increasing order and there must be twice as many 658c2ecf20Sopenharmony_ci * elements in this array as there are in webcam_sizes. 668c2ecf20Sopenharmony_ci */ 678c2ecf20Sopenharmony_cistatic const struct v4l2_fract webcam_intervals[VIVID_WEBCAM_IVALS] = { 688c2ecf20Sopenharmony_ci { 1, 1 }, 698c2ecf20Sopenharmony_ci { 1, 2 }, 708c2ecf20Sopenharmony_ci { 1, 4 }, 718c2ecf20Sopenharmony_ci { 1, 5 }, 728c2ecf20Sopenharmony_ci { 1, 10 }, 738c2ecf20Sopenharmony_ci { 2, 25 }, 748c2ecf20Sopenharmony_ci { 1, 15 }, 758c2ecf20Sopenharmony_ci { 1, 25 }, 768c2ecf20Sopenharmony_ci { 1, 30 }, 778c2ecf20Sopenharmony_ci { 1, 40 }, 788c2ecf20Sopenharmony_ci { 1, 50 }, 798c2ecf20Sopenharmony_ci { 1, 60 }, 808c2ecf20Sopenharmony_ci}; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic int vid_cap_queue_setup(struct vb2_queue *vq, 838c2ecf20Sopenharmony_ci unsigned *nbuffers, unsigned *nplanes, 848c2ecf20Sopenharmony_ci unsigned sizes[], struct device *alloc_devs[]) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci struct vivid_dev *dev = vb2_get_drv_priv(vq); 878c2ecf20Sopenharmony_ci unsigned buffers = tpg_g_buffers(&dev->tpg); 888c2ecf20Sopenharmony_ci unsigned h = dev->fmt_cap_rect.height; 898c2ecf20Sopenharmony_ci unsigned p; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci if (dev->field_cap == V4L2_FIELD_ALTERNATE) { 928c2ecf20Sopenharmony_ci /* 938c2ecf20Sopenharmony_ci * You cannot use read() with FIELD_ALTERNATE since the field 948c2ecf20Sopenharmony_ci * information (TOP/BOTTOM) cannot be passed back to the user. 958c2ecf20Sopenharmony_ci */ 968c2ecf20Sopenharmony_ci if (vb2_fileio_is_active(vq)) 978c2ecf20Sopenharmony_ci return -EINVAL; 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci if (dev->queue_setup_error) { 1018c2ecf20Sopenharmony_ci /* 1028c2ecf20Sopenharmony_ci * Error injection: test what happens if queue_setup() returns 1038c2ecf20Sopenharmony_ci * an error. 1048c2ecf20Sopenharmony_ci */ 1058c2ecf20Sopenharmony_ci dev->queue_setup_error = false; 1068c2ecf20Sopenharmony_ci return -EINVAL; 1078c2ecf20Sopenharmony_ci } 1088c2ecf20Sopenharmony_ci if (*nplanes) { 1098c2ecf20Sopenharmony_ci /* 1108c2ecf20Sopenharmony_ci * Check if the number of requested planes match 1118c2ecf20Sopenharmony_ci * the number of buffers in the current format. You can't mix that. 1128c2ecf20Sopenharmony_ci */ 1138c2ecf20Sopenharmony_ci if (*nplanes != buffers) 1148c2ecf20Sopenharmony_ci return -EINVAL; 1158c2ecf20Sopenharmony_ci for (p = 0; p < buffers; p++) { 1168c2ecf20Sopenharmony_ci if (sizes[p] < tpg_g_line_width(&dev->tpg, p) * h + 1178c2ecf20Sopenharmony_ci dev->fmt_cap->data_offset[p]) 1188c2ecf20Sopenharmony_ci return -EINVAL; 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci } else { 1218c2ecf20Sopenharmony_ci for (p = 0; p < buffers; p++) 1228c2ecf20Sopenharmony_ci sizes[p] = (tpg_g_line_width(&dev->tpg, p) * h) / 1238c2ecf20Sopenharmony_ci dev->fmt_cap->vdownsampling[p] + 1248c2ecf20Sopenharmony_ci dev->fmt_cap->data_offset[p]; 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci if (vq->num_buffers + *nbuffers < 2) 1288c2ecf20Sopenharmony_ci *nbuffers = 2 - vq->num_buffers; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci *nplanes = buffers; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci dprintk(dev, 1, "%s: count=%d\n", __func__, *nbuffers); 1338c2ecf20Sopenharmony_ci for (p = 0; p < buffers; p++) 1348c2ecf20Sopenharmony_ci dprintk(dev, 1, "%s: size[%u]=%u\n", __func__, p, sizes[p]); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci return 0; 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic int vid_cap_buf_prepare(struct vb2_buffer *vb) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); 1428c2ecf20Sopenharmony_ci unsigned long size; 1438c2ecf20Sopenharmony_ci unsigned buffers = tpg_g_buffers(&dev->tpg); 1448c2ecf20Sopenharmony_ci unsigned p; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci dprintk(dev, 1, "%s\n", __func__); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci if (WARN_ON(NULL == dev->fmt_cap)) 1498c2ecf20Sopenharmony_ci return -EINVAL; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci if (dev->buf_prepare_error) { 1528c2ecf20Sopenharmony_ci /* 1538c2ecf20Sopenharmony_ci * Error injection: test what happens if buf_prepare() returns 1548c2ecf20Sopenharmony_ci * an error. 1558c2ecf20Sopenharmony_ci */ 1568c2ecf20Sopenharmony_ci dev->buf_prepare_error = false; 1578c2ecf20Sopenharmony_ci return -EINVAL; 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci for (p = 0; p < buffers; p++) { 1608c2ecf20Sopenharmony_ci size = (tpg_g_line_width(&dev->tpg, p) * 1618c2ecf20Sopenharmony_ci dev->fmt_cap_rect.height) / 1628c2ecf20Sopenharmony_ci dev->fmt_cap->vdownsampling[p] + 1638c2ecf20Sopenharmony_ci dev->fmt_cap->data_offset[p]; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci if (vb2_plane_size(vb, p) < size) { 1668c2ecf20Sopenharmony_ci dprintk(dev, 1, "%s data will not fit into plane %u (%lu < %lu)\n", 1678c2ecf20Sopenharmony_ci __func__, p, vb2_plane_size(vb, p), size); 1688c2ecf20Sopenharmony_ci return -EINVAL; 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci vb2_set_plane_payload(vb, p, size); 1728c2ecf20Sopenharmony_ci vb->planes[p].data_offset = dev->fmt_cap->data_offset[p]; 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci return 0; 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic void vid_cap_buf_finish(struct vb2_buffer *vb) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 1818c2ecf20Sopenharmony_ci struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); 1828c2ecf20Sopenharmony_ci struct v4l2_timecode *tc = &vbuf->timecode; 1838c2ecf20Sopenharmony_ci unsigned fps = 25; 1848c2ecf20Sopenharmony_ci unsigned seq = vbuf->sequence; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci if (!vivid_is_sdtv_cap(dev)) 1878c2ecf20Sopenharmony_ci return; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci /* 1908c2ecf20Sopenharmony_ci * Set the timecode. Rarely used, so it is interesting to 1918c2ecf20Sopenharmony_ci * test this. 1928c2ecf20Sopenharmony_ci */ 1938c2ecf20Sopenharmony_ci vbuf->flags |= V4L2_BUF_FLAG_TIMECODE; 1948c2ecf20Sopenharmony_ci if (dev->std_cap[dev->input] & V4L2_STD_525_60) 1958c2ecf20Sopenharmony_ci fps = 30; 1968c2ecf20Sopenharmony_ci tc->type = (fps == 30) ? V4L2_TC_TYPE_30FPS : V4L2_TC_TYPE_25FPS; 1978c2ecf20Sopenharmony_ci tc->flags = 0; 1988c2ecf20Sopenharmony_ci tc->frames = seq % fps; 1998c2ecf20Sopenharmony_ci tc->seconds = (seq / fps) % 60; 2008c2ecf20Sopenharmony_ci tc->minutes = (seq / (60 * fps)) % 60; 2018c2ecf20Sopenharmony_ci tc->hours = (seq / (60 * 60 * fps)) % 24; 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic void vid_cap_buf_queue(struct vb2_buffer *vb) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 2078c2ecf20Sopenharmony_ci struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); 2088c2ecf20Sopenharmony_ci struct vivid_buffer *buf = container_of(vbuf, struct vivid_buffer, vb); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci dprintk(dev, 1, "%s\n", __func__); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci spin_lock(&dev->slock); 2138c2ecf20Sopenharmony_ci list_add_tail(&buf->list, &dev->vid_cap_active); 2148c2ecf20Sopenharmony_ci spin_unlock(&dev->slock); 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic int vid_cap_start_streaming(struct vb2_queue *vq, unsigned count) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci struct vivid_dev *dev = vb2_get_drv_priv(vq); 2208c2ecf20Sopenharmony_ci unsigned i; 2218c2ecf20Sopenharmony_ci int err; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci if (vb2_is_streaming(&dev->vb_vid_out_q)) 2248c2ecf20Sopenharmony_ci dev->can_loop_video = vivid_vid_can_loop(dev); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci dev->vid_cap_seq_count = 0; 2278c2ecf20Sopenharmony_ci dprintk(dev, 1, "%s\n", __func__); 2288c2ecf20Sopenharmony_ci for (i = 0; i < VIDEO_MAX_FRAME; i++) 2298c2ecf20Sopenharmony_ci dev->must_blank[i] = tpg_g_perc_fill(&dev->tpg) < 100; 2308c2ecf20Sopenharmony_ci if (dev->start_streaming_error) { 2318c2ecf20Sopenharmony_ci dev->start_streaming_error = false; 2328c2ecf20Sopenharmony_ci err = -EINVAL; 2338c2ecf20Sopenharmony_ci } else { 2348c2ecf20Sopenharmony_ci err = vivid_start_generating_vid_cap(dev, &dev->vid_cap_streaming); 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci if (err) { 2378c2ecf20Sopenharmony_ci struct vivid_buffer *buf, *tmp; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci list_for_each_entry_safe(buf, tmp, &dev->vid_cap_active, list) { 2408c2ecf20Sopenharmony_ci list_del(&buf->list); 2418c2ecf20Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, 2428c2ecf20Sopenharmony_ci VB2_BUF_STATE_QUEUED); 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci return err; 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci/* abort streaming and wait for last buffer */ 2498c2ecf20Sopenharmony_cistatic void vid_cap_stop_streaming(struct vb2_queue *vq) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci struct vivid_dev *dev = vb2_get_drv_priv(vq); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci dprintk(dev, 1, "%s\n", __func__); 2548c2ecf20Sopenharmony_ci vivid_stop_generating_vid_cap(dev, &dev->vid_cap_streaming); 2558c2ecf20Sopenharmony_ci dev->can_loop_video = false; 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistatic void vid_cap_buf_request_complete(struct vb2_buffer *vb) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci v4l2_ctrl_request_complete(vb->req_obj.req, &dev->ctrl_hdl_vid_cap); 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ciconst struct vb2_ops vivid_vid_cap_qops = { 2668c2ecf20Sopenharmony_ci .queue_setup = vid_cap_queue_setup, 2678c2ecf20Sopenharmony_ci .buf_prepare = vid_cap_buf_prepare, 2688c2ecf20Sopenharmony_ci .buf_finish = vid_cap_buf_finish, 2698c2ecf20Sopenharmony_ci .buf_queue = vid_cap_buf_queue, 2708c2ecf20Sopenharmony_ci .start_streaming = vid_cap_start_streaming, 2718c2ecf20Sopenharmony_ci .stop_streaming = vid_cap_stop_streaming, 2728c2ecf20Sopenharmony_ci .buf_request_complete = vid_cap_buf_request_complete, 2738c2ecf20Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 2748c2ecf20Sopenharmony_ci .wait_finish = vb2_ops_wait_finish, 2758c2ecf20Sopenharmony_ci}; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci/* 2788c2ecf20Sopenharmony_ci * Determine the 'picture' quality based on the current TV frequency: either 2798c2ecf20Sopenharmony_ci * COLOR for a good 'signal', GRAY (grayscale picture) for a slightly off 2808c2ecf20Sopenharmony_ci * signal or NOISE for no signal. 2818c2ecf20Sopenharmony_ci */ 2828c2ecf20Sopenharmony_civoid vivid_update_quality(struct vivid_dev *dev) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci unsigned freq_modulus; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci if (dev->loop_video && (vivid_is_svid_cap(dev) || vivid_is_hdmi_cap(dev))) { 2878c2ecf20Sopenharmony_ci /* 2888c2ecf20Sopenharmony_ci * The 'noise' will only be replaced by the actual video 2898c2ecf20Sopenharmony_ci * if the output video matches the input video settings. 2908c2ecf20Sopenharmony_ci */ 2918c2ecf20Sopenharmony_ci tpg_s_quality(&dev->tpg, TPG_QUAL_NOISE, 0); 2928c2ecf20Sopenharmony_ci return; 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci if (vivid_is_hdmi_cap(dev) && 2958c2ecf20Sopenharmony_ci VIVID_INVALID_SIGNAL(dev->dv_timings_signal_mode[dev->input])) { 2968c2ecf20Sopenharmony_ci tpg_s_quality(&dev->tpg, TPG_QUAL_NOISE, 0); 2978c2ecf20Sopenharmony_ci return; 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci if (vivid_is_sdtv_cap(dev) && 3008c2ecf20Sopenharmony_ci VIVID_INVALID_SIGNAL(dev->std_signal_mode[dev->input])) { 3018c2ecf20Sopenharmony_ci tpg_s_quality(&dev->tpg, TPG_QUAL_NOISE, 0); 3028c2ecf20Sopenharmony_ci return; 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci if (!vivid_is_tv_cap(dev)) { 3058c2ecf20Sopenharmony_ci tpg_s_quality(&dev->tpg, TPG_QUAL_COLOR, 0); 3068c2ecf20Sopenharmony_ci return; 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci /* 3108c2ecf20Sopenharmony_ci * There is a fake channel every 6 MHz at 49.25, 55.25, etc. 3118c2ecf20Sopenharmony_ci * From +/- 0.25 MHz around the channel there is color, and from 3128c2ecf20Sopenharmony_ci * +/- 1 MHz there is grayscale (chroma is lost). 3138c2ecf20Sopenharmony_ci * Everywhere else it is just noise. 3148c2ecf20Sopenharmony_ci */ 3158c2ecf20Sopenharmony_ci freq_modulus = (dev->tv_freq - 676 /* (43.25-1) * 16 */) % (6 * 16); 3168c2ecf20Sopenharmony_ci if (freq_modulus > 2 * 16) { 3178c2ecf20Sopenharmony_ci tpg_s_quality(&dev->tpg, TPG_QUAL_NOISE, 3188c2ecf20Sopenharmony_ci next_pseudo_random32(dev->tv_freq ^ 0x55) & 0x3f); 3198c2ecf20Sopenharmony_ci return; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci if (freq_modulus < 12 /*0.75 * 16*/ || freq_modulus > 20 /*1.25 * 16*/) 3228c2ecf20Sopenharmony_ci tpg_s_quality(&dev->tpg, TPG_QUAL_GRAY, 0); 3238c2ecf20Sopenharmony_ci else 3248c2ecf20Sopenharmony_ci tpg_s_quality(&dev->tpg, TPG_QUAL_COLOR, 0); 3258c2ecf20Sopenharmony_ci} 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci/* 3288c2ecf20Sopenharmony_ci * Get the current picture quality and the associated afc value. 3298c2ecf20Sopenharmony_ci */ 3308c2ecf20Sopenharmony_cistatic enum tpg_quality vivid_get_quality(struct vivid_dev *dev, s32 *afc) 3318c2ecf20Sopenharmony_ci{ 3328c2ecf20Sopenharmony_ci unsigned freq_modulus; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci if (afc) 3358c2ecf20Sopenharmony_ci *afc = 0; 3368c2ecf20Sopenharmony_ci if (tpg_g_quality(&dev->tpg) == TPG_QUAL_COLOR || 3378c2ecf20Sopenharmony_ci tpg_g_quality(&dev->tpg) == TPG_QUAL_NOISE) 3388c2ecf20Sopenharmony_ci return tpg_g_quality(&dev->tpg); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci /* 3418c2ecf20Sopenharmony_ci * There is a fake channel every 6 MHz at 49.25, 55.25, etc. 3428c2ecf20Sopenharmony_ci * From +/- 0.25 MHz around the channel there is color, and from 3438c2ecf20Sopenharmony_ci * +/- 1 MHz there is grayscale (chroma is lost). 3448c2ecf20Sopenharmony_ci * Everywhere else it is just gray. 3458c2ecf20Sopenharmony_ci */ 3468c2ecf20Sopenharmony_ci freq_modulus = (dev->tv_freq - 676 /* (43.25-1) * 16 */) % (6 * 16); 3478c2ecf20Sopenharmony_ci if (afc) 3488c2ecf20Sopenharmony_ci *afc = freq_modulus - 1 * 16; 3498c2ecf20Sopenharmony_ci return TPG_QUAL_GRAY; 3508c2ecf20Sopenharmony_ci} 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_cienum tpg_video_aspect vivid_get_video_aspect(const struct vivid_dev *dev) 3538c2ecf20Sopenharmony_ci{ 3548c2ecf20Sopenharmony_ci if (vivid_is_sdtv_cap(dev)) 3558c2ecf20Sopenharmony_ci return dev->std_aspect_ratio[dev->input]; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci if (vivid_is_hdmi_cap(dev)) 3588c2ecf20Sopenharmony_ci return dev->dv_timings_aspect_ratio[dev->input]; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci return TPG_VIDEO_ASPECT_IMAGE; 3618c2ecf20Sopenharmony_ci} 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cistatic enum tpg_pixel_aspect vivid_get_pixel_aspect(const struct vivid_dev *dev) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci if (vivid_is_sdtv_cap(dev)) 3668c2ecf20Sopenharmony_ci return (dev->std_cap[dev->input] & V4L2_STD_525_60) ? 3678c2ecf20Sopenharmony_ci TPG_PIXEL_ASPECT_NTSC : TPG_PIXEL_ASPECT_PAL; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci if (vivid_is_hdmi_cap(dev) && 3708c2ecf20Sopenharmony_ci dev->src_rect.width == 720 && dev->src_rect.height <= 576) 3718c2ecf20Sopenharmony_ci return dev->src_rect.height == 480 ? 3728c2ecf20Sopenharmony_ci TPG_PIXEL_ASPECT_NTSC : TPG_PIXEL_ASPECT_PAL; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci return TPG_PIXEL_ASPECT_SQUARE; 3758c2ecf20Sopenharmony_ci} 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci/* 3788c2ecf20Sopenharmony_ci * Called whenever the format has to be reset which can occur when 3798c2ecf20Sopenharmony_ci * changing inputs, standard, timings, etc. 3808c2ecf20Sopenharmony_ci */ 3818c2ecf20Sopenharmony_civoid vivid_update_format_cap(struct vivid_dev *dev, bool keep_controls) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci struct v4l2_bt_timings *bt = &dev->dv_timings_cap[dev->input].bt; 3848c2ecf20Sopenharmony_ci unsigned size; 3858c2ecf20Sopenharmony_ci u64 pixelclock; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci switch (dev->input_type[dev->input]) { 3888c2ecf20Sopenharmony_ci case WEBCAM: 3898c2ecf20Sopenharmony_ci default: 3908c2ecf20Sopenharmony_ci dev->src_rect.width = webcam_sizes[dev->webcam_size_idx].width; 3918c2ecf20Sopenharmony_ci dev->src_rect.height = webcam_sizes[dev->webcam_size_idx].height; 3928c2ecf20Sopenharmony_ci dev->timeperframe_vid_cap = webcam_intervals[dev->webcam_ival_idx]; 3938c2ecf20Sopenharmony_ci dev->field_cap = V4L2_FIELD_NONE; 3948c2ecf20Sopenharmony_ci tpg_s_rgb_range(&dev->tpg, V4L2_DV_RGB_RANGE_AUTO); 3958c2ecf20Sopenharmony_ci break; 3968c2ecf20Sopenharmony_ci case TV: 3978c2ecf20Sopenharmony_ci case SVID: 3988c2ecf20Sopenharmony_ci dev->field_cap = dev->tv_field_cap; 3998c2ecf20Sopenharmony_ci dev->src_rect.width = 720; 4008c2ecf20Sopenharmony_ci if (dev->std_cap[dev->input] & V4L2_STD_525_60) { 4018c2ecf20Sopenharmony_ci dev->src_rect.height = 480; 4028c2ecf20Sopenharmony_ci dev->timeperframe_vid_cap = (struct v4l2_fract) { 1001, 30000 }; 4038c2ecf20Sopenharmony_ci dev->service_set_cap = V4L2_SLICED_CAPTION_525; 4048c2ecf20Sopenharmony_ci } else { 4058c2ecf20Sopenharmony_ci dev->src_rect.height = 576; 4068c2ecf20Sopenharmony_ci dev->timeperframe_vid_cap = (struct v4l2_fract) { 1000, 25000 }; 4078c2ecf20Sopenharmony_ci dev->service_set_cap = V4L2_SLICED_WSS_625 | V4L2_SLICED_TELETEXT_B; 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci tpg_s_rgb_range(&dev->tpg, V4L2_DV_RGB_RANGE_AUTO); 4108c2ecf20Sopenharmony_ci break; 4118c2ecf20Sopenharmony_ci case HDMI: 4128c2ecf20Sopenharmony_ci dev->src_rect.width = bt->width; 4138c2ecf20Sopenharmony_ci dev->src_rect.height = bt->height; 4148c2ecf20Sopenharmony_ci size = V4L2_DV_BT_FRAME_WIDTH(bt) * V4L2_DV_BT_FRAME_HEIGHT(bt); 4158c2ecf20Sopenharmony_ci if (dev->reduced_fps && can_reduce_fps(bt)) { 4168c2ecf20Sopenharmony_ci pixelclock = div_u64(bt->pixelclock * 1000, 1001); 4178c2ecf20Sopenharmony_ci bt->flags |= V4L2_DV_FL_REDUCED_FPS; 4188c2ecf20Sopenharmony_ci } else { 4198c2ecf20Sopenharmony_ci pixelclock = bt->pixelclock; 4208c2ecf20Sopenharmony_ci bt->flags &= ~V4L2_DV_FL_REDUCED_FPS; 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci dev->timeperframe_vid_cap = (struct v4l2_fract) { 4238c2ecf20Sopenharmony_ci size / 100, (u32)pixelclock / 100 4248c2ecf20Sopenharmony_ci }; 4258c2ecf20Sopenharmony_ci if (bt->interlaced) 4268c2ecf20Sopenharmony_ci dev->field_cap = V4L2_FIELD_ALTERNATE; 4278c2ecf20Sopenharmony_ci else 4288c2ecf20Sopenharmony_ci dev->field_cap = V4L2_FIELD_NONE; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci /* 4318c2ecf20Sopenharmony_ci * We can be called from within s_ctrl, in that case we can't 4328c2ecf20Sopenharmony_ci * set/get controls. Luckily we don't need to in that case. 4338c2ecf20Sopenharmony_ci */ 4348c2ecf20Sopenharmony_ci if (keep_controls || !dev->colorspace) 4358c2ecf20Sopenharmony_ci break; 4368c2ecf20Sopenharmony_ci if (bt->flags & V4L2_DV_FL_IS_CE_VIDEO) { 4378c2ecf20Sopenharmony_ci if (bt->width == 720 && bt->height <= 576) 4388c2ecf20Sopenharmony_ci v4l2_ctrl_s_ctrl(dev->colorspace, VIVID_CS_170M); 4398c2ecf20Sopenharmony_ci else 4408c2ecf20Sopenharmony_ci v4l2_ctrl_s_ctrl(dev->colorspace, VIVID_CS_709); 4418c2ecf20Sopenharmony_ci v4l2_ctrl_s_ctrl(dev->real_rgb_range_cap, 1); 4428c2ecf20Sopenharmony_ci } else { 4438c2ecf20Sopenharmony_ci v4l2_ctrl_s_ctrl(dev->colorspace, VIVID_CS_SRGB); 4448c2ecf20Sopenharmony_ci v4l2_ctrl_s_ctrl(dev->real_rgb_range_cap, 0); 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci tpg_s_rgb_range(&dev->tpg, v4l2_ctrl_g_ctrl(dev->rgb_range_cap)); 4478c2ecf20Sopenharmony_ci break; 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci vfree(dev->bitmap_cap); 4508c2ecf20Sopenharmony_ci dev->bitmap_cap = NULL; 4518c2ecf20Sopenharmony_ci vivid_update_quality(dev); 4528c2ecf20Sopenharmony_ci tpg_reset_source(&dev->tpg, dev->src_rect.width, dev->src_rect.height, dev->field_cap); 4538c2ecf20Sopenharmony_ci dev->crop_cap = dev->src_rect; 4548c2ecf20Sopenharmony_ci dev->crop_bounds_cap = dev->src_rect; 4558c2ecf20Sopenharmony_ci if (dev->bitmap_cap && 4568c2ecf20Sopenharmony_ci (dev->compose_cap.width != dev->crop_cap.width || 4578c2ecf20Sopenharmony_ci dev->compose_cap.height != dev->crop_cap.height)) { 4588c2ecf20Sopenharmony_ci vfree(dev->bitmap_cap); 4598c2ecf20Sopenharmony_ci dev->bitmap_cap = NULL; 4608c2ecf20Sopenharmony_ci } 4618c2ecf20Sopenharmony_ci dev->compose_cap = dev->crop_cap; 4628c2ecf20Sopenharmony_ci if (V4L2_FIELD_HAS_T_OR_B(dev->field_cap)) 4638c2ecf20Sopenharmony_ci dev->compose_cap.height /= 2; 4648c2ecf20Sopenharmony_ci dev->fmt_cap_rect = dev->compose_cap; 4658c2ecf20Sopenharmony_ci tpg_s_video_aspect(&dev->tpg, vivid_get_video_aspect(dev)); 4668c2ecf20Sopenharmony_ci tpg_s_pixel_aspect(&dev->tpg, vivid_get_pixel_aspect(dev)); 4678c2ecf20Sopenharmony_ci tpg_update_mv_step(&dev->tpg); 4688c2ecf20Sopenharmony_ci} 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci/* Map the field to something that is valid for the current input */ 4718c2ecf20Sopenharmony_cistatic enum v4l2_field vivid_field_cap(struct vivid_dev *dev, enum v4l2_field field) 4728c2ecf20Sopenharmony_ci{ 4738c2ecf20Sopenharmony_ci if (vivid_is_sdtv_cap(dev)) { 4748c2ecf20Sopenharmony_ci switch (field) { 4758c2ecf20Sopenharmony_ci case V4L2_FIELD_INTERLACED_TB: 4768c2ecf20Sopenharmony_ci case V4L2_FIELD_INTERLACED_BT: 4778c2ecf20Sopenharmony_ci case V4L2_FIELD_SEQ_TB: 4788c2ecf20Sopenharmony_ci case V4L2_FIELD_SEQ_BT: 4798c2ecf20Sopenharmony_ci case V4L2_FIELD_TOP: 4808c2ecf20Sopenharmony_ci case V4L2_FIELD_BOTTOM: 4818c2ecf20Sopenharmony_ci case V4L2_FIELD_ALTERNATE: 4828c2ecf20Sopenharmony_ci return field; 4838c2ecf20Sopenharmony_ci case V4L2_FIELD_INTERLACED: 4848c2ecf20Sopenharmony_ci default: 4858c2ecf20Sopenharmony_ci return V4L2_FIELD_INTERLACED; 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci if (vivid_is_hdmi_cap(dev)) 4898c2ecf20Sopenharmony_ci return dev->dv_timings_cap[dev->input].bt.interlaced ? 4908c2ecf20Sopenharmony_ci V4L2_FIELD_ALTERNATE : V4L2_FIELD_NONE; 4918c2ecf20Sopenharmony_ci return V4L2_FIELD_NONE; 4928c2ecf20Sopenharmony_ci} 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_cistatic unsigned vivid_colorspace_cap(struct vivid_dev *dev) 4958c2ecf20Sopenharmony_ci{ 4968c2ecf20Sopenharmony_ci if (!dev->loop_video || vivid_is_webcam(dev) || vivid_is_tv_cap(dev)) 4978c2ecf20Sopenharmony_ci return tpg_g_colorspace(&dev->tpg); 4988c2ecf20Sopenharmony_ci return dev->colorspace_out; 4998c2ecf20Sopenharmony_ci} 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_cistatic unsigned vivid_xfer_func_cap(struct vivid_dev *dev) 5028c2ecf20Sopenharmony_ci{ 5038c2ecf20Sopenharmony_ci if (!dev->loop_video || vivid_is_webcam(dev) || vivid_is_tv_cap(dev)) 5048c2ecf20Sopenharmony_ci return tpg_g_xfer_func(&dev->tpg); 5058c2ecf20Sopenharmony_ci return dev->xfer_func_out; 5068c2ecf20Sopenharmony_ci} 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_cistatic unsigned vivid_ycbcr_enc_cap(struct vivid_dev *dev) 5098c2ecf20Sopenharmony_ci{ 5108c2ecf20Sopenharmony_ci if (!dev->loop_video || vivid_is_webcam(dev) || vivid_is_tv_cap(dev)) 5118c2ecf20Sopenharmony_ci return tpg_g_ycbcr_enc(&dev->tpg); 5128c2ecf20Sopenharmony_ci return dev->ycbcr_enc_out; 5138c2ecf20Sopenharmony_ci} 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_cistatic unsigned int vivid_hsv_enc_cap(struct vivid_dev *dev) 5168c2ecf20Sopenharmony_ci{ 5178c2ecf20Sopenharmony_ci if (!dev->loop_video || vivid_is_webcam(dev) || vivid_is_tv_cap(dev)) 5188c2ecf20Sopenharmony_ci return tpg_g_hsv_enc(&dev->tpg); 5198c2ecf20Sopenharmony_ci return dev->hsv_enc_out; 5208c2ecf20Sopenharmony_ci} 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_cistatic unsigned vivid_quantization_cap(struct vivid_dev *dev) 5238c2ecf20Sopenharmony_ci{ 5248c2ecf20Sopenharmony_ci if (!dev->loop_video || vivid_is_webcam(dev) || vivid_is_tv_cap(dev)) 5258c2ecf20Sopenharmony_ci return tpg_g_quantization(&dev->tpg); 5268c2ecf20Sopenharmony_ci return dev->quantization_out; 5278c2ecf20Sopenharmony_ci} 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ciint vivid_g_fmt_vid_cap(struct file *file, void *priv, 5308c2ecf20Sopenharmony_ci struct v4l2_format *f) 5318c2ecf20Sopenharmony_ci{ 5328c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 5338c2ecf20Sopenharmony_ci struct v4l2_pix_format_mplane *mp = &f->fmt.pix_mp; 5348c2ecf20Sopenharmony_ci unsigned p; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci mp->width = dev->fmt_cap_rect.width; 5378c2ecf20Sopenharmony_ci mp->height = dev->fmt_cap_rect.height; 5388c2ecf20Sopenharmony_ci mp->field = dev->field_cap; 5398c2ecf20Sopenharmony_ci mp->pixelformat = dev->fmt_cap->fourcc; 5408c2ecf20Sopenharmony_ci mp->colorspace = vivid_colorspace_cap(dev); 5418c2ecf20Sopenharmony_ci mp->xfer_func = vivid_xfer_func_cap(dev); 5428c2ecf20Sopenharmony_ci if (dev->fmt_cap->color_enc == TGP_COLOR_ENC_HSV) 5438c2ecf20Sopenharmony_ci mp->hsv_enc = vivid_hsv_enc_cap(dev); 5448c2ecf20Sopenharmony_ci else 5458c2ecf20Sopenharmony_ci mp->ycbcr_enc = vivid_ycbcr_enc_cap(dev); 5468c2ecf20Sopenharmony_ci mp->quantization = vivid_quantization_cap(dev); 5478c2ecf20Sopenharmony_ci mp->num_planes = dev->fmt_cap->buffers; 5488c2ecf20Sopenharmony_ci for (p = 0; p < mp->num_planes; p++) { 5498c2ecf20Sopenharmony_ci mp->plane_fmt[p].bytesperline = tpg_g_bytesperline(&dev->tpg, p); 5508c2ecf20Sopenharmony_ci mp->plane_fmt[p].sizeimage = 5518c2ecf20Sopenharmony_ci (tpg_g_line_width(&dev->tpg, p) * mp->height) / 5528c2ecf20Sopenharmony_ci dev->fmt_cap->vdownsampling[p] + 5538c2ecf20Sopenharmony_ci dev->fmt_cap->data_offset[p]; 5548c2ecf20Sopenharmony_ci } 5558c2ecf20Sopenharmony_ci return 0; 5568c2ecf20Sopenharmony_ci} 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ciint vivid_try_fmt_vid_cap(struct file *file, void *priv, 5598c2ecf20Sopenharmony_ci struct v4l2_format *f) 5608c2ecf20Sopenharmony_ci{ 5618c2ecf20Sopenharmony_ci struct v4l2_pix_format_mplane *mp = &f->fmt.pix_mp; 5628c2ecf20Sopenharmony_ci struct v4l2_plane_pix_format *pfmt = mp->plane_fmt; 5638c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 5648c2ecf20Sopenharmony_ci const struct vivid_fmt *fmt; 5658c2ecf20Sopenharmony_ci unsigned bytesperline, max_bpl; 5668c2ecf20Sopenharmony_ci unsigned factor = 1; 5678c2ecf20Sopenharmony_ci unsigned w, h; 5688c2ecf20Sopenharmony_ci unsigned p; 5698c2ecf20Sopenharmony_ci bool user_set_csc = !!(mp->flags & V4L2_PIX_FMT_FLAG_SET_CSC); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci fmt = vivid_get_format(dev, mp->pixelformat); 5728c2ecf20Sopenharmony_ci if (!fmt) { 5738c2ecf20Sopenharmony_ci dprintk(dev, 1, "Fourcc format (0x%08x) unknown.\n", 5748c2ecf20Sopenharmony_ci mp->pixelformat); 5758c2ecf20Sopenharmony_ci mp->pixelformat = V4L2_PIX_FMT_YUYV; 5768c2ecf20Sopenharmony_ci fmt = vivid_get_format(dev, mp->pixelformat); 5778c2ecf20Sopenharmony_ci } 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci mp->field = vivid_field_cap(dev, mp->field); 5808c2ecf20Sopenharmony_ci if (vivid_is_webcam(dev)) { 5818c2ecf20Sopenharmony_ci const struct v4l2_frmsize_discrete *sz = 5828c2ecf20Sopenharmony_ci v4l2_find_nearest_size(webcam_sizes, 5838c2ecf20Sopenharmony_ci VIVID_WEBCAM_SIZES, width, 5848c2ecf20Sopenharmony_ci height, mp->width, mp->height); 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci w = sz->width; 5878c2ecf20Sopenharmony_ci h = sz->height; 5888c2ecf20Sopenharmony_ci } else if (vivid_is_sdtv_cap(dev)) { 5898c2ecf20Sopenharmony_ci w = 720; 5908c2ecf20Sopenharmony_ci h = (dev->std_cap[dev->input] & V4L2_STD_525_60) ? 480 : 576; 5918c2ecf20Sopenharmony_ci } else { 5928c2ecf20Sopenharmony_ci w = dev->src_rect.width; 5938c2ecf20Sopenharmony_ci h = dev->src_rect.height; 5948c2ecf20Sopenharmony_ci } 5958c2ecf20Sopenharmony_ci if (V4L2_FIELD_HAS_T_OR_B(mp->field)) 5968c2ecf20Sopenharmony_ci factor = 2; 5978c2ecf20Sopenharmony_ci if (vivid_is_webcam(dev) || 5988c2ecf20Sopenharmony_ci (!dev->has_scaler_cap && !dev->has_crop_cap && !dev->has_compose_cap)) { 5998c2ecf20Sopenharmony_ci mp->width = w; 6008c2ecf20Sopenharmony_ci mp->height = h / factor; 6018c2ecf20Sopenharmony_ci } else { 6028c2ecf20Sopenharmony_ci struct v4l2_rect r = { 0, 0, mp->width, mp->height * factor }; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci v4l2_rect_set_min_size(&r, &vivid_min_rect); 6058c2ecf20Sopenharmony_ci v4l2_rect_set_max_size(&r, &vivid_max_rect); 6068c2ecf20Sopenharmony_ci if (dev->has_scaler_cap && !dev->has_compose_cap) { 6078c2ecf20Sopenharmony_ci struct v4l2_rect max_r = { 0, 0, MAX_ZOOM * w, MAX_ZOOM * h }; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci v4l2_rect_set_max_size(&r, &max_r); 6108c2ecf20Sopenharmony_ci } else if (!dev->has_scaler_cap && dev->has_crop_cap && !dev->has_compose_cap) { 6118c2ecf20Sopenharmony_ci v4l2_rect_set_max_size(&r, &dev->src_rect); 6128c2ecf20Sopenharmony_ci } else if (!dev->has_scaler_cap && !dev->has_crop_cap) { 6138c2ecf20Sopenharmony_ci v4l2_rect_set_min_size(&r, &dev->src_rect); 6148c2ecf20Sopenharmony_ci } 6158c2ecf20Sopenharmony_ci mp->width = r.width; 6168c2ecf20Sopenharmony_ci mp->height = r.height / factor; 6178c2ecf20Sopenharmony_ci } 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci /* This driver supports custom bytesperline values */ 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci mp->num_planes = fmt->buffers; 6228c2ecf20Sopenharmony_ci for (p = 0; p < fmt->buffers; p++) { 6238c2ecf20Sopenharmony_ci /* Calculate the minimum supported bytesperline value */ 6248c2ecf20Sopenharmony_ci bytesperline = (mp->width * fmt->bit_depth[p]) >> 3; 6258c2ecf20Sopenharmony_ci /* Calculate the maximum supported bytesperline value */ 6268c2ecf20Sopenharmony_ci max_bpl = (MAX_ZOOM * MAX_WIDTH * fmt->bit_depth[p]) >> 3; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci if (pfmt[p].bytesperline > max_bpl) 6298c2ecf20Sopenharmony_ci pfmt[p].bytesperline = max_bpl; 6308c2ecf20Sopenharmony_ci if (pfmt[p].bytesperline < bytesperline) 6318c2ecf20Sopenharmony_ci pfmt[p].bytesperline = bytesperline; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci pfmt[p].sizeimage = (pfmt[p].bytesperline * mp->height) / 6348c2ecf20Sopenharmony_ci fmt->vdownsampling[p] + fmt->data_offset[p]; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci memset(pfmt[p].reserved, 0, sizeof(pfmt[p].reserved)); 6378c2ecf20Sopenharmony_ci } 6388c2ecf20Sopenharmony_ci for (p = fmt->buffers; p < fmt->planes; p++) 6398c2ecf20Sopenharmony_ci pfmt[0].sizeimage += (pfmt[0].bytesperline * mp->height * 6408c2ecf20Sopenharmony_ci (fmt->bit_depth[p] / fmt->vdownsampling[p])) / 6418c2ecf20Sopenharmony_ci (fmt->bit_depth[0] / fmt->vdownsampling[0]); 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci if (!user_set_csc || !v4l2_is_colorspace_valid(mp->colorspace)) 6448c2ecf20Sopenharmony_ci mp->colorspace = vivid_colorspace_cap(dev); 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci if (!user_set_csc || !v4l2_is_xfer_func_valid(mp->xfer_func)) 6478c2ecf20Sopenharmony_ci mp->xfer_func = vivid_xfer_func_cap(dev); 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci if (fmt->color_enc == TGP_COLOR_ENC_HSV) { 6508c2ecf20Sopenharmony_ci if (!user_set_csc || !v4l2_is_hsv_enc_valid(mp->hsv_enc)) 6518c2ecf20Sopenharmony_ci mp->hsv_enc = vivid_hsv_enc_cap(dev); 6528c2ecf20Sopenharmony_ci } else if (fmt->color_enc == TGP_COLOR_ENC_YCBCR) { 6538c2ecf20Sopenharmony_ci if (!user_set_csc || !v4l2_is_ycbcr_enc_valid(mp->ycbcr_enc)) 6548c2ecf20Sopenharmony_ci mp->ycbcr_enc = vivid_ycbcr_enc_cap(dev); 6558c2ecf20Sopenharmony_ci } else { 6568c2ecf20Sopenharmony_ci mp->ycbcr_enc = vivid_ycbcr_enc_cap(dev); 6578c2ecf20Sopenharmony_ci } 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci if (fmt->color_enc == TGP_COLOR_ENC_YCBCR || 6608c2ecf20Sopenharmony_ci fmt->color_enc == TGP_COLOR_ENC_RGB) { 6618c2ecf20Sopenharmony_ci if (!user_set_csc || !v4l2_is_quant_valid(mp->quantization)) 6628c2ecf20Sopenharmony_ci mp->quantization = vivid_quantization_cap(dev); 6638c2ecf20Sopenharmony_ci } else { 6648c2ecf20Sopenharmony_ci mp->quantization = vivid_quantization_cap(dev); 6658c2ecf20Sopenharmony_ci } 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci memset(mp->reserved, 0, sizeof(mp->reserved)); 6688c2ecf20Sopenharmony_ci return 0; 6698c2ecf20Sopenharmony_ci} 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ciint vivid_s_fmt_vid_cap(struct file *file, void *priv, 6728c2ecf20Sopenharmony_ci struct v4l2_format *f) 6738c2ecf20Sopenharmony_ci{ 6748c2ecf20Sopenharmony_ci struct v4l2_pix_format_mplane *mp = &f->fmt.pix_mp; 6758c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 6768c2ecf20Sopenharmony_ci struct v4l2_rect *crop = &dev->crop_cap; 6778c2ecf20Sopenharmony_ci struct v4l2_rect *compose = &dev->compose_cap; 6788c2ecf20Sopenharmony_ci struct vb2_queue *q = &dev->vb_vid_cap_q; 6798c2ecf20Sopenharmony_ci int ret = vivid_try_fmt_vid_cap(file, priv, f); 6808c2ecf20Sopenharmony_ci unsigned factor = 1; 6818c2ecf20Sopenharmony_ci unsigned p; 6828c2ecf20Sopenharmony_ci unsigned i; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci if (ret < 0) 6858c2ecf20Sopenharmony_ci return ret; 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci if (vb2_is_busy(q)) { 6888c2ecf20Sopenharmony_ci dprintk(dev, 1, "%s device busy\n", __func__); 6898c2ecf20Sopenharmony_ci return -EBUSY; 6908c2ecf20Sopenharmony_ci } 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci if (dev->overlay_cap_owner && dev->fb_cap.fmt.pixelformat != mp->pixelformat) { 6938c2ecf20Sopenharmony_ci dprintk(dev, 1, "overlay is active, can't change pixelformat\n"); 6948c2ecf20Sopenharmony_ci return -EBUSY; 6958c2ecf20Sopenharmony_ci } 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci dev->fmt_cap = vivid_get_format(dev, mp->pixelformat); 6988c2ecf20Sopenharmony_ci if (V4L2_FIELD_HAS_T_OR_B(mp->field)) 6998c2ecf20Sopenharmony_ci factor = 2; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci /* Note: the webcam input doesn't support scaling, cropping or composing */ 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci if (!vivid_is_webcam(dev) && 7048c2ecf20Sopenharmony_ci (dev->has_scaler_cap || dev->has_crop_cap || dev->has_compose_cap)) { 7058c2ecf20Sopenharmony_ci struct v4l2_rect r = { 0, 0, mp->width, mp->height }; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci if (dev->has_scaler_cap) { 7088c2ecf20Sopenharmony_ci if (dev->has_compose_cap) 7098c2ecf20Sopenharmony_ci v4l2_rect_map_inside(compose, &r); 7108c2ecf20Sopenharmony_ci else 7118c2ecf20Sopenharmony_ci *compose = r; 7128c2ecf20Sopenharmony_ci if (dev->has_crop_cap && !dev->has_compose_cap) { 7138c2ecf20Sopenharmony_ci struct v4l2_rect min_r = { 7148c2ecf20Sopenharmony_ci 0, 0, 7158c2ecf20Sopenharmony_ci r.width / MAX_ZOOM, 7168c2ecf20Sopenharmony_ci factor * r.height / MAX_ZOOM 7178c2ecf20Sopenharmony_ci }; 7188c2ecf20Sopenharmony_ci struct v4l2_rect max_r = { 7198c2ecf20Sopenharmony_ci 0, 0, 7208c2ecf20Sopenharmony_ci r.width * MAX_ZOOM, 7218c2ecf20Sopenharmony_ci factor * r.height * MAX_ZOOM 7228c2ecf20Sopenharmony_ci }; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci v4l2_rect_set_min_size(crop, &min_r); 7258c2ecf20Sopenharmony_ci v4l2_rect_set_max_size(crop, &max_r); 7268c2ecf20Sopenharmony_ci v4l2_rect_map_inside(crop, &dev->crop_bounds_cap); 7278c2ecf20Sopenharmony_ci } else if (dev->has_crop_cap) { 7288c2ecf20Sopenharmony_ci struct v4l2_rect min_r = { 7298c2ecf20Sopenharmony_ci 0, 0, 7308c2ecf20Sopenharmony_ci compose->width / MAX_ZOOM, 7318c2ecf20Sopenharmony_ci factor * compose->height / MAX_ZOOM 7328c2ecf20Sopenharmony_ci }; 7338c2ecf20Sopenharmony_ci struct v4l2_rect max_r = { 7348c2ecf20Sopenharmony_ci 0, 0, 7358c2ecf20Sopenharmony_ci compose->width * MAX_ZOOM, 7368c2ecf20Sopenharmony_ci factor * compose->height * MAX_ZOOM 7378c2ecf20Sopenharmony_ci }; 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci v4l2_rect_set_min_size(crop, &min_r); 7408c2ecf20Sopenharmony_ci v4l2_rect_set_max_size(crop, &max_r); 7418c2ecf20Sopenharmony_ci v4l2_rect_map_inside(crop, &dev->crop_bounds_cap); 7428c2ecf20Sopenharmony_ci } 7438c2ecf20Sopenharmony_ci } else if (dev->has_crop_cap && !dev->has_compose_cap) { 7448c2ecf20Sopenharmony_ci r.height *= factor; 7458c2ecf20Sopenharmony_ci v4l2_rect_set_size_to(crop, &r); 7468c2ecf20Sopenharmony_ci v4l2_rect_map_inside(crop, &dev->crop_bounds_cap); 7478c2ecf20Sopenharmony_ci r = *crop; 7488c2ecf20Sopenharmony_ci r.height /= factor; 7498c2ecf20Sopenharmony_ci v4l2_rect_set_size_to(compose, &r); 7508c2ecf20Sopenharmony_ci } else if (!dev->has_crop_cap) { 7518c2ecf20Sopenharmony_ci v4l2_rect_map_inside(compose, &r); 7528c2ecf20Sopenharmony_ci } else { 7538c2ecf20Sopenharmony_ci r.height *= factor; 7548c2ecf20Sopenharmony_ci v4l2_rect_set_max_size(crop, &r); 7558c2ecf20Sopenharmony_ci v4l2_rect_map_inside(crop, &dev->crop_bounds_cap); 7568c2ecf20Sopenharmony_ci compose->top *= factor; 7578c2ecf20Sopenharmony_ci compose->height *= factor; 7588c2ecf20Sopenharmony_ci v4l2_rect_set_size_to(compose, crop); 7598c2ecf20Sopenharmony_ci v4l2_rect_map_inside(compose, &r); 7608c2ecf20Sopenharmony_ci compose->top /= factor; 7618c2ecf20Sopenharmony_ci compose->height /= factor; 7628c2ecf20Sopenharmony_ci } 7638c2ecf20Sopenharmony_ci } else if (vivid_is_webcam(dev)) { 7648c2ecf20Sopenharmony_ci /* Guaranteed to be a match */ 7658c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(webcam_sizes); i++) 7668c2ecf20Sopenharmony_ci if (webcam_sizes[i].width == mp->width && 7678c2ecf20Sopenharmony_ci webcam_sizes[i].height == mp->height) 7688c2ecf20Sopenharmony_ci break; 7698c2ecf20Sopenharmony_ci dev->webcam_size_idx = i; 7708c2ecf20Sopenharmony_ci if (dev->webcam_ival_idx >= 2 * (VIVID_WEBCAM_SIZES - i)) 7718c2ecf20Sopenharmony_ci dev->webcam_ival_idx = 2 * (VIVID_WEBCAM_SIZES - i) - 1; 7728c2ecf20Sopenharmony_ci vivid_update_format_cap(dev, false); 7738c2ecf20Sopenharmony_ci } else { 7748c2ecf20Sopenharmony_ci struct v4l2_rect r = { 0, 0, mp->width, mp->height }; 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci v4l2_rect_set_size_to(compose, &r); 7778c2ecf20Sopenharmony_ci r.height *= factor; 7788c2ecf20Sopenharmony_ci v4l2_rect_set_size_to(crop, &r); 7798c2ecf20Sopenharmony_ci } 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci dev->fmt_cap_rect.width = mp->width; 7828c2ecf20Sopenharmony_ci dev->fmt_cap_rect.height = mp->height; 7838c2ecf20Sopenharmony_ci tpg_s_buf_height(&dev->tpg, mp->height); 7848c2ecf20Sopenharmony_ci tpg_s_fourcc(&dev->tpg, dev->fmt_cap->fourcc); 7858c2ecf20Sopenharmony_ci for (p = 0; p < tpg_g_buffers(&dev->tpg); p++) 7868c2ecf20Sopenharmony_ci tpg_s_bytesperline(&dev->tpg, p, mp->plane_fmt[p].bytesperline); 7878c2ecf20Sopenharmony_ci dev->field_cap = mp->field; 7888c2ecf20Sopenharmony_ci if (dev->field_cap == V4L2_FIELD_ALTERNATE) 7898c2ecf20Sopenharmony_ci tpg_s_field(&dev->tpg, V4L2_FIELD_TOP, true); 7908c2ecf20Sopenharmony_ci else 7918c2ecf20Sopenharmony_ci tpg_s_field(&dev->tpg, dev->field_cap, false); 7928c2ecf20Sopenharmony_ci tpg_s_crop_compose(&dev->tpg, &dev->crop_cap, &dev->compose_cap); 7938c2ecf20Sopenharmony_ci if (vivid_is_sdtv_cap(dev)) 7948c2ecf20Sopenharmony_ci dev->tv_field_cap = mp->field; 7958c2ecf20Sopenharmony_ci tpg_update_mv_step(&dev->tpg); 7968c2ecf20Sopenharmony_ci dev->tpg.colorspace = mp->colorspace; 7978c2ecf20Sopenharmony_ci dev->tpg.xfer_func = mp->xfer_func; 7988c2ecf20Sopenharmony_ci if (dev->fmt_cap->color_enc == TGP_COLOR_ENC_YCBCR) 7998c2ecf20Sopenharmony_ci dev->tpg.ycbcr_enc = mp->ycbcr_enc; 8008c2ecf20Sopenharmony_ci else 8018c2ecf20Sopenharmony_ci dev->tpg.hsv_enc = mp->hsv_enc; 8028c2ecf20Sopenharmony_ci dev->tpg.quantization = mp->quantization; 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci return 0; 8058c2ecf20Sopenharmony_ci} 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ciint vidioc_g_fmt_vid_cap_mplane(struct file *file, void *priv, 8088c2ecf20Sopenharmony_ci struct v4l2_format *f) 8098c2ecf20Sopenharmony_ci{ 8108c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci if (!dev->multiplanar) 8138c2ecf20Sopenharmony_ci return -ENOTTY; 8148c2ecf20Sopenharmony_ci return vivid_g_fmt_vid_cap(file, priv, f); 8158c2ecf20Sopenharmony_ci} 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ciint vidioc_try_fmt_vid_cap_mplane(struct file *file, void *priv, 8188c2ecf20Sopenharmony_ci struct v4l2_format *f) 8198c2ecf20Sopenharmony_ci{ 8208c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci if (!dev->multiplanar) 8238c2ecf20Sopenharmony_ci return -ENOTTY; 8248c2ecf20Sopenharmony_ci return vivid_try_fmt_vid_cap(file, priv, f); 8258c2ecf20Sopenharmony_ci} 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ciint vidioc_s_fmt_vid_cap_mplane(struct file *file, void *priv, 8288c2ecf20Sopenharmony_ci struct v4l2_format *f) 8298c2ecf20Sopenharmony_ci{ 8308c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci if (!dev->multiplanar) 8338c2ecf20Sopenharmony_ci return -ENOTTY; 8348c2ecf20Sopenharmony_ci return vivid_s_fmt_vid_cap(file, priv, f); 8358c2ecf20Sopenharmony_ci} 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ciint vidioc_g_fmt_vid_cap(struct file *file, void *priv, 8388c2ecf20Sopenharmony_ci struct v4l2_format *f) 8398c2ecf20Sopenharmony_ci{ 8408c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci if (dev->multiplanar) 8438c2ecf20Sopenharmony_ci return -ENOTTY; 8448c2ecf20Sopenharmony_ci return fmt_sp2mp_func(file, priv, f, vivid_g_fmt_vid_cap); 8458c2ecf20Sopenharmony_ci} 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ciint vidioc_try_fmt_vid_cap(struct file *file, void *priv, 8488c2ecf20Sopenharmony_ci struct v4l2_format *f) 8498c2ecf20Sopenharmony_ci{ 8508c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci if (dev->multiplanar) 8538c2ecf20Sopenharmony_ci return -ENOTTY; 8548c2ecf20Sopenharmony_ci return fmt_sp2mp_func(file, priv, f, vivid_try_fmt_vid_cap); 8558c2ecf20Sopenharmony_ci} 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ciint vidioc_s_fmt_vid_cap(struct file *file, void *priv, 8588c2ecf20Sopenharmony_ci struct v4l2_format *f) 8598c2ecf20Sopenharmony_ci{ 8608c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci if (dev->multiplanar) 8638c2ecf20Sopenharmony_ci return -ENOTTY; 8648c2ecf20Sopenharmony_ci return fmt_sp2mp_func(file, priv, f, vivid_s_fmt_vid_cap); 8658c2ecf20Sopenharmony_ci} 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ciint vivid_vid_cap_g_selection(struct file *file, void *priv, 8688c2ecf20Sopenharmony_ci struct v4l2_selection *sel) 8698c2ecf20Sopenharmony_ci{ 8708c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci if (!dev->has_crop_cap && !dev->has_compose_cap) 8738c2ecf20Sopenharmony_ci return -ENOTTY; 8748c2ecf20Sopenharmony_ci if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 8758c2ecf20Sopenharmony_ci return -EINVAL; 8768c2ecf20Sopenharmony_ci if (vivid_is_webcam(dev)) 8778c2ecf20Sopenharmony_ci return -ENODATA; 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci sel->r.left = sel->r.top = 0; 8808c2ecf20Sopenharmony_ci switch (sel->target) { 8818c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP: 8828c2ecf20Sopenharmony_ci if (!dev->has_crop_cap) 8838c2ecf20Sopenharmony_ci return -EINVAL; 8848c2ecf20Sopenharmony_ci sel->r = dev->crop_cap; 8858c2ecf20Sopenharmony_ci break; 8868c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP_DEFAULT: 8878c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP_BOUNDS: 8888c2ecf20Sopenharmony_ci if (!dev->has_crop_cap) 8898c2ecf20Sopenharmony_ci return -EINVAL; 8908c2ecf20Sopenharmony_ci sel->r = dev->src_rect; 8918c2ecf20Sopenharmony_ci break; 8928c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_BOUNDS: 8938c2ecf20Sopenharmony_ci if (!dev->has_compose_cap) 8948c2ecf20Sopenharmony_ci return -EINVAL; 8958c2ecf20Sopenharmony_ci sel->r = vivid_max_rect; 8968c2ecf20Sopenharmony_ci break; 8978c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE: 8988c2ecf20Sopenharmony_ci if (!dev->has_compose_cap) 8998c2ecf20Sopenharmony_ci return -EINVAL; 9008c2ecf20Sopenharmony_ci sel->r = dev->compose_cap; 9018c2ecf20Sopenharmony_ci break; 9028c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_DEFAULT: 9038c2ecf20Sopenharmony_ci if (!dev->has_compose_cap) 9048c2ecf20Sopenharmony_ci return -EINVAL; 9058c2ecf20Sopenharmony_ci sel->r = dev->fmt_cap_rect; 9068c2ecf20Sopenharmony_ci break; 9078c2ecf20Sopenharmony_ci default: 9088c2ecf20Sopenharmony_ci return -EINVAL; 9098c2ecf20Sopenharmony_ci } 9108c2ecf20Sopenharmony_ci return 0; 9118c2ecf20Sopenharmony_ci} 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ciint vivid_vid_cap_s_selection(struct file *file, void *fh, struct v4l2_selection *s) 9148c2ecf20Sopenharmony_ci{ 9158c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 9168c2ecf20Sopenharmony_ci struct v4l2_rect *crop = &dev->crop_cap; 9178c2ecf20Sopenharmony_ci struct v4l2_rect *compose = &dev->compose_cap; 9188c2ecf20Sopenharmony_ci unsigned orig_compose_w = compose->width; 9198c2ecf20Sopenharmony_ci unsigned orig_compose_h = compose->height; 9208c2ecf20Sopenharmony_ci unsigned factor = V4L2_FIELD_HAS_T_OR_B(dev->field_cap) ? 2 : 1; 9218c2ecf20Sopenharmony_ci int ret; 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci if (!dev->has_crop_cap && !dev->has_compose_cap) 9248c2ecf20Sopenharmony_ci return -ENOTTY; 9258c2ecf20Sopenharmony_ci if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 9268c2ecf20Sopenharmony_ci return -EINVAL; 9278c2ecf20Sopenharmony_ci if (vivid_is_webcam(dev)) 9288c2ecf20Sopenharmony_ci return -ENODATA; 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci switch (s->target) { 9318c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP: 9328c2ecf20Sopenharmony_ci if (!dev->has_crop_cap) 9338c2ecf20Sopenharmony_ci return -EINVAL; 9348c2ecf20Sopenharmony_ci ret = vivid_vid_adjust_sel(s->flags, &s->r); 9358c2ecf20Sopenharmony_ci if (ret) 9368c2ecf20Sopenharmony_ci return ret; 9378c2ecf20Sopenharmony_ci v4l2_rect_set_min_size(&s->r, &vivid_min_rect); 9388c2ecf20Sopenharmony_ci v4l2_rect_set_max_size(&s->r, &dev->src_rect); 9398c2ecf20Sopenharmony_ci v4l2_rect_map_inside(&s->r, &dev->crop_bounds_cap); 9408c2ecf20Sopenharmony_ci s->r.top /= factor; 9418c2ecf20Sopenharmony_ci s->r.height /= factor; 9428c2ecf20Sopenharmony_ci if (dev->has_scaler_cap) { 9438c2ecf20Sopenharmony_ci struct v4l2_rect fmt = dev->fmt_cap_rect; 9448c2ecf20Sopenharmony_ci struct v4l2_rect max_rect = { 9458c2ecf20Sopenharmony_ci 0, 0, 9468c2ecf20Sopenharmony_ci s->r.width * MAX_ZOOM, 9478c2ecf20Sopenharmony_ci s->r.height * MAX_ZOOM 9488c2ecf20Sopenharmony_ci }; 9498c2ecf20Sopenharmony_ci struct v4l2_rect min_rect = { 9508c2ecf20Sopenharmony_ci 0, 0, 9518c2ecf20Sopenharmony_ci s->r.width / MAX_ZOOM, 9528c2ecf20Sopenharmony_ci s->r.height / MAX_ZOOM 9538c2ecf20Sopenharmony_ci }; 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci v4l2_rect_set_min_size(&fmt, &min_rect); 9568c2ecf20Sopenharmony_ci if (!dev->has_compose_cap) 9578c2ecf20Sopenharmony_ci v4l2_rect_set_max_size(&fmt, &max_rect); 9588c2ecf20Sopenharmony_ci if (!v4l2_rect_same_size(&dev->fmt_cap_rect, &fmt) && 9598c2ecf20Sopenharmony_ci vb2_is_busy(&dev->vb_vid_cap_q)) 9608c2ecf20Sopenharmony_ci return -EBUSY; 9618c2ecf20Sopenharmony_ci if (dev->has_compose_cap) { 9628c2ecf20Sopenharmony_ci v4l2_rect_set_min_size(compose, &min_rect); 9638c2ecf20Sopenharmony_ci v4l2_rect_set_max_size(compose, &max_rect); 9648c2ecf20Sopenharmony_ci v4l2_rect_map_inside(compose, &fmt); 9658c2ecf20Sopenharmony_ci } 9668c2ecf20Sopenharmony_ci dev->fmt_cap_rect = fmt; 9678c2ecf20Sopenharmony_ci tpg_s_buf_height(&dev->tpg, fmt.height); 9688c2ecf20Sopenharmony_ci } else if (dev->has_compose_cap) { 9698c2ecf20Sopenharmony_ci struct v4l2_rect fmt = dev->fmt_cap_rect; 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci v4l2_rect_set_min_size(&fmt, &s->r); 9728c2ecf20Sopenharmony_ci if (!v4l2_rect_same_size(&dev->fmt_cap_rect, &fmt) && 9738c2ecf20Sopenharmony_ci vb2_is_busy(&dev->vb_vid_cap_q)) 9748c2ecf20Sopenharmony_ci return -EBUSY; 9758c2ecf20Sopenharmony_ci dev->fmt_cap_rect = fmt; 9768c2ecf20Sopenharmony_ci tpg_s_buf_height(&dev->tpg, fmt.height); 9778c2ecf20Sopenharmony_ci v4l2_rect_set_size_to(compose, &s->r); 9788c2ecf20Sopenharmony_ci v4l2_rect_map_inside(compose, &dev->fmt_cap_rect); 9798c2ecf20Sopenharmony_ci } else { 9808c2ecf20Sopenharmony_ci if (!v4l2_rect_same_size(&s->r, &dev->fmt_cap_rect) && 9818c2ecf20Sopenharmony_ci vb2_is_busy(&dev->vb_vid_cap_q)) 9828c2ecf20Sopenharmony_ci return -EBUSY; 9838c2ecf20Sopenharmony_ci v4l2_rect_set_size_to(&dev->fmt_cap_rect, &s->r); 9848c2ecf20Sopenharmony_ci v4l2_rect_set_size_to(compose, &s->r); 9858c2ecf20Sopenharmony_ci v4l2_rect_map_inside(compose, &dev->fmt_cap_rect); 9868c2ecf20Sopenharmony_ci tpg_s_buf_height(&dev->tpg, dev->fmt_cap_rect.height); 9878c2ecf20Sopenharmony_ci } 9888c2ecf20Sopenharmony_ci s->r.top *= factor; 9898c2ecf20Sopenharmony_ci s->r.height *= factor; 9908c2ecf20Sopenharmony_ci *crop = s->r; 9918c2ecf20Sopenharmony_ci break; 9928c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE: 9938c2ecf20Sopenharmony_ci if (!dev->has_compose_cap) 9948c2ecf20Sopenharmony_ci return -EINVAL; 9958c2ecf20Sopenharmony_ci ret = vivid_vid_adjust_sel(s->flags, &s->r); 9968c2ecf20Sopenharmony_ci if (ret) 9978c2ecf20Sopenharmony_ci return ret; 9988c2ecf20Sopenharmony_ci v4l2_rect_set_min_size(&s->r, &vivid_min_rect); 9998c2ecf20Sopenharmony_ci v4l2_rect_set_max_size(&s->r, &dev->fmt_cap_rect); 10008c2ecf20Sopenharmony_ci if (dev->has_scaler_cap) { 10018c2ecf20Sopenharmony_ci struct v4l2_rect max_rect = { 10028c2ecf20Sopenharmony_ci 0, 0, 10038c2ecf20Sopenharmony_ci dev->src_rect.width * MAX_ZOOM, 10048c2ecf20Sopenharmony_ci (dev->src_rect.height / factor) * MAX_ZOOM 10058c2ecf20Sopenharmony_ci }; 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci v4l2_rect_set_max_size(&s->r, &max_rect); 10088c2ecf20Sopenharmony_ci if (dev->has_crop_cap) { 10098c2ecf20Sopenharmony_ci struct v4l2_rect min_rect = { 10108c2ecf20Sopenharmony_ci 0, 0, 10118c2ecf20Sopenharmony_ci s->r.width / MAX_ZOOM, 10128c2ecf20Sopenharmony_ci (s->r.height * factor) / MAX_ZOOM 10138c2ecf20Sopenharmony_ci }; 10148c2ecf20Sopenharmony_ci struct v4l2_rect max_rect = { 10158c2ecf20Sopenharmony_ci 0, 0, 10168c2ecf20Sopenharmony_ci s->r.width * MAX_ZOOM, 10178c2ecf20Sopenharmony_ci (s->r.height * factor) * MAX_ZOOM 10188c2ecf20Sopenharmony_ci }; 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci v4l2_rect_set_min_size(crop, &min_rect); 10218c2ecf20Sopenharmony_ci v4l2_rect_set_max_size(crop, &max_rect); 10228c2ecf20Sopenharmony_ci v4l2_rect_map_inside(crop, &dev->crop_bounds_cap); 10238c2ecf20Sopenharmony_ci } 10248c2ecf20Sopenharmony_ci } else if (dev->has_crop_cap) { 10258c2ecf20Sopenharmony_ci s->r.top *= factor; 10268c2ecf20Sopenharmony_ci s->r.height *= factor; 10278c2ecf20Sopenharmony_ci v4l2_rect_set_max_size(&s->r, &dev->src_rect); 10288c2ecf20Sopenharmony_ci v4l2_rect_set_size_to(crop, &s->r); 10298c2ecf20Sopenharmony_ci v4l2_rect_map_inside(crop, &dev->crop_bounds_cap); 10308c2ecf20Sopenharmony_ci s->r.top /= factor; 10318c2ecf20Sopenharmony_ci s->r.height /= factor; 10328c2ecf20Sopenharmony_ci } else { 10338c2ecf20Sopenharmony_ci v4l2_rect_set_size_to(&s->r, &dev->src_rect); 10348c2ecf20Sopenharmony_ci s->r.height /= factor; 10358c2ecf20Sopenharmony_ci } 10368c2ecf20Sopenharmony_ci v4l2_rect_map_inside(&s->r, &dev->fmt_cap_rect); 10378c2ecf20Sopenharmony_ci *compose = s->r; 10388c2ecf20Sopenharmony_ci break; 10398c2ecf20Sopenharmony_ci default: 10408c2ecf20Sopenharmony_ci return -EINVAL; 10418c2ecf20Sopenharmony_ci } 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci if (dev->bitmap_cap && (compose->width != orig_compose_w || 10448c2ecf20Sopenharmony_ci compose->height != orig_compose_h)) { 10458c2ecf20Sopenharmony_ci vfree(dev->bitmap_cap); 10468c2ecf20Sopenharmony_ci dev->bitmap_cap = NULL; 10478c2ecf20Sopenharmony_ci } 10488c2ecf20Sopenharmony_ci tpg_s_crop_compose(&dev->tpg, crop, compose); 10498c2ecf20Sopenharmony_ci return 0; 10508c2ecf20Sopenharmony_ci} 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ciint vivid_vid_cap_g_pixelaspect(struct file *file, void *priv, 10538c2ecf20Sopenharmony_ci int type, struct v4l2_fract *f) 10548c2ecf20Sopenharmony_ci{ 10558c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 10588c2ecf20Sopenharmony_ci return -EINVAL; 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci switch (vivid_get_pixel_aspect(dev)) { 10618c2ecf20Sopenharmony_ci case TPG_PIXEL_ASPECT_NTSC: 10628c2ecf20Sopenharmony_ci f->numerator = 11; 10638c2ecf20Sopenharmony_ci f->denominator = 10; 10648c2ecf20Sopenharmony_ci break; 10658c2ecf20Sopenharmony_ci case TPG_PIXEL_ASPECT_PAL: 10668c2ecf20Sopenharmony_ci f->numerator = 54; 10678c2ecf20Sopenharmony_ci f->denominator = 59; 10688c2ecf20Sopenharmony_ci break; 10698c2ecf20Sopenharmony_ci default: 10708c2ecf20Sopenharmony_ci break; 10718c2ecf20Sopenharmony_ci } 10728c2ecf20Sopenharmony_ci return 0; 10738c2ecf20Sopenharmony_ci} 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ciint vidioc_enum_fmt_vid_overlay(struct file *file, void *priv, 10768c2ecf20Sopenharmony_ci struct v4l2_fmtdesc *f) 10778c2ecf20Sopenharmony_ci{ 10788c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 10798c2ecf20Sopenharmony_ci const struct vivid_fmt *fmt; 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci if (dev->multiplanar) 10828c2ecf20Sopenharmony_ci return -ENOTTY; 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci if (f->index >= ARRAY_SIZE(formats_ovl)) 10858c2ecf20Sopenharmony_ci return -EINVAL; 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci fmt = &formats_ovl[f->index]; 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci f->pixelformat = fmt->fourcc; 10908c2ecf20Sopenharmony_ci return 0; 10918c2ecf20Sopenharmony_ci} 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ciint vidioc_g_fmt_vid_overlay(struct file *file, void *priv, 10948c2ecf20Sopenharmony_ci struct v4l2_format *f) 10958c2ecf20Sopenharmony_ci{ 10968c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 10978c2ecf20Sopenharmony_ci const struct v4l2_rect *compose = &dev->compose_cap; 10988c2ecf20Sopenharmony_ci struct v4l2_window *win = &f->fmt.win; 10998c2ecf20Sopenharmony_ci unsigned clipcount = win->clipcount; 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci if (dev->multiplanar) 11028c2ecf20Sopenharmony_ci return -ENOTTY; 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci win->w.top = dev->overlay_cap_top; 11058c2ecf20Sopenharmony_ci win->w.left = dev->overlay_cap_left; 11068c2ecf20Sopenharmony_ci win->w.width = compose->width; 11078c2ecf20Sopenharmony_ci win->w.height = compose->height; 11088c2ecf20Sopenharmony_ci win->field = dev->overlay_cap_field; 11098c2ecf20Sopenharmony_ci win->clipcount = dev->clipcount_cap; 11108c2ecf20Sopenharmony_ci if (clipcount > dev->clipcount_cap) 11118c2ecf20Sopenharmony_ci clipcount = dev->clipcount_cap; 11128c2ecf20Sopenharmony_ci if (dev->bitmap_cap == NULL) 11138c2ecf20Sopenharmony_ci win->bitmap = NULL; 11148c2ecf20Sopenharmony_ci else if (win->bitmap) { 11158c2ecf20Sopenharmony_ci if (copy_to_user(win->bitmap, dev->bitmap_cap, 11168c2ecf20Sopenharmony_ci ((compose->width + 7) / 8) * compose->height)) 11178c2ecf20Sopenharmony_ci return -EFAULT; 11188c2ecf20Sopenharmony_ci } 11198c2ecf20Sopenharmony_ci if (clipcount && win->clips) { 11208c2ecf20Sopenharmony_ci if (copy_to_user(win->clips, dev->clips_cap, 11218c2ecf20Sopenharmony_ci clipcount * sizeof(dev->clips_cap[0]))) 11228c2ecf20Sopenharmony_ci return -EFAULT; 11238c2ecf20Sopenharmony_ci } 11248c2ecf20Sopenharmony_ci return 0; 11258c2ecf20Sopenharmony_ci} 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ciint vidioc_try_fmt_vid_overlay(struct file *file, void *priv, 11288c2ecf20Sopenharmony_ci struct v4l2_format *f) 11298c2ecf20Sopenharmony_ci{ 11308c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 11318c2ecf20Sopenharmony_ci const struct v4l2_rect *compose = &dev->compose_cap; 11328c2ecf20Sopenharmony_ci struct v4l2_window *win = &f->fmt.win; 11338c2ecf20Sopenharmony_ci int i, j; 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci if (dev->multiplanar) 11368c2ecf20Sopenharmony_ci return -ENOTTY; 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci win->w.left = clamp_t(int, win->w.left, 11398c2ecf20Sopenharmony_ci -dev->fb_cap.fmt.width, dev->fb_cap.fmt.width); 11408c2ecf20Sopenharmony_ci win->w.top = clamp_t(int, win->w.top, 11418c2ecf20Sopenharmony_ci -dev->fb_cap.fmt.height, dev->fb_cap.fmt.height); 11428c2ecf20Sopenharmony_ci win->w.width = compose->width; 11438c2ecf20Sopenharmony_ci win->w.height = compose->height; 11448c2ecf20Sopenharmony_ci if (win->field != V4L2_FIELD_BOTTOM && win->field != V4L2_FIELD_TOP) 11458c2ecf20Sopenharmony_ci win->field = V4L2_FIELD_ANY; 11468c2ecf20Sopenharmony_ci win->chromakey = 0; 11478c2ecf20Sopenharmony_ci win->global_alpha = 0; 11488c2ecf20Sopenharmony_ci if (win->clipcount && !win->clips) 11498c2ecf20Sopenharmony_ci win->clipcount = 0; 11508c2ecf20Sopenharmony_ci if (win->clipcount > MAX_CLIPS) 11518c2ecf20Sopenharmony_ci win->clipcount = MAX_CLIPS; 11528c2ecf20Sopenharmony_ci if (win->clipcount) { 11538c2ecf20Sopenharmony_ci if (copy_from_user(dev->try_clips_cap, win->clips, 11548c2ecf20Sopenharmony_ci win->clipcount * sizeof(dev->clips_cap[0]))) 11558c2ecf20Sopenharmony_ci return -EFAULT; 11568c2ecf20Sopenharmony_ci for (i = 0; i < win->clipcount; i++) { 11578c2ecf20Sopenharmony_ci struct v4l2_rect *r = &dev->try_clips_cap[i].c; 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci r->top = clamp_t(s32, r->top, 0, dev->fb_cap.fmt.height - 1); 11608c2ecf20Sopenharmony_ci r->height = clamp_t(s32, r->height, 1, dev->fb_cap.fmt.height - r->top); 11618c2ecf20Sopenharmony_ci r->left = clamp_t(u32, r->left, 0, dev->fb_cap.fmt.width - 1); 11628c2ecf20Sopenharmony_ci r->width = clamp_t(u32, r->width, 1, dev->fb_cap.fmt.width - r->left); 11638c2ecf20Sopenharmony_ci } 11648c2ecf20Sopenharmony_ci /* 11658c2ecf20Sopenharmony_ci * Yeah, so sue me, it's an O(n^2) algorithm. But n is a small 11668c2ecf20Sopenharmony_ci * number and it's typically a one-time deal. 11678c2ecf20Sopenharmony_ci */ 11688c2ecf20Sopenharmony_ci for (i = 0; i < win->clipcount - 1; i++) { 11698c2ecf20Sopenharmony_ci struct v4l2_rect *r1 = &dev->try_clips_cap[i].c; 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci for (j = i + 1; j < win->clipcount; j++) { 11728c2ecf20Sopenharmony_ci struct v4l2_rect *r2 = &dev->try_clips_cap[j].c; 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci if (v4l2_rect_overlap(r1, r2)) 11758c2ecf20Sopenharmony_ci return -EINVAL; 11768c2ecf20Sopenharmony_ci } 11778c2ecf20Sopenharmony_ci } 11788c2ecf20Sopenharmony_ci if (copy_to_user(win->clips, dev->try_clips_cap, 11798c2ecf20Sopenharmony_ci win->clipcount * sizeof(dev->clips_cap[0]))) 11808c2ecf20Sopenharmony_ci return -EFAULT; 11818c2ecf20Sopenharmony_ci } 11828c2ecf20Sopenharmony_ci return 0; 11838c2ecf20Sopenharmony_ci} 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ciint vidioc_s_fmt_vid_overlay(struct file *file, void *priv, 11868c2ecf20Sopenharmony_ci struct v4l2_format *f) 11878c2ecf20Sopenharmony_ci{ 11888c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 11898c2ecf20Sopenharmony_ci const struct v4l2_rect *compose = &dev->compose_cap; 11908c2ecf20Sopenharmony_ci struct v4l2_window *win = &f->fmt.win; 11918c2ecf20Sopenharmony_ci int ret = vidioc_try_fmt_vid_overlay(file, priv, f); 11928c2ecf20Sopenharmony_ci unsigned bitmap_size = ((compose->width + 7) / 8) * compose->height; 11938c2ecf20Sopenharmony_ci unsigned clips_size = win->clipcount * sizeof(dev->clips_cap[0]); 11948c2ecf20Sopenharmony_ci void *new_bitmap = NULL; 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci if (ret) 11978c2ecf20Sopenharmony_ci return ret; 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci if (win->bitmap) { 12008c2ecf20Sopenharmony_ci new_bitmap = vzalloc(bitmap_size); 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci if (new_bitmap == NULL) 12038c2ecf20Sopenharmony_ci return -ENOMEM; 12048c2ecf20Sopenharmony_ci if (copy_from_user(new_bitmap, win->bitmap, bitmap_size)) { 12058c2ecf20Sopenharmony_ci vfree(new_bitmap); 12068c2ecf20Sopenharmony_ci return -EFAULT; 12078c2ecf20Sopenharmony_ci } 12088c2ecf20Sopenharmony_ci } 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci dev->overlay_cap_top = win->w.top; 12118c2ecf20Sopenharmony_ci dev->overlay_cap_left = win->w.left; 12128c2ecf20Sopenharmony_ci dev->overlay_cap_field = win->field; 12138c2ecf20Sopenharmony_ci vfree(dev->bitmap_cap); 12148c2ecf20Sopenharmony_ci dev->bitmap_cap = new_bitmap; 12158c2ecf20Sopenharmony_ci dev->clipcount_cap = win->clipcount; 12168c2ecf20Sopenharmony_ci if (dev->clipcount_cap) 12178c2ecf20Sopenharmony_ci memcpy(dev->clips_cap, dev->try_clips_cap, clips_size); 12188c2ecf20Sopenharmony_ci return 0; 12198c2ecf20Sopenharmony_ci} 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ciint vivid_vid_cap_overlay(struct file *file, void *fh, unsigned i) 12228c2ecf20Sopenharmony_ci{ 12238c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci if (dev->multiplanar) 12268c2ecf20Sopenharmony_ci return -ENOTTY; 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci if (i && dev->fb_vbase_cap == NULL) 12298c2ecf20Sopenharmony_ci return -EINVAL; 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci if (i && dev->fb_cap.fmt.pixelformat != dev->fmt_cap->fourcc) { 12328c2ecf20Sopenharmony_ci dprintk(dev, 1, "mismatch between overlay and video capture pixelformats\n"); 12338c2ecf20Sopenharmony_ci return -EINVAL; 12348c2ecf20Sopenharmony_ci } 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ci if (dev->overlay_cap_owner && dev->overlay_cap_owner != fh) 12378c2ecf20Sopenharmony_ci return -EBUSY; 12388c2ecf20Sopenharmony_ci dev->overlay_cap_owner = i ? fh : NULL; 12398c2ecf20Sopenharmony_ci return 0; 12408c2ecf20Sopenharmony_ci} 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ciint vivid_vid_cap_g_fbuf(struct file *file, void *fh, 12438c2ecf20Sopenharmony_ci struct v4l2_framebuffer *a) 12448c2ecf20Sopenharmony_ci{ 12458c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci if (dev->multiplanar) 12488c2ecf20Sopenharmony_ci return -ENOTTY; 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci *a = dev->fb_cap; 12518c2ecf20Sopenharmony_ci a->capability = V4L2_FBUF_CAP_BITMAP_CLIPPING | 12528c2ecf20Sopenharmony_ci V4L2_FBUF_CAP_LIST_CLIPPING; 12538c2ecf20Sopenharmony_ci a->flags = V4L2_FBUF_FLAG_PRIMARY; 12548c2ecf20Sopenharmony_ci a->fmt.field = V4L2_FIELD_NONE; 12558c2ecf20Sopenharmony_ci a->fmt.colorspace = V4L2_COLORSPACE_SRGB; 12568c2ecf20Sopenharmony_ci a->fmt.priv = 0; 12578c2ecf20Sopenharmony_ci return 0; 12588c2ecf20Sopenharmony_ci} 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_ciint vivid_vid_cap_s_fbuf(struct file *file, void *fh, 12618c2ecf20Sopenharmony_ci const struct v4l2_framebuffer *a) 12628c2ecf20Sopenharmony_ci{ 12638c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 12648c2ecf20Sopenharmony_ci const struct vivid_fmt *fmt; 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci if (dev->multiplanar) 12678c2ecf20Sopenharmony_ci return -ENOTTY; 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO)) 12708c2ecf20Sopenharmony_ci return -EPERM; 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci if (dev->overlay_cap_owner) 12738c2ecf20Sopenharmony_ci return -EBUSY; 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci if (a->base == NULL) { 12768c2ecf20Sopenharmony_ci dev->fb_cap.base = NULL; 12778c2ecf20Sopenharmony_ci dev->fb_vbase_cap = NULL; 12788c2ecf20Sopenharmony_ci return 0; 12798c2ecf20Sopenharmony_ci } 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_ci if (a->fmt.width < 48 || a->fmt.height < 32) 12828c2ecf20Sopenharmony_ci return -EINVAL; 12838c2ecf20Sopenharmony_ci fmt = vivid_get_format(dev, a->fmt.pixelformat); 12848c2ecf20Sopenharmony_ci if (!fmt || !fmt->can_do_overlay) 12858c2ecf20Sopenharmony_ci return -EINVAL; 12868c2ecf20Sopenharmony_ci if (a->fmt.bytesperline < (a->fmt.width * fmt->bit_depth[0]) / 8) 12878c2ecf20Sopenharmony_ci return -EINVAL; 12888c2ecf20Sopenharmony_ci if (a->fmt.bytesperline > a->fmt.sizeimage / a->fmt.height) 12898c2ecf20Sopenharmony_ci return -EINVAL; 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci /* 12928c2ecf20Sopenharmony_ci * Only support the framebuffer of one of the vivid instances. 12938c2ecf20Sopenharmony_ci * Anything else is rejected. 12948c2ecf20Sopenharmony_ci */ 12958c2ecf20Sopenharmony_ci if (!vivid_validate_fb(a)) 12968c2ecf20Sopenharmony_ci return -EINVAL; 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci dev->fb_vbase_cap = phys_to_virt((unsigned long)a->base); 12998c2ecf20Sopenharmony_ci dev->fb_cap = *a; 13008c2ecf20Sopenharmony_ci dev->overlay_cap_left = clamp_t(int, dev->overlay_cap_left, 13018c2ecf20Sopenharmony_ci -dev->fb_cap.fmt.width, dev->fb_cap.fmt.width); 13028c2ecf20Sopenharmony_ci dev->overlay_cap_top = clamp_t(int, dev->overlay_cap_top, 13038c2ecf20Sopenharmony_ci -dev->fb_cap.fmt.height, dev->fb_cap.fmt.height); 13048c2ecf20Sopenharmony_ci return 0; 13058c2ecf20Sopenharmony_ci} 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_cistatic const struct v4l2_audio vivid_audio_inputs[] = { 13088c2ecf20Sopenharmony_ci { 0, "TV", V4L2_AUDCAP_STEREO }, 13098c2ecf20Sopenharmony_ci { 1, "Line-In", V4L2_AUDCAP_STEREO }, 13108c2ecf20Sopenharmony_ci}; 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_ciint vidioc_enum_input(struct file *file, void *priv, 13138c2ecf20Sopenharmony_ci struct v4l2_input *inp) 13148c2ecf20Sopenharmony_ci{ 13158c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci if (inp->index >= dev->num_inputs) 13188c2ecf20Sopenharmony_ci return -EINVAL; 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_ci inp->type = V4L2_INPUT_TYPE_CAMERA; 13218c2ecf20Sopenharmony_ci switch (dev->input_type[inp->index]) { 13228c2ecf20Sopenharmony_ci case WEBCAM: 13238c2ecf20Sopenharmony_ci snprintf(inp->name, sizeof(inp->name), "Webcam %u", 13248c2ecf20Sopenharmony_ci dev->input_name_counter[inp->index]); 13258c2ecf20Sopenharmony_ci inp->capabilities = 0; 13268c2ecf20Sopenharmony_ci break; 13278c2ecf20Sopenharmony_ci case TV: 13288c2ecf20Sopenharmony_ci snprintf(inp->name, sizeof(inp->name), "TV %u", 13298c2ecf20Sopenharmony_ci dev->input_name_counter[inp->index]); 13308c2ecf20Sopenharmony_ci inp->type = V4L2_INPUT_TYPE_TUNER; 13318c2ecf20Sopenharmony_ci inp->std = V4L2_STD_ALL; 13328c2ecf20Sopenharmony_ci if (dev->has_audio_inputs) 13338c2ecf20Sopenharmony_ci inp->audioset = (1 << ARRAY_SIZE(vivid_audio_inputs)) - 1; 13348c2ecf20Sopenharmony_ci inp->capabilities = V4L2_IN_CAP_STD; 13358c2ecf20Sopenharmony_ci break; 13368c2ecf20Sopenharmony_ci case SVID: 13378c2ecf20Sopenharmony_ci snprintf(inp->name, sizeof(inp->name), "S-Video %u", 13388c2ecf20Sopenharmony_ci dev->input_name_counter[inp->index]); 13398c2ecf20Sopenharmony_ci inp->std = V4L2_STD_ALL; 13408c2ecf20Sopenharmony_ci if (dev->has_audio_inputs) 13418c2ecf20Sopenharmony_ci inp->audioset = (1 << ARRAY_SIZE(vivid_audio_inputs)) - 1; 13428c2ecf20Sopenharmony_ci inp->capabilities = V4L2_IN_CAP_STD; 13438c2ecf20Sopenharmony_ci break; 13448c2ecf20Sopenharmony_ci case HDMI: 13458c2ecf20Sopenharmony_ci snprintf(inp->name, sizeof(inp->name), "HDMI %u", 13468c2ecf20Sopenharmony_ci dev->input_name_counter[inp->index]); 13478c2ecf20Sopenharmony_ci inp->capabilities = V4L2_IN_CAP_DV_TIMINGS; 13488c2ecf20Sopenharmony_ci if (dev->edid_blocks == 0 || 13498c2ecf20Sopenharmony_ci dev->dv_timings_signal_mode[dev->input] == NO_SIGNAL) 13508c2ecf20Sopenharmony_ci inp->status |= V4L2_IN_ST_NO_SIGNAL; 13518c2ecf20Sopenharmony_ci else if (dev->dv_timings_signal_mode[dev->input] == NO_LOCK || 13528c2ecf20Sopenharmony_ci dev->dv_timings_signal_mode[dev->input] == OUT_OF_RANGE) 13538c2ecf20Sopenharmony_ci inp->status |= V4L2_IN_ST_NO_H_LOCK; 13548c2ecf20Sopenharmony_ci break; 13558c2ecf20Sopenharmony_ci } 13568c2ecf20Sopenharmony_ci if (dev->sensor_hflip) 13578c2ecf20Sopenharmony_ci inp->status |= V4L2_IN_ST_HFLIP; 13588c2ecf20Sopenharmony_ci if (dev->sensor_vflip) 13598c2ecf20Sopenharmony_ci inp->status |= V4L2_IN_ST_VFLIP; 13608c2ecf20Sopenharmony_ci if (dev->input == inp->index && vivid_is_sdtv_cap(dev)) { 13618c2ecf20Sopenharmony_ci if (dev->std_signal_mode[dev->input] == NO_SIGNAL) { 13628c2ecf20Sopenharmony_ci inp->status |= V4L2_IN_ST_NO_SIGNAL; 13638c2ecf20Sopenharmony_ci } else if (dev->std_signal_mode[dev->input] == NO_LOCK) { 13648c2ecf20Sopenharmony_ci inp->status |= V4L2_IN_ST_NO_H_LOCK; 13658c2ecf20Sopenharmony_ci } else if (vivid_is_tv_cap(dev)) { 13668c2ecf20Sopenharmony_ci switch (tpg_g_quality(&dev->tpg)) { 13678c2ecf20Sopenharmony_ci case TPG_QUAL_GRAY: 13688c2ecf20Sopenharmony_ci inp->status |= V4L2_IN_ST_COLOR_KILL; 13698c2ecf20Sopenharmony_ci break; 13708c2ecf20Sopenharmony_ci case TPG_QUAL_NOISE: 13718c2ecf20Sopenharmony_ci inp->status |= V4L2_IN_ST_NO_H_LOCK; 13728c2ecf20Sopenharmony_ci break; 13738c2ecf20Sopenharmony_ci default: 13748c2ecf20Sopenharmony_ci break; 13758c2ecf20Sopenharmony_ci } 13768c2ecf20Sopenharmony_ci } 13778c2ecf20Sopenharmony_ci } 13788c2ecf20Sopenharmony_ci return 0; 13798c2ecf20Sopenharmony_ci} 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_ciint vidioc_g_input(struct file *file, void *priv, unsigned *i) 13828c2ecf20Sopenharmony_ci{ 13838c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 13848c2ecf20Sopenharmony_ci 13858c2ecf20Sopenharmony_ci *i = dev->input; 13868c2ecf20Sopenharmony_ci return 0; 13878c2ecf20Sopenharmony_ci} 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_ciint vidioc_s_input(struct file *file, void *priv, unsigned i) 13908c2ecf20Sopenharmony_ci{ 13918c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 13928c2ecf20Sopenharmony_ci struct v4l2_bt_timings *bt = &dev->dv_timings_cap[dev->input].bt; 13938c2ecf20Sopenharmony_ci unsigned brightness; 13948c2ecf20Sopenharmony_ci 13958c2ecf20Sopenharmony_ci if (i >= dev->num_inputs) 13968c2ecf20Sopenharmony_ci return -EINVAL; 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_ci if (i == dev->input) 13998c2ecf20Sopenharmony_ci return 0; 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_ci if (vb2_is_busy(&dev->vb_vid_cap_q) || 14028c2ecf20Sopenharmony_ci vb2_is_busy(&dev->vb_vbi_cap_q) || 14038c2ecf20Sopenharmony_ci vb2_is_busy(&dev->vb_meta_cap_q)) 14048c2ecf20Sopenharmony_ci return -EBUSY; 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_ci dev->input = i; 14078c2ecf20Sopenharmony_ci dev->vid_cap_dev.tvnorms = 0; 14088c2ecf20Sopenharmony_ci if (dev->input_type[i] == TV || dev->input_type[i] == SVID) { 14098c2ecf20Sopenharmony_ci dev->tv_audio_input = (dev->input_type[i] == TV) ? 0 : 1; 14108c2ecf20Sopenharmony_ci dev->vid_cap_dev.tvnorms = V4L2_STD_ALL; 14118c2ecf20Sopenharmony_ci } 14128c2ecf20Sopenharmony_ci dev->vbi_cap_dev.tvnorms = dev->vid_cap_dev.tvnorms; 14138c2ecf20Sopenharmony_ci dev->meta_cap_dev.tvnorms = dev->vid_cap_dev.tvnorms; 14148c2ecf20Sopenharmony_ci vivid_update_format_cap(dev, false); 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_ci if (dev->colorspace) { 14178c2ecf20Sopenharmony_ci switch (dev->input_type[i]) { 14188c2ecf20Sopenharmony_ci case WEBCAM: 14198c2ecf20Sopenharmony_ci v4l2_ctrl_s_ctrl(dev->colorspace, VIVID_CS_SRGB); 14208c2ecf20Sopenharmony_ci break; 14218c2ecf20Sopenharmony_ci case TV: 14228c2ecf20Sopenharmony_ci case SVID: 14238c2ecf20Sopenharmony_ci v4l2_ctrl_s_ctrl(dev->colorspace, VIVID_CS_170M); 14248c2ecf20Sopenharmony_ci break; 14258c2ecf20Sopenharmony_ci case HDMI: 14268c2ecf20Sopenharmony_ci if (bt->flags & V4L2_DV_FL_IS_CE_VIDEO) { 14278c2ecf20Sopenharmony_ci if (dev->src_rect.width == 720 && dev->src_rect.height <= 576) 14288c2ecf20Sopenharmony_ci v4l2_ctrl_s_ctrl(dev->colorspace, VIVID_CS_170M); 14298c2ecf20Sopenharmony_ci else 14308c2ecf20Sopenharmony_ci v4l2_ctrl_s_ctrl(dev->colorspace, VIVID_CS_709); 14318c2ecf20Sopenharmony_ci } else { 14328c2ecf20Sopenharmony_ci v4l2_ctrl_s_ctrl(dev->colorspace, VIVID_CS_SRGB); 14338c2ecf20Sopenharmony_ci } 14348c2ecf20Sopenharmony_ci break; 14358c2ecf20Sopenharmony_ci } 14368c2ecf20Sopenharmony_ci } 14378c2ecf20Sopenharmony_ci 14388c2ecf20Sopenharmony_ci /* 14398c2ecf20Sopenharmony_ci * Modify the brightness range depending on the input. 14408c2ecf20Sopenharmony_ci * This makes it easy to use vivid to test if applications can 14418c2ecf20Sopenharmony_ci * handle control range modifications and is also how this is 14428c2ecf20Sopenharmony_ci * typically used in practice as different inputs may be hooked 14438c2ecf20Sopenharmony_ci * up to different receivers with different control ranges. 14448c2ecf20Sopenharmony_ci */ 14458c2ecf20Sopenharmony_ci brightness = 128 * i + dev->input_brightness[i]; 14468c2ecf20Sopenharmony_ci v4l2_ctrl_modify_range(dev->brightness, 14478c2ecf20Sopenharmony_ci 128 * i, 255 + 128 * i, 1, 128 + 128 * i); 14488c2ecf20Sopenharmony_ci v4l2_ctrl_s_ctrl(dev->brightness, brightness); 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_ci /* Restore per-input states. */ 14518c2ecf20Sopenharmony_ci v4l2_ctrl_activate(dev->ctrl_dv_timings_signal_mode, 14528c2ecf20Sopenharmony_ci vivid_is_hdmi_cap(dev)); 14538c2ecf20Sopenharmony_ci v4l2_ctrl_activate(dev->ctrl_dv_timings, vivid_is_hdmi_cap(dev) && 14548c2ecf20Sopenharmony_ci dev->dv_timings_signal_mode[dev->input] == 14558c2ecf20Sopenharmony_ci SELECTED_DV_TIMINGS); 14568c2ecf20Sopenharmony_ci v4l2_ctrl_activate(dev->ctrl_std_signal_mode, vivid_is_sdtv_cap(dev)); 14578c2ecf20Sopenharmony_ci v4l2_ctrl_activate(dev->ctrl_standard, vivid_is_sdtv_cap(dev) && 14588c2ecf20Sopenharmony_ci dev->std_signal_mode[dev->input]); 14598c2ecf20Sopenharmony_ci 14608c2ecf20Sopenharmony_ci if (vivid_is_hdmi_cap(dev)) { 14618c2ecf20Sopenharmony_ci v4l2_ctrl_s_ctrl(dev->ctrl_dv_timings_signal_mode, 14628c2ecf20Sopenharmony_ci dev->dv_timings_signal_mode[dev->input]); 14638c2ecf20Sopenharmony_ci v4l2_ctrl_s_ctrl(dev->ctrl_dv_timings, 14648c2ecf20Sopenharmony_ci dev->query_dv_timings[dev->input]); 14658c2ecf20Sopenharmony_ci } else if (vivid_is_sdtv_cap(dev)) { 14668c2ecf20Sopenharmony_ci v4l2_ctrl_s_ctrl(dev->ctrl_std_signal_mode, 14678c2ecf20Sopenharmony_ci dev->std_signal_mode[dev->input]); 14688c2ecf20Sopenharmony_ci v4l2_ctrl_s_ctrl(dev->ctrl_standard, 14698c2ecf20Sopenharmony_ci dev->std_signal_mode[dev->input]); 14708c2ecf20Sopenharmony_ci } 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_ci return 0; 14738c2ecf20Sopenharmony_ci} 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ciint vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *vin) 14768c2ecf20Sopenharmony_ci{ 14778c2ecf20Sopenharmony_ci if (vin->index >= ARRAY_SIZE(vivid_audio_inputs)) 14788c2ecf20Sopenharmony_ci return -EINVAL; 14798c2ecf20Sopenharmony_ci *vin = vivid_audio_inputs[vin->index]; 14808c2ecf20Sopenharmony_ci return 0; 14818c2ecf20Sopenharmony_ci} 14828c2ecf20Sopenharmony_ci 14838c2ecf20Sopenharmony_ciint vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *vin) 14848c2ecf20Sopenharmony_ci{ 14858c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_ci if (!vivid_is_sdtv_cap(dev)) 14888c2ecf20Sopenharmony_ci return -EINVAL; 14898c2ecf20Sopenharmony_ci *vin = vivid_audio_inputs[dev->tv_audio_input]; 14908c2ecf20Sopenharmony_ci return 0; 14918c2ecf20Sopenharmony_ci} 14928c2ecf20Sopenharmony_ci 14938c2ecf20Sopenharmony_ciint vidioc_s_audio(struct file *file, void *fh, const struct v4l2_audio *vin) 14948c2ecf20Sopenharmony_ci{ 14958c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 14968c2ecf20Sopenharmony_ci 14978c2ecf20Sopenharmony_ci if (!vivid_is_sdtv_cap(dev)) 14988c2ecf20Sopenharmony_ci return -EINVAL; 14998c2ecf20Sopenharmony_ci if (vin->index >= ARRAY_SIZE(vivid_audio_inputs)) 15008c2ecf20Sopenharmony_ci return -EINVAL; 15018c2ecf20Sopenharmony_ci dev->tv_audio_input = vin->index; 15028c2ecf20Sopenharmony_ci return 0; 15038c2ecf20Sopenharmony_ci} 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ciint vivid_video_g_frequency(struct file *file, void *fh, struct v4l2_frequency *vf) 15068c2ecf20Sopenharmony_ci{ 15078c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci if (vf->tuner != 0) 15108c2ecf20Sopenharmony_ci return -EINVAL; 15118c2ecf20Sopenharmony_ci vf->frequency = dev->tv_freq; 15128c2ecf20Sopenharmony_ci return 0; 15138c2ecf20Sopenharmony_ci} 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_ciint vivid_video_s_frequency(struct file *file, void *fh, const struct v4l2_frequency *vf) 15168c2ecf20Sopenharmony_ci{ 15178c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 15188c2ecf20Sopenharmony_ci 15198c2ecf20Sopenharmony_ci if (vf->tuner != 0) 15208c2ecf20Sopenharmony_ci return -EINVAL; 15218c2ecf20Sopenharmony_ci dev->tv_freq = clamp_t(unsigned, vf->frequency, MIN_TV_FREQ, MAX_TV_FREQ); 15228c2ecf20Sopenharmony_ci if (vivid_is_tv_cap(dev)) 15238c2ecf20Sopenharmony_ci vivid_update_quality(dev); 15248c2ecf20Sopenharmony_ci return 0; 15258c2ecf20Sopenharmony_ci} 15268c2ecf20Sopenharmony_ci 15278c2ecf20Sopenharmony_ciint vivid_video_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *vt) 15288c2ecf20Sopenharmony_ci{ 15298c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 15308c2ecf20Sopenharmony_ci 15318c2ecf20Sopenharmony_ci if (vt->index != 0) 15328c2ecf20Sopenharmony_ci return -EINVAL; 15338c2ecf20Sopenharmony_ci if (vt->audmode > V4L2_TUNER_MODE_LANG1_LANG2) 15348c2ecf20Sopenharmony_ci return -EINVAL; 15358c2ecf20Sopenharmony_ci dev->tv_audmode = vt->audmode; 15368c2ecf20Sopenharmony_ci return 0; 15378c2ecf20Sopenharmony_ci} 15388c2ecf20Sopenharmony_ci 15398c2ecf20Sopenharmony_ciint vivid_video_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) 15408c2ecf20Sopenharmony_ci{ 15418c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 15428c2ecf20Sopenharmony_ci enum tpg_quality qual; 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_ci if (vt->index != 0) 15458c2ecf20Sopenharmony_ci return -EINVAL; 15468c2ecf20Sopenharmony_ci 15478c2ecf20Sopenharmony_ci vt->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO | 15488c2ecf20Sopenharmony_ci V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; 15498c2ecf20Sopenharmony_ci vt->audmode = dev->tv_audmode; 15508c2ecf20Sopenharmony_ci vt->rangelow = MIN_TV_FREQ; 15518c2ecf20Sopenharmony_ci vt->rangehigh = MAX_TV_FREQ; 15528c2ecf20Sopenharmony_ci qual = vivid_get_quality(dev, &vt->afc); 15538c2ecf20Sopenharmony_ci if (qual == TPG_QUAL_COLOR) 15548c2ecf20Sopenharmony_ci vt->signal = 0xffff; 15558c2ecf20Sopenharmony_ci else if (qual == TPG_QUAL_GRAY) 15568c2ecf20Sopenharmony_ci vt->signal = 0x8000; 15578c2ecf20Sopenharmony_ci else 15588c2ecf20Sopenharmony_ci vt->signal = 0; 15598c2ecf20Sopenharmony_ci if (qual == TPG_QUAL_NOISE) { 15608c2ecf20Sopenharmony_ci vt->rxsubchans = 0; 15618c2ecf20Sopenharmony_ci } else if (qual == TPG_QUAL_GRAY) { 15628c2ecf20Sopenharmony_ci vt->rxsubchans = V4L2_TUNER_SUB_MONO; 15638c2ecf20Sopenharmony_ci } else { 15648c2ecf20Sopenharmony_ci unsigned int channel_nr = dev->tv_freq / (6 * 16); 15658c2ecf20Sopenharmony_ci unsigned int options = 15668c2ecf20Sopenharmony_ci (dev->std_cap[dev->input] & V4L2_STD_NTSC_M) ? 4 : 3; 15678c2ecf20Sopenharmony_ci 15688c2ecf20Sopenharmony_ci switch (channel_nr % options) { 15698c2ecf20Sopenharmony_ci case 0: 15708c2ecf20Sopenharmony_ci vt->rxsubchans = V4L2_TUNER_SUB_MONO; 15718c2ecf20Sopenharmony_ci break; 15728c2ecf20Sopenharmony_ci case 1: 15738c2ecf20Sopenharmony_ci vt->rxsubchans = V4L2_TUNER_SUB_STEREO; 15748c2ecf20Sopenharmony_ci break; 15758c2ecf20Sopenharmony_ci case 2: 15768c2ecf20Sopenharmony_ci if (dev->std_cap[dev->input] & V4L2_STD_NTSC_M) 15778c2ecf20Sopenharmony_ci vt->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_SAP; 15788c2ecf20Sopenharmony_ci else 15798c2ecf20Sopenharmony_ci vt->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; 15808c2ecf20Sopenharmony_ci break; 15818c2ecf20Sopenharmony_ci case 3: 15828c2ecf20Sopenharmony_ci vt->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_SAP; 15838c2ecf20Sopenharmony_ci break; 15848c2ecf20Sopenharmony_ci } 15858c2ecf20Sopenharmony_ci } 15868c2ecf20Sopenharmony_ci strscpy(vt->name, "TV Tuner", sizeof(vt->name)); 15878c2ecf20Sopenharmony_ci return 0; 15888c2ecf20Sopenharmony_ci} 15898c2ecf20Sopenharmony_ci 15908c2ecf20Sopenharmony_ci/* Must remain in sync with the vivid_ctrl_standard_strings array */ 15918c2ecf20Sopenharmony_ciconst v4l2_std_id vivid_standard[] = { 15928c2ecf20Sopenharmony_ci V4L2_STD_NTSC_M, 15938c2ecf20Sopenharmony_ci V4L2_STD_NTSC_M_JP, 15948c2ecf20Sopenharmony_ci V4L2_STD_NTSC_M_KR, 15958c2ecf20Sopenharmony_ci V4L2_STD_NTSC_443, 15968c2ecf20Sopenharmony_ci V4L2_STD_PAL_BG | V4L2_STD_PAL_H, 15978c2ecf20Sopenharmony_ci V4L2_STD_PAL_I, 15988c2ecf20Sopenharmony_ci V4L2_STD_PAL_DK, 15998c2ecf20Sopenharmony_ci V4L2_STD_PAL_M, 16008c2ecf20Sopenharmony_ci V4L2_STD_PAL_N, 16018c2ecf20Sopenharmony_ci V4L2_STD_PAL_Nc, 16028c2ecf20Sopenharmony_ci V4L2_STD_PAL_60, 16038c2ecf20Sopenharmony_ci V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H, 16048c2ecf20Sopenharmony_ci V4L2_STD_SECAM_DK, 16058c2ecf20Sopenharmony_ci V4L2_STD_SECAM_L, 16068c2ecf20Sopenharmony_ci V4L2_STD_SECAM_LC, 16078c2ecf20Sopenharmony_ci V4L2_STD_UNKNOWN 16088c2ecf20Sopenharmony_ci}; 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_ci/* Must remain in sync with the vivid_standard array */ 16118c2ecf20Sopenharmony_ciconst char * const vivid_ctrl_standard_strings[] = { 16128c2ecf20Sopenharmony_ci "NTSC-M", 16138c2ecf20Sopenharmony_ci "NTSC-M-JP", 16148c2ecf20Sopenharmony_ci "NTSC-M-KR", 16158c2ecf20Sopenharmony_ci "NTSC-443", 16168c2ecf20Sopenharmony_ci "PAL-BGH", 16178c2ecf20Sopenharmony_ci "PAL-I", 16188c2ecf20Sopenharmony_ci "PAL-DK", 16198c2ecf20Sopenharmony_ci "PAL-M", 16208c2ecf20Sopenharmony_ci "PAL-N", 16218c2ecf20Sopenharmony_ci "PAL-Nc", 16228c2ecf20Sopenharmony_ci "PAL-60", 16238c2ecf20Sopenharmony_ci "SECAM-BGH", 16248c2ecf20Sopenharmony_ci "SECAM-DK", 16258c2ecf20Sopenharmony_ci "SECAM-L", 16268c2ecf20Sopenharmony_ci "SECAM-Lc", 16278c2ecf20Sopenharmony_ci NULL, 16288c2ecf20Sopenharmony_ci}; 16298c2ecf20Sopenharmony_ci 16308c2ecf20Sopenharmony_ciint vidioc_querystd(struct file *file, void *priv, v4l2_std_id *id) 16318c2ecf20Sopenharmony_ci{ 16328c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 16338c2ecf20Sopenharmony_ci unsigned int last = dev->query_std_last[dev->input]; 16348c2ecf20Sopenharmony_ci 16358c2ecf20Sopenharmony_ci if (!vivid_is_sdtv_cap(dev)) 16368c2ecf20Sopenharmony_ci return -ENODATA; 16378c2ecf20Sopenharmony_ci if (dev->std_signal_mode[dev->input] == NO_SIGNAL || 16388c2ecf20Sopenharmony_ci dev->std_signal_mode[dev->input] == NO_LOCK) { 16398c2ecf20Sopenharmony_ci *id = V4L2_STD_UNKNOWN; 16408c2ecf20Sopenharmony_ci return 0; 16418c2ecf20Sopenharmony_ci } 16428c2ecf20Sopenharmony_ci if (vivid_is_tv_cap(dev) && tpg_g_quality(&dev->tpg) == TPG_QUAL_NOISE) { 16438c2ecf20Sopenharmony_ci *id = V4L2_STD_UNKNOWN; 16448c2ecf20Sopenharmony_ci } else if (dev->std_signal_mode[dev->input] == CURRENT_STD) { 16458c2ecf20Sopenharmony_ci *id = dev->std_cap[dev->input]; 16468c2ecf20Sopenharmony_ci } else if (dev->std_signal_mode[dev->input] == SELECTED_STD) { 16478c2ecf20Sopenharmony_ci *id = dev->query_std[dev->input]; 16488c2ecf20Sopenharmony_ci } else { 16498c2ecf20Sopenharmony_ci *id = vivid_standard[last]; 16508c2ecf20Sopenharmony_ci dev->query_std_last[dev->input] = 16518c2ecf20Sopenharmony_ci (last + 1) % ARRAY_SIZE(vivid_standard); 16528c2ecf20Sopenharmony_ci } 16538c2ecf20Sopenharmony_ci 16548c2ecf20Sopenharmony_ci return 0; 16558c2ecf20Sopenharmony_ci} 16568c2ecf20Sopenharmony_ci 16578c2ecf20Sopenharmony_ciint vivid_vid_cap_s_std(struct file *file, void *priv, v4l2_std_id id) 16588c2ecf20Sopenharmony_ci{ 16598c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 16608c2ecf20Sopenharmony_ci 16618c2ecf20Sopenharmony_ci if (!vivid_is_sdtv_cap(dev)) 16628c2ecf20Sopenharmony_ci return -ENODATA; 16638c2ecf20Sopenharmony_ci if (dev->std_cap[dev->input] == id) 16648c2ecf20Sopenharmony_ci return 0; 16658c2ecf20Sopenharmony_ci if (vb2_is_busy(&dev->vb_vid_cap_q) || vb2_is_busy(&dev->vb_vbi_cap_q)) 16668c2ecf20Sopenharmony_ci return -EBUSY; 16678c2ecf20Sopenharmony_ci dev->std_cap[dev->input] = id; 16688c2ecf20Sopenharmony_ci vivid_update_format_cap(dev, false); 16698c2ecf20Sopenharmony_ci return 0; 16708c2ecf20Sopenharmony_ci} 16718c2ecf20Sopenharmony_ci 16728c2ecf20Sopenharmony_cistatic void find_aspect_ratio(u32 width, u32 height, 16738c2ecf20Sopenharmony_ci u32 *num, u32 *denom) 16748c2ecf20Sopenharmony_ci{ 16758c2ecf20Sopenharmony_ci if (!(height % 3) && ((height * 4 / 3) == width)) { 16768c2ecf20Sopenharmony_ci *num = 4; 16778c2ecf20Sopenharmony_ci *denom = 3; 16788c2ecf20Sopenharmony_ci } else if (!(height % 9) && ((height * 16 / 9) == width)) { 16798c2ecf20Sopenharmony_ci *num = 16; 16808c2ecf20Sopenharmony_ci *denom = 9; 16818c2ecf20Sopenharmony_ci } else if (!(height % 10) && ((height * 16 / 10) == width)) { 16828c2ecf20Sopenharmony_ci *num = 16; 16838c2ecf20Sopenharmony_ci *denom = 10; 16848c2ecf20Sopenharmony_ci } else if (!(height % 4) && ((height * 5 / 4) == width)) { 16858c2ecf20Sopenharmony_ci *num = 5; 16868c2ecf20Sopenharmony_ci *denom = 4; 16878c2ecf20Sopenharmony_ci } else if (!(height % 9) && ((height * 15 / 9) == width)) { 16888c2ecf20Sopenharmony_ci *num = 15; 16898c2ecf20Sopenharmony_ci *denom = 9; 16908c2ecf20Sopenharmony_ci } else { /* default to 16:9 */ 16918c2ecf20Sopenharmony_ci *num = 16; 16928c2ecf20Sopenharmony_ci *denom = 9; 16938c2ecf20Sopenharmony_ci } 16948c2ecf20Sopenharmony_ci} 16958c2ecf20Sopenharmony_ci 16968c2ecf20Sopenharmony_cistatic bool valid_cvt_gtf_timings(struct v4l2_dv_timings *timings) 16978c2ecf20Sopenharmony_ci{ 16988c2ecf20Sopenharmony_ci struct v4l2_bt_timings *bt = &timings->bt; 16998c2ecf20Sopenharmony_ci u32 total_h_pixel; 17008c2ecf20Sopenharmony_ci u32 total_v_lines; 17018c2ecf20Sopenharmony_ci u32 h_freq; 17028c2ecf20Sopenharmony_ci 17038c2ecf20Sopenharmony_ci if (!v4l2_valid_dv_timings(timings, &vivid_dv_timings_cap, 17048c2ecf20Sopenharmony_ci NULL, NULL)) 17058c2ecf20Sopenharmony_ci return false; 17068c2ecf20Sopenharmony_ci 17078c2ecf20Sopenharmony_ci total_h_pixel = V4L2_DV_BT_FRAME_WIDTH(bt); 17088c2ecf20Sopenharmony_ci total_v_lines = V4L2_DV_BT_FRAME_HEIGHT(bt); 17098c2ecf20Sopenharmony_ci 17108c2ecf20Sopenharmony_ci h_freq = (u32)bt->pixelclock / total_h_pixel; 17118c2ecf20Sopenharmony_ci 17128c2ecf20Sopenharmony_ci if (bt->standards == 0 || (bt->standards & V4L2_DV_BT_STD_CVT)) { 17138c2ecf20Sopenharmony_ci if (v4l2_detect_cvt(total_v_lines, h_freq, bt->vsync, bt->width, 17148c2ecf20Sopenharmony_ci bt->polarities, bt->interlaced, timings)) 17158c2ecf20Sopenharmony_ci return true; 17168c2ecf20Sopenharmony_ci } 17178c2ecf20Sopenharmony_ci 17188c2ecf20Sopenharmony_ci if (bt->standards == 0 || (bt->standards & V4L2_DV_BT_STD_GTF)) { 17198c2ecf20Sopenharmony_ci struct v4l2_fract aspect_ratio; 17208c2ecf20Sopenharmony_ci 17218c2ecf20Sopenharmony_ci find_aspect_ratio(bt->width, bt->height, 17228c2ecf20Sopenharmony_ci &aspect_ratio.numerator, 17238c2ecf20Sopenharmony_ci &aspect_ratio.denominator); 17248c2ecf20Sopenharmony_ci if (v4l2_detect_gtf(total_v_lines, h_freq, bt->vsync, 17258c2ecf20Sopenharmony_ci bt->polarities, bt->interlaced, 17268c2ecf20Sopenharmony_ci aspect_ratio, timings)) 17278c2ecf20Sopenharmony_ci return true; 17288c2ecf20Sopenharmony_ci } 17298c2ecf20Sopenharmony_ci return false; 17308c2ecf20Sopenharmony_ci} 17318c2ecf20Sopenharmony_ci 17328c2ecf20Sopenharmony_ciint vivid_vid_cap_s_dv_timings(struct file *file, void *_fh, 17338c2ecf20Sopenharmony_ci struct v4l2_dv_timings *timings) 17348c2ecf20Sopenharmony_ci{ 17358c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 17368c2ecf20Sopenharmony_ci 17378c2ecf20Sopenharmony_ci if (!vivid_is_hdmi_cap(dev)) 17388c2ecf20Sopenharmony_ci return -ENODATA; 17398c2ecf20Sopenharmony_ci if (!v4l2_find_dv_timings_cap(timings, &vivid_dv_timings_cap, 17408c2ecf20Sopenharmony_ci 0, NULL, NULL) && 17418c2ecf20Sopenharmony_ci !valid_cvt_gtf_timings(timings)) 17428c2ecf20Sopenharmony_ci return -EINVAL; 17438c2ecf20Sopenharmony_ci 17448c2ecf20Sopenharmony_ci if (v4l2_match_dv_timings(timings, &dev->dv_timings_cap[dev->input], 17458c2ecf20Sopenharmony_ci 0, false)) 17468c2ecf20Sopenharmony_ci return 0; 17478c2ecf20Sopenharmony_ci if (vb2_is_busy(&dev->vb_vid_cap_q)) 17488c2ecf20Sopenharmony_ci return -EBUSY; 17498c2ecf20Sopenharmony_ci 17508c2ecf20Sopenharmony_ci dev->dv_timings_cap[dev->input] = *timings; 17518c2ecf20Sopenharmony_ci vivid_update_format_cap(dev, false); 17528c2ecf20Sopenharmony_ci return 0; 17538c2ecf20Sopenharmony_ci} 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_ciint vidioc_query_dv_timings(struct file *file, void *_fh, 17568c2ecf20Sopenharmony_ci struct v4l2_dv_timings *timings) 17578c2ecf20Sopenharmony_ci{ 17588c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 17598c2ecf20Sopenharmony_ci unsigned int input = dev->input; 17608c2ecf20Sopenharmony_ci unsigned int last = dev->query_dv_timings_last[input]; 17618c2ecf20Sopenharmony_ci 17628c2ecf20Sopenharmony_ci if (!vivid_is_hdmi_cap(dev)) 17638c2ecf20Sopenharmony_ci return -ENODATA; 17648c2ecf20Sopenharmony_ci if (dev->dv_timings_signal_mode[input] == NO_SIGNAL || 17658c2ecf20Sopenharmony_ci dev->edid_blocks == 0) 17668c2ecf20Sopenharmony_ci return -ENOLINK; 17678c2ecf20Sopenharmony_ci if (dev->dv_timings_signal_mode[input] == NO_LOCK) 17688c2ecf20Sopenharmony_ci return -ENOLCK; 17698c2ecf20Sopenharmony_ci if (dev->dv_timings_signal_mode[input] == OUT_OF_RANGE) { 17708c2ecf20Sopenharmony_ci timings->bt.pixelclock = vivid_dv_timings_cap.bt.max_pixelclock * 2; 17718c2ecf20Sopenharmony_ci return -ERANGE; 17728c2ecf20Sopenharmony_ci } 17738c2ecf20Sopenharmony_ci if (dev->dv_timings_signal_mode[input] == CURRENT_DV_TIMINGS) { 17748c2ecf20Sopenharmony_ci *timings = dev->dv_timings_cap[input]; 17758c2ecf20Sopenharmony_ci } else if (dev->dv_timings_signal_mode[input] == 17768c2ecf20Sopenharmony_ci SELECTED_DV_TIMINGS) { 17778c2ecf20Sopenharmony_ci *timings = 17788c2ecf20Sopenharmony_ci v4l2_dv_timings_presets[dev->query_dv_timings[input]]; 17798c2ecf20Sopenharmony_ci } else { 17808c2ecf20Sopenharmony_ci *timings = 17818c2ecf20Sopenharmony_ci v4l2_dv_timings_presets[last]; 17828c2ecf20Sopenharmony_ci dev->query_dv_timings_last[input] = 17838c2ecf20Sopenharmony_ci (last + 1) % dev->query_dv_timings_size; 17848c2ecf20Sopenharmony_ci } 17858c2ecf20Sopenharmony_ci return 0; 17868c2ecf20Sopenharmony_ci} 17878c2ecf20Sopenharmony_ci 17888c2ecf20Sopenharmony_ciint vidioc_s_edid(struct file *file, void *_fh, 17898c2ecf20Sopenharmony_ci struct v4l2_edid *edid) 17908c2ecf20Sopenharmony_ci{ 17918c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 17928c2ecf20Sopenharmony_ci u16 phys_addr; 17938c2ecf20Sopenharmony_ci u32 display_present = 0; 17948c2ecf20Sopenharmony_ci unsigned int i, j; 17958c2ecf20Sopenharmony_ci int ret; 17968c2ecf20Sopenharmony_ci 17978c2ecf20Sopenharmony_ci memset(edid->reserved, 0, sizeof(edid->reserved)); 17988c2ecf20Sopenharmony_ci if (edid->pad >= dev->num_inputs) 17998c2ecf20Sopenharmony_ci return -EINVAL; 18008c2ecf20Sopenharmony_ci if (dev->input_type[edid->pad] != HDMI || edid->start_block) 18018c2ecf20Sopenharmony_ci return -EINVAL; 18028c2ecf20Sopenharmony_ci if (edid->blocks == 0) { 18038c2ecf20Sopenharmony_ci dev->edid_blocks = 0; 18048c2ecf20Sopenharmony_ci v4l2_ctrl_s_ctrl(dev->ctrl_tx_edid_present, 0); 18058c2ecf20Sopenharmony_ci v4l2_ctrl_s_ctrl(dev->ctrl_tx_hotplug, 0); 18068c2ecf20Sopenharmony_ci phys_addr = CEC_PHYS_ADDR_INVALID; 18078c2ecf20Sopenharmony_ci goto set_phys_addr; 18088c2ecf20Sopenharmony_ci } 18098c2ecf20Sopenharmony_ci if (edid->blocks > dev->edid_max_blocks) { 18108c2ecf20Sopenharmony_ci edid->blocks = dev->edid_max_blocks; 18118c2ecf20Sopenharmony_ci return -E2BIG; 18128c2ecf20Sopenharmony_ci } 18138c2ecf20Sopenharmony_ci phys_addr = cec_get_edid_phys_addr(edid->edid, edid->blocks * 128, NULL); 18148c2ecf20Sopenharmony_ci ret = v4l2_phys_addr_validate(phys_addr, &phys_addr, NULL); 18158c2ecf20Sopenharmony_ci if (ret) 18168c2ecf20Sopenharmony_ci return ret; 18178c2ecf20Sopenharmony_ci 18188c2ecf20Sopenharmony_ci if (vb2_is_busy(&dev->vb_vid_cap_q)) 18198c2ecf20Sopenharmony_ci return -EBUSY; 18208c2ecf20Sopenharmony_ci 18218c2ecf20Sopenharmony_ci dev->edid_blocks = edid->blocks; 18228c2ecf20Sopenharmony_ci memcpy(dev->edid, edid->edid, edid->blocks * 128); 18238c2ecf20Sopenharmony_ci 18248c2ecf20Sopenharmony_ci for (i = 0, j = 0; i < dev->num_outputs; i++) 18258c2ecf20Sopenharmony_ci if (dev->output_type[i] == HDMI) 18268c2ecf20Sopenharmony_ci display_present |= 18278c2ecf20Sopenharmony_ci dev->display_present[i] << j++; 18288c2ecf20Sopenharmony_ci 18298c2ecf20Sopenharmony_ci v4l2_ctrl_s_ctrl(dev->ctrl_tx_edid_present, display_present); 18308c2ecf20Sopenharmony_ci v4l2_ctrl_s_ctrl(dev->ctrl_tx_hotplug, display_present); 18318c2ecf20Sopenharmony_ci 18328c2ecf20Sopenharmony_ciset_phys_addr: 18338c2ecf20Sopenharmony_ci /* TODO: a proper hotplug detect cycle should be emulated here */ 18348c2ecf20Sopenharmony_ci cec_s_phys_addr(dev->cec_rx_adap, phys_addr, false); 18358c2ecf20Sopenharmony_ci 18368c2ecf20Sopenharmony_ci for (i = 0; i < MAX_OUTPUTS && dev->cec_tx_adap[i]; i++) 18378c2ecf20Sopenharmony_ci cec_s_phys_addr(dev->cec_tx_adap[i], 18388c2ecf20Sopenharmony_ci dev->display_present[i] ? 18398c2ecf20Sopenharmony_ci v4l2_phys_addr_for_input(phys_addr, i + 1) : 18408c2ecf20Sopenharmony_ci CEC_PHYS_ADDR_INVALID, 18418c2ecf20Sopenharmony_ci false); 18428c2ecf20Sopenharmony_ci return 0; 18438c2ecf20Sopenharmony_ci} 18448c2ecf20Sopenharmony_ci 18458c2ecf20Sopenharmony_ciint vidioc_enum_framesizes(struct file *file, void *fh, 18468c2ecf20Sopenharmony_ci struct v4l2_frmsizeenum *fsize) 18478c2ecf20Sopenharmony_ci{ 18488c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 18498c2ecf20Sopenharmony_ci 18508c2ecf20Sopenharmony_ci if (!vivid_is_webcam(dev) && !dev->has_scaler_cap) 18518c2ecf20Sopenharmony_ci return -EINVAL; 18528c2ecf20Sopenharmony_ci if (vivid_get_format(dev, fsize->pixel_format) == NULL) 18538c2ecf20Sopenharmony_ci return -EINVAL; 18548c2ecf20Sopenharmony_ci if (vivid_is_webcam(dev)) { 18558c2ecf20Sopenharmony_ci if (fsize->index >= ARRAY_SIZE(webcam_sizes)) 18568c2ecf20Sopenharmony_ci return -EINVAL; 18578c2ecf20Sopenharmony_ci fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; 18588c2ecf20Sopenharmony_ci fsize->discrete = webcam_sizes[fsize->index]; 18598c2ecf20Sopenharmony_ci return 0; 18608c2ecf20Sopenharmony_ci } 18618c2ecf20Sopenharmony_ci if (fsize->index) 18628c2ecf20Sopenharmony_ci return -EINVAL; 18638c2ecf20Sopenharmony_ci fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; 18648c2ecf20Sopenharmony_ci fsize->stepwise.min_width = MIN_WIDTH; 18658c2ecf20Sopenharmony_ci fsize->stepwise.max_width = MAX_WIDTH * MAX_ZOOM; 18668c2ecf20Sopenharmony_ci fsize->stepwise.step_width = 2; 18678c2ecf20Sopenharmony_ci fsize->stepwise.min_height = MIN_HEIGHT; 18688c2ecf20Sopenharmony_ci fsize->stepwise.max_height = MAX_HEIGHT * MAX_ZOOM; 18698c2ecf20Sopenharmony_ci fsize->stepwise.step_height = 2; 18708c2ecf20Sopenharmony_ci return 0; 18718c2ecf20Sopenharmony_ci} 18728c2ecf20Sopenharmony_ci 18738c2ecf20Sopenharmony_ci/* timeperframe is arbitrary and continuous */ 18748c2ecf20Sopenharmony_ciint vidioc_enum_frameintervals(struct file *file, void *priv, 18758c2ecf20Sopenharmony_ci struct v4l2_frmivalenum *fival) 18768c2ecf20Sopenharmony_ci{ 18778c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 18788c2ecf20Sopenharmony_ci const struct vivid_fmt *fmt; 18798c2ecf20Sopenharmony_ci int i; 18808c2ecf20Sopenharmony_ci 18818c2ecf20Sopenharmony_ci fmt = vivid_get_format(dev, fival->pixel_format); 18828c2ecf20Sopenharmony_ci if (!fmt) 18838c2ecf20Sopenharmony_ci return -EINVAL; 18848c2ecf20Sopenharmony_ci 18858c2ecf20Sopenharmony_ci if (!vivid_is_webcam(dev)) { 18868c2ecf20Sopenharmony_ci if (fival->index) 18878c2ecf20Sopenharmony_ci return -EINVAL; 18888c2ecf20Sopenharmony_ci if (fival->width < MIN_WIDTH || fival->width > MAX_WIDTH * MAX_ZOOM) 18898c2ecf20Sopenharmony_ci return -EINVAL; 18908c2ecf20Sopenharmony_ci if (fival->height < MIN_HEIGHT || fival->height > MAX_HEIGHT * MAX_ZOOM) 18918c2ecf20Sopenharmony_ci return -EINVAL; 18928c2ecf20Sopenharmony_ci fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; 18938c2ecf20Sopenharmony_ci fival->discrete = dev->timeperframe_vid_cap; 18948c2ecf20Sopenharmony_ci return 0; 18958c2ecf20Sopenharmony_ci } 18968c2ecf20Sopenharmony_ci 18978c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(webcam_sizes); i++) 18988c2ecf20Sopenharmony_ci if (fival->width == webcam_sizes[i].width && 18998c2ecf20Sopenharmony_ci fival->height == webcam_sizes[i].height) 19008c2ecf20Sopenharmony_ci break; 19018c2ecf20Sopenharmony_ci if (i == ARRAY_SIZE(webcam_sizes)) 19028c2ecf20Sopenharmony_ci return -EINVAL; 19038c2ecf20Sopenharmony_ci if (fival->index >= 2 * (VIVID_WEBCAM_SIZES - i)) 19048c2ecf20Sopenharmony_ci return -EINVAL; 19058c2ecf20Sopenharmony_ci fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; 19068c2ecf20Sopenharmony_ci fival->discrete = webcam_intervals[fival->index]; 19078c2ecf20Sopenharmony_ci return 0; 19088c2ecf20Sopenharmony_ci} 19098c2ecf20Sopenharmony_ci 19108c2ecf20Sopenharmony_ciint vivid_vid_cap_g_parm(struct file *file, void *priv, 19118c2ecf20Sopenharmony_ci struct v4l2_streamparm *parm) 19128c2ecf20Sopenharmony_ci{ 19138c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 19148c2ecf20Sopenharmony_ci 19158c2ecf20Sopenharmony_ci if (parm->type != (dev->multiplanar ? 19168c2ecf20Sopenharmony_ci V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE : 19178c2ecf20Sopenharmony_ci V4L2_BUF_TYPE_VIDEO_CAPTURE)) 19188c2ecf20Sopenharmony_ci return -EINVAL; 19198c2ecf20Sopenharmony_ci 19208c2ecf20Sopenharmony_ci parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; 19218c2ecf20Sopenharmony_ci parm->parm.capture.timeperframe = dev->timeperframe_vid_cap; 19228c2ecf20Sopenharmony_ci parm->parm.capture.readbuffers = 1; 19238c2ecf20Sopenharmony_ci return 0; 19248c2ecf20Sopenharmony_ci} 19258c2ecf20Sopenharmony_ci 19268c2ecf20Sopenharmony_ciint vivid_vid_cap_s_parm(struct file *file, void *priv, 19278c2ecf20Sopenharmony_ci struct v4l2_streamparm *parm) 19288c2ecf20Sopenharmony_ci{ 19298c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 19308c2ecf20Sopenharmony_ci unsigned ival_sz = 2 * (VIVID_WEBCAM_SIZES - dev->webcam_size_idx); 19318c2ecf20Sopenharmony_ci struct v4l2_fract tpf; 19328c2ecf20Sopenharmony_ci unsigned i; 19338c2ecf20Sopenharmony_ci 19348c2ecf20Sopenharmony_ci if (parm->type != (dev->multiplanar ? 19358c2ecf20Sopenharmony_ci V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE : 19368c2ecf20Sopenharmony_ci V4L2_BUF_TYPE_VIDEO_CAPTURE)) 19378c2ecf20Sopenharmony_ci return -EINVAL; 19388c2ecf20Sopenharmony_ci if (!vivid_is_webcam(dev)) 19398c2ecf20Sopenharmony_ci return vivid_vid_cap_g_parm(file, priv, parm); 19408c2ecf20Sopenharmony_ci 19418c2ecf20Sopenharmony_ci tpf = parm->parm.capture.timeperframe; 19428c2ecf20Sopenharmony_ci 19438c2ecf20Sopenharmony_ci if (tpf.denominator == 0) 19448c2ecf20Sopenharmony_ci tpf = webcam_intervals[ival_sz - 1]; 19458c2ecf20Sopenharmony_ci for (i = 0; i < ival_sz; i++) 19468c2ecf20Sopenharmony_ci if (V4L2_FRACT_COMPARE(tpf, >=, webcam_intervals[i])) 19478c2ecf20Sopenharmony_ci break; 19488c2ecf20Sopenharmony_ci if (i == ival_sz) 19498c2ecf20Sopenharmony_ci i = ival_sz - 1; 19508c2ecf20Sopenharmony_ci dev->webcam_ival_idx = i; 19518c2ecf20Sopenharmony_ci tpf = webcam_intervals[dev->webcam_ival_idx]; 19528c2ecf20Sopenharmony_ci 19538c2ecf20Sopenharmony_ci /* resync the thread's timings */ 19548c2ecf20Sopenharmony_ci dev->cap_seq_resync = true; 19558c2ecf20Sopenharmony_ci dev->timeperframe_vid_cap = tpf; 19568c2ecf20Sopenharmony_ci parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; 19578c2ecf20Sopenharmony_ci parm->parm.capture.timeperframe = tpf; 19588c2ecf20Sopenharmony_ci parm->parm.capture.readbuffers = 1; 19598c2ecf20Sopenharmony_ci return 0; 19608c2ecf20Sopenharmony_ci} 1961