162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * vivid-vid-cap.c - video capture support functions. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/errno.h> 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/sched.h> 1162306a36Sopenharmony_ci#include <linux/vmalloc.h> 1262306a36Sopenharmony_ci#include <linux/videodev2.h> 1362306a36Sopenharmony_ci#include <linux/v4l2-dv-timings.h> 1462306a36Sopenharmony_ci#include <media/v4l2-common.h> 1562306a36Sopenharmony_ci#include <media/v4l2-event.h> 1662306a36Sopenharmony_ci#include <media/v4l2-dv-timings.h> 1762306a36Sopenharmony_ci#include <media/v4l2-rect.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include "vivid-core.h" 2062306a36Sopenharmony_ci#include "vivid-vid-common.h" 2162306a36Sopenharmony_ci#include "vivid-kthread-cap.h" 2262306a36Sopenharmony_ci#include "vivid-vid-cap.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* Sizes must be in increasing order */ 2562306a36Sopenharmony_cistatic const struct v4l2_frmsize_discrete webcam_sizes[] = { 2662306a36Sopenharmony_ci { 320, 180 }, 2762306a36Sopenharmony_ci { 640, 360 }, 2862306a36Sopenharmony_ci { 640, 480 }, 2962306a36Sopenharmony_ci { 1280, 720 }, 3062306a36Sopenharmony_ci { 1920, 1080 }, 3162306a36Sopenharmony_ci { 3840, 2160 }, 3262306a36Sopenharmony_ci}; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* 3562306a36Sopenharmony_ci * Intervals must be in increasing order and there must be twice as many 3662306a36Sopenharmony_ci * elements in this array as there are in webcam_sizes. 3762306a36Sopenharmony_ci */ 3862306a36Sopenharmony_cistatic const struct v4l2_fract webcam_intervals[] = { 3962306a36Sopenharmony_ci { 1, 1 }, 4062306a36Sopenharmony_ci { 1, 2 }, 4162306a36Sopenharmony_ci { 1, 4 }, 4262306a36Sopenharmony_ci { 1, 5 }, 4362306a36Sopenharmony_ci { 1, 10 }, 4462306a36Sopenharmony_ci { 2, 25 }, 4562306a36Sopenharmony_ci { 1, 15 }, /* 7 - maximum for 2160p */ 4662306a36Sopenharmony_ci { 1, 25 }, 4762306a36Sopenharmony_ci { 1, 30 }, /* 9 - maximum for 1080p */ 4862306a36Sopenharmony_ci { 1, 40 }, 4962306a36Sopenharmony_ci { 1, 50 }, 5062306a36Sopenharmony_ci { 1, 60 }, /* 12 - maximum for 720p */ 5162306a36Sopenharmony_ci { 1, 120 }, 5262306a36Sopenharmony_ci}; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/* Limit maximum FPS rates for high resolutions */ 5562306a36Sopenharmony_ci#define IVAL_COUNT_720P 12 /* 720p and up is limited to 60 fps */ 5662306a36Sopenharmony_ci#define IVAL_COUNT_1080P 9 /* 1080p and up is limited to 30 fps */ 5762306a36Sopenharmony_ci#define IVAL_COUNT_2160P 7 /* 2160p and up is limited to 15 fps */ 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic inline unsigned int webcam_ival_count(const struct vivid_dev *dev, 6062306a36Sopenharmony_ci unsigned int frmsize_idx) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci if (webcam_sizes[frmsize_idx].height >= 2160) 6362306a36Sopenharmony_ci return IVAL_COUNT_2160P; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci if (webcam_sizes[frmsize_idx].height >= 1080) 6662306a36Sopenharmony_ci return IVAL_COUNT_1080P; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci if (webcam_sizes[frmsize_idx].height >= 720) 6962306a36Sopenharmony_ci return IVAL_COUNT_720P; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci /* For low resolutions, allow all FPS rates */ 7262306a36Sopenharmony_ci return ARRAY_SIZE(webcam_intervals); 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic int vid_cap_queue_setup(struct vb2_queue *vq, 7662306a36Sopenharmony_ci unsigned *nbuffers, unsigned *nplanes, 7762306a36Sopenharmony_ci unsigned sizes[], struct device *alloc_devs[]) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci struct vivid_dev *dev = vb2_get_drv_priv(vq); 8062306a36Sopenharmony_ci unsigned buffers = tpg_g_buffers(&dev->tpg); 8162306a36Sopenharmony_ci unsigned h = dev->fmt_cap_rect.height; 8262306a36Sopenharmony_ci unsigned p; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci if (dev->field_cap == V4L2_FIELD_ALTERNATE) { 8562306a36Sopenharmony_ci /* 8662306a36Sopenharmony_ci * You cannot use read() with FIELD_ALTERNATE since the field 8762306a36Sopenharmony_ci * information (TOP/BOTTOM) cannot be passed back to the user. 8862306a36Sopenharmony_ci */ 8962306a36Sopenharmony_ci if (vb2_fileio_is_active(vq)) 9062306a36Sopenharmony_ci return -EINVAL; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (dev->queue_setup_error) { 9462306a36Sopenharmony_ci /* 9562306a36Sopenharmony_ci * Error injection: test what happens if queue_setup() returns 9662306a36Sopenharmony_ci * an error. 9762306a36Sopenharmony_ci */ 9862306a36Sopenharmony_ci dev->queue_setup_error = false; 9962306a36Sopenharmony_ci return -EINVAL; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci if (*nplanes) { 10262306a36Sopenharmony_ci /* 10362306a36Sopenharmony_ci * Check if the number of requested planes match 10462306a36Sopenharmony_ci * the number of buffers in the current format. You can't mix that. 10562306a36Sopenharmony_ci */ 10662306a36Sopenharmony_ci if (*nplanes != buffers) 10762306a36Sopenharmony_ci return -EINVAL; 10862306a36Sopenharmony_ci for (p = 0; p < buffers; p++) { 10962306a36Sopenharmony_ci if (sizes[p] < tpg_g_line_width(&dev->tpg, p) * h + 11062306a36Sopenharmony_ci dev->fmt_cap->data_offset[p]) 11162306a36Sopenharmony_ci return -EINVAL; 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci } else { 11462306a36Sopenharmony_ci for (p = 0; p < buffers; p++) 11562306a36Sopenharmony_ci sizes[p] = (tpg_g_line_width(&dev->tpg, p) * h) / 11662306a36Sopenharmony_ci dev->fmt_cap->vdownsampling[p] + 11762306a36Sopenharmony_ci dev->fmt_cap->data_offset[p]; 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci if (vq->num_buffers + *nbuffers < 2) 12162306a36Sopenharmony_ci *nbuffers = 2 - vq->num_buffers; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci *nplanes = buffers; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci dprintk(dev, 1, "%s: count=%d\n", __func__, *nbuffers); 12662306a36Sopenharmony_ci for (p = 0; p < buffers; p++) 12762306a36Sopenharmony_ci dprintk(dev, 1, "%s: size[%u]=%u\n", __func__, p, sizes[p]); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci return 0; 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic int vid_cap_buf_prepare(struct vb2_buffer *vb) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); 13562306a36Sopenharmony_ci unsigned long size; 13662306a36Sopenharmony_ci unsigned buffers = tpg_g_buffers(&dev->tpg); 13762306a36Sopenharmony_ci unsigned p; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci dprintk(dev, 1, "%s\n", __func__); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci if (WARN_ON(NULL == dev->fmt_cap)) 14262306a36Sopenharmony_ci return -EINVAL; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci if (dev->buf_prepare_error) { 14562306a36Sopenharmony_ci /* 14662306a36Sopenharmony_ci * Error injection: test what happens if buf_prepare() returns 14762306a36Sopenharmony_ci * an error. 14862306a36Sopenharmony_ci */ 14962306a36Sopenharmony_ci dev->buf_prepare_error = false; 15062306a36Sopenharmony_ci return -EINVAL; 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci for (p = 0; p < buffers; p++) { 15362306a36Sopenharmony_ci size = (tpg_g_line_width(&dev->tpg, p) * 15462306a36Sopenharmony_ci dev->fmt_cap_rect.height) / 15562306a36Sopenharmony_ci dev->fmt_cap->vdownsampling[p] + 15662306a36Sopenharmony_ci dev->fmt_cap->data_offset[p]; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (vb2_plane_size(vb, p) < size) { 15962306a36Sopenharmony_ci dprintk(dev, 1, "%s data will not fit into plane %u (%lu < %lu)\n", 16062306a36Sopenharmony_ci __func__, p, vb2_plane_size(vb, p), size); 16162306a36Sopenharmony_ci return -EINVAL; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci vb2_set_plane_payload(vb, p, size); 16562306a36Sopenharmony_ci vb->planes[p].data_offset = dev->fmt_cap->data_offset[p]; 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci return 0; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic void vid_cap_buf_finish(struct vb2_buffer *vb) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 17462306a36Sopenharmony_ci struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); 17562306a36Sopenharmony_ci struct v4l2_timecode *tc = &vbuf->timecode; 17662306a36Sopenharmony_ci unsigned fps = 25; 17762306a36Sopenharmony_ci unsigned seq = vbuf->sequence; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (!vivid_is_sdtv_cap(dev)) 18062306a36Sopenharmony_ci return; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci /* 18362306a36Sopenharmony_ci * Set the timecode. Rarely used, so it is interesting to 18462306a36Sopenharmony_ci * test this. 18562306a36Sopenharmony_ci */ 18662306a36Sopenharmony_ci vbuf->flags |= V4L2_BUF_FLAG_TIMECODE; 18762306a36Sopenharmony_ci if (dev->std_cap[dev->input] & V4L2_STD_525_60) 18862306a36Sopenharmony_ci fps = 30; 18962306a36Sopenharmony_ci tc->type = (fps == 30) ? V4L2_TC_TYPE_30FPS : V4L2_TC_TYPE_25FPS; 19062306a36Sopenharmony_ci tc->flags = 0; 19162306a36Sopenharmony_ci tc->frames = seq % fps; 19262306a36Sopenharmony_ci tc->seconds = (seq / fps) % 60; 19362306a36Sopenharmony_ci tc->minutes = (seq / (60 * fps)) % 60; 19462306a36Sopenharmony_ci tc->hours = (seq / (60 * 60 * fps)) % 24; 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic void vid_cap_buf_queue(struct vb2_buffer *vb) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 20062306a36Sopenharmony_ci struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); 20162306a36Sopenharmony_ci struct vivid_buffer *buf = container_of(vbuf, struct vivid_buffer, vb); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci dprintk(dev, 1, "%s\n", __func__); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci spin_lock(&dev->slock); 20662306a36Sopenharmony_ci list_add_tail(&buf->list, &dev->vid_cap_active); 20762306a36Sopenharmony_ci spin_unlock(&dev->slock); 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic int vid_cap_start_streaming(struct vb2_queue *vq, unsigned count) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci struct vivid_dev *dev = vb2_get_drv_priv(vq); 21362306a36Sopenharmony_ci unsigned i; 21462306a36Sopenharmony_ci int err; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci if (vb2_is_streaming(&dev->vb_vid_out_q)) 21762306a36Sopenharmony_ci dev->can_loop_video = vivid_vid_can_loop(dev); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci dev->vid_cap_seq_count = 0; 22062306a36Sopenharmony_ci dprintk(dev, 1, "%s\n", __func__); 22162306a36Sopenharmony_ci for (i = 0; i < VIDEO_MAX_FRAME; i++) 22262306a36Sopenharmony_ci dev->must_blank[i] = tpg_g_perc_fill(&dev->tpg) < 100; 22362306a36Sopenharmony_ci if (dev->start_streaming_error) { 22462306a36Sopenharmony_ci dev->start_streaming_error = false; 22562306a36Sopenharmony_ci err = -EINVAL; 22662306a36Sopenharmony_ci } else { 22762306a36Sopenharmony_ci err = vivid_start_generating_vid_cap(dev, &dev->vid_cap_streaming); 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci if (err) { 23062306a36Sopenharmony_ci struct vivid_buffer *buf, *tmp; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci list_for_each_entry_safe(buf, tmp, &dev->vid_cap_active, list) { 23362306a36Sopenharmony_ci list_del(&buf->list); 23462306a36Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, 23562306a36Sopenharmony_ci VB2_BUF_STATE_QUEUED); 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci return err; 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci/* abort streaming and wait for last buffer */ 24262306a36Sopenharmony_cistatic void vid_cap_stop_streaming(struct vb2_queue *vq) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci struct vivid_dev *dev = vb2_get_drv_priv(vq); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci dprintk(dev, 1, "%s\n", __func__); 24762306a36Sopenharmony_ci vivid_stop_generating_vid_cap(dev, &dev->vid_cap_streaming); 24862306a36Sopenharmony_ci dev->can_loop_video = false; 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic void vid_cap_buf_request_complete(struct vb2_buffer *vb) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci v4l2_ctrl_request_complete(vb->req_obj.req, &dev->ctrl_hdl_vid_cap); 25662306a36Sopenharmony_ci} 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ciconst struct vb2_ops vivid_vid_cap_qops = { 25962306a36Sopenharmony_ci .queue_setup = vid_cap_queue_setup, 26062306a36Sopenharmony_ci .buf_prepare = vid_cap_buf_prepare, 26162306a36Sopenharmony_ci .buf_finish = vid_cap_buf_finish, 26262306a36Sopenharmony_ci .buf_queue = vid_cap_buf_queue, 26362306a36Sopenharmony_ci .start_streaming = vid_cap_start_streaming, 26462306a36Sopenharmony_ci .stop_streaming = vid_cap_stop_streaming, 26562306a36Sopenharmony_ci .buf_request_complete = vid_cap_buf_request_complete, 26662306a36Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 26762306a36Sopenharmony_ci .wait_finish = vb2_ops_wait_finish, 26862306a36Sopenharmony_ci}; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci/* 27162306a36Sopenharmony_ci * Determine the 'picture' quality based on the current TV frequency: either 27262306a36Sopenharmony_ci * COLOR for a good 'signal', GRAY (grayscale picture) for a slightly off 27362306a36Sopenharmony_ci * signal or NOISE for no signal. 27462306a36Sopenharmony_ci */ 27562306a36Sopenharmony_civoid vivid_update_quality(struct vivid_dev *dev) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci unsigned freq_modulus; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if (dev->loop_video && (vivid_is_svid_cap(dev) || vivid_is_hdmi_cap(dev))) { 28062306a36Sopenharmony_ci /* 28162306a36Sopenharmony_ci * The 'noise' will only be replaced by the actual video 28262306a36Sopenharmony_ci * if the output video matches the input video settings. 28362306a36Sopenharmony_ci */ 28462306a36Sopenharmony_ci tpg_s_quality(&dev->tpg, TPG_QUAL_NOISE, 0); 28562306a36Sopenharmony_ci return; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci if (vivid_is_hdmi_cap(dev) && 28862306a36Sopenharmony_ci VIVID_INVALID_SIGNAL(dev->dv_timings_signal_mode[dev->input])) { 28962306a36Sopenharmony_ci tpg_s_quality(&dev->tpg, TPG_QUAL_NOISE, 0); 29062306a36Sopenharmony_ci return; 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci if (vivid_is_sdtv_cap(dev) && 29362306a36Sopenharmony_ci VIVID_INVALID_SIGNAL(dev->std_signal_mode[dev->input])) { 29462306a36Sopenharmony_ci tpg_s_quality(&dev->tpg, TPG_QUAL_NOISE, 0); 29562306a36Sopenharmony_ci return; 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci if (!vivid_is_tv_cap(dev)) { 29862306a36Sopenharmony_ci tpg_s_quality(&dev->tpg, TPG_QUAL_COLOR, 0); 29962306a36Sopenharmony_ci return; 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci /* 30362306a36Sopenharmony_ci * There is a fake channel every 6 MHz at 49.25, 55.25, etc. 30462306a36Sopenharmony_ci * From +/- 0.25 MHz around the channel there is color, and from 30562306a36Sopenharmony_ci * +/- 1 MHz there is grayscale (chroma is lost). 30662306a36Sopenharmony_ci * Everywhere else it is just noise. 30762306a36Sopenharmony_ci */ 30862306a36Sopenharmony_ci freq_modulus = (dev->tv_freq - 676 /* (43.25-1) * 16 */) % (6 * 16); 30962306a36Sopenharmony_ci if (freq_modulus > 2 * 16) { 31062306a36Sopenharmony_ci tpg_s_quality(&dev->tpg, TPG_QUAL_NOISE, 31162306a36Sopenharmony_ci next_pseudo_random32(dev->tv_freq ^ 0x55) & 0x3f); 31262306a36Sopenharmony_ci return; 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci if (freq_modulus < 12 /*0.75 * 16*/ || freq_modulus > 20 /*1.25 * 16*/) 31562306a36Sopenharmony_ci tpg_s_quality(&dev->tpg, TPG_QUAL_GRAY, 0); 31662306a36Sopenharmony_ci else 31762306a36Sopenharmony_ci tpg_s_quality(&dev->tpg, TPG_QUAL_COLOR, 0); 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci/* 32162306a36Sopenharmony_ci * Get the current picture quality and the associated afc value. 32262306a36Sopenharmony_ci */ 32362306a36Sopenharmony_cistatic enum tpg_quality vivid_get_quality(struct vivid_dev *dev, s32 *afc) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci unsigned freq_modulus; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci if (afc) 32862306a36Sopenharmony_ci *afc = 0; 32962306a36Sopenharmony_ci if (tpg_g_quality(&dev->tpg) == TPG_QUAL_COLOR || 33062306a36Sopenharmony_ci tpg_g_quality(&dev->tpg) == TPG_QUAL_NOISE) 33162306a36Sopenharmony_ci return tpg_g_quality(&dev->tpg); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci /* 33462306a36Sopenharmony_ci * There is a fake channel every 6 MHz at 49.25, 55.25, etc. 33562306a36Sopenharmony_ci * From +/- 0.25 MHz around the channel there is color, and from 33662306a36Sopenharmony_ci * +/- 1 MHz there is grayscale (chroma is lost). 33762306a36Sopenharmony_ci * Everywhere else it is just gray. 33862306a36Sopenharmony_ci */ 33962306a36Sopenharmony_ci freq_modulus = (dev->tv_freq - 676 /* (43.25-1) * 16 */) % (6 * 16); 34062306a36Sopenharmony_ci if (afc) 34162306a36Sopenharmony_ci *afc = freq_modulus - 1 * 16; 34262306a36Sopenharmony_ci return TPG_QUAL_GRAY; 34362306a36Sopenharmony_ci} 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_cienum tpg_video_aspect vivid_get_video_aspect(const struct vivid_dev *dev) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci if (vivid_is_sdtv_cap(dev)) 34862306a36Sopenharmony_ci return dev->std_aspect_ratio[dev->input]; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci if (vivid_is_hdmi_cap(dev)) 35162306a36Sopenharmony_ci return dev->dv_timings_aspect_ratio[dev->input]; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci return TPG_VIDEO_ASPECT_IMAGE; 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic enum tpg_pixel_aspect vivid_get_pixel_aspect(const struct vivid_dev *dev) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci if (vivid_is_sdtv_cap(dev)) 35962306a36Sopenharmony_ci return (dev->std_cap[dev->input] & V4L2_STD_525_60) ? 36062306a36Sopenharmony_ci TPG_PIXEL_ASPECT_NTSC : TPG_PIXEL_ASPECT_PAL; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci if (vivid_is_hdmi_cap(dev) && 36362306a36Sopenharmony_ci dev->src_rect.width == 720 && dev->src_rect.height <= 576) 36462306a36Sopenharmony_ci return dev->src_rect.height == 480 ? 36562306a36Sopenharmony_ci TPG_PIXEL_ASPECT_NTSC : TPG_PIXEL_ASPECT_PAL; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci return TPG_PIXEL_ASPECT_SQUARE; 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci/* 37162306a36Sopenharmony_ci * Called whenever the format has to be reset which can occur when 37262306a36Sopenharmony_ci * changing inputs, standard, timings, etc. 37362306a36Sopenharmony_ci */ 37462306a36Sopenharmony_civoid vivid_update_format_cap(struct vivid_dev *dev, bool keep_controls) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci struct v4l2_bt_timings *bt = &dev->dv_timings_cap[dev->input].bt; 37762306a36Sopenharmony_ci u32 dims[V4L2_CTRL_MAX_DIMS] = {}; 37862306a36Sopenharmony_ci unsigned size; 37962306a36Sopenharmony_ci u64 pixelclock; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci switch (dev->input_type[dev->input]) { 38262306a36Sopenharmony_ci case WEBCAM: 38362306a36Sopenharmony_ci default: 38462306a36Sopenharmony_ci dev->src_rect.width = webcam_sizes[dev->webcam_size_idx].width; 38562306a36Sopenharmony_ci dev->src_rect.height = webcam_sizes[dev->webcam_size_idx].height; 38662306a36Sopenharmony_ci dev->timeperframe_vid_cap = webcam_intervals[dev->webcam_ival_idx]; 38762306a36Sopenharmony_ci dev->field_cap = V4L2_FIELD_NONE; 38862306a36Sopenharmony_ci tpg_s_rgb_range(&dev->tpg, V4L2_DV_RGB_RANGE_AUTO); 38962306a36Sopenharmony_ci break; 39062306a36Sopenharmony_ci case TV: 39162306a36Sopenharmony_ci case SVID: 39262306a36Sopenharmony_ci dev->field_cap = dev->tv_field_cap; 39362306a36Sopenharmony_ci dev->src_rect.width = 720; 39462306a36Sopenharmony_ci if (dev->std_cap[dev->input] & V4L2_STD_525_60) { 39562306a36Sopenharmony_ci dev->src_rect.height = 480; 39662306a36Sopenharmony_ci dev->timeperframe_vid_cap = (struct v4l2_fract) { 1001, 30000 }; 39762306a36Sopenharmony_ci dev->service_set_cap = V4L2_SLICED_CAPTION_525; 39862306a36Sopenharmony_ci } else { 39962306a36Sopenharmony_ci dev->src_rect.height = 576; 40062306a36Sopenharmony_ci dev->timeperframe_vid_cap = (struct v4l2_fract) { 1000, 25000 }; 40162306a36Sopenharmony_ci dev->service_set_cap = V4L2_SLICED_WSS_625 | V4L2_SLICED_TELETEXT_B; 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci tpg_s_rgb_range(&dev->tpg, V4L2_DV_RGB_RANGE_AUTO); 40462306a36Sopenharmony_ci break; 40562306a36Sopenharmony_ci case HDMI: 40662306a36Sopenharmony_ci dev->src_rect.width = bt->width; 40762306a36Sopenharmony_ci dev->src_rect.height = bt->height; 40862306a36Sopenharmony_ci size = V4L2_DV_BT_FRAME_WIDTH(bt) * V4L2_DV_BT_FRAME_HEIGHT(bt); 40962306a36Sopenharmony_ci if (dev->reduced_fps && can_reduce_fps(bt)) { 41062306a36Sopenharmony_ci pixelclock = div_u64(bt->pixelclock * 1000, 1001); 41162306a36Sopenharmony_ci bt->flags |= V4L2_DV_FL_REDUCED_FPS; 41262306a36Sopenharmony_ci } else { 41362306a36Sopenharmony_ci pixelclock = bt->pixelclock; 41462306a36Sopenharmony_ci bt->flags &= ~V4L2_DV_FL_REDUCED_FPS; 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci dev->timeperframe_vid_cap = (struct v4l2_fract) { 41762306a36Sopenharmony_ci size / 100, (u32)pixelclock / 100 41862306a36Sopenharmony_ci }; 41962306a36Sopenharmony_ci if (bt->interlaced) 42062306a36Sopenharmony_ci dev->field_cap = V4L2_FIELD_ALTERNATE; 42162306a36Sopenharmony_ci else 42262306a36Sopenharmony_ci dev->field_cap = V4L2_FIELD_NONE; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci /* 42562306a36Sopenharmony_ci * We can be called from within s_ctrl, in that case we can't 42662306a36Sopenharmony_ci * set/get controls. Luckily we don't need to in that case. 42762306a36Sopenharmony_ci */ 42862306a36Sopenharmony_ci if (keep_controls || !dev->colorspace) 42962306a36Sopenharmony_ci break; 43062306a36Sopenharmony_ci if (bt->flags & V4L2_DV_FL_IS_CE_VIDEO) { 43162306a36Sopenharmony_ci if (bt->width == 720 && bt->height <= 576) 43262306a36Sopenharmony_ci v4l2_ctrl_s_ctrl(dev->colorspace, VIVID_CS_170M); 43362306a36Sopenharmony_ci else 43462306a36Sopenharmony_ci v4l2_ctrl_s_ctrl(dev->colorspace, VIVID_CS_709); 43562306a36Sopenharmony_ci v4l2_ctrl_s_ctrl(dev->real_rgb_range_cap, 1); 43662306a36Sopenharmony_ci } else { 43762306a36Sopenharmony_ci v4l2_ctrl_s_ctrl(dev->colorspace, VIVID_CS_SRGB); 43862306a36Sopenharmony_ci v4l2_ctrl_s_ctrl(dev->real_rgb_range_cap, 0); 43962306a36Sopenharmony_ci } 44062306a36Sopenharmony_ci tpg_s_rgb_range(&dev->tpg, v4l2_ctrl_g_ctrl(dev->rgb_range_cap)); 44162306a36Sopenharmony_ci break; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci vivid_update_quality(dev); 44462306a36Sopenharmony_ci tpg_reset_source(&dev->tpg, dev->src_rect.width, dev->src_rect.height, dev->field_cap); 44562306a36Sopenharmony_ci dev->crop_cap = dev->src_rect; 44662306a36Sopenharmony_ci dev->crop_bounds_cap = dev->src_rect; 44762306a36Sopenharmony_ci dev->compose_cap = dev->crop_cap; 44862306a36Sopenharmony_ci if (V4L2_FIELD_HAS_T_OR_B(dev->field_cap)) 44962306a36Sopenharmony_ci dev->compose_cap.height /= 2; 45062306a36Sopenharmony_ci dev->fmt_cap_rect = dev->compose_cap; 45162306a36Sopenharmony_ci tpg_s_video_aspect(&dev->tpg, vivid_get_video_aspect(dev)); 45262306a36Sopenharmony_ci tpg_s_pixel_aspect(&dev->tpg, vivid_get_pixel_aspect(dev)); 45362306a36Sopenharmony_ci tpg_update_mv_step(&dev->tpg); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci /* 45662306a36Sopenharmony_ci * We can be called from within s_ctrl, in that case we can't 45762306a36Sopenharmony_ci * modify controls. Luckily we don't need to in that case. 45862306a36Sopenharmony_ci */ 45962306a36Sopenharmony_ci if (keep_controls) 46062306a36Sopenharmony_ci return; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci dims[0] = roundup(dev->src_rect.width, PIXEL_ARRAY_DIV); 46362306a36Sopenharmony_ci dims[1] = roundup(dev->src_rect.height, PIXEL_ARRAY_DIV); 46462306a36Sopenharmony_ci v4l2_ctrl_modify_dimensions(dev->pixel_array, dims); 46562306a36Sopenharmony_ci} 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci/* Map the field to something that is valid for the current input */ 46862306a36Sopenharmony_cistatic enum v4l2_field vivid_field_cap(struct vivid_dev *dev, enum v4l2_field field) 46962306a36Sopenharmony_ci{ 47062306a36Sopenharmony_ci if (vivid_is_sdtv_cap(dev)) { 47162306a36Sopenharmony_ci switch (field) { 47262306a36Sopenharmony_ci case V4L2_FIELD_INTERLACED_TB: 47362306a36Sopenharmony_ci case V4L2_FIELD_INTERLACED_BT: 47462306a36Sopenharmony_ci case V4L2_FIELD_SEQ_TB: 47562306a36Sopenharmony_ci case V4L2_FIELD_SEQ_BT: 47662306a36Sopenharmony_ci case V4L2_FIELD_TOP: 47762306a36Sopenharmony_ci case V4L2_FIELD_BOTTOM: 47862306a36Sopenharmony_ci case V4L2_FIELD_ALTERNATE: 47962306a36Sopenharmony_ci return field; 48062306a36Sopenharmony_ci case V4L2_FIELD_INTERLACED: 48162306a36Sopenharmony_ci default: 48262306a36Sopenharmony_ci return V4L2_FIELD_INTERLACED; 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci if (vivid_is_hdmi_cap(dev)) 48662306a36Sopenharmony_ci return dev->dv_timings_cap[dev->input].bt.interlaced ? 48762306a36Sopenharmony_ci V4L2_FIELD_ALTERNATE : V4L2_FIELD_NONE; 48862306a36Sopenharmony_ci return V4L2_FIELD_NONE; 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_cistatic unsigned vivid_colorspace_cap(struct vivid_dev *dev) 49262306a36Sopenharmony_ci{ 49362306a36Sopenharmony_ci if (!dev->loop_video || vivid_is_webcam(dev) || vivid_is_tv_cap(dev)) 49462306a36Sopenharmony_ci return tpg_g_colorspace(&dev->tpg); 49562306a36Sopenharmony_ci return dev->colorspace_out; 49662306a36Sopenharmony_ci} 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_cistatic unsigned vivid_xfer_func_cap(struct vivid_dev *dev) 49962306a36Sopenharmony_ci{ 50062306a36Sopenharmony_ci if (!dev->loop_video || vivid_is_webcam(dev) || vivid_is_tv_cap(dev)) 50162306a36Sopenharmony_ci return tpg_g_xfer_func(&dev->tpg); 50262306a36Sopenharmony_ci return dev->xfer_func_out; 50362306a36Sopenharmony_ci} 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_cistatic unsigned vivid_ycbcr_enc_cap(struct vivid_dev *dev) 50662306a36Sopenharmony_ci{ 50762306a36Sopenharmony_ci if (!dev->loop_video || vivid_is_webcam(dev) || vivid_is_tv_cap(dev)) 50862306a36Sopenharmony_ci return tpg_g_ycbcr_enc(&dev->tpg); 50962306a36Sopenharmony_ci return dev->ycbcr_enc_out; 51062306a36Sopenharmony_ci} 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_cistatic unsigned int vivid_hsv_enc_cap(struct vivid_dev *dev) 51362306a36Sopenharmony_ci{ 51462306a36Sopenharmony_ci if (!dev->loop_video || vivid_is_webcam(dev) || vivid_is_tv_cap(dev)) 51562306a36Sopenharmony_ci return tpg_g_hsv_enc(&dev->tpg); 51662306a36Sopenharmony_ci return dev->hsv_enc_out; 51762306a36Sopenharmony_ci} 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_cistatic unsigned vivid_quantization_cap(struct vivid_dev *dev) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci if (!dev->loop_video || vivid_is_webcam(dev) || vivid_is_tv_cap(dev)) 52262306a36Sopenharmony_ci return tpg_g_quantization(&dev->tpg); 52362306a36Sopenharmony_ci return dev->quantization_out; 52462306a36Sopenharmony_ci} 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ciint vivid_g_fmt_vid_cap(struct file *file, void *priv, 52762306a36Sopenharmony_ci struct v4l2_format *f) 52862306a36Sopenharmony_ci{ 52962306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 53062306a36Sopenharmony_ci struct v4l2_pix_format_mplane *mp = &f->fmt.pix_mp; 53162306a36Sopenharmony_ci unsigned p; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci mp->width = dev->fmt_cap_rect.width; 53462306a36Sopenharmony_ci mp->height = dev->fmt_cap_rect.height; 53562306a36Sopenharmony_ci mp->field = dev->field_cap; 53662306a36Sopenharmony_ci mp->pixelformat = dev->fmt_cap->fourcc; 53762306a36Sopenharmony_ci mp->colorspace = vivid_colorspace_cap(dev); 53862306a36Sopenharmony_ci mp->xfer_func = vivid_xfer_func_cap(dev); 53962306a36Sopenharmony_ci if (dev->fmt_cap->color_enc == TGP_COLOR_ENC_HSV) 54062306a36Sopenharmony_ci mp->hsv_enc = vivid_hsv_enc_cap(dev); 54162306a36Sopenharmony_ci else 54262306a36Sopenharmony_ci mp->ycbcr_enc = vivid_ycbcr_enc_cap(dev); 54362306a36Sopenharmony_ci mp->quantization = vivid_quantization_cap(dev); 54462306a36Sopenharmony_ci mp->num_planes = dev->fmt_cap->buffers; 54562306a36Sopenharmony_ci for (p = 0; p < mp->num_planes; p++) { 54662306a36Sopenharmony_ci mp->plane_fmt[p].bytesperline = tpg_g_bytesperline(&dev->tpg, p); 54762306a36Sopenharmony_ci mp->plane_fmt[p].sizeimage = 54862306a36Sopenharmony_ci (tpg_g_line_width(&dev->tpg, p) * mp->height) / 54962306a36Sopenharmony_ci dev->fmt_cap->vdownsampling[p] + 55062306a36Sopenharmony_ci dev->fmt_cap->data_offset[p]; 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci return 0; 55362306a36Sopenharmony_ci} 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ciint vivid_try_fmt_vid_cap(struct file *file, void *priv, 55662306a36Sopenharmony_ci struct v4l2_format *f) 55762306a36Sopenharmony_ci{ 55862306a36Sopenharmony_ci struct v4l2_pix_format_mplane *mp = &f->fmt.pix_mp; 55962306a36Sopenharmony_ci struct v4l2_plane_pix_format *pfmt = mp->plane_fmt; 56062306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 56162306a36Sopenharmony_ci const struct vivid_fmt *fmt; 56262306a36Sopenharmony_ci unsigned bytesperline, max_bpl; 56362306a36Sopenharmony_ci unsigned factor = 1; 56462306a36Sopenharmony_ci unsigned w, h; 56562306a36Sopenharmony_ci unsigned p; 56662306a36Sopenharmony_ci bool user_set_csc = !!(mp->flags & V4L2_PIX_FMT_FLAG_SET_CSC); 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci fmt = vivid_get_format(dev, mp->pixelformat); 56962306a36Sopenharmony_ci if (!fmt) { 57062306a36Sopenharmony_ci dprintk(dev, 1, "Fourcc format (0x%08x) unknown.\n", 57162306a36Sopenharmony_ci mp->pixelformat); 57262306a36Sopenharmony_ci mp->pixelformat = V4L2_PIX_FMT_YUYV; 57362306a36Sopenharmony_ci fmt = vivid_get_format(dev, mp->pixelformat); 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci mp->field = vivid_field_cap(dev, mp->field); 57762306a36Sopenharmony_ci if (vivid_is_webcam(dev)) { 57862306a36Sopenharmony_ci const struct v4l2_frmsize_discrete *sz = 57962306a36Sopenharmony_ci v4l2_find_nearest_size(webcam_sizes, 58062306a36Sopenharmony_ci ARRAY_SIZE(webcam_sizes), width, 58162306a36Sopenharmony_ci height, mp->width, mp->height); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci w = sz->width; 58462306a36Sopenharmony_ci h = sz->height; 58562306a36Sopenharmony_ci } else if (vivid_is_sdtv_cap(dev)) { 58662306a36Sopenharmony_ci w = 720; 58762306a36Sopenharmony_ci h = (dev->std_cap[dev->input] & V4L2_STD_525_60) ? 480 : 576; 58862306a36Sopenharmony_ci } else { 58962306a36Sopenharmony_ci w = dev->src_rect.width; 59062306a36Sopenharmony_ci h = dev->src_rect.height; 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci if (V4L2_FIELD_HAS_T_OR_B(mp->field)) 59362306a36Sopenharmony_ci factor = 2; 59462306a36Sopenharmony_ci if (vivid_is_webcam(dev) || 59562306a36Sopenharmony_ci (!dev->has_scaler_cap && !dev->has_crop_cap && !dev->has_compose_cap)) { 59662306a36Sopenharmony_ci mp->width = w; 59762306a36Sopenharmony_ci mp->height = h / factor; 59862306a36Sopenharmony_ci } else { 59962306a36Sopenharmony_ci struct v4l2_rect r = { 0, 0, mp->width, mp->height * factor }; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci v4l2_rect_set_min_size(&r, &vivid_min_rect); 60262306a36Sopenharmony_ci v4l2_rect_set_max_size(&r, &vivid_max_rect); 60362306a36Sopenharmony_ci if (dev->has_scaler_cap && !dev->has_compose_cap) { 60462306a36Sopenharmony_ci struct v4l2_rect max_r = { 0, 0, MAX_ZOOM * w, MAX_ZOOM * h }; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci v4l2_rect_set_max_size(&r, &max_r); 60762306a36Sopenharmony_ci } else if (!dev->has_scaler_cap && dev->has_crop_cap && !dev->has_compose_cap) { 60862306a36Sopenharmony_ci v4l2_rect_set_max_size(&r, &dev->src_rect); 60962306a36Sopenharmony_ci } else if (!dev->has_scaler_cap && !dev->has_crop_cap) { 61062306a36Sopenharmony_ci v4l2_rect_set_min_size(&r, &dev->src_rect); 61162306a36Sopenharmony_ci } 61262306a36Sopenharmony_ci mp->width = r.width; 61362306a36Sopenharmony_ci mp->height = r.height / factor; 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci /* This driver supports custom bytesperline values */ 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci mp->num_planes = fmt->buffers; 61962306a36Sopenharmony_ci for (p = 0; p < fmt->buffers; p++) { 62062306a36Sopenharmony_ci /* Calculate the minimum supported bytesperline value */ 62162306a36Sopenharmony_ci bytesperline = (mp->width * fmt->bit_depth[p]) >> 3; 62262306a36Sopenharmony_ci /* Calculate the maximum supported bytesperline value */ 62362306a36Sopenharmony_ci max_bpl = (MAX_ZOOM * MAX_WIDTH * fmt->bit_depth[p]) >> 3; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci if (pfmt[p].bytesperline > max_bpl) 62662306a36Sopenharmony_ci pfmt[p].bytesperline = max_bpl; 62762306a36Sopenharmony_ci if (pfmt[p].bytesperline < bytesperline) 62862306a36Sopenharmony_ci pfmt[p].bytesperline = bytesperline; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci pfmt[p].sizeimage = (pfmt[p].bytesperline * mp->height) / 63162306a36Sopenharmony_ci fmt->vdownsampling[p] + fmt->data_offset[p]; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci memset(pfmt[p].reserved, 0, sizeof(pfmt[p].reserved)); 63462306a36Sopenharmony_ci } 63562306a36Sopenharmony_ci for (p = fmt->buffers; p < fmt->planes; p++) 63662306a36Sopenharmony_ci pfmt[0].sizeimage += (pfmt[0].bytesperline * mp->height * 63762306a36Sopenharmony_ci (fmt->bit_depth[p] / fmt->vdownsampling[p])) / 63862306a36Sopenharmony_ci (fmt->bit_depth[0] / fmt->vdownsampling[0]); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci if (!user_set_csc || !v4l2_is_colorspace_valid(mp->colorspace)) 64162306a36Sopenharmony_ci mp->colorspace = vivid_colorspace_cap(dev); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci if (!user_set_csc || !v4l2_is_xfer_func_valid(mp->xfer_func)) 64462306a36Sopenharmony_ci mp->xfer_func = vivid_xfer_func_cap(dev); 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci if (fmt->color_enc == TGP_COLOR_ENC_HSV) { 64762306a36Sopenharmony_ci if (!user_set_csc || !v4l2_is_hsv_enc_valid(mp->hsv_enc)) 64862306a36Sopenharmony_ci mp->hsv_enc = vivid_hsv_enc_cap(dev); 64962306a36Sopenharmony_ci } else if (fmt->color_enc == TGP_COLOR_ENC_YCBCR) { 65062306a36Sopenharmony_ci if (!user_set_csc || !v4l2_is_ycbcr_enc_valid(mp->ycbcr_enc)) 65162306a36Sopenharmony_ci mp->ycbcr_enc = vivid_ycbcr_enc_cap(dev); 65262306a36Sopenharmony_ci } else { 65362306a36Sopenharmony_ci mp->ycbcr_enc = vivid_ycbcr_enc_cap(dev); 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci if (fmt->color_enc == TGP_COLOR_ENC_YCBCR || 65762306a36Sopenharmony_ci fmt->color_enc == TGP_COLOR_ENC_RGB) { 65862306a36Sopenharmony_ci if (!user_set_csc || !v4l2_is_quant_valid(mp->quantization)) 65962306a36Sopenharmony_ci mp->quantization = vivid_quantization_cap(dev); 66062306a36Sopenharmony_ci } else { 66162306a36Sopenharmony_ci mp->quantization = vivid_quantization_cap(dev); 66262306a36Sopenharmony_ci } 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci memset(mp->reserved, 0, sizeof(mp->reserved)); 66562306a36Sopenharmony_ci return 0; 66662306a36Sopenharmony_ci} 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ciint vivid_s_fmt_vid_cap(struct file *file, void *priv, 66962306a36Sopenharmony_ci struct v4l2_format *f) 67062306a36Sopenharmony_ci{ 67162306a36Sopenharmony_ci struct v4l2_pix_format_mplane *mp = &f->fmt.pix_mp; 67262306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 67362306a36Sopenharmony_ci struct v4l2_rect *crop = &dev->crop_cap; 67462306a36Sopenharmony_ci struct v4l2_rect *compose = &dev->compose_cap; 67562306a36Sopenharmony_ci struct vb2_queue *q = &dev->vb_vid_cap_q; 67662306a36Sopenharmony_ci int ret = vivid_try_fmt_vid_cap(file, priv, f); 67762306a36Sopenharmony_ci unsigned factor = 1; 67862306a36Sopenharmony_ci unsigned p; 67962306a36Sopenharmony_ci unsigned i; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci if (ret < 0) 68262306a36Sopenharmony_ci return ret; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci if (vb2_is_busy(q)) { 68562306a36Sopenharmony_ci dprintk(dev, 1, "%s device busy\n", __func__); 68662306a36Sopenharmony_ci return -EBUSY; 68762306a36Sopenharmony_ci } 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci dev->fmt_cap = vivid_get_format(dev, mp->pixelformat); 69062306a36Sopenharmony_ci if (V4L2_FIELD_HAS_T_OR_B(mp->field)) 69162306a36Sopenharmony_ci factor = 2; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci /* Note: the webcam input doesn't support scaling, cropping or composing */ 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci if (!vivid_is_webcam(dev) && 69662306a36Sopenharmony_ci (dev->has_scaler_cap || dev->has_crop_cap || dev->has_compose_cap)) { 69762306a36Sopenharmony_ci struct v4l2_rect r = { 0, 0, mp->width, mp->height }; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci if (dev->has_scaler_cap) { 70062306a36Sopenharmony_ci if (dev->has_compose_cap) 70162306a36Sopenharmony_ci v4l2_rect_map_inside(compose, &r); 70262306a36Sopenharmony_ci else 70362306a36Sopenharmony_ci *compose = r; 70462306a36Sopenharmony_ci if (dev->has_crop_cap && !dev->has_compose_cap) { 70562306a36Sopenharmony_ci struct v4l2_rect min_r = { 70662306a36Sopenharmony_ci 0, 0, 70762306a36Sopenharmony_ci r.width / MAX_ZOOM, 70862306a36Sopenharmony_ci factor * r.height / MAX_ZOOM 70962306a36Sopenharmony_ci }; 71062306a36Sopenharmony_ci struct v4l2_rect max_r = { 71162306a36Sopenharmony_ci 0, 0, 71262306a36Sopenharmony_ci r.width * MAX_ZOOM, 71362306a36Sopenharmony_ci factor * r.height * MAX_ZOOM 71462306a36Sopenharmony_ci }; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci v4l2_rect_set_min_size(crop, &min_r); 71762306a36Sopenharmony_ci v4l2_rect_set_max_size(crop, &max_r); 71862306a36Sopenharmony_ci v4l2_rect_map_inside(crop, &dev->crop_bounds_cap); 71962306a36Sopenharmony_ci } else if (dev->has_crop_cap) { 72062306a36Sopenharmony_ci struct v4l2_rect min_r = { 72162306a36Sopenharmony_ci 0, 0, 72262306a36Sopenharmony_ci compose->width / MAX_ZOOM, 72362306a36Sopenharmony_ci factor * compose->height / MAX_ZOOM 72462306a36Sopenharmony_ci }; 72562306a36Sopenharmony_ci struct v4l2_rect max_r = { 72662306a36Sopenharmony_ci 0, 0, 72762306a36Sopenharmony_ci compose->width * MAX_ZOOM, 72862306a36Sopenharmony_ci factor * compose->height * MAX_ZOOM 72962306a36Sopenharmony_ci }; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci v4l2_rect_set_min_size(crop, &min_r); 73262306a36Sopenharmony_ci v4l2_rect_set_max_size(crop, &max_r); 73362306a36Sopenharmony_ci v4l2_rect_map_inside(crop, &dev->crop_bounds_cap); 73462306a36Sopenharmony_ci } 73562306a36Sopenharmony_ci } else if (dev->has_crop_cap && !dev->has_compose_cap) { 73662306a36Sopenharmony_ci r.height *= factor; 73762306a36Sopenharmony_ci v4l2_rect_set_size_to(crop, &r); 73862306a36Sopenharmony_ci v4l2_rect_map_inside(crop, &dev->crop_bounds_cap); 73962306a36Sopenharmony_ci r = *crop; 74062306a36Sopenharmony_ci r.height /= factor; 74162306a36Sopenharmony_ci v4l2_rect_set_size_to(compose, &r); 74262306a36Sopenharmony_ci } else if (!dev->has_crop_cap) { 74362306a36Sopenharmony_ci v4l2_rect_map_inside(compose, &r); 74462306a36Sopenharmony_ci } else { 74562306a36Sopenharmony_ci r.height *= factor; 74662306a36Sopenharmony_ci v4l2_rect_set_max_size(crop, &r); 74762306a36Sopenharmony_ci v4l2_rect_map_inside(crop, &dev->crop_bounds_cap); 74862306a36Sopenharmony_ci compose->top *= factor; 74962306a36Sopenharmony_ci compose->height *= factor; 75062306a36Sopenharmony_ci v4l2_rect_set_size_to(compose, crop); 75162306a36Sopenharmony_ci v4l2_rect_map_inside(compose, &r); 75262306a36Sopenharmony_ci compose->top /= factor; 75362306a36Sopenharmony_ci compose->height /= factor; 75462306a36Sopenharmony_ci } 75562306a36Sopenharmony_ci } else if (vivid_is_webcam(dev)) { 75662306a36Sopenharmony_ci unsigned int ival_sz = webcam_ival_count(dev, dev->webcam_size_idx); 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci /* Guaranteed to be a match */ 75962306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(webcam_sizes); i++) 76062306a36Sopenharmony_ci if (webcam_sizes[i].width == mp->width && 76162306a36Sopenharmony_ci webcam_sizes[i].height == mp->height) 76262306a36Sopenharmony_ci break; 76362306a36Sopenharmony_ci dev->webcam_size_idx = i; 76462306a36Sopenharmony_ci if (dev->webcam_ival_idx >= ival_sz) 76562306a36Sopenharmony_ci dev->webcam_ival_idx = ival_sz - 1; 76662306a36Sopenharmony_ci vivid_update_format_cap(dev, false); 76762306a36Sopenharmony_ci } else { 76862306a36Sopenharmony_ci struct v4l2_rect r = { 0, 0, mp->width, mp->height }; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci v4l2_rect_set_size_to(compose, &r); 77162306a36Sopenharmony_ci r.height *= factor; 77262306a36Sopenharmony_ci v4l2_rect_set_size_to(crop, &r); 77362306a36Sopenharmony_ci } 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci dev->fmt_cap_rect.width = mp->width; 77662306a36Sopenharmony_ci dev->fmt_cap_rect.height = mp->height; 77762306a36Sopenharmony_ci tpg_s_buf_height(&dev->tpg, mp->height); 77862306a36Sopenharmony_ci tpg_s_fourcc(&dev->tpg, dev->fmt_cap->fourcc); 77962306a36Sopenharmony_ci for (p = 0; p < tpg_g_buffers(&dev->tpg); p++) 78062306a36Sopenharmony_ci tpg_s_bytesperline(&dev->tpg, p, mp->plane_fmt[p].bytesperline); 78162306a36Sopenharmony_ci dev->field_cap = mp->field; 78262306a36Sopenharmony_ci if (dev->field_cap == V4L2_FIELD_ALTERNATE) 78362306a36Sopenharmony_ci tpg_s_field(&dev->tpg, V4L2_FIELD_TOP, true); 78462306a36Sopenharmony_ci else 78562306a36Sopenharmony_ci tpg_s_field(&dev->tpg, dev->field_cap, false); 78662306a36Sopenharmony_ci tpg_s_crop_compose(&dev->tpg, &dev->crop_cap, &dev->compose_cap); 78762306a36Sopenharmony_ci if (vivid_is_sdtv_cap(dev)) 78862306a36Sopenharmony_ci dev->tv_field_cap = mp->field; 78962306a36Sopenharmony_ci tpg_update_mv_step(&dev->tpg); 79062306a36Sopenharmony_ci dev->tpg.colorspace = mp->colorspace; 79162306a36Sopenharmony_ci dev->tpg.xfer_func = mp->xfer_func; 79262306a36Sopenharmony_ci if (dev->fmt_cap->color_enc == TGP_COLOR_ENC_YCBCR) 79362306a36Sopenharmony_ci dev->tpg.ycbcr_enc = mp->ycbcr_enc; 79462306a36Sopenharmony_ci else 79562306a36Sopenharmony_ci dev->tpg.hsv_enc = mp->hsv_enc; 79662306a36Sopenharmony_ci dev->tpg.quantization = mp->quantization; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci return 0; 79962306a36Sopenharmony_ci} 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ciint vidioc_g_fmt_vid_cap_mplane(struct file *file, void *priv, 80262306a36Sopenharmony_ci struct v4l2_format *f) 80362306a36Sopenharmony_ci{ 80462306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci if (!dev->multiplanar) 80762306a36Sopenharmony_ci return -ENOTTY; 80862306a36Sopenharmony_ci return vivid_g_fmt_vid_cap(file, priv, f); 80962306a36Sopenharmony_ci} 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ciint vidioc_try_fmt_vid_cap_mplane(struct file *file, void *priv, 81262306a36Sopenharmony_ci struct v4l2_format *f) 81362306a36Sopenharmony_ci{ 81462306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci if (!dev->multiplanar) 81762306a36Sopenharmony_ci return -ENOTTY; 81862306a36Sopenharmony_ci return vivid_try_fmt_vid_cap(file, priv, f); 81962306a36Sopenharmony_ci} 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ciint vidioc_s_fmt_vid_cap_mplane(struct file *file, void *priv, 82262306a36Sopenharmony_ci struct v4l2_format *f) 82362306a36Sopenharmony_ci{ 82462306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci if (!dev->multiplanar) 82762306a36Sopenharmony_ci return -ENOTTY; 82862306a36Sopenharmony_ci return vivid_s_fmt_vid_cap(file, priv, f); 82962306a36Sopenharmony_ci} 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ciint vidioc_g_fmt_vid_cap(struct file *file, void *priv, 83262306a36Sopenharmony_ci struct v4l2_format *f) 83362306a36Sopenharmony_ci{ 83462306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci if (dev->multiplanar) 83762306a36Sopenharmony_ci return -ENOTTY; 83862306a36Sopenharmony_ci return fmt_sp2mp_func(file, priv, f, vivid_g_fmt_vid_cap); 83962306a36Sopenharmony_ci} 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ciint vidioc_try_fmt_vid_cap(struct file *file, void *priv, 84262306a36Sopenharmony_ci struct v4l2_format *f) 84362306a36Sopenharmony_ci{ 84462306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci if (dev->multiplanar) 84762306a36Sopenharmony_ci return -ENOTTY; 84862306a36Sopenharmony_ci return fmt_sp2mp_func(file, priv, f, vivid_try_fmt_vid_cap); 84962306a36Sopenharmony_ci} 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ciint vidioc_s_fmt_vid_cap(struct file *file, void *priv, 85262306a36Sopenharmony_ci struct v4l2_format *f) 85362306a36Sopenharmony_ci{ 85462306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci if (dev->multiplanar) 85762306a36Sopenharmony_ci return -ENOTTY; 85862306a36Sopenharmony_ci return fmt_sp2mp_func(file, priv, f, vivid_s_fmt_vid_cap); 85962306a36Sopenharmony_ci} 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ciint vivid_vid_cap_g_selection(struct file *file, void *priv, 86262306a36Sopenharmony_ci struct v4l2_selection *sel) 86362306a36Sopenharmony_ci{ 86462306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci if (!dev->has_crop_cap && !dev->has_compose_cap) 86762306a36Sopenharmony_ci return -ENOTTY; 86862306a36Sopenharmony_ci if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 86962306a36Sopenharmony_ci return -EINVAL; 87062306a36Sopenharmony_ci if (vivid_is_webcam(dev)) 87162306a36Sopenharmony_ci return -ENODATA; 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci sel->r.left = sel->r.top = 0; 87462306a36Sopenharmony_ci switch (sel->target) { 87562306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP: 87662306a36Sopenharmony_ci if (!dev->has_crop_cap) 87762306a36Sopenharmony_ci return -EINVAL; 87862306a36Sopenharmony_ci sel->r = dev->crop_cap; 87962306a36Sopenharmony_ci break; 88062306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP_DEFAULT: 88162306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP_BOUNDS: 88262306a36Sopenharmony_ci if (!dev->has_crop_cap) 88362306a36Sopenharmony_ci return -EINVAL; 88462306a36Sopenharmony_ci sel->r = dev->src_rect; 88562306a36Sopenharmony_ci break; 88662306a36Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_BOUNDS: 88762306a36Sopenharmony_ci if (!dev->has_compose_cap) 88862306a36Sopenharmony_ci return -EINVAL; 88962306a36Sopenharmony_ci sel->r = vivid_max_rect; 89062306a36Sopenharmony_ci break; 89162306a36Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE: 89262306a36Sopenharmony_ci if (!dev->has_compose_cap) 89362306a36Sopenharmony_ci return -EINVAL; 89462306a36Sopenharmony_ci sel->r = dev->compose_cap; 89562306a36Sopenharmony_ci break; 89662306a36Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_DEFAULT: 89762306a36Sopenharmony_ci if (!dev->has_compose_cap) 89862306a36Sopenharmony_ci return -EINVAL; 89962306a36Sopenharmony_ci sel->r = dev->fmt_cap_rect; 90062306a36Sopenharmony_ci break; 90162306a36Sopenharmony_ci default: 90262306a36Sopenharmony_ci return -EINVAL; 90362306a36Sopenharmony_ci } 90462306a36Sopenharmony_ci return 0; 90562306a36Sopenharmony_ci} 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ciint vivid_vid_cap_s_selection(struct file *file, void *fh, struct v4l2_selection *s) 90862306a36Sopenharmony_ci{ 90962306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 91062306a36Sopenharmony_ci struct v4l2_rect *crop = &dev->crop_cap; 91162306a36Sopenharmony_ci struct v4l2_rect *compose = &dev->compose_cap; 91262306a36Sopenharmony_ci unsigned factor = V4L2_FIELD_HAS_T_OR_B(dev->field_cap) ? 2 : 1; 91362306a36Sopenharmony_ci int ret; 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci if (!dev->has_crop_cap && !dev->has_compose_cap) 91662306a36Sopenharmony_ci return -ENOTTY; 91762306a36Sopenharmony_ci if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 91862306a36Sopenharmony_ci return -EINVAL; 91962306a36Sopenharmony_ci if (vivid_is_webcam(dev)) 92062306a36Sopenharmony_ci return -ENODATA; 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci switch (s->target) { 92362306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP: 92462306a36Sopenharmony_ci if (!dev->has_crop_cap) 92562306a36Sopenharmony_ci return -EINVAL; 92662306a36Sopenharmony_ci ret = vivid_vid_adjust_sel(s->flags, &s->r); 92762306a36Sopenharmony_ci if (ret) 92862306a36Sopenharmony_ci return ret; 92962306a36Sopenharmony_ci v4l2_rect_set_min_size(&s->r, &vivid_min_rect); 93062306a36Sopenharmony_ci v4l2_rect_set_max_size(&s->r, &dev->src_rect); 93162306a36Sopenharmony_ci v4l2_rect_map_inside(&s->r, &dev->crop_bounds_cap); 93262306a36Sopenharmony_ci s->r.top /= factor; 93362306a36Sopenharmony_ci s->r.height /= factor; 93462306a36Sopenharmony_ci if (dev->has_scaler_cap) { 93562306a36Sopenharmony_ci struct v4l2_rect fmt = dev->fmt_cap_rect; 93662306a36Sopenharmony_ci struct v4l2_rect max_rect = { 93762306a36Sopenharmony_ci 0, 0, 93862306a36Sopenharmony_ci s->r.width * MAX_ZOOM, 93962306a36Sopenharmony_ci s->r.height * MAX_ZOOM 94062306a36Sopenharmony_ci }; 94162306a36Sopenharmony_ci struct v4l2_rect min_rect = { 94262306a36Sopenharmony_ci 0, 0, 94362306a36Sopenharmony_ci s->r.width / MAX_ZOOM, 94462306a36Sopenharmony_ci s->r.height / MAX_ZOOM 94562306a36Sopenharmony_ci }; 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci v4l2_rect_set_min_size(&fmt, &min_rect); 94862306a36Sopenharmony_ci if (!dev->has_compose_cap) 94962306a36Sopenharmony_ci v4l2_rect_set_max_size(&fmt, &max_rect); 95062306a36Sopenharmony_ci if (!v4l2_rect_same_size(&dev->fmt_cap_rect, &fmt) && 95162306a36Sopenharmony_ci vb2_is_busy(&dev->vb_vid_cap_q)) 95262306a36Sopenharmony_ci return -EBUSY; 95362306a36Sopenharmony_ci if (dev->has_compose_cap) { 95462306a36Sopenharmony_ci v4l2_rect_set_min_size(compose, &min_rect); 95562306a36Sopenharmony_ci v4l2_rect_set_max_size(compose, &max_rect); 95662306a36Sopenharmony_ci v4l2_rect_map_inside(compose, &fmt); 95762306a36Sopenharmony_ci } 95862306a36Sopenharmony_ci dev->fmt_cap_rect = fmt; 95962306a36Sopenharmony_ci tpg_s_buf_height(&dev->tpg, fmt.height); 96062306a36Sopenharmony_ci } else if (dev->has_compose_cap) { 96162306a36Sopenharmony_ci struct v4l2_rect fmt = dev->fmt_cap_rect; 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci v4l2_rect_set_min_size(&fmt, &s->r); 96462306a36Sopenharmony_ci if (!v4l2_rect_same_size(&dev->fmt_cap_rect, &fmt) && 96562306a36Sopenharmony_ci vb2_is_busy(&dev->vb_vid_cap_q)) 96662306a36Sopenharmony_ci return -EBUSY; 96762306a36Sopenharmony_ci dev->fmt_cap_rect = fmt; 96862306a36Sopenharmony_ci tpg_s_buf_height(&dev->tpg, fmt.height); 96962306a36Sopenharmony_ci v4l2_rect_set_size_to(compose, &s->r); 97062306a36Sopenharmony_ci v4l2_rect_map_inside(compose, &dev->fmt_cap_rect); 97162306a36Sopenharmony_ci } else { 97262306a36Sopenharmony_ci if (!v4l2_rect_same_size(&s->r, &dev->fmt_cap_rect) && 97362306a36Sopenharmony_ci vb2_is_busy(&dev->vb_vid_cap_q)) 97462306a36Sopenharmony_ci return -EBUSY; 97562306a36Sopenharmony_ci v4l2_rect_set_size_to(&dev->fmt_cap_rect, &s->r); 97662306a36Sopenharmony_ci v4l2_rect_set_size_to(compose, &s->r); 97762306a36Sopenharmony_ci v4l2_rect_map_inside(compose, &dev->fmt_cap_rect); 97862306a36Sopenharmony_ci tpg_s_buf_height(&dev->tpg, dev->fmt_cap_rect.height); 97962306a36Sopenharmony_ci } 98062306a36Sopenharmony_ci s->r.top *= factor; 98162306a36Sopenharmony_ci s->r.height *= factor; 98262306a36Sopenharmony_ci *crop = s->r; 98362306a36Sopenharmony_ci break; 98462306a36Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE: 98562306a36Sopenharmony_ci if (!dev->has_compose_cap) 98662306a36Sopenharmony_ci return -EINVAL; 98762306a36Sopenharmony_ci ret = vivid_vid_adjust_sel(s->flags, &s->r); 98862306a36Sopenharmony_ci if (ret) 98962306a36Sopenharmony_ci return ret; 99062306a36Sopenharmony_ci v4l2_rect_set_min_size(&s->r, &vivid_min_rect); 99162306a36Sopenharmony_ci v4l2_rect_set_max_size(&s->r, &dev->fmt_cap_rect); 99262306a36Sopenharmony_ci if (dev->has_scaler_cap) { 99362306a36Sopenharmony_ci struct v4l2_rect max_rect = { 99462306a36Sopenharmony_ci 0, 0, 99562306a36Sopenharmony_ci dev->src_rect.width * MAX_ZOOM, 99662306a36Sopenharmony_ci (dev->src_rect.height / factor) * MAX_ZOOM 99762306a36Sopenharmony_ci }; 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci v4l2_rect_set_max_size(&s->r, &max_rect); 100062306a36Sopenharmony_ci if (dev->has_crop_cap) { 100162306a36Sopenharmony_ci struct v4l2_rect min_rect = { 100262306a36Sopenharmony_ci 0, 0, 100362306a36Sopenharmony_ci s->r.width / MAX_ZOOM, 100462306a36Sopenharmony_ci (s->r.height * factor) / MAX_ZOOM 100562306a36Sopenharmony_ci }; 100662306a36Sopenharmony_ci struct v4l2_rect max_rect = { 100762306a36Sopenharmony_ci 0, 0, 100862306a36Sopenharmony_ci s->r.width * MAX_ZOOM, 100962306a36Sopenharmony_ci (s->r.height * factor) * MAX_ZOOM 101062306a36Sopenharmony_ci }; 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci v4l2_rect_set_min_size(crop, &min_rect); 101362306a36Sopenharmony_ci v4l2_rect_set_max_size(crop, &max_rect); 101462306a36Sopenharmony_ci v4l2_rect_map_inside(crop, &dev->crop_bounds_cap); 101562306a36Sopenharmony_ci } 101662306a36Sopenharmony_ci } else if (dev->has_crop_cap) { 101762306a36Sopenharmony_ci s->r.top *= factor; 101862306a36Sopenharmony_ci s->r.height *= factor; 101962306a36Sopenharmony_ci v4l2_rect_set_max_size(&s->r, &dev->src_rect); 102062306a36Sopenharmony_ci v4l2_rect_set_size_to(crop, &s->r); 102162306a36Sopenharmony_ci v4l2_rect_map_inside(crop, &dev->crop_bounds_cap); 102262306a36Sopenharmony_ci s->r.top /= factor; 102362306a36Sopenharmony_ci s->r.height /= factor; 102462306a36Sopenharmony_ci } else { 102562306a36Sopenharmony_ci v4l2_rect_set_size_to(&s->r, &dev->src_rect); 102662306a36Sopenharmony_ci s->r.height /= factor; 102762306a36Sopenharmony_ci } 102862306a36Sopenharmony_ci v4l2_rect_map_inside(&s->r, &dev->fmt_cap_rect); 102962306a36Sopenharmony_ci *compose = s->r; 103062306a36Sopenharmony_ci break; 103162306a36Sopenharmony_ci default: 103262306a36Sopenharmony_ci return -EINVAL; 103362306a36Sopenharmony_ci } 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci tpg_s_crop_compose(&dev->tpg, crop, compose); 103662306a36Sopenharmony_ci return 0; 103762306a36Sopenharmony_ci} 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ciint vivid_vid_cap_g_pixelaspect(struct file *file, void *priv, 104062306a36Sopenharmony_ci int type, struct v4l2_fract *f) 104162306a36Sopenharmony_ci{ 104262306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 104562306a36Sopenharmony_ci return -EINVAL; 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci switch (vivid_get_pixel_aspect(dev)) { 104862306a36Sopenharmony_ci case TPG_PIXEL_ASPECT_NTSC: 104962306a36Sopenharmony_ci f->numerator = 11; 105062306a36Sopenharmony_ci f->denominator = 10; 105162306a36Sopenharmony_ci break; 105262306a36Sopenharmony_ci case TPG_PIXEL_ASPECT_PAL: 105362306a36Sopenharmony_ci f->numerator = 54; 105462306a36Sopenharmony_ci f->denominator = 59; 105562306a36Sopenharmony_ci break; 105662306a36Sopenharmony_ci default: 105762306a36Sopenharmony_ci break; 105862306a36Sopenharmony_ci } 105962306a36Sopenharmony_ci return 0; 106062306a36Sopenharmony_ci} 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_cistatic const struct v4l2_audio vivid_audio_inputs[] = { 106362306a36Sopenharmony_ci { 0, "TV", V4L2_AUDCAP_STEREO }, 106462306a36Sopenharmony_ci { 1, "Line-In", V4L2_AUDCAP_STEREO }, 106562306a36Sopenharmony_ci}; 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ciint vidioc_enum_input(struct file *file, void *priv, 106862306a36Sopenharmony_ci struct v4l2_input *inp) 106962306a36Sopenharmony_ci{ 107062306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci if (inp->index >= dev->num_inputs) 107362306a36Sopenharmony_ci return -EINVAL; 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci inp->type = V4L2_INPUT_TYPE_CAMERA; 107662306a36Sopenharmony_ci switch (dev->input_type[inp->index]) { 107762306a36Sopenharmony_ci case WEBCAM: 107862306a36Sopenharmony_ci snprintf(inp->name, sizeof(inp->name), "Webcam %u", 107962306a36Sopenharmony_ci dev->input_name_counter[inp->index]); 108062306a36Sopenharmony_ci inp->capabilities = 0; 108162306a36Sopenharmony_ci break; 108262306a36Sopenharmony_ci case TV: 108362306a36Sopenharmony_ci snprintf(inp->name, sizeof(inp->name), "TV %u", 108462306a36Sopenharmony_ci dev->input_name_counter[inp->index]); 108562306a36Sopenharmony_ci inp->type = V4L2_INPUT_TYPE_TUNER; 108662306a36Sopenharmony_ci inp->std = V4L2_STD_ALL; 108762306a36Sopenharmony_ci if (dev->has_audio_inputs) 108862306a36Sopenharmony_ci inp->audioset = (1 << ARRAY_SIZE(vivid_audio_inputs)) - 1; 108962306a36Sopenharmony_ci inp->capabilities = V4L2_IN_CAP_STD; 109062306a36Sopenharmony_ci break; 109162306a36Sopenharmony_ci case SVID: 109262306a36Sopenharmony_ci snprintf(inp->name, sizeof(inp->name), "S-Video %u", 109362306a36Sopenharmony_ci dev->input_name_counter[inp->index]); 109462306a36Sopenharmony_ci inp->std = V4L2_STD_ALL; 109562306a36Sopenharmony_ci if (dev->has_audio_inputs) 109662306a36Sopenharmony_ci inp->audioset = (1 << ARRAY_SIZE(vivid_audio_inputs)) - 1; 109762306a36Sopenharmony_ci inp->capabilities = V4L2_IN_CAP_STD; 109862306a36Sopenharmony_ci break; 109962306a36Sopenharmony_ci case HDMI: 110062306a36Sopenharmony_ci snprintf(inp->name, sizeof(inp->name), "HDMI %u", 110162306a36Sopenharmony_ci dev->input_name_counter[inp->index]); 110262306a36Sopenharmony_ci inp->capabilities = V4L2_IN_CAP_DV_TIMINGS; 110362306a36Sopenharmony_ci if (dev->edid_blocks == 0 || 110462306a36Sopenharmony_ci dev->dv_timings_signal_mode[dev->input] == NO_SIGNAL) 110562306a36Sopenharmony_ci inp->status |= V4L2_IN_ST_NO_SIGNAL; 110662306a36Sopenharmony_ci else if (dev->dv_timings_signal_mode[dev->input] == NO_LOCK || 110762306a36Sopenharmony_ci dev->dv_timings_signal_mode[dev->input] == OUT_OF_RANGE) 110862306a36Sopenharmony_ci inp->status |= V4L2_IN_ST_NO_H_LOCK; 110962306a36Sopenharmony_ci break; 111062306a36Sopenharmony_ci } 111162306a36Sopenharmony_ci if (dev->sensor_hflip) 111262306a36Sopenharmony_ci inp->status |= V4L2_IN_ST_HFLIP; 111362306a36Sopenharmony_ci if (dev->sensor_vflip) 111462306a36Sopenharmony_ci inp->status |= V4L2_IN_ST_VFLIP; 111562306a36Sopenharmony_ci if (dev->input == inp->index && vivid_is_sdtv_cap(dev)) { 111662306a36Sopenharmony_ci if (dev->std_signal_mode[dev->input] == NO_SIGNAL) { 111762306a36Sopenharmony_ci inp->status |= V4L2_IN_ST_NO_SIGNAL; 111862306a36Sopenharmony_ci } else if (dev->std_signal_mode[dev->input] == NO_LOCK) { 111962306a36Sopenharmony_ci inp->status |= V4L2_IN_ST_NO_H_LOCK; 112062306a36Sopenharmony_ci } else if (vivid_is_tv_cap(dev)) { 112162306a36Sopenharmony_ci switch (tpg_g_quality(&dev->tpg)) { 112262306a36Sopenharmony_ci case TPG_QUAL_GRAY: 112362306a36Sopenharmony_ci inp->status |= V4L2_IN_ST_COLOR_KILL; 112462306a36Sopenharmony_ci break; 112562306a36Sopenharmony_ci case TPG_QUAL_NOISE: 112662306a36Sopenharmony_ci inp->status |= V4L2_IN_ST_NO_H_LOCK; 112762306a36Sopenharmony_ci break; 112862306a36Sopenharmony_ci default: 112962306a36Sopenharmony_ci break; 113062306a36Sopenharmony_ci } 113162306a36Sopenharmony_ci } 113262306a36Sopenharmony_ci } 113362306a36Sopenharmony_ci return 0; 113462306a36Sopenharmony_ci} 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ciint vidioc_g_input(struct file *file, void *priv, unsigned *i) 113762306a36Sopenharmony_ci{ 113862306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci *i = dev->input; 114162306a36Sopenharmony_ci return 0; 114262306a36Sopenharmony_ci} 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ciint vidioc_s_input(struct file *file, void *priv, unsigned i) 114562306a36Sopenharmony_ci{ 114662306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 114762306a36Sopenharmony_ci struct v4l2_bt_timings *bt = &dev->dv_timings_cap[dev->input].bt; 114862306a36Sopenharmony_ci unsigned brightness; 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci if (i >= dev->num_inputs) 115162306a36Sopenharmony_ci return -EINVAL; 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci if (i == dev->input) 115462306a36Sopenharmony_ci return 0; 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci if (vb2_is_busy(&dev->vb_vid_cap_q) || 115762306a36Sopenharmony_ci vb2_is_busy(&dev->vb_vbi_cap_q) || 115862306a36Sopenharmony_ci vb2_is_busy(&dev->vb_meta_cap_q)) 115962306a36Sopenharmony_ci return -EBUSY; 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci dev->input = i; 116262306a36Sopenharmony_ci dev->vid_cap_dev.tvnorms = 0; 116362306a36Sopenharmony_ci if (dev->input_type[i] == TV || dev->input_type[i] == SVID) { 116462306a36Sopenharmony_ci dev->tv_audio_input = (dev->input_type[i] == TV) ? 0 : 1; 116562306a36Sopenharmony_ci dev->vid_cap_dev.tvnorms = V4L2_STD_ALL; 116662306a36Sopenharmony_ci } 116762306a36Sopenharmony_ci dev->vbi_cap_dev.tvnorms = dev->vid_cap_dev.tvnorms; 116862306a36Sopenharmony_ci dev->meta_cap_dev.tvnorms = dev->vid_cap_dev.tvnorms; 116962306a36Sopenharmony_ci vivid_update_format_cap(dev, false); 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci if (dev->colorspace) { 117262306a36Sopenharmony_ci switch (dev->input_type[i]) { 117362306a36Sopenharmony_ci case WEBCAM: 117462306a36Sopenharmony_ci v4l2_ctrl_s_ctrl(dev->colorspace, VIVID_CS_SRGB); 117562306a36Sopenharmony_ci break; 117662306a36Sopenharmony_ci case TV: 117762306a36Sopenharmony_ci case SVID: 117862306a36Sopenharmony_ci v4l2_ctrl_s_ctrl(dev->colorspace, VIVID_CS_170M); 117962306a36Sopenharmony_ci break; 118062306a36Sopenharmony_ci case HDMI: 118162306a36Sopenharmony_ci if (bt->flags & V4L2_DV_FL_IS_CE_VIDEO) { 118262306a36Sopenharmony_ci if (dev->src_rect.width == 720 && dev->src_rect.height <= 576) 118362306a36Sopenharmony_ci v4l2_ctrl_s_ctrl(dev->colorspace, VIVID_CS_170M); 118462306a36Sopenharmony_ci else 118562306a36Sopenharmony_ci v4l2_ctrl_s_ctrl(dev->colorspace, VIVID_CS_709); 118662306a36Sopenharmony_ci } else { 118762306a36Sopenharmony_ci v4l2_ctrl_s_ctrl(dev->colorspace, VIVID_CS_SRGB); 118862306a36Sopenharmony_ci } 118962306a36Sopenharmony_ci break; 119062306a36Sopenharmony_ci } 119162306a36Sopenharmony_ci } 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci /* 119462306a36Sopenharmony_ci * Modify the brightness range depending on the input. 119562306a36Sopenharmony_ci * This makes it easy to use vivid to test if applications can 119662306a36Sopenharmony_ci * handle control range modifications and is also how this is 119762306a36Sopenharmony_ci * typically used in practice as different inputs may be hooked 119862306a36Sopenharmony_ci * up to different receivers with different control ranges. 119962306a36Sopenharmony_ci */ 120062306a36Sopenharmony_ci brightness = 128 * i + dev->input_brightness[i]; 120162306a36Sopenharmony_ci v4l2_ctrl_modify_range(dev->brightness, 120262306a36Sopenharmony_ci 128 * i, 255 + 128 * i, 1, 128 + 128 * i); 120362306a36Sopenharmony_ci v4l2_ctrl_s_ctrl(dev->brightness, brightness); 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci /* Restore per-input states. */ 120662306a36Sopenharmony_ci v4l2_ctrl_activate(dev->ctrl_dv_timings_signal_mode, 120762306a36Sopenharmony_ci vivid_is_hdmi_cap(dev)); 120862306a36Sopenharmony_ci v4l2_ctrl_activate(dev->ctrl_dv_timings, vivid_is_hdmi_cap(dev) && 120962306a36Sopenharmony_ci dev->dv_timings_signal_mode[dev->input] == 121062306a36Sopenharmony_ci SELECTED_DV_TIMINGS); 121162306a36Sopenharmony_ci v4l2_ctrl_activate(dev->ctrl_std_signal_mode, vivid_is_sdtv_cap(dev)); 121262306a36Sopenharmony_ci v4l2_ctrl_activate(dev->ctrl_standard, vivid_is_sdtv_cap(dev) && 121362306a36Sopenharmony_ci dev->std_signal_mode[dev->input]); 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci if (vivid_is_hdmi_cap(dev)) { 121662306a36Sopenharmony_ci v4l2_ctrl_s_ctrl(dev->ctrl_dv_timings_signal_mode, 121762306a36Sopenharmony_ci dev->dv_timings_signal_mode[dev->input]); 121862306a36Sopenharmony_ci v4l2_ctrl_s_ctrl(dev->ctrl_dv_timings, 121962306a36Sopenharmony_ci dev->query_dv_timings[dev->input]); 122062306a36Sopenharmony_ci } else if (vivid_is_sdtv_cap(dev)) { 122162306a36Sopenharmony_ci v4l2_ctrl_s_ctrl(dev->ctrl_std_signal_mode, 122262306a36Sopenharmony_ci dev->std_signal_mode[dev->input]); 122362306a36Sopenharmony_ci v4l2_ctrl_s_ctrl(dev->ctrl_standard, 122462306a36Sopenharmony_ci dev->std_signal_mode[dev->input]); 122562306a36Sopenharmony_ci } 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci return 0; 122862306a36Sopenharmony_ci} 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ciint vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *vin) 123162306a36Sopenharmony_ci{ 123262306a36Sopenharmony_ci if (vin->index >= ARRAY_SIZE(vivid_audio_inputs)) 123362306a36Sopenharmony_ci return -EINVAL; 123462306a36Sopenharmony_ci *vin = vivid_audio_inputs[vin->index]; 123562306a36Sopenharmony_ci return 0; 123662306a36Sopenharmony_ci} 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ciint vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *vin) 123962306a36Sopenharmony_ci{ 124062306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci if (!vivid_is_sdtv_cap(dev)) 124362306a36Sopenharmony_ci return -EINVAL; 124462306a36Sopenharmony_ci *vin = vivid_audio_inputs[dev->tv_audio_input]; 124562306a36Sopenharmony_ci return 0; 124662306a36Sopenharmony_ci} 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ciint vidioc_s_audio(struct file *file, void *fh, const struct v4l2_audio *vin) 124962306a36Sopenharmony_ci{ 125062306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci if (!vivid_is_sdtv_cap(dev)) 125362306a36Sopenharmony_ci return -EINVAL; 125462306a36Sopenharmony_ci if (vin->index >= ARRAY_SIZE(vivid_audio_inputs)) 125562306a36Sopenharmony_ci return -EINVAL; 125662306a36Sopenharmony_ci dev->tv_audio_input = vin->index; 125762306a36Sopenharmony_ci return 0; 125862306a36Sopenharmony_ci} 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ciint vivid_video_g_frequency(struct file *file, void *fh, struct v4l2_frequency *vf) 126162306a36Sopenharmony_ci{ 126262306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci if (vf->tuner != 0) 126562306a36Sopenharmony_ci return -EINVAL; 126662306a36Sopenharmony_ci vf->frequency = dev->tv_freq; 126762306a36Sopenharmony_ci return 0; 126862306a36Sopenharmony_ci} 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ciint vivid_video_s_frequency(struct file *file, void *fh, const struct v4l2_frequency *vf) 127162306a36Sopenharmony_ci{ 127262306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci if (vf->tuner != 0) 127562306a36Sopenharmony_ci return -EINVAL; 127662306a36Sopenharmony_ci dev->tv_freq = clamp_t(unsigned, vf->frequency, MIN_TV_FREQ, MAX_TV_FREQ); 127762306a36Sopenharmony_ci if (vivid_is_tv_cap(dev)) 127862306a36Sopenharmony_ci vivid_update_quality(dev); 127962306a36Sopenharmony_ci return 0; 128062306a36Sopenharmony_ci} 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ciint vivid_video_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *vt) 128362306a36Sopenharmony_ci{ 128462306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci if (vt->index != 0) 128762306a36Sopenharmony_ci return -EINVAL; 128862306a36Sopenharmony_ci if (vt->audmode > V4L2_TUNER_MODE_LANG1_LANG2) 128962306a36Sopenharmony_ci return -EINVAL; 129062306a36Sopenharmony_ci dev->tv_audmode = vt->audmode; 129162306a36Sopenharmony_ci return 0; 129262306a36Sopenharmony_ci} 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ciint vivid_video_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) 129562306a36Sopenharmony_ci{ 129662306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 129762306a36Sopenharmony_ci enum tpg_quality qual; 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci if (vt->index != 0) 130062306a36Sopenharmony_ci return -EINVAL; 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_ci vt->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO | 130362306a36Sopenharmony_ci V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; 130462306a36Sopenharmony_ci vt->audmode = dev->tv_audmode; 130562306a36Sopenharmony_ci vt->rangelow = MIN_TV_FREQ; 130662306a36Sopenharmony_ci vt->rangehigh = MAX_TV_FREQ; 130762306a36Sopenharmony_ci qual = vivid_get_quality(dev, &vt->afc); 130862306a36Sopenharmony_ci if (qual == TPG_QUAL_COLOR) 130962306a36Sopenharmony_ci vt->signal = 0xffff; 131062306a36Sopenharmony_ci else if (qual == TPG_QUAL_GRAY) 131162306a36Sopenharmony_ci vt->signal = 0x8000; 131262306a36Sopenharmony_ci else 131362306a36Sopenharmony_ci vt->signal = 0; 131462306a36Sopenharmony_ci if (qual == TPG_QUAL_NOISE) { 131562306a36Sopenharmony_ci vt->rxsubchans = 0; 131662306a36Sopenharmony_ci } else if (qual == TPG_QUAL_GRAY) { 131762306a36Sopenharmony_ci vt->rxsubchans = V4L2_TUNER_SUB_MONO; 131862306a36Sopenharmony_ci } else { 131962306a36Sopenharmony_ci unsigned int channel_nr = dev->tv_freq / (6 * 16); 132062306a36Sopenharmony_ci unsigned int options = 132162306a36Sopenharmony_ci (dev->std_cap[dev->input] & V4L2_STD_NTSC_M) ? 4 : 3; 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_ci switch (channel_nr % options) { 132462306a36Sopenharmony_ci case 0: 132562306a36Sopenharmony_ci vt->rxsubchans = V4L2_TUNER_SUB_MONO; 132662306a36Sopenharmony_ci break; 132762306a36Sopenharmony_ci case 1: 132862306a36Sopenharmony_ci vt->rxsubchans = V4L2_TUNER_SUB_STEREO; 132962306a36Sopenharmony_ci break; 133062306a36Sopenharmony_ci case 2: 133162306a36Sopenharmony_ci if (dev->std_cap[dev->input] & V4L2_STD_NTSC_M) 133262306a36Sopenharmony_ci vt->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_SAP; 133362306a36Sopenharmony_ci else 133462306a36Sopenharmony_ci vt->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; 133562306a36Sopenharmony_ci break; 133662306a36Sopenharmony_ci case 3: 133762306a36Sopenharmony_ci vt->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_SAP; 133862306a36Sopenharmony_ci break; 133962306a36Sopenharmony_ci } 134062306a36Sopenharmony_ci } 134162306a36Sopenharmony_ci strscpy(vt->name, "TV Tuner", sizeof(vt->name)); 134262306a36Sopenharmony_ci return 0; 134362306a36Sopenharmony_ci} 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_ci/* Must remain in sync with the vivid_ctrl_standard_strings array */ 134662306a36Sopenharmony_ciconst v4l2_std_id vivid_standard[] = { 134762306a36Sopenharmony_ci V4L2_STD_NTSC_M, 134862306a36Sopenharmony_ci V4L2_STD_NTSC_M_JP, 134962306a36Sopenharmony_ci V4L2_STD_NTSC_M_KR, 135062306a36Sopenharmony_ci V4L2_STD_NTSC_443, 135162306a36Sopenharmony_ci V4L2_STD_PAL_BG | V4L2_STD_PAL_H, 135262306a36Sopenharmony_ci V4L2_STD_PAL_I, 135362306a36Sopenharmony_ci V4L2_STD_PAL_DK, 135462306a36Sopenharmony_ci V4L2_STD_PAL_M, 135562306a36Sopenharmony_ci V4L2_STD_PAL_N, 135662306a36Sopenharmony_ci V4L2_STD_PAL_Nc, 135762306a36Sopenharmony_ci V4L2_STD_PAL_60, 135862306a36Sopenharmony_ci V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H, 135962306a36Sopenharmony_ci V4L2_STD_SECAM_DK, 136062306a36Sopenharmony_ci V4L2_STD_SECAM_L, 136162306a36Sopenharmony_ci V4L2_STD_SECAM_LC, 136262306a36Sopenharmony_ci V4L2_STD_UNKNOWN 136362306a36Sopenharmony_ci}; 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_ci/* Must remain in sync with the vivid_standard array */ 136662306a36Sopenharmony_ciconst char * const vivid_ctrl_standard_strings[] = { 136762306a36Sopenharmony_ci "NTSC-M", 136862306a36Sopenharmony_ci "NTSC-M-JP", 136962306a36Sopenharmony_ci "NTSC-M-KR", 137062306a36Sopenharmony_ci "NTSC-443", 137162306a36Sopenharmony_ci "PAL-BGH", 137262306a36Sopenharmony_ci "PAL-I", 137362306a36Sopenharmony_ci "PAL-DK", 137462306a36Sopenharmony_ci "PAL-M", 137562306a36Sopenharmony_ci "PAL-N", 137662306a36Sopenharmony_ci "PAL-Nc", 137762306a36Sopenharmony_ci "PAL-60", 137862306a36Sopenharmony_ci "SECAM-BGH", 137962306a36Sopenharmony_ci "SECAM-DK", 138062306a36Sopenharmony_ci "SECAM-L", 138162306a36Sopenharmony_ci "SECAM-Lc", 138262306a36Sopenharmony_ci NULL, 138362306a36Sopenharmony_ci}; 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_ciint vidioc_querystd(struct file *file, void *priv, v4l2_std_id *id) 138662306a36Sopenharmony_ci{ 138762306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 138862306a36Sopenharmony_ci unsigned int last = dev->query_std_last[dev->input]; 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci if (!vivid_is_sdtv_cap(dev)) 139162306a36Sopenharmony_ci return -ENODATA; 139262306a36Sopenharmony_ci if (dev->std_signal_mode[dev->input] == NO_SIGNAL || 139362306a36Sopenharmony_ci dev->std_signal_mode[dev->input] == NO_LOCK) { 139462306a36Sopenharmony_ci *id = V4L2_STD_UNKNOWN; 139562306a36Sopenharmony_ci return 0; 139662306a36Sopenharmony_ci } 139762306a36Sopenharmony_ci if (vivid_is_tv_cap(dev) && tpg_g_quality(&dev->tpg) == TPG_QUAL_NOISE) { 139862306a36Sopenharmony_ci *id = V4L2_STD_UNKNOWN; 139962306a36Sopenharmony_ci } else if (dev->std_signal_mode[dev->input] == CURRENT_STD) { 140062306a36Sopenharmony_ci *id = dev->std_cap[dev->input]; 140162306a36Sopenharmony_ci } else if (dev->std_signal_mode[dev->input] == SELECTED_STD) { 140262306a36Sopenharmony_ci *id = dev->query_std[dev->input]; 140362306a36Sopenharmony_ci } else { 140462306a36Sopenharmony_ci *id = vivid_standard[last]; 140562306a36Sopenharmony_ci dev->query_std_last[dev->input] = 140662306a36Sopenharmony_ci (last + 1) % ARRAY_SIZE(vivid_standard); 140762306a36Sopenharmony_ci } 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_ci return 0; 141062306a36Sopenharmony_ci} 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ciint vivid_vid_cap_s_std(struct file *file, void *priv, v4l2_std_id id) 141362306a36Sopenharmony_ci{ 141462306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_ci if (!vivid_is_sdtv_cap(dev)) 141762306a36Sopenharmony_ci return -ENODATA; 141862306a36Sopenharmony_ci if (dev->std_cap[dev->input] == id) 141962306a36Sopenharmony_ci return 0; 142062306a36Sopenharmony_ci if (vb2_is_busy(&dev->vb_vid_cap_q) || vb2_is_busy(&dev->vb_vbi_cap_q)) 142162306a36Sopenharmony_ci return -EBUSY; 142262306a36Sopenharmony_ci dev->std_cap[dev->input] = id; 142362306a36Sopenharmony_ci vivid_update_format_cap(dev, false); 142462306a36Sopenharmony_ci return 0; 142562306a36Sopenharmony_ci} 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_cistatic void find_aspect_ratio(u32 width, u32 height, 142862306a36Sopenharmony_ci u32 *num, u32 *denom) 142962306a36Sopenharmony_ci{ 143062306a36Sopenharmony_ci if (!(height % 3) && ((height * 4 / 3) == width)) { 143162306a36Sopenharmony_ci *num = 4; 143262306a36Sopenharmony_ci *denom = 3; 143362306a36Sopenharmony_ci } else if (!(height % 9) && ((height * 16 / 9) == width)) { 143462306a36Sopenharmony_ci *num = 16; 143562306a36Sopenharmony_ci *denom = 9; 143662306a36Sopenharmony_ci } else if (!(height % 10) && ((height * 16 / 10) == width)) { 143762306a36Sopenharmony_ci *num = 16; 143862306a36Sopenharmony_ci *denom = 10; 143962306a36Sopenharmony_ci } else if (!(height % 4) && ((height * 5 / 4) == width)) { 144062306a36Sopenharmony_ci *num = 5; 144162306a36Sopenharmony_ci *denom = 4; 144262306a36Sopenharmony_ci } else if (!(height % 9) && ((height * 15 / 9) == width)) { 144362306a36Sopenharmony_ci *num = 15; 144462306a36Sopenharmony_ci *denom = 9; 144562306a36Sopenharmony_ci } else { /* default to 16:9 */ 144662306a36Sopenharmony_ci *num = 16; 144762306a36Sopenharmony_ci *denom = 9; 144862306a36Sopenharmony_ci } 144962306a36Sopenharmony_ci} 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_cistatic bool valid_cvt_gtf_timings(struct v4l2_dv_timings *timings) 145262306a36Sopenharmony_ci{ 145362306a36Sopenharmony_ci struct v4l2_bt_timings *bt = &timings->bt; 145462306a36Sopenharmony_ci u32 total_h_pixel; 145562306a36Sopenharmony_ci u32 total_v_lines; 145662306a36Sopenharmony_ci u32 h_freq; 145762306a36Sopenharmony_ci 145862306a36Sopenharmony_ci if (!v4l2_valid_dv_timings(timings, &vivid_dv_timings_cap, 145962306a36Sopenharmony_ci NULL, NULL)) 146062306a36Sopenharmony_ci return false; 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ci total_h_pixel = V4L2_DV_BT_FRAME_WIDTH(bt); 146362306a36Sopenharmony_ci total_v_lines = V4L2_DV_BT_FRAME_HEIGHT(bt); 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ci h_freq = (u32)bt->pixelclock / total_h_pixel; 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_ci if (bt->standards == 0 || (bt->standards & V4L2_DV_BT_STD_CVT)) { 146862306a36Sopenharmony_ci if (v4l2_detect_cvt(total_v_lines, h_freq, bt->vsync, bt->width, 146962306a36Sopenharmony_ci bt->polarities, bt->interlaced, timings)) 147062306a36Sopenharmony_ci return true; 147162306a36Sopenharmony_ci } 147262306a36Sopenharmony_ci 147362306a36Sopenharmony_ci if (bt->standards == 0 || (bt->standards & V4L2_DV_BT_STD_GTF)) { 147462306a36Sopenharmony_ci struct v4l2_fract aspect_ratio; 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_ci find_aspect_ratio(bt->width, bt->height, 147762306a36Sopenharmony_ci &aspect_ratio.numerator, 147862306a36Sopenharmony_ci &aspect_ratio.denominator); 147962306a36Sopenharmony_ci if (v4l2_detect_gtf(total_v_lines, h_freq, bt->vsync, 148062306a36Sopenharmony_ci bt->polarities, bt->interlaced, 148162306a36Sopenharmony_ci aspect_ratio, timings)) 148262306a36Sopenharmony_ci return true; 148362306a36Sopenharmony_ci } 148462306a36Sopenharmony_ci return false; 148562306a36Sopenharmony_ci} 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ciint vivid_vid_cap_s_dv_timings(struct file *file, void *_fh, 148862306a36Sopenharmony_ci struct v4l2_dv_timings *timings) 148962306a36Sopenharmony_ci{ 149062306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci if (!vivid_is_hdmi_cap(dev)) 149362306a36Sopenharmony_ci return -ENODATA; 149462306a36Sopenharmony_ci if (!v4l2_find_dv_timings_cap(timings, &vivid_dv_timings_cap, 149562306a36Sopenharmony_ci 0, NULL, NULL) && 149662306a36Sopenharmony_ci !valid_cvt_gtf_timings(timings)) 149762306a36Sopenharmony_ci return -EINVAL; 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_ci if (v4l2_match_dv_timings(timings, &dev->dv_timings_cap[dev->input], 150062306a36Sopenharmony_ci 0, false)) 150162306a36Sopenharmony_ci return 0; 150262306a36Sopenharmony_ci if (vb2_is_busy(&dev->vb_vid_cap_q)) 150362306a36Sopenharmony_ci return -EBUSY; 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_ci dev->dv_timings_cap[dev->input] = *timings; 150662306a36Sopenharmony_ci vivid_update_format_cap(dev, false); 150762306a36Sopenharmony_ci return 0; 150862306a36Sopenharmony_ci} 150962306a36Sopenharmony_ci 151062306a36Sopenharmony_ciint vidioc_query_dv_timings(struct file *file, void *_fh, 151162306a36Sopenharmony_ci struct v4l2_dv_timings *timings) 151262306a36Sopenharmony_ci{ 151362306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 151462306a36Sopenharmony_ci unsigned int input = dev->input; 151562306a36Sopenharmony_ci unsigned int last = dev->query_dv_timings_last[input]; 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_ci if (!vivid_is_hdmi_cap(dev)) 151862306a36Sopenharmony_ci return -ENODATA; 151962306a36Sopenharmony_ci if (dev->dv_timings_signal_mode[input] == NO_SIGNAL || 152062306a36Sopenharmony_ci dev->edid_blocks == 0) 152162306a36Sopenharmony_ci return -ENOLINK; 152262306a36Sopenharmony_ci if (dev->dv_timings_signal_mode[input] == NO_LOCK) 152362306a36Sopenharmony_ci return -ENOLCK; 152462306a36Sopenharmony_ci if (dev->dv_timings_signal_mode[input] == OUT_OF_RANGE) { 152562306a36Sopenharmony_ci timings->bt.pixelclock = vivid_dv_timings_cap.bt.max_pixelclock * 2; 152662306a36Sopenharmony_ci return -ERANGE; 152762306a36Sopenharmony_ci } 152862306a36Sopenharmony_ci if (dev->dv_timings_signal_mode[input] == CURRENT_DV_TIMINGS) { 152962306a36Sopenharmony_ci *timings = dev->dv_timings_cap[input]; 153062306a36Sopenharmony_ci } else if (dev->dv_timings_signal_mode[input] == 153162306a36Sopenharmony_ci SELECTED_DV_TIMINGS) { 153262306a36Sopenharmony_ci *timings = 153362306a36Sopenharmony_ci v4l2_dv_timings_presets[dev->query_dv_timings[input]]; 153462306a36Sopenharmony_ci } else { 153562306a36Sopenharmony_ci *timings = 153662306a36Sopenharmony_ci v4l2_dv_timings_presets[last]; 153762306a36Sopenharmony_ci dev->query_dv_timings_last[input] = 153862306a36Sopenharmony_ci (last + 1) % dev->query_dv_timings_size; 153962306a36Sopenharmony_ci } 154062306a36Sopenharmony_ci return 0; 154162306a36Sopenharmony_ci} 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_ciint vidioc_s_edid(struct file *file, void *_fh, 154462306a36Sopenharmony_ci struct v4l2_edid *edid) 154562306a36Sopenharmony_ci{ 154662306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 154762306a36Sopenharmony_ci u16 phys_addr; 154862306a36Sopenharmony_ci u32 display_present = 0; 154962306a36Sopenharmony_ci unsigned int i, j; 155062306a36Sopenharmony_ci int ret; 155162306a36Sopenharmony_ci 155262306a36Sopenharmony_ci memset(edid->reserved, 0, sizeof(edid->reserved)); 155362306a36Sopenharmony_ci if (edid->pad >= dev->num_inputs) 155462306a36Sopenharmony_ci return -EINVAL; 155562306a36Sopenharmony_ci if (dev->input_type[edid->pad] != HDMI || edid->start_block) 155662306a36Sopenharmony_ci return -EINVAL; 155762306a36Sopenharmony_ci if (edid->blocks == 0) { 155862306a36Sopenharmony_ci dev->edid_blocks = 0; 155962306a36Sopenharmony_ci v4l2_ctrl_s_ctrl(dev->ctrl_tx_edid_present, 0); 156062306a36Sopenharmony_ci v4l2_ctrl_s_ctrl(dev->ctrl_tx_hotplug, 0); 156162306a36Sopenharmony_ci phys_addr = CEC_PHYS_ADDR_INVALID; 156262306a36Sopenharmony_ci goto set_phys_addr; 156362306a36Sopenharmony_ci } 156462306a36Sopenharmony_ci if (edid->blocks > dev->edid_max_blocks) { 156562306a36Sopenharmony_ci edid->blocks = dev->edid_max_blocks; 156662306a36Sopenharmony_ci return -E2BIG; 156762306a36Sopenharmony_ci } 156862306a36Sopenharmony_ci phys_addr = cec_get_edid_phys_addr(edid->edid, edid->blocks * 128, NULL); 156962306a36Sopenharmony_ci ret = v4l2_phys_addr_validate(phys_addr, &phys_addr, NULL); 157062306a36Sopenharmony_ci if (ret) 157162306a36Sopenharmony_ci return ret; 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_ci if (vb2_is_busy(&dev->vb_vid_cap_q)) 157462306a36Sopenharmony_ci return -EBUSY; 157562306a36Sopenharmony_ci 157662306a36Sopenharmony_ci dev->edid_blocks = edid->blocks; 157762306a36Sopenharmony_ci memcpy(dev->edid, edid->edid, edid->blocks * 128); 157862306a36Sopenharmony_ci 157962306a36Sopenharmony_ci for (i = 0, j = 0; i < dev->num_outputs; i++) 158062306a36Sopenharmony_ci if (dev->output_type[i] == HDMI) 158162306a36Sopenharmony_ci display_present |= 158262306a36Sopenharmony_ci dev->display_present[i] << j++; 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci v4l2_ctrl_s_ctrl(dev->ctrl_tx_edid_present, display_present); 158562306a36Sopenharmony_ci v4l2_ctrl_s_ctrl(dev->ctrl_tx_hotplug, display_present); 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_ciset_phys_addr: 158862306a36Sopenharmony_ci /* TODO: a proper hotplug detect cycle should be emulated here */ 158962306a36Sopenharmony_ci cec_s_phys_addr(dev->cec_rx_adap, phys_addr, false); 159062306a36Sopenharmony_ci 159162306a36Sopenharmony_ci for (i = 0; i < MAX_OUTPUTS && dev->cec_tx_adap[i]; i++) 159262306a36Sopenharmony_ci cec_s_phys_addr(dev->cec_tx_adap[i], 159362306a36Sopenharmony_ci dev->display_present[i] ? 159462306a36Sopenharmony_ci v4l2_phys_addr_for_input(phys_addr, i + 1) : 159562306a36Sopenharmony_ci CEC_PHYS_ADDR_INVALID, 159662306a36Sopenharmony_ci false); 159762306a36Sopenharmony_ci return 0; 159862306a36Sopenharmony_ci} 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_ciint vidioc_enum_framesizes(struct file *file, void *fh, 160162306a36Sopenharmony_ci struct v4l2_frmsizeenum *fsize) 160262306a36Sopenharmony_ci{ 160362306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 160462306a36Sopenharmony_ci 160562306a36Sopenharmony_ci if (!vivid_is_webcam(dev) && !dev->has_scaler_cap) 160662306a36Sopenharmony_ci return -EINVAL; 160762306a36Sopenharmony_ci if (vivid_get_format(dev, fsize->pixel_format) == NULL) 160862306a36Sopenharmony_ci return -EINVAL; 160962306a36Sopenharmony_ci if (vivid_is_webcam(dev)) { 161062306a36Sopenharmony_ci if (fsize->index >= ARRAY_SIZE(webcam_sizes)) 161162306a36Sopenharmony_ci return -EINVAL; 161262306a36Sopenharmony_ci fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; 161362306a36Sopenharmony_ci fsize->discrete = webcam_sizes[fsize->index]; 161462306a36Sopenharmony_ci return 0; 161562306a36Sopenharmony_ci } 161662306a36Sopenharmony_ci if (fsize->index) 161762306a36Sopenharmony_ci return -EINVAL; 161862306a36Sopenharmony_ci fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; 161962306a36Sopenharmony_ci fsize->stepwise.min_width = MIN_WIDTH; 162062306a36Sopenharmony_ci fsize->stepwise.max_width = MAX_WIDTH * MAX_ZOOM; 162162306a36Sopenharmony_ci fsize->stepwise.step_width = 2; 162262306a36Sopenharmony_ci fsize->stepwise.min_height = MIN_HEIGHT; 162362306a36Sopenharmony_ci fsize->stepwise.max_height = MAX_HEIGHT * MAX_ZOOM; 162462306a36Sopenharmony_ci fsize->stepwise.step_height = 2; 162562306a36Sopenharmony_ci return 0; 162662306a36Sopenharmony_ci} 162762306a36Sopenharmony_ci 162862306a36Sopenharmony_ci/* timeperframe is arbitrary and continuous */ 162962306a36Sopenharmony_ciint vidioc_enum_frameintervals(struct file *file, void *priv, 163062306a36Sopenharmony_ci struct v4l2_frmivalenum *fival) 163162306a36Sopenharmony_ci{ 163262306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 163362306a36Sopenharmony_ci const struct vivid_fmt *fmt; 163462306a36Sopenharmony_ci int i; 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_ci fmt = vivid_get_format(dev, fival->pixel_format); 163762306a36Sopenharmony_ci if (!fmt) 163862306a36Sopenharmony_ci return -EINVAL; 163962306a36Sopenharmony_ci 164062306a36Sopenharmony_ci if (!vivid_is_webcam(dev)) { 164162306a36Sopenharmony_ci if (fival->index) 164262306a36Sopenharmony_ci return -EINVAL; 164362306a36Sopenharmony_ci if (fival->width < MIN_WIDTH || fival->width > MAX_WIDTH * MAX_ZOOM) 164462306a36Sopenharmony_ci return -EINVAL; 164562306a36Sopenharmony_ci if (fival->height < MIN_HEIGHT || fival->height > MAX_HEIGHT * MAX_ZOOM) 164662306a36Sopenharmony_ci return -EINVAL; 164762306a36Sopenharmony_ci fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; 164862306a36Sopenharmony_ci fival->discrete = dev->timeperframe_vid_cap; 164962306a36Sopenharmony_ci return 0; 165062306a36Sopenharmony_ci } 165162306a36Sopenharmony_ci 165262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(webcam_sizes); i++) 165362306a36Sopenharmony_ci if (fival->width == webcam_sizes[i].width && 165462306a36Sopenharmony_ci fival->height == webcam_sizes[i].height) 165562306a36Sopenharmony_ci break; 165662306a36Sopenharmony_ci if (i == ARRAY_SIZE(webcam_sizes)) 165762306a36Sopenharmony_ci return -EINVAL; 165862306a36Sopenharmony_ci if (fival->index >= webcam_ival_count(dev, i)) 165962306a36Sopenharmony_ci return -EINVAL; 166062306a36Sopenharmony_ci fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; 166162306a36Sopenharmony_ci fival->discrete = webcam_intervals[fival->index]; 166262306a36Sopenharmony_ci return 0; 166362306a36Sopenharmony_ci} 166462306a36Sopenharmony_ci 166562306a36Sopenharmony_ciint vivid_vid_cap_g_parm(struct file *file, void *priv, 166662306a36Sopenharmony_ci struct v4l2_streamparm *parm) 166762306a36Sopenharmony_ci{ 166862306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_ci if (parm->type != (dev->multiplanar ? 167162306a36Sopenharmony_ci V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE : 167262306a36Sopenharmony_ci V4L2_BUF_TYPE_VIDEO_CAPTURE)) 167362306a36Sopenharmony_ci return -EINVAL; 167462306a36Sopenharmony_ci 167562306a36Sopenharmony_ci parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; 167662306a36Sopenharmony_ci parm->parm.capture.timeperframe = dev->timeperframe_vid_cap; 167762306a36Sopenharmony_ci parm->parm.capture.readbuffers = 1; 167862306a36Sopenharmony_ci return 0; 167962306a36Sopenharmony_ci} 168062306a36Sopenharmony_ci 168162306a36Sopenharmony_ciint vivid_vid_cap_s_parm(struct file *file, void *priv, 168262306a36Sopenharmony_ci struct v4l2_streamparm *parm) 168362306a36Sopenharmony_ci{ 168462306a36Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 168562306a36Sopenharmony_ci unsigned int ival_sz = webcam_ival_count(dev, dev->webcam_size_idx); 168662306a36Sopenharmony_ci struct v4l2_fract tpf; 168762306a36Sopenharmony_ci unsigned i; 168862306a36Sopenharmony_ci 168962306a36Sopenharmony_ci if (parm->type != (dev->multiplanar ? 169062306a36Sopenharmony_ci V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE : 169162306a36Sopenharmony_ci V4L2_BUF_TYPE_VIDEO_CAPTURE)) 169262306a36Sopenharmony_ci return -EINVAL; 169362306a36Sopenharmony_ci if (!vivid_is_webcam(dev)) 169462306a36Sopenharmony_ci return vivid_vid_cap_g_parm(file, priv, parm); 169562306a36Sopenharmony_ci 169662306a36Sopenharmony_ci tpf = parm->parm.capture.timeperframe; 169762306a36Sopenharmony_ci 169862306a36Sopenharmony_ci if (tpf.denominator == 0) 169962306a36Sopenharmony_ci tpf = webcam_intervals[ival_sz - 1]; 170062306a36Sopenharmony_ci for (i = 0; i < ival_sz; i++) 170162306a36Sopenharmony_ci if (V4L2_FRACT_COMPARE(tpf, >=, webcam_intervals[i])) 170262306a36Sopenharmony_ci break; 170362306a36Sopenharmony_ci if (i == ival_sz) 170462306a36Sopenharmony_ci i = ival_sz - 1; 170562306a36Sopenharmony_ci dev->webcam_ival_idx = i; 170662306a36Sopenharmony_ci tpf = webcam_intervals[dev->webcam_ival_idx]; 170762306a36Sopenharmony_ci 170862306a36Sopenharmony_ci /* resync the thread's timings */ 170962306a36Sopenharmony_ci dev->cap_seq_resync = true; 171062306a36Sopenharmony_ci dev->timeperframe_vid_cap = tpf; 171162306a36Sopenharmony_ci parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; 171262306a36Sopenharmony_ci parm->parm.capture.timeperframe = tpf; 171362306a36Sopenharmony_ci parm->parm.capture.readbuffers = 1; 171462306a36Sopenharmony_ci return 0; 171562306a36Sopenharmony_ci} 1716