summaryrefslogtreecommitdiff
path: root/drivers/media/test-drivers/vivid/vivid-vid-cap.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/test-drivers/vivid/vivid-vid-cap.c')
-rw-r--r--drivers/media/test-drivers/vivid/vivid-vid-cap.c232
1 files changed, 147 insertions, 85 deletions
diff --git a/drivers/media/test-drivers/vivid/vivid-vid-cap.c b/drivers/media/test-drivers/vivid/vivid-vid-cap.c
index 801286dc1448..b95f06a9b5ae 100644
--- a/drivers/media/test-drivers/vivid/vivid-vid-cap.c
+++ b/drivers/media/test-drivers/vivid/vivid-vid-cap.c
@@ -10,6 +10,7 @@
#include <linux/sched.h>
#include <linux/vmalloc.h>
#include <linux/videodev2.h>
+#include <linux/prandom.h>
#include <linux/v4l2-dv-timings.h>
#include <media/v4l2-common.h>
#include <media/v4l2-event.h>
@@ -21,40 +22,59 @@
#include "vivid-kthread-cap.h"
#include "vivid-vid-cap.h"
-/* The number of discrete webcam framesizes */
-#define VIVID_WEBCAM_SIZES 6
-/* The number of discrete webcam frameintervals */
-#define VIVID_WEBCAM_IVALS (VIVID_WEBCAM_SIZES * 2)
-
/* Sizes must be in increasing order */
-static const struct v4l2_frmsize_discrete webcam_sizes[VIVID_WEBCAM_SIZES] = {
+static const struct v4l2_frmsize_discrete webcam_sizes[] = {
{ 320, 180 },
+ { 320, 240 },
{ 640, 360 },
{ 640, 480 },
{ 1280, 720 },
+ { 1280, 960 },
+ { 1600, 1200 },
{ 1920, 1080 },
{ 3840, 2160 },
};
/*
- * Intervals must be in increasing order and there must be twice as many
- * elements in this array as there are in webcam_sizes.
+ * Intervals must be in increasing order.
*/
-static const struct v4l2_fract webcam_intervals[VIVID_WEBCAM_IVALS] = {
+static const struct v4l2_fract webcam_intervals[] = {
{ 1, 1 },
{ 1, 2 },
{ 1, 4 },
{ 1, 5 },
{ 1, 10 },
{ 2, 25 },
- { 1, 15 },
+ { 1, 15 }, /* 7 - maximum for 2160p */
{ 1, 25 },
- { 1, 30 },
+ { 1, 30 }, /* 9 - maximum for 1080p */
{ 1, 40 },
{ 1, 50 },
- { 1, 60 },
+ { 1, 60 }, /* 12 - maximum for 720p */
+ { 1, 120 },
};
+/* Limit maximum FPS rates for high resolutions */
+#define IVAL_COUNT_720P 12 /* 720p and up is limited to 60 fps */
+#define IVAL_COUNT_1080P 9 /* 1080p and up is limited to 30 fps */
+#define IVAL_COUNT_2160P 7 /* 2160p and up is limited to 15 fps */
+
+static inline unsigned int webcam_ival_count(const struct vivid_dev *dev,
+ unsigned int frmsize_idx)
+{
+ if (webcam_sizes[frmsize_idx].height >= 2160)
+ return IVAL_COUNT_2160P;
+
+ if (webcam_sizes[frmsize_idx].height >= 1080)
+ return IVAL_COUNT_1080P;
+
+ if (webcam_sizes[frmsize_idx].height >= 720)
+ return IVAL_COUNT_720P;
+
+ /* For low resolutions, allow all FPS rates */
+ return ARRAY_SIZE(webcam_intervals);
+}
+
static int vid_cap_queue_setup(struct vb2_queue *vq,
unsigned *nbuffers, unsigned *nplanes,
unsigned sizes[], struct device *alloc_devs[])
@@ -89,8 +109,9 @@ static int vid_cap_queue_setup(struct vb2_queue *vq,
if (*nplanes != buffers)
return -EINVAL;
for (p = 0; p < buffers; p++) {
- if (sizes[p] < tpg_g_line_width(&dev->tpg, p) * h +
- dev->fmt_cap->data_offset[p])
+ if (sizes[p] < tpg_g_line_width(&dev->tpg, p) * h /
+ dev->fmt_cap->vdownsampling[p] +
+ dev->fmt_cap->data_offset[p])
return -EINVAL;
}
} else {
@@ -100,9 +121,6 @@ static int vid_cap_queue_setup(struct vb2_queue *vq,
dev->fmt_cap->data_offset[p];
}
- if (vq->num_buffers + *nbuffers < 2)
- *nbuffers = 2 - vq->num_buffers;
-
*nplanes = buffers;
dprintk(dev, 1, "%s: count=%d\n", __func__, *nbuffers);
@@ -196,12 +214,9 @@ static int vid_cap_start_streaming(struct vb2_queue *vq, unsigned count)
unsigned i;
int err;
- if (vb2_is_streaming(&dev->vb_vid_out_q))
- dev->can_loop_video = vivid_vid_can_loop(dev);
-
dev->vid_cap_seq_count = 0;
dprintk(dev, 1, "%s\n", __func__);
- for (i = 0; i < VIDEO_MAX_FRAME; i++)
+ for (i = 0; i < MAX_VID_CAP_BUFFERS; i++)
dev->must_blank[i] = tpg_g_perc_fill(&dev->tpg) < 100;
if (dev->start_streaming_error) {
dev->start_streaming_error = false;
@@ -228,7 +243,6 @@ static void vid_cap_stop_streaming(struct vb2_queue *vq)
dprintk(dev, 1, "%s\n", __func__);
vivid_stop_generating_vid_cap(dev, &dev->vid_cap_streaming);
- dev->can_loop_video = false;
}
static void vid_cap_buf_request_complete(struct vb2_buffer *vb)
@@ -246,8 +260,6 @@ const struct vb2_ops vivid_vid_cap_qops = {
.start_streaming = vid_cap_start_streaming,
.stop_streaming = vid_cap_stop_streaming,
.buf_request_complete = vid_cap_buf_request_complete,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
};
/*
@@ -259,7 +271,7 @@ void vivid_update_quality(struct vivid_dev *dev)
{
unsigned freq_modulus;
- if (dev->loop_video && (vivid_is_svid_cap(dev) || vivid_is_hdmi_cap(dev))) {
+ if (dev->input_is_connected_to_output[dev->input]) {
/*
* The 'noise' will only be replaced by the actual video
* if the output video matches the input video settings.
@@ -290,8 +302,10 @@ void vivid_update_quality(struct vivid_dev *dev)
*/
freq_modulus = (dev->tv_freq - 676 /* (43.25-1) * 16 */) % (6 * 16);
if (freq_modulus > 2 * 16) {
+ struct rnd_state prng;
+ prandom_seed_state(&prng, dev->tv_freq ^ 0x55);
tpg_s_quality(&dev->tpg, TPG_QUAL_NOISE,
- next_pseudo_random32(dev->tv_freq ^ 0x55) & 0x3f);
+ prandom_u32_state(&prng) & 0x3f);
return;
}
if (freq_modulus < 12 /*0.75 * 16*/ || freq_modulus > 20 /*1.25 * 16*/)
@@ -442,8 +456,8 @@ void vivid_update_format_cap(struct vivid_dev *dev, bool keep_controls)
if (keep_controls)
return;
- dims[0] = roundup(dev->src_rect.width, PIXEL_ARRAY_DIV);
- dims[1] = roundup(dev->src_rect.height, PIXEL_ARRAY_DIV);
+ dims[0] = DIV_ROUND_UP(dev->src_rect.height, PIXEL_ARRAY_DIV);
+ dims[1] = DIV_ROUND_UP(dev->src_rect.width, PIXEL_ARRAY_DIV);
v4l2_ctrl_modify_dimensions(dev->pixel_array, dims);
}
@@ -473,35 +487,35 @@ static enum v4l2_field vivid_field_cap(struct vivid_dev *dev, enum v4l2_field fi
static unsigned vivid_colorspace_cap(struct vivid_dev *dev)
{
- if (!dev->loop_video || vivid_is_webcam(dev) || vivid_is_tv_cap(dev))
+ if (!vivid_input_is_connected_to(dev))
return tpg_g_colorspace(&dev->tpg);
return dev->colorspace_out;
}
static unsigned vivid_xfer_func_cap(struct vivid_dev *dev)
{
- if (!dev->loop_video || vivid_is_webcam(dev) || vivid_is_tv_cap(dev))
+ if (!vivid_input_is_connected_to(dev))
return tpg_g_xfer_func(&dev->tpg);
return dev->xfer_func_out;
}
static unsigned vivid_ycbcr_enc_cap(struct vivid_dev *dev)
{
- if (!dev->loop_video || vivid_is_webcam(dev) || vivid_is_tv_cap(dev))
+ if (!vivid_input_is_connected_to(dev))
return tpg_g_ycbcr_enc(&dev->tpg);
return dev->ycbcr_enc_out;
}
static unsigned int vivid_hsv_enc_cap(struct vivid_dev *dev)
{
- if (!dev->loop_video || vivid_is_webcam(dev) || vivid_is_tv_cap(dev))
+ if (!vivid_input_is_connected_to(dev))
return tpg_g_hsv_enc(&dev->tpg);
return dev->hsv_enc_out;
}
static unsigned vivid_quantization_cap(struct vivid_dev *dev)
{
- if (!dev->loop_video || vivid_is_webcam(dev) || vivid_is_tv_cap(dev))
+ if (!vivid_input_is_connected_to(dev))
return tpg_g_quantization(&dev->tpg);
return dev->quantization_out;
}
@@ -560,7 +574,7 @@ int vivid_try_fmt_vid_cap(struct file *file, void *priv,
if (vivid_is_webcam(dev)) {
const struct v4l2_frmsize_discrete *sz =
v4l2_find_nearest_size(webcam_sizes,
- VIVID_WEBCAM_SIZES, width,
+ ARRAY_SIZE(webcam_sizes), width,
height, mp->width, mp->height);
w = sz->width;
@@ -736,14 +750,16 @@ int vivid_s_fmt_vid_cap(struct file *file, void *priv,
compose->height /= factor;
}
} else if (vivid_is_webcam(dev)) {
+ unsigned int ival_sz = webcam_ival_count(dev, dev->webcam_size_idx);
+
/* Guaranteed to be a match */
for (i = 0; i < ARRAY_SIZE(webcam_sizes); i++)
if (webcam_sizes[i].width == mp->width &&
webcam_sizes[i].height == mp->height)
break;
dev->webcam_size_idx = i;
- if (dev->webcam_ival_idx >= 2 * (VIVID_WEBCAM_SIZES - i))
- dev->webcam_ival_idx = 2 * (VIVID_WEBCAM_SIZES - i) - 1;
+ if (dev->webcam_ival_idx >= ival_sz)
+ dev->webcam_ival_idx = ival_sz - 1;
vivid_update_format_cap(dev, false);
} else {
struct v4l2_rect r = { 0, 0, mp->width, mp->height };
@@ -885,7 +901,7 @@ int vivid_vid_cap_g_selection(struct file *file, void *priv,
return 0;
}
-int vivid_vid_cap_s_selection(struct file *file, void *fh, struct v4l2_selection *s)
+int vivid_vid_cap_s_selection(struct file *file, void *priv, struct v4l2_selection *s)
{
struct vivid_dev *dev = video_drvdata(file);
struct v4l2_rect *crop = &dev->crop_cap;
@@ -934,8 +950,8 @@ int vivid_vid_cap_s_selection(struct file *file, void *fh, struct v4l2_selection
if (dev->has_compose_cap) {
v4l2_rect_set_min_size(compose, &min_rect);
v4l2_rect_set_max_size(compose, &max_rect);
- v4l2_rect_map_inside(compose, &fmt);
}
+ v4l2_rect_map_inside(compose, &fmt);
dev->fmt_cap_rect = fmt;
tpg_s_buf_height(&dev->tpg, fmt.height);
} else if (dev->has_compose_cap) {
@@ -1056,13 +1072,13 @@ int vidioc_enum_input(struct file *file, void *priv,
inp->type = V4L2_INPUT_TYPE_CAMERA;
switch (dev->input_type[inp->index]) {
case WEBCAM:
- snprintf(inp->name, sizeof(inp->name), "Webcam %u",
- dev->input_name_counter[inp->index]);
+ snprintf(inp->name, sizeof(inp->name), "Webcam %03u-%u",
+ dev->inst, dev->input_name_counter[inp->index]);
inp->capabilities = 0;
break;
case TV:
- snprintf(inp->name, sizeof(inp->name), "TV %u",
- dev->input_name_counter[inp->index]);
+ snprintf(inp->name, sizeof(inp->name), "TV %03u-%u",
+ dev->inst, dev->input_name_counter[inp->index]);
inp->type = V4L2_INPUT_TYPE_TUNER;
inp->std = V4L2_STD_ALL;
if (dev->has_audio_inputs)
@@ -1070,16 +1086,16 @@ int vidioc_enum_input(struct file *file, void *priv,
inp->capabilities = V4L2_IN_CAP_STD;
break;
case SVID:
- snprintf(inp->name, sizeof(inp->name), "S-Video %u",
- dev->input_name_counter[inp->index]);
+ snprintf(inp->name, sizeof(inp->name), "S-Video %03u-%u",
+ dev->inst, dev->input_name_counter[inp->index]);
inp->std = V4L2_STD_ALL;
if (dev->has_audio_inputs)
inp->audioset = (1 << ARRAY_SIZE(vivid_audio_inputs)) - 1;
inp->capabilities = V4L2_IN_CAP_STD;
break;
case HDMI:
- snprintf(inp->name, sizeof(inp->name), "HDMI %u",
- dev->input_name_counter[inp->index]);
+ snprintf(inp->name, sizeof(inp->name), "HDMI %03u-%u",
+ dev->inst, dev->input_name_counter[inp->index]);
inp->capabilities = V4L2_IN_CAP_DV_TIMINGS;
if (dev->edid_blocks == 0 ||
dev->dv_timings_signal_mode[dev->input] == NO_SIGNAL)
@@ -1208,7 +1224,7 @@ int vidioc_s_input(struct file *file, void *priv, unsigned i)
return 0;
}
-int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *vin)
+int vidioc_enumaudio(struct file *file, void *priv, struct v4l2_audio *vin)
{
if (vin->index >= ARRAY_SIZE(vivid_audio_inputs))
return -EINVAL;
@@ -1216,7 +1232,7 @@ int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *vin)
return 0;
}
-int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *vin)
+int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *vin)
{
struct vivid_dev *dev = video_drvdata(file);
@@ -1226,7 +1242,7 @@ int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *vin)
return 0;
}
-int vidioc_s_audio(struct file *file, void *fh, const struct v4l2_audio *vin)
+int vidioc_s_audio(struct file *file, void *priv, const struct v4l2_audio *vin)
{
struct vivid_dev *dev = video_drvdata(file);
@@ -1238,7 +1254,7 @@ int vidioc_s_audio(struct file *file, void *fh, const struct v4l2_audio *vin)
return 0;
}
-int vivid_video_g_frequency(struct file *file, void *fh, struct v4l2_frequency *vf)
+int vivid_video_g_frequency(struct file *file, void *priv, struct v4l2_frequency *vf)
{
struct vivid_dev *dev = video_drvdata(file);
@@ -1248,7 +1264,7 @@ int vivid_video_g_frequency(struct file *file, void *fh, struct v4l2_frequency *
return 0;
}
-int vivid_video_s_frequency(struct file *file, void *fh, const struct v4l2_frequency *vf)
+int vivid_video_s_frequency(struct file *file, void *priv, const struct v4l2_frequency *vf)
{
struct vivid_dev *dev = video_drvdata(file);
@@ -1260,7 +1276,7 @@ int vivid_video_s_frequency(struct file *file, void *fh, const struct v4l2_frequ
return 0;
}
-int vivid_video_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *vt)
+int vivid_video_s_tuner(struct file *file, void *priv, const struct v4l2_tuner *vt)
{
struct vivid_dev *dev = video_drvdata(file);
@@ -1272,7 +1288,7 @@ int vivid_video_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *vt
return 0;
}
-int vivid_video_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
+int vivid_video_g_tuner(struct file *file, void *priv, struct v4l2_tuner *vt)
{
struct vivid_dev *dev = video_drvdata(file);
enum tpg_quality qual;
@@ -1446,12 +1462,19 @@ static bool valid_cvt_gtf_timings(struct v4l2_dv_timings *timings)
h_freq = (u32)bt->pixelclock / total_h_pixel;
if (bt->standards == 0 || (bt->standards & V4L2_DV_BT_STD_CVT)) {
+ struct v4l2_dv_timings cvt = {};
+
if (v4l2_detect_cvt(total_v_lines, h_freq, bt->vsync, bt->width,
- bt->polarities, bt->interlaced, timings))
+ bt->polarities, bt->interlaced,
+ &vivid_dv_timings_cap, &cvt) &&
+ cvt.bt.width == bt->width && cvt.bt.height == bt->height) {
+ *timings = cvt;
return true;
+ }
}
if (bt->standards == 0 || (bt->standards & V4L2_DV_BT_STD_GTF)) {
+ struct v4l2_dv_timings gtf = {};
struct v4l2_fract aspect_ratio;
find_aspect_ratio(bt->width, bt->height,
@@ -1459,13 +1482,17 @@ static bool valid_cvt_gtf_timings(struct v4l2_dv_timings *timings)
&aspect_ratio.denominator);
if (v4l2_detect_gtf(total_v_lines, h_freq, bt->vsync,
bt->polarities, bt->interlaced,
- aspect_ratio, timings))
+ aspect_ratio, &vivid_dv_timings_cap,
+ &gtf) &&
+ gtf.bt.width == bt->width && gtf.bt.height == bt->height) {
+ *timings = gtf;
return true;
+ }
}
return false;
}
-int vivid_vid_cap_s_dv_timings(struct file *file, void *_fh,
+int vivid_vid_cap_s_dv_timings(struct file *file, void *priv,
struct v4l2_dv_timings *timings)
{
struct vivid_dev *dev = video_drvdata(file);
@@ -1488,7 +1515,7 @@ int vivid_vid_cap_s_dv_timings(struct file *file, void *_fh,
return 0;
}
-int vidioc_query_dv_timings(struct file *file, void *_fh,
+int vidioc_query_dv_timings(struct file *file, void *priv,
struct v4l2_dv_timings *timings)
{
struct vivid_dev *dev = video_drvdata(file);
@@ -1521,13 +1548,65 @@ int vidioc_query_dv_timings(struct file *file, void *_fh,
return 0;
}
-int vidioc_s_edid(struct file *file, void *_fh,
+void vivid_update_outputs(struct vivid_dev *dev)
+{
+ u32 edid_present = 0;
+
+ if (!dev || !dev->num_outputs)
+ return;
+ for (unsigned int i = 0, j = 0; i < dev->num_outputs; i++) {
+ if (dev->output_type[i] != HDMI)
+ continue;
+
+ struct vivid_dev *dev_rx = dev->output_to_input_instance[i];
+
+ if (dev_rx && dev_rx->edid_blocks)
+ edid_present |= 1 << j;
+ j++;
+ }
+ v4l2_ctrl_s_ctrl(dev->ctrl_tx_edid_present, edid_present);
+ v4l2_ctrl_s_ctrl(dev->ctrl_tx_hotplug, edid_present);
+ v4l2_ctrl_s_ctrl(dev->ctrl_tx_rxsense, edid_present);
+}
+
+void vivid_update_connected_outputs(struct vivid_dev *dev)
+{
+ u16 phys_addr = cec_get_edid_phys_addr(dev->edid, dev->edid_blocks * 128, NULL);
+
+ for (unsigned int i = 0, j = 0; i < dev->num_inputs; i++) {
+ unsigned int menu_idx =
+ dev->input_is_connected_to_output[i];
+
+ if (dev->input_type[i] != HDMI)
+ continue;
+ j++;
+ if (menu_idx < FIXED_MENU_ITEMS)
+ continue;
+
+ struct vivid_dev *dev_tx = vivid_ctrl_hdmi_to_output_instance[menu_idx];
+ unsigned int output = vivid_ctrl_hdmi_to_output_index[menu_idx];
+
+ if (!dev_tx)
+ continue;
+
+ unsigned int hdmi_output = dev_tx->output_to_iface_index[output];
+
+ vivid_update_outputs(dev_tx);
+ if (dev->edid_blocks) {
+ cec_s_phys_addr(dev_tx->cec_tx_adap[hdmi_output],
+ v4l2_phys_addr_for_input(phys_addr, j),
+ false);
+ } else {
+ cec_phys_addr_invalidate(dev_tx->cec_tx_adap[hdmi_output]);
+ }
+ }
+}
+
+int vidioc_s_edid(struct file *file, void *priv,
struct v4l2_edid *edid)
{
struct vivid_dev *dev = video_drvdata(file);
u16 phys_addr;
- u32 display_present = 0;
- unsigned int i, j;
int ret;
memset(edid->reserved, 0, sizeof(edid->reserved));
@@ -1536,11 +1615,11 @@ int vidioc_s_edid(struct file *file, void *_fh,
if (dev->input_type[edid->pad] != HDMI || edid->start_block)
return -EINVAL;
if (edid->blocks == 0) {
+ if (vb2_is_busy(&dev->vb_vid_cap_q))
+ return -EBUSY;
dev->edid_blocks = 0;
- v4l2_ctrl_s_ctrl(dev->ctrl_tx_edid_present, 0);
- v4l2_ctrl_s_ctrl(dev->ctrl_tx_hotplug, 0);
- phys_addr = CEC_PHYS_ADDR_INVALID;
- goto set_phys_addr;
+ vivid_update_connected_outputs(dev);
+ return 0;
}
if (edid->blocks > dev->edid_max_blocks) {
edid->blocks = dev->edid_max_blocks;
@@ -1557,28 +1636,11 @@ int vidioc_s_edid(struct file *file, void *_fh,
dev->edid_blocks = edid->blocks;
memcpy(dev->edid, edid->edid, edid->blocks * 128);
- for (i = 0, j = 0; i < dev->num_outputs; i++)
- if (dev->output_type[i] == HDMI)
- display_present |=
- dev->display_present[i] << j++;
-
- v4l2_ctrl_s_ctrl(dev->ctrl_tx_edid_present, display_present);
- v4l2_ctrl_s_ctrl(dev->ctrl_tx_hotplug, display_present);
-
-set_phys_addr:
- /* TODO: a proper hotplug detect cycle should be emulated here */
- cec_s_phys_addr(dev->cec_rx_adap, phys_addr, false);
-
- for (i = 0; i < MAX_OUTPUTS && dev->cec_tx_adap[i]; i++)
- cec_s_phys_addr(dev->cec_tx_adap[i],
- dev->display_present[i] ?
- v4l2_phys_addr_for_input(phys_addr, i + 1) :
- CEC_PHYS_ADDR_INVALID,
- false);
+ vivid_update_connected_outputs(dev);
return 0;
}
-int vidioc_enum_framesizes(struct file *file, void *fh,
+int vidioc_enum_framesizes(struct file *file, void *priv,
struct v4l2_frmsizeenum *fsize)
{
struct vivid_dev *dev = video_drvdata(file);
@@ -1636,7 +1698,7 @@ int vidioc_enum_frameintervals(struct file *file, void *priv,
break;
if (i == ARRAY_SIZE(webcam_sizes))
return -EINVAL;
- if (fival->index >= 2 * (VIVID_WEBCAM_SIZES - i))
+ if (fival->index >= webcam_ival_count(dev, i))
return -EINVAL;
fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
fival->discrete = webcam_intervals[fival->index];
@@ -1663,7 +1725,7 @@ int vivid_vid_cap_s_parm(struct file *file, void *priv,
struct v4l2_streamparm *parm)
{
struct vivid_dev *dev = video_drvdata(file);
- unsigned ival_sz = 2 * (VIVID_WEBCAM_SIZES - dev->webcam_size_idx);
+ unsigned int ival_sz = webcam_ival_count(dev, dev->webcam_size_idx);
struct v4l2_fract tpf;
unsigned i;