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