summaryrefslogtreecommitdiffstats
path: root/drivers/media/test-drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/test-drivers')
-rw-r--r--drivers/media/test-drivers/vimc/vimc-capture.c2
-rw-r--r--drivers/media/test-drivers/vimc/vimc-common.c25
-rw-r--r--drivers/media/test-drivers/vimc/vimc-common.h14
-rw-r--r--drivers/media/test-drivers/vimc/vimc-core.c2
-rw-r--r--drivers/media/test-drivers/vimc/vimc-debayer.c201
-rw-r--r--drivers/media/test-drivers/vimc/vimc-lens.c5
-rw-r--r--drivers/media/test-drivers/vimc/vimc-scaler.c134
-rw-r--r--drivers/media/test-drivers/vimc/vimc-sensor.c125
-rw-r--r--drivers/media/test-drivers/vivid/vivid-cec.c88
-rw-r--r--drivers/media/test-drivers/vivid/vivid-core.c250
-rw-r--r--drivers/media/test-drivers/vivid/vivid-core.h125
-rw-r--r--drivers/media/test-drivers/vivid/vivid-ctrls.c262
-rw-r--r--drivers/media/test-drivers/vivid/vivid-kthread-cap.c86
-rw-r--r--drivers/media/test-drivers/vivid/vivid-sdr-cap.c7
-rw-r--r--drivers/media/test-drivers/vivid/vivid-touch-cap.c6
-rw-r--r--drivers/media/test-drivers/vivid/vivid-vbi-cap.c7
-rw-r--r--drivers/media/test-drivers/vivid/vivid-vbi-out.c2
-rw-r--r--drivers/media/test-drivers/vivid/vivid-vid-cap.c120
-rw-r--r--drivers/media/test-drivers/vivid/vivid-vid-cap.h2
-rw-r--r--drivers/media/test-drivers/vivid/vivid-vid-common.c134
-rw-r--r--drivers/media/test-drivers/vivid/vivid-vid-common.h5
-rw-r--r--drivers/media/test-drivers/vivid/vivid-vid-out.c41
22 files changed, 1057 insertions, 586 deletions
diff --git a/drivers/media/test-drivers/vimc/vimc-capture.c b/drivers/media/test-drivers/vimc/vimc-capture.c
index ba7550b8ba7e..89506ae00901 100644
--- a/drivers/media/test-drivers/vimc/vimc-capture.c
+++ b/drivers/media/test-drivers/vimc/vimc-capture.c
@@ -494,7 +494,7 @@ err_free_vcapture:
return ERR_PTR(ret);
}
-struct vimc_ent_type vimc_capture_type = {
+const struct vimc_ent_type vimc_capture_type = {
.add = vimc_capture_add,
.unregister = vimc_capture_unregister,
.release = vimc_capture_release
diff --git a/drivers/media/test-drivers/vimc/vimc-common.c b/drivers/media/test-drivers/vimc/vimc-common.c
index 2e72974e35b4..4f4fcb26e236 100644
--- a/drivers/media/test-drivers/vimc/vimc-common.c
+++ b/drivers/media/test-drivers/vimc/vimc-common.c
@@ -8,6 +8,8 @@
#include <linux/init.h>
#include <linux/module.h>
+#include <media/v4l2-ctrls.h>
+
#include "vimc-common.h"
/*
@@ -358,6 +360,7 @@ int vimc_ent_sd_register(struct vimc_ent_device *ved,
u32 function,
u16 num_pads,
struct media_pad *pads,
+ const struct v4l2_subdev_internal_ops *int_ops,
const struct v4l2_subdev_ops *sd_ops)
{
int ret;
@@ -367,6 +370,7 @@ int vimc_ent_sd_register(struct vimc_ent_device *ved,
/* Initialize the subdev */
v4l2_subdev_init(sd, sd_ops);
+ sd->internal_ops = int_ops;
sd->entity.function = function;
sd->entity.ops = &vimc_ent_sd_mops;
sd->owner = THIS_MODULE;
@@ -383,17 +387,36 @@ int vimc_ent_sd_register(struct vimc_ent_device *ved,
if (ret)
return ret;
+ /*
+ * Finalize the subdev initialization if it supports active states. Use
+ * the control handler lock as the state lock if available.
+ */
+ if (int_ops && int_ops->init_state) {
+ if (sd->ctrl_handler)
+ sd->state_lock = sd->ctrl_handler->lock;
+
+ ret = v4l2_subdev_init_finalize(sd);
+ if (ret) {
+ dev_err(v4l2_dev->dev,
+ "%s: subdev initialization failed (err=%d)\n",
+ name, ret);
+ goto err_clean_m_ent;
+ }
+ }
+
/* Register the subdev with the v4l2 and the media framework */
ret = v4l2_device_register_subdev(v4l2_dev, sd);
if (ret) {
dev_err(v4l2_dev->dev,
"%s: subdev register failed (err=%d)\n",
name, ret);
- goto err_clean_m_ent;
+ goto err_clean_sd;
}
return 0;
+err_clean_sd:
+ v4l2_subdev_cleanup(sd);
err_clean_m_ent:
media_entity_cleanup(&sd->entity);
return ret;
diff --git a/drivers/media/test-drivers/vimc/vimc-common.h b/drivers/media/test-drivers/vimc/vimc-common.h
index 7641a101a728..7a45a2117748 100644
--- a/drivers/media/test-drivers/vimc/vimc-common.h
+++ b/drivers/media/test-drivers/vimc/vimc-common.h
@@ -156,7 +156,7 @@ struct vimc_ent_type {
*/
struct vimc_ent_config {
const char *name;
- struct vimc_ent_type *type;
+ const struct vimc_ent_type *type;
};
/**
@@ -167,11 +167,11 @@ struct vimc_ent_config {
*/
bool vimc_is_source(struct media_entity *ent);
-extern struct vimc_ent_type vimc_sensor_type;
-extern struct vimc_ent_type vimc_debayer_type;
-extern struct vimc_ent_type vimc_scaler_type;
-extern struct vimc_ent_type vimc_capture_type;
-extern struct vimc_ent_type vimc_lens_type;
+extern const struct vimc_ent_type vimc_sensor_type;
+extern const struct vimc_ent_type vimc_debayer_type;
+extern const struct vimc_ent_type vimc_scaler_type;
+extern const struct vimc_ent_type vimc_capture_type;
+extern const struct vimc_ent_type vimc_lens_type;
/**
* vimc_pix_map_by_index - get vimc_pix_map struct by its index
@@ -215,6 +215,7 @@ const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat);
* @num_pads: number of pads to initialize
* @pads: the array of pads of the entity, the caller should set the
* flags of the pads
+ * @int_ops: pointer to &struct v4l2_subdev_internal_ops.
* @sd_ops: pointer to &struct v4l2_subdev_ops.
*
* Helper function initialize and register the struct vimc_ent_device and struct
@@ -227,6 +228,7 @@ int vimc_ent_sd_register(struct vimc_ent_device *ved,
u32 function,
u16 num_pads,
struct media_pad *pads,
+ const struct v4l2_subdev_internal_ops *int_ops,
const struct v4l2_subdev_ops *sd_ops);
/**
diff --git a/drivers/media/test-drivers/vimc/vimc-core.c b/drivers/media/test-drivers/vimc/vimc-core.c
index af127476e920..2083c60e34d6 100644
--- a/drivers/media/test-drivers/vimc/vimc-core.c
+++ b/drivers/media/test-drivers/vimc/vimc-core.c
@@ -81,7 +81,7 @@ struct vimc_pipeline_config {
* Topology Configuration
*/
-static struct vimc_ent_config ent_config[] = {
+static const struct vimc_ent_config ent_config[] = {
[SENSOR_A] = {
.name = "Sensor A",
.type = &vimc_sensor_type
diff --git a/drivers/media/test-drivers/vimc/vimc-debayer.c b/drivers/media/test-drivers/vimc/vimc-debayer.c
index d72ed086e00b..bbb7c7a86df0 100644
--- a/drivers/media/test-drivers/vimc/vimc-debayer.c
+++ b/drivers/media/test-drivers/vimc/vimc-debayer.c
@@ -15,6 +15,9 @@
#include "vimc-common.h"
+/* TODO: Add support for more output formats, we only support RGB888 for now. */
+#define VIMC_DEBAYER_SOURCE_MBUS_FMT MEDIA_BUS_FMT_RGB888_1X24
+
enum vimc_debayer_rgb_colors {
VIMC_DEBAYER_RED = 0,
VIMC_DEBAYER_GREEN = 1,
@@ -29,19 +32,26 @@ struct vimc_debayer_pix_map {
struct vimc_debayer_device {
struct vimc_ent_device ved;
struct v4l2_subdev sd;
- /* The active format */
- struct v4l2_mbus_framefmt sink_fmt;
- u32 src_code;
+ struct v4l2_ctrl_handler hdl;
+ struct media_pad pads[2];
+
+ u8 *src_frame;
+
void (*set_rgb_src)(struct vimc_debayer_device *vdebayer,
unsigned int lin, unsigned int col,
unsigned int rgb[3]);
- /* Values calculated when the stream starts */
- u8 *src_frame;
- const struct vimc_debayer_pix_map *sink_pix_map;
- unsigned int sink_bpp;
- unsigned int mean_win_size;
- struct v4l2_ctrl_handler hdl;
- struct media_pad pads[2];
+
+ /*
+ * Virtual "hardware" configuration, filled when the stream starts or
+ * when controls are set.
+ */
+ struct {
+ const struct vimc_debayer_pix_map *sink_pix_map;
+ unsigned int sink_bpp;
+ struct v4l2_area size;
+ unsigned int mean_win_size;
+ u32 src_code;
+ } hw;
};
static const struct v4l2_mbus_framefmt sink_fmt_default = {
@@ -153,18 +163,14 @@ static bool vimc_debayer_src_code_is_valid(u32 code)
static int vimc_debayer_init_state(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state)
{
- struct vimc_debayer_device *vdebayer = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *mf;
- unsigned int i;
mf = v4l2_subdev_state_get_format(sd_state, 0);
*mf = sink_fmt_default;
- for (i = 1; i < sd->entity.num_pads; i++) {
- mf = v4l2_subdev_state_get_format(sd_state, i);
- *mf = sink_fmt_default;
- mf->code = vdebayer->src_code;
- }
+ mf = v4l2_subdev_state_get_format(sd_state, 1);
+ *mf = sink_fmt_default;
+ mf->code = VIMC_DEBAYER_SOURCE_MBUS_FMT;
return 0;
}
@@ -213,24 +219,6 @@ static int vimc_debayer_enum_frame_size(struct v4l2_subdev *sd,
return 0;
}
-static int vimc_debayer_get_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_format *fmt)
-{
- struct vimc_debayer_device *vdebayer = v4l2_get_subdevdata(sd);
-
- /* Get the current sink format */
- fmt->format = fmt->which == V4L2_SUBDEV_FORMAT_TRY ?
- *v4l2_subdev_state_get_format(sd_state, 0) :
- vdebayer->sink_fmt;
-
- /* Set the right code for the source pad */
- if (VIMC_IS_SRC(fmt->pad))
- fmt->format.code = vdebayer->src_code;
-
- return 0;
-}
-
static void vimc_debayer_adjust_sink_fmt(struct v4l2_mbus_framefmt *fmt)
{
const struct vimc_debayer_pix_map *vpix;
@@ -256,52 +244,42 @@ static int vimc_debayer_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_format *fmt)
{
struct vimc_debayer_device *vdebayer = v4l2_get_subdevdata(sd);
- struct v4l2_mbus_framefmt *sink_fmt;
- u32 *src_code;
+ struct v4l2_mbus_framefmt *format;
- if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
- /* Do not change the format while stream is on */
- if (vdebayer->src_frame)
- return -EBUSY;
-
- sink_fmt = &vdebayer->sink_fmt;
- src_code = &vdebayer->src_code;
- } else {
- sink_fmt = v4l2_subdev_state_get_format(sd_state, 0);
- src_code = &v4l2_subdev_state_get_format(sd_state, 1)->code;
- }
+ /* Do not change the format while stream is on. */
+ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE && vdebayer->src_frame)
+ return -EBUSY;
/*
- * Do not change the format of the source pad,
- * it is propagated from the sink
+ * Do not change the format of the source pad, it is propagated from
+ * the sink.
*/
- if (VIMC_IS_SRC(fmt->pad)) {
- u32 code = fmt->format.code;
+ if (VIMC_IS_SRC(fmt->pad))
+ return v4l2_subdev_get_fmt(sd, sd_state, fmt);
- fmt->format = *sink_fmt;
+ /* Set the new format in the sink pad. */
+ vimc_debayer_adjust_sink_fmt(&fmt->format);
- if (vimc_debayer_src_code_is_valid(code))
- *src_code = code;
+ format = v4l2_subdev_state_get_format(sd_state, 0);
- fmt->format.code = *src_code;
- } else {
- /* Set the new format in the sink pad */
- vimc_debayer_adjust_sink_fmt(&fmt->format);
-
- dev_dbg(vdebayer->ved.dev, "%s: sink format update: "
- "old:%dx%d (0x%x, %d, %d, %d, %d) "
- "new:%dx%d (0x%x, %d, %d, %d, %d)\n", vdebayer->sd.name,
- /* old */
- sink_fmt->width, sink_fmt->height, sink_fmt->code,
- sink_fmt->colorspace, sink_fmt->quantization,
- sink_fmt->xfer_func, sink_fmt->ycbcr_enc,
- /* new */
- fmt->format.width, fmt->format.height, fmt->format.code,
- fmt->format.colorspace, fmt->format.quantization,
- fmt->format.xfer_func, fmt->format.ycbcr_enc);
-
- *sink_fmt = fmt->format;
- }
+ dev_dbg(vdebayer->ved.dev, "%s: sink format update: "
+ "old:%dx%d (0x%x, %d, %d, %d, %d) "
+ "new:%dx%d (0x%x, %d, %d, %d, %d)\n", vdebayer->sd.name,
+ /* old */
+ format->width, format->height, format->code,
+ format->colorspace, format->quantization,
+ format->xfer_func, format->ycbcr_enc,
+ /* new */
+ fmt->format.width, fmt->format.height, fmt->format.code,
+ fmt->format.colorspace, fmt->format.quantization,
+ fmt->format.xfer_func, fmt->format.ycbcr_enc);
+
+ *format = fmt->format;
+
+ /* Propagate the format to the source pad. */
+ format = v4l2_subdev_state_get_format(sd_state, 1);
+ *format = fmt->format;
+ format->code = VIMC_DEBAYER_SOURCE_MBUS_FMT;
return 0;
}
@@ -309,7 +287,7 @@ static int vimc_debayer_set_fmt(struct v4l2_subdev *sd,
static const struct v4l2_subdev_pad_ops vimc_debayer_pad_ops = {
.enum_mbus_code = vimc_debayer_enum_mbus_code,
.enum_frame_size = vimc_debayer_enum_frame_size,
- .get_fmt = vimc_debayer_get_fmt,
+ .get_fmt = v4l2_subdev_get_fmt,
.set_fmt = vimc_debayer_set_fmt,
};
@@ -321,8 +299,8 @@ static void vimc_debayer_process_rgb_frame(struct vimc_debayer_device *vdebayer,
const struct vimc_pix_map *vpix;
unsigned int i, index;
- vpix = vimc_pix_map_by_code(vdebayer->src_code);
- index = VIMC_FRAME_INDEX(lin, col, vdebayer->sink_fmt.width, 3);
+ vpix = vimc_pix_map_by_code(vdebayer->hw.src_code);
+ index = VIMC_FRAME_INDEX(lin, col, vdebayer->hw.size.width, 3);
for (i = 0; i < 3; i++) {
switch (vpix->pixelformat) {
case V4L2_PIX_FMT_RGB24:
@@ -340,24 +318,37 @@ static int vimc_debayer_s_stream(struct v4l2_subdev *sd, int enable)
struct vimc_debayer_device *vdebayer = v4l2_get_subdevdata(sd);
if (enable) {
+ const struct v4l2_mbus_framefmt *sink_fmt;
+ const struct v4l2_mbus_framefmt *src_fmt;
+ struct v4l2_subdev_state *state;
const struct vimc_pix_map *vpix;
unsigned int frame_size;
if (vdebayer->src_frame)
return 0;
+ state = v4l2_subdev_lock_and_get_active_state(sd);
+ sink_fmt = v4l2_subdev_state_get_format(state, 0);
+ src_fmt = v4l2_subdev_state_get_format(state, 1);
+
/* Calculate the frame size of the source pad */
- vpix = vimc_pix_map_by_code(vdebayer->src_code);
- frame_size = vdebayer->sink_fmt.width * vdebayer->sink_fmt.height *
- vpix->bpp;
+ vpix = vimc_pix_map_by_code(src_fmt->code);
+ frame_size = src_fmt->width * src_fmt->height * vpix->bpp;
/* Save the bytes per pixel of the sink */
- vpix = vimc_pix_map_by_code(vdebayer->sink_fmt.code);
- vdebayer->sink_bpp = vpix->bpp;
+ vpix = vimc_pix_map_by_code(sink_fmt->code);
+ vdebayer->hw.sink_bpp = vpix->bpp;
/* Get the corresponding pixel map from the table */
- vdebayer->sink_pix_map =
- vimc_debayer_pix_map_by_code(vdebayer->sink_fmt.code);
+ vdebayer->hw.sink_pix_map =
+ vimc_debayer_pix_map_by_code(sink_fmt->code);
+
+ vdebayer->hw.size.width = sink_fmt->width;
+ vdebayer->hw.size.height = sink_fmt->height;
+
+ vdebayer->hw.src_code = src_fmt->code;
+
+ v4l2_subdev_unlock_state(state);
/*
* Allocate the frame buffer. Use vmalloc to be able to
@@ -366,7 +357,6 @@ static int vimc_debayer_s_stream(struct v4l2_subdev *sd, int enable)
vdebayer->src_frame = vmalloc(frame_size);
if (!vdebayer->src_frame)
return -ENOMEM;
-
} else {
if (!vdebayer->src_frame)
return 0;
@@ -427,13 +417,13 @@ static void vimc_debayer_calc_rgb_sink(struct vimc_debayer_device *vdebayer,
* the top left corner of the mean window (considering the current
* pixel as the center)
*/
- seek = vdebayer->mean_win_size / 2;
+ seek = vdebayer->hw.mean_win_size / 2;
/* Sum the values of the colors in the mean window */
dev_dbg(vdebayer->ved.dev,
"deb: %s: --- Calc pixel %dx%d, window mean %d, seek %d ---\n",
- vdebayer->sd.name, lin, col, vdebayer->sink_fmt.height, seek);
+ vdebayer->sd.name, lin, col, vdebayer->hw.size.height, seek);
/*
* Iterate through all the lines in the mean window, start
@@ -442,7 +432,7 @@ static void vimc_debayer_calc_rgb_sink(struct vimc_debayer_device *vdebayer,
* frame
*/
for (wlin = seek > lin ? 0 : lin - seek;
- wlin < lin + seek + 1 && wlin < vdebayer->sink_fmt.height;
+ wlin < lin + seek + 1 && wlin < vdebayer->hw.size.height;
wlin++) {
/*
@@ -452,17 +442,17 @@ static void vimc_debayer_calc_rgb_sink(struct vimc_debayer_device *vdebayer,
* frame
*/
for (wcol = seek > col ? 0 : col - seek;
- wcol < col + seek + 1 && wcol < vdebayer->sink_fmt.width;
+ wcol < col + seek + 1 && wcol < vdebayer->hw.size.width;
wcol++) {
enum vimc_debayer_rgb_colors color;
unsigned int index;
/* Check which color this pixel is */
- color = vdebayer->sink_pix_map->order[wlin % 2][wcol % 2];
+ color = vdebayer->hw.sink_pix_map->order[wlin % 2][wcol % 2];
index = VIMC_FRAME_INDEX(wlin, wcol,
- vdebayer->sink_fmt.width,
- vdebayer->sink_bpp);
+ vdebayer->hw.size.width,
+ vdebayer->hw.sink_bpp);
dev_dbg(vdebayer->ved.dev,
"deb: %s: RGB CALC: frame index %d, win pos %dx%d, color %d\n",
@@ -471,7 +461,7 @@ static void vimc_debayer_calc_rgb_sink(struct vimc_debayer_device *vdebayer,
/* Get its value */
rgb[color] = rgb[color] +
vimc_debayer_get_val(&frame[index],
- vdebayer->sink_bpp);
+ vdebayer->hw.sink_bpp);
/* Save how many values we already added */
n_rgb[color]++;
@@ -509,8 +499,8 @@ static void *vimc_debayer_process_frame(struct vimc_ent_device *ved,
if (!vdebayer->src_frame)
return ERR_PTR(-EINVAL);
- for (i = 0; i < vdebayer->sink_fmt.height; i++)
- for (j = 0; j < vdebayer->sink_fmt.width; j++) {
+ for (i = 0; i < vdebayer->hw.size.height; i++)
+ for (j = 0; j < vdebayer->hw.size.width; j++) {
vimc_debayer_calc_rgb_sink(vdebayer, sink_frame, i, j, rgb);
vdebayer->set_rgb_src(vdebayer, i, j, rgb);
}
@@ -525,7 +515,7 @@ static int vimc_debayer_s_ctrl(struct v4l2_ctrl *ctrl)
switch (ctrl->id) {
case VIMC_CID_MEAN_WIN_SIZE:
- vdebayer->mean_win_size = ctrl->val;
+ vdebayer->hw.mean_win_size = ctrl->val;
break;
default:
return -EINVAL;
@@ -543,6 +533,7 @@ static void vimc_debayer_release(struct vimc_ent_device *ved)
container_of(ved, struct vimc_debayer_device, ved);
v4l2_ctrl_handler_free(&vdebayer->hdl);
+ v4l2_subdev_cleanup(&vdebayer->sd);
media_entity_cleanup(vdebayer->ved.ent);
kfree(vdebayer);
}
@@ -594,25 +585,15 @@ static struct vimc_ent_device *vimc_debayer_add(struct vimc_device *vimc,
ret = vimc_ent_sd_register(&vdebayer->ved, &vdebayer->sd, v4l2_dev,
vcfg_name,
MEDIA_ENT_F_PROC_VIDEO_PIXEL_ENC_CONV, 2,
- vdebayer->pads, &vimc_debayer_ops);
+ vdebayer->pads, &vimc_debayer_internal_ops,
+ &vimc_debayer_ops);
if (ret)
goto err_free_hdl;
- vdebayer->sd.internal_ops = &vimc_debayer_internal_ops;
-
vdebayer->ved.process_frame = vimc_debayer_process_frame;
vdebayer->ved.dev = vimc->mdev.dev;
- vdebayer->mean_win_size = vimc_debayer_ctrl_mean_win_size.def;
+ vdebayer->hw.mean_win_size = vimc_debayer_ctrl_mean_win_size.def;
- /* Initialize the frame format */
- vdebayer->sink_fmt = sink_fmt_default;
- /*
- * TODO: Add support for more output formats, we only support
- * RGB888 for now
- * NOTE: the src format is always the same as the sink, except
- * for the code
- */
- vdebayer->src_code = MEDIA_BUS_FMT_RGB888_1X24;
vdebayer->set_rgb_src = vimc_debayer_process_rgb_frame;
return &vdebayer->ved;
@@ -625,7 +606,7 @@ err_free_vdebayer:
return ERR_PTR(ret);
}
-struct vimc_ent_type vimc_debayer_type = {
+const struct vimc_ent_type vimc_debayer_type = {
.add = vimc_debayer_add,
.release = vimc_debayer_release
};
diff --git a/drivers/media/test-drivers/vimc/vimc-lens.c b/drivers/media/test-drivers/vimc/vimc-lens.c
index 3ce7f4b4d2cc..96399057a2b5 100644
--- a/drivers/media/test-drivers/vimc/vimc-lens.c
+++ b/drivers/media/test-drivers/vimc/vimc-lens.c
@@ -72,7 +72,7 @@ static struct vimc_ent_device *vimc_lens_add(struct vimc_device *vimc,
ret = vimc_ent_sd_register(&vlens->ved, &vlens->sd, v4l2_dev,
vcfg_name, MEDIA_ENT_F_LENS, 0,
- NULL, &vimc_lens_ops);
+ NULL, NULL, &vimc_lens_ops);
if (ret)
goto err_free_hdl;
@@ -92,11 +92,12 @@ static void vimc_lens_release(struct vimc_ent_device *ved)
container_of(ved, struct vimc_lens_device, ved);
v4l2_ctrl_handler_free(&vlens->hdl);
+ v4l2_subdev_cleanup(&vlens->sd);
media_entity_cleanup(vlens->ved.ent);
kfree(vlens);
}
-struct vimc_ent_type vimc_lens_type = {
+const struct vimc_ent_type vimc_lens_type = {
.add = vimc_lens_add,
.release = vimc_lens_release
};
diff --git a/drivers/media/test-drivers/vimc/vimc-scaler.c b/drivers/media/test-drivers/vimc/vimc-scaler.c
index afe13d6af321..47d0d63865a0 100644
--- a/drivers/media/test-drivers/vimc/vimc-scaler.c
+++ b/drivers/media/test-drivers/vimc/vimc-scaler.c
@@ -15,7 +15,7 @@
#include "vimc-common.h"
/* Pad identifier */
-enum vic_sca_pad {
+enum vimc_scaler_pad {
VIMC_SCALER_SINK = 0,
VIMC_SCALER_SRC = 1,
};
@@ -26,13 +26,20 @@ enum vic_sca_pad {
struct vimc_scaler_device {
struct vimc_ent_device ved;
struct v4l2_subdev sd;
- struct v4l2_rect crop_rect;
- /* Frame format for both sink and src pad */
- struct v4l2_mbus_framefmt fmt[2];
- /* Values calculated when the stream starts */
- u8 *src_frame;
- unsigned int bpp;
struct media_pad pads[2];
+
+ u8 *src_frame;
+
+ /*
+ * Virtual "hardware" configuration, filled when the stream starts or
+ * when controls are set.
+ */
+ struct {
+ struct v4l2_mbus_framefmt sink_fmt;
+ struct v4l2_mbus_framefmt src_fmt;
+ struct v4l2_rect sink_crop;
+ unsigned int bpp;
+ } hw;
};
static const struct v4l2_mbus_framefmt fmt_default = {
@@ -132,39 +139,6 @@ static int vimc_scaler_enum_frame_size(struct v4l2_subdev *sd,
return 0;
}
-static struct v4l2_mbus_framefmt *
-vimc_scaler_pad_format(struct vimc_scaler_device *vscaler,
- struct v4l2_subdev_state *sd_state, u32 pad,
- enum v4l2_subdev_format_whence which)
-{
- if (which == V4L2_SUBDEV_FORMAT_TRY)
- return v4l2_subdev_state_get_format(sd_state, pad);
- else
- return &vscaler->fmt[pad];
-}
-
-static struct v4l2_rect *
-vimc_scaler_pad_crop(struct vimc_scaler_device *vscaler,
- struct v4l2_subdev_state *sd_state,
- enum v4l2_subdev_format_whence which)
-{
- if (which == V4L2_SUBDEV_FORMAT_TRY)
- return v4l2_subdev_state_get_crop(sd_state, VIMC_SCALER_SINK);
- else
- return &vscaler->crop_rect;
-}
-
-static int vimc_scaler_get_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_format *format)
-{
- struct vimc_scaler_device *vscaler = v4l2_get_subdevdata(sd);
-
- format->format = *vimc_scaler_pad_format(vscaler, sd_state, format->pad,
- format->which);
- return 0;
-}
-
static int vimc_scaler_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
@@ -176,7 +150,7 @@ static int vimc_scaler_set_fmt(struct v4l2_subdev *sd,
if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE && vscaler->src_frame)
return -EBUSY;
- fmt = vimc_scaler_pad_format(vscaler, sd_state, format->pad, format->which);
+ fmt = v4l2_subdev_state_get_format(sd_state, format->pad);
/*
* The media bus code and colorspace can only be changed on the sink
@@ -214,14 +188,13 @@ static int vimc_scaler_set_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *src_fmt;
struct v4l2_rect *crop;
- crop = vimc_scaler_pad_crop(vscaler, sd_state, format->which);
+ crop = v4l2_subdev_state_get_crop(sd_state, VIMC_SCALER_SINK);
crop->width = fmt->width;
crop->height = fmt->height;
crop->top = 0;
crop->left = 0;
- src_fmt = vimc_scaler_pad_format(vscaler, sd_state, VIMC_SCALER_SRC,
- format->which);
+ src_fmt = v4l2_subdev_state_get_format(sd_state, VIMC_SCALER_SRC);
*src_fmt = *fmt;
}
@@ -234,7 +207,6 @@ static int vimc_scaler_get_selection(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
- struct vimc_scaler_device *vscaler = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *sink_fmt;
if (VIMC_IS_SRC(sel->pad))
@@ -242,11 +214,10 @@ static int vimc_scaler_get_selection(struct v4l2_subdev *sd,
switch (sel->target) {
case V4L2_SEL_TGT_CROP:
- sel->r = *vimc_scaler_pad_crop(vscaler, sd_state, sel->which);
+ sel->r = *v4l2_subdev_state_get_crop(sd_state, VIMC_SCALER_SINK);
break;
case V4L2_SEL_TGT_CROP_BOUNDS:
- sink_fmt = vimc_scaler_pad_format(vscaler, sd_state, VIMC_SCALER_SINK,
- sel->which);
+ sink_fmt = v4l2_subdev_state_get_format(sd_state, VIMC_SCALER_SINK);
sel->r = vimc_scaler_get_crop_bound_sink(sink_fmt);
break;
default:
@@ -282,9 +253,8 @@ static int vimc_scaler_set_selection(struct v4l2_subdev *sd,
if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE && vscaler->src_frame)
return -EBUSY;
- crop_rect = vimc_scaler_pad_crop(vscaler, sd_state, sel->which);
- sink_fmt = vimc_scaler_pad_format(vscaler, sd_state, VIMC_SCALER_SINK,
- sel->which);
+ crop_rect = v4l2_subdev_state_get_crop(sd_state, VIMC_SCALER_SINK);
+ sink_fmt = v4l2_subdev_state_get_format(sd_state, VIMC_SCALER_SINK);
vimc_scaler_adjust_sink_crop(&sel->r, sink_fmt);
*crop_rect = sel->r;
@@ -294,7 +264,7 @@ static int vimc_scaler_set_selection(struct v4l2_subdev *sd,
static const struct v4l2_subdev_pad_ops vimc_scaler_pad_ops = {
.enum_mbus_code = vimc_scaler_enum_mbus_code,
.enum_frame_size = vimc_scaler_enum_frame_size,
- .get_fmt = vimc_scaler_get_fmt,
+ .get_fmt = v4l2_subdev_get_fmt,
.set_fmt = vimc_scaler_set_fmt,
.get_selection = vimc_scaler_get_selection,
.set_selection = vimc_scaler_set_selection,
@@ -305,27 +275,38 @@ static int vimc_scaler_s_stream(struct v4l2_subdev *sd, int enable)
struct vimc_scaler_device *vscaler = v4l2_get_subdevdata(sd);
if (enable) {
- const struct vimc_pix_map *vpix;
+ struct v4l2_subdev_state *state;
+ const struct v4l2_mbus_framefmt *format;
+ const struct v4l2_rect *rect;
unsigned int frame_size;
if (vscaler->src_frame)
return 0;
- /* Save the bytes per pixel of the sink */
- vpix = vimc_pix_map_by_code(vscaler->fmt[VIMC_SCALER_SINK].code);
- vscaler->bpp = vpix->bpp;
+ state = v4l2_subdev_lock_and_get_active_state(sd);
- /* Calculate the frame size of the source pad */
- frame_size = vscaler->fmt[VIMC_SCALER_SRC].width
- * vscaler->fmt[VIMC_SCALER_SRC].height * vscaler->bpp;
+ /* Save the bytes per pixel of the sink. */
+ format = v4l2_subdev_state_get_format(state, VIMC_SCALER_SINK);
+ vscaler->hw.sink_fmt = *format;
+ vscaler->hw.bpp = vimc_pix_map_by_code(format->code)->bpp;
- /* Allocate the frame buffer. Use vmalloc to be able to
- * allocate a large amount of memory
+ /* Calculate the frame size of the source pad. */
+ format = v4l2_subdev_state_get_format(state, VIMC_SCALER_SRC);
+ vscaler->hw.src_fmt = *format;
+ frame_size = format->width * format->height * vscaler->hw.bpp;
+
+ rect = v4l2_subdev_state_get_crop(state, VIMC_SCALER_SINK);
+ vscaler->hw.sink_crop = *rect;
+
+ v4l2_subdev_unlock_state(state);
+
+ /*
+ * Allocate the frame buffer. Use vmalloc to be able to allocate
+ * a large amount of memory.
*/
vscaler->src_frame = vmalloc(frame_size);
if (!vscaler->src_frame)
return -ENOMEM;
-
} else {
if (!vscaler->src_frame)
return 0;
@@ -353,9 +334,9 @@ static const struct v4l2_subdev_internal_ops vimc_scaler_internal_ops = {
static void vimc_scaler_fill_src_frame(const struct vimc_scaler_device *const vscaler,
const u8 *const sink_frame)
{
- const struct v4l2_mbus_framefmt *src_fmt = &vscaler->fmt[VIMC_SCALER_SRC];
- const struct v4l2_rect *r = &vscaler->crop_rect;
- unsigned int snk_width = vscaler->fmt[VIMC_SCALER_SINK].width;
+ const struct v4l2_mbus_framefmt *sink_fmt = &vscaler->hw.sink_fmt;
+ const struct v4l2_mbus_framefmt *src_fmt = &vscaler->hw.src_fmt;
+ const struct v4l2_rect *r = &vscaler->hw.sink_crop;
unsigned int src_x, src_y;
u8 *walker = vscaler->src_frame;
@@ -364,16 +345,16 @@ static void vimc_scaler_fill_src_frame(const struct vimc_scaler_device *const vs
unsigned int snk_y, y_offset;
snk_y = (src_y * r->height) / src_fmt->height + r->top;
- y_offset = snk_y * snk_width * vscaler->bpp;
+ y_offset = snk_y * sink_fmt->width * vscaler->hw.bpp;
for (src_x = 0; src_x < src_fmt->width; src_x++) {
unsigned int snk_x, x_offset, index;
snk_x = (src_x * r->width) / src_fmt->width + r->left;
- x_offset = snk_x * vscaler->bpp;
+ x_offset = snk_x * vscaler->hw.bpp;
index = y_offset + x_offset;
- memcpy(walker, &sink_frame[index], vscaler->bpp);
- walker += vscaler->bpp;
+ memcpy(walker, &sink_frame[index], vscaler->hw.bpp);
+ walker += vscaler->hw.bpp;
}
}
}
@@ -398,6 +379,7 @@ static void vimc_scaler_release(struct vimc_ent_device *ved)
struct vimc_scaler_device *vscaler =
container_of(ved, struct vimc_scaler_device, ved);
+ v4l2_subdev_cleanup(&vscaler->sd);
media_entity_cleanup(vscaler->ved.ent);
kfree(vscaler);
}
@@ -421,28 +403,20 @@ static struct vimc_ent_device *vimc_scaler_add(struct vimc_device *vimc,
ret = vimc_ent_sd_register(&vscaler->ved, &vscaler->sd, v4l2_dev,
vcfg_name,
MEDIA_ENT_F_PROC_VIDEO_SCALER, 2,
- vscaler->pads, &vimc_scaler_ops);
+ vscaler->pads, &vimc_scaler_internal_ops,
+ &vimc_scaler_ops);
if (ret) {
kfree(vscaler);
return ERR_PTR(ret);
}
- vscaler->sd.internal_ops = &vimc_scaler_internal_ops;
-
vscaler->ved.process_frame = vimc_scaler_process_frame;
vscaler->ved.dev = vimc->mdev.dev;
- /* Initialize the frame format */
- vscaler->fmt[VIMC_SCALER_SINK] = fmt_default;
- vscaler->fmt[VIMC_SCALER_SRC] = fmt_default;
-
- /* Initialize the crop selection */
- vscaler->crop_rect = crop_rect_default;
-
return &vscaler->ved;
}
-struct vimc_ent_type vimc_scaler_type = {
+const struct vimc_ent_type vimc_scaler_type = {
.add = vimc_scaler_add,
.release = vimc_scaler_release
};
diff --git a/drivers/media/test-drivers/vimc/vimc-sensor.c b/drivers/media/test-drivers/vimc/vimc-sensor.c
index 5e34b1aed95e..027767777763 100644
--- a/drivers/media/test-drivers/vimc/vimc-sensor.c
+++ b/drivers/media/test-drivers/vimc/vimc-sensor.c
@@ -24,13 +24,20 @@ struct vimc_sensor_device {
struct vimc_ent_device ved;
struct v4l2_subdev sd;
struct tpg_data tpg;
- u8 *frame;
- enum vimc_sensor_osd_mode osd_value;
- u64 start_stream_ts;
- /* The active format */
- struct v4l2_mbus_framefmt mbus_format;
struct v4l2_ctrl_handler hdl;
struct media_pad pad;
+
+ u8 *frame;
+
+ /*
+ * Virtual "hardware" configuration, filled when the stream starts or
+ * when controls are set.
+ */
+ struct {
+ struct v4l2_area size;
+ enum vimc_sensor_osd_mode osd_value;
+ u64 start_stream_ts;
+ } hw;
};
static const struct v4l2_mbus_framefmt fmt_default = {
@@ -44,14 +51,10 @@ static const struct v4l2_mbus_framefmt fmt_default = {
static int vimc_sensor_init_state(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state)
{
- unsigned int i;
-
- for (i = 0; i < sd->entity.num_pads; i++) {
- struct v4l2_mbus_framefmt *mf;
+ struct v4l2_mbus_framefmt *mf;
- mf = v4l2_subdev_state_get_format(sd_state, i);
- *mf = fmt_default;
- }
+ mf = v4l2_subdev_state_get_format(sd_state, 0);
+ *mf = fmt_default;
return 0;
}
@@ -92,36 +95,22 @@ static int vimc_sensor_enum_frame_size(struct v4l2_subdev *sd,
return 0;
}
-static int vimc_sensor_get_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_format *fmt)
+static void vimc_sensor_tpg_s_format(struct vimc_sensor_device *vsensor,
+ const struct v4l2_mbus_framefmt *format)
{
- struct vimc_sensor_device *vsensor =
- container_of(sd, struct vimc_sensor_device, sd);
-
- fmt->format = fmt->which == V4L2_SUBDEV_FORMAT_TRY ?
- *v4l2_subdev_state_get_format(sd_state, fmt->pad) :
- vsensor->mbus_format;
+ const struct vimc_pix_map *vpix = vimc_pix_map_by_code(format->code);
- return 0;
-}
-
-static void vimc_sensor_tpg_s_format(struct vimc_sensor_device *vsensor)
-{
- const struct vimc_pix_map *vpix =
- vimc_pix_map_by_code(vsensor->mbus_format.code);
-
- tpg_reset_source(&vsensor->tpg, vsensor->mbus_format.width,
- vsensor->mbus_format.height, vsensor->mbus_format.field);
- tpg_s_bytesperline(&vsensor->tpg, 0, vsensor->mbus_format.width * vpix->bpp);
- tpg_s_buf_height(&vsensor->tpg, vsensor->mbus_format.height);
+ tpg_reset_source(&vsensor->tpg, format->width, format->height,
+ format->field);
+ tpg_s_bytesperline(&vsensor->tpg, 0, format->width * vpix->bpp);
+ tpg_s_buf_height(&vsensor->tpg, format->height);
tpg_s_fourcc(&vsensor->tpg, vpix->pixelformat);
/* TODO: add support for V4L2_FIELD_ALTERNATE */
- tpg_s_field(&vsensor->tpg, vsensor->mbus_format.field, false);
- tpg_s_colorspace(&vsensor->tpg, vsensor->mbus_format.colorspace);
- tpg_s_ycbcr_enc(&vsensor->tpg, vsensor->mbus_format.ycbcr_enc);
- tpg_s_quantization(&vsensor->tpg, vsensor->mbus_format.quantization);
- tpg_s_xfer_func(&vsensor->tpg, vsensor->mbus_format.xfer_func);
+ tpg_s_field(&vsensor->tpg, format->field, false);
+ tpg_s_colorspace(&vsensor->tpg, format->colorspace);
+ tpg_s_ycbcr_enc(&vsensor->tpg, format->ycbcr_enc);
+ tpg_s_quantization(&vsensor->tpg, format->quantization);
+ tpg_s_xfer_func(&vsensor->tpg, format->xfer_func);
}
static void vimc_sensor_adjust_fmt(struct v4l2_mbus_framefmt *fmt)
@@ -152,15 +141,11 @@ static int vimc_sensor_set_fmt(struct v4l2_subdev *sd,
struct vimc_sensor_device *vsensor = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *mf;
- if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
- /* Do not change the format while stream is on */
- if (vsensor->frame)
- return -EBUSY;
+ /* Do not change the format while stream is on */
+ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE && vsensor->frame)
+ return -EBUSY;
- mf = &vsensor->mbus_format;
- } else {
- mf = v4l2_subdev_state_get_format(sd_state, fmt->pad);
- }
+ mf = v4l2_subdev_state_get_format(sd_state, fmt->pad);
/* Set the new format */
vimc_sensor_adjust_fmt(&fmt->format);
@@ -185,7 +170,7 @@ static int vimc_sensor_set_fmt(struct v4l2_subdev *sd,
static const struct v4l2_subdev_pad_ops vimc_sensor_pad_ops = {
.enum_mbus_code = vimc_sensor_enum_mbus_code,
.enum_frame_size = vimc_sensor_enum_frame_size,
- .get_fmt = vimc_sensor_get_fmt,
+ .get_fmt = v4l2_subdev_get_fmt,
.set_fmt = vimc_sensor_set_fmt,
};
@@ -202,7 +187,7 @@ static void *vimc_sensor_process_frame(struct vimc_ent_device *ved,
tpg_fill_plane_buffer(&vsensor->tpg, 0, 0, vsensor->frame);
tpg_calc_text_basep(&vsensor->tpg, basep, 0, vsensor->frame);
- switch (vsensor->osd_value) {
+ switch (vsensor->hw.osd_value) {
case VIMC_SENSOR_OSD_SHOW_ALL: {
const char *order = tpg_g_color_order(&vsensor->tpg);
@@ -216,15 +201,14 @@ static void *vimc_sensor_process_frame(struct vimc_ent_device *ved,
vsensor->tpg.hue);
tpg_gen_text(&vsensor->tpg, basep, line++ * line_height, 16, str);
snprintf(str, sizeof(str), "sensor size: %dx%d",
- vsensor->mbus_format.width,
- vsensor->mbus_format.height);
+ vsensor->hw.size.width, vsensor->hw.size.height);
tpg_gen_text(&vsensor->tpg, basep, line++ * line_height, 16, str);
fallthrough;
}
case VIMC_SENSOR_OSD_SHOW_COUNTERS: {
unsigned int ms;
- ms = div_u64(ktime_get_ns() - vsensor->start_stream_ts, 1000000);
+ ms = div_u64(ktime_get_ns() - vsensor->hw.start_stream_ts, 1000000);
snprintf(str, sizeof(str), "%02d:%02d:%02d:%03d",
(ms / (60 * 60 * 1000)) % 24,
(ms / (60 * 1000)) % 60,
@@ -247,15 +231,25 @@ static int vimc_sensor_s_stream(struct v4l2_subdev *sd, int enable)
container_of(sd, struct vimc_sensor_device, sd);
if (enable) {
+ const struct v4l2_mbus_framefmt *format;
+ struct v4l2_subdev_state *state;
const struct vimc_pix_map *vpix;
unsigned int frame_size;
- vsensor->start_stream_ts = ktime_get_ns();
+ state = v4l2_subdev_lock_and_get_active_state(sd);
+ format = v4l2_subdev_state_get_format(state, 0);
- /* Calculate the frame size */
- vpix = vimc_pix_map_by_code(vsensor->mbus_format.code);
- frame_size = vsensor->mbus_format.width * vpix->bpp *
- vsensor->mbus_format.height;
+ /* Configure the test pattern generator. */
+ vimc_sensor_tpg_s_format(vsensor, format);
+
+ /* Calculate the frame size. */
+ vpix = vimc_pix_map_by_code(format->code);
+ frame_size = format->width * vpix->bpp * format->height;
+
+ vsensor->hw.size.width = format->width;
+ vsensor->hw.size.height = format->height;
+
+ v4l2_subdev_unlock_state(state);
/*
* Allocate the frame buffer. Use vmalloc to be able to
@@ -265,9 +259,7 @@ static int vimc_sensor_s_stream(struct v4l2_subdev *sd, int enable)
if (!vsensor->frame)
return -ENOMEM;
- /* configure the test pattern generator */
- vimc_sensor_tpg_s_format(vsensor);
-
+ vsensor->hw.start_stream_ts = ktime_get_ns();
} else {
vfree(vsensor->frame);
@@ -325,7 +317,7 @@ static int vimc_sensor_s_ctrl(struct v4l2_ctrl *ctrl)
tpg_s_saturation(&vsensor->tpg, ctrl->val);
break;
case VIMC_CID_OSD_TEXT_MODE:
- vsensor->osd_value = ctrl->val;
+ vsensor->hw.osd_value = ctrl->val;
break;
default:
return -EINVAL;
@@ -344,6 +336,7 @@ static void vimc_sensor_release(struct vimc_ent_device *ved)
v4l2_ctrl_handler_free(&vsensor->hdl);
tpg_free(&vsensor->tpg);
+ v4l2_subdev_cleanup(&vsensor->sd);
media_entity_cleanup(vsensor->ved.ent);
kfree(vsensor);
}
@@ -417,8 +410,7 @@ static struct vimc_ent_device *vimc_sensor_add(struct vimc_device *vimc,
}
/* Initialize the test pattern generator */
- tpg_init(&vsensor->tpg, vsensor->mbus_format.width,
- vsensor->mbus_format.height);
+ tpg_init(&vsensor->tpg, fmt_default.width, fmt_default.height);
ret = tpg_alloc(&vsensor->tpg, VIMC_FRAME_MAX_WIDTH);
if (ret)
goto err_free_hdl;
@@ -428,18 +420,13 @@ static struct vimc_ent_device *vimc_sensor_add(struct vimc_device *vimc,
ret = vimc_ent_sd_register(&vsensor->ved, &vsensor->sd, v4l2_dev,
vcfg_name,
MEDIA_ENT_F_CAM_SENSOR, 1, &vsensor->pad,
- &vimc_sensor_ops);
+ &vimc_sensor_internal_ops, &vimc_sensor_ops);
if (ret)
goto err_free_tpg;
- vsensor->sd.internal_ops = &vimc_sensor_internal_ops;
-
vsensor->ved.process_frame = vimc_sensor_process_frame;
vsensor->ved.dev = vimc->mdev.dev;
- /* Initialize the frame format */
- vsensor->mbus_format = fmt_default;
-
return &vsensor->ved;
err_free_tpg:
@@ -452,7 +439,7 @@ err_free_vsensor:
return ERR_PTR(ret);
}
-struct vimc_ent_type vimc_sensor_type = {
+const struct vimc_ent_type vimc_sensor_type = {
.add = vimc_sensor_add,
.release = vimc_sensor_release
};
diff --git a/drivers/media/test-drivers/vivid/vivid-cec.c b/drivers/media/test-drivers/vivid/vivid-cec.c
index 1f7469ff04d5..941ef4263214 100644
--- a/drivers/media/test-drivers/vivid/vivid-cec.c
+++ b/drivers/media/test-drivers/vivid/vivid-cec.c
@@ -23,7 +23,7 @@ struct xfer_on_bus {
static bool find_dest_adap(struct vivid_dev *dev,
struct cec_adapter *adap, u8 dest)
{
- unsigned int i;
+ unsigned int i, j;
if (dest >= 0xf)
return false;
@@ -33,12 +33,29 @@ static bool find_dest_adap(struct vivid_dev *dev,
cec_has_log_addr(dev->cec_rx_adap, dest))
return true;
- for (i = 0; i < MAX_OUTPUTS && dev->cec_tx_adap[i]; i++) {
- if (adap == dev->cec_tx_adap[i])
+ for (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;
- if (!dev->cec_tx_adap[i]->is_configured)
+
+ unsigned int hdmi_output = dev_tx->output_to_iface_index[output];
+
+ if (adap == dev_tx->cec_tx_adap[hdmi_output])
+ continue;
+ if (!dev_tx->cec_tx_adap[hdmi_output]->is_configured)
continue;
- if (cec_has_log_addr(dev->cec_tx_adap[i], dest))
+ if (cec_has_log_addr(dev_tx->cec_tx_adap[hdmi_output], dest))
return true;
}
return false;
@@ -96,7 +113,7 @@ static void adjust_sfts(struct vivid_dev *dev)
int vivid_cec_bus_thread(void *_dev)
{
u32 last_sft;
- unsigned int i;
+ unsigned int i, j;
unsigned int dest;
ktime_t start, end;
s64 delta_us, retry_us;
@@ -193,9 +210,27 @@ int vivid_cec_bus_thread(void *_dev)
if (first_status == CEC_TX_STATUS_OK) {
if (xfers_on_bus[first_idx].adap != dev->cec_rx_adap)
cec_received_msg(dev->cec_rx_adap, &first_msg);
- for (i = 0; i < MAX_OUTPUTS && dev->cec_tx_adap[i]; i++)
- if (xfers_on_bus[first_idx].adap != dev->cec_tx_adap[i])
- cec_received_msg(dev->cec_tx_adap[i], &first_msg);
+ for (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];
+
+ if (xfers_on_bus[first_idx].adap != dev_tx->cec_tx_adap[hdmi_output])
+ cec_received_msg(dev_tx->cec_tx_adap[hdmi_output], &first_msg);
+ }
}
end = ktime_get();
/*
@@ -242,21 +277,36 @@ static int vivid_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
u32 signal_free_time, struct cec_msg *msg)
{
struct vivid_dev *dev = cec_get_drvdata(adap);
+ struct vivid_dev *dev_rx = dev;
u8 idx = cec_msg_initiator(msg);
+ u8 output = 0;
- spin_lock(&dev->cec_xfers_slock);
- dev->xfers[idx].adap = adap;
- memcpy(dev->xfers[idx].msg, msg->msg, CEC_MAX_MSG_SIZE);
- dev->xfers[idx].len = msg->len;
- dev->xfers[idx].sft = CEC_SIGNAL_FREE_TIME_RETRY;
+ if (dev->cec_rx_adap != adap) {
+ int i;
+
+ for (i = 0; i < dev->num_hdmi_outputs; i++)
+ if (dev->cec_tx_adap[i] == adap)
+ break;
+ if (i == dev->num_hdmi_outputs)
+ return -ENONET;
+ output = dev->hdmi_index_to_output_index[i];
+ dev_rx = dev->output_to_input_instance[output];
+ if (!dev_rx)
+ return -ENONET;
+ }
+ spin_lock(&dev_rx->cec_xfers_slock);
+ dev_rx->xfers[idx].adap = adap;
+ memcpy(dev_rx->xfers[idx].msg, msg->msg, CEC_MAX_MSG_SIZE);
+ dev_rx->xfers[idx].len = msg->len;
+ dev_rx->xfers[idx].sft = CEC_SIGNAL_FREE_TIME_RETRY;
if (signal_free_time > CEC_SIGNAL_FREE_TIME_RETRY) {
- if (idx == dev->last_initiator)
- dev->xfers[idx].sft = CEC_SIGNAL_FREE_TIME_NEXT_XFER;
+ if (idx == dev_rx->last_initiator)
+ dev_rx->xfers[idx].sft = CEC_SIGNAL_FREE_TIME_NEXT_XFER;
else
- dev->xfers[idx].sft = CEC_SIGNAL_FREE_TIME_NEW_INITIATOR;
+ dev_rx->xfers[idx].sft = CEC_SIGNAL_FREE_TIME_NEW_INITIATOR;
}
- spin_unlock(&dev->cec_xfers_slock);
- wake_up_interruptible(&dev->kthread_waitq_cec);
+ spin_unlock(&dev_rx->cec_xfers_slock);
+ wake_up_interruptible(&dev_rx->kthread_waitq_cec);
return 0;
}
diff --git a/drivers/media/test-drivers/vivid/vivid-core.c b/drivers/media/test-drivers/vivid/vivid-core.c
index 0273bc9863b0..00e0d08af357 100644
--- a/drivers/media/test-drivers/vivid/vivid-core.c
+++ b/drivers/media/test-drivers/vivid/vivid-core.c
@@ -1,4 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-only
+
/*
* vivid-core.c - A Virtual Video Test Driver, core initialization
*
@@ -42,15 +43,13 @@
#include "vivid-touch-cap.h"
#define VIVID_MODULE_NAME "vivid"
-
-/* The maximum number of vivid devices */
-#define VIVID_MAX_DEVS CONFIG_VIDEO_VIVID_MAX_DEVS
+#define MAX_STRING_LENGTH 23
MODULE_DESCRIPTION("Virtual Video Test Driver");
MODULE_AUTHOR("Hans Verkuil");
MODULE_LICENSE("GPL");
-static unsigned n_devs = 1;
+unsigned int n_devs = 1;
module_param(n_devs, uint, 0444);
MODULE_PARM_DESC(n_devs, " number of driver instances to create");
@@ -186,7 +185,32 @@ MODULE_PARM_DESC(supports_requests, " support for requests, default is 1.\n"
"\t\t 1 == supports requests\n"
"\t\t 2 == requires requests");
-static struct vivid_dev *vivid_devs[VIVID_MAX_DEVS];
+struct vivid_dev *vivid_devs[VIVID_MAX_DEVS];
+
+DEFINE_SPINLOCK(hdmi_output_skip_mask_lock);
+struct workqueue_struct *update_hdmi_ctrls_workqueue;
+u64 hdmi_to_output_menu_skip_mask;
+u64 hdmi_input_update_outputs_mask;
+
+struct vivid_dev *vivid_ctrl_hdmi_to_output_instance[MAX_MENU_ITEMS];
+unsigned int vivid_ctrl_hdmi_to_output_index[MAX_MENU_ITEMS];
+
+char *vivid_ctrl_hdmi_to_output_strings[MAX_MENU_ITEMS + 1] = {
+ "Test Pattern Generator",
+ "None"
+};
+
+DEFINE_SPINLOCK(svid_output_skip_mask_lock);
+struct workqueue_struct *update_svid_ctrls_workqueue;
+u64 svid_to_output_menu_skip_mask;
+
+struct vivid_dev *vivid_ctrl_svid_to_output_instance[MAX_MENU_ITEMS];
+unsigned int vivid_ctrl_svid_to_output_index[MAX_MENU_ITEMS];
+
+char *vivid_ctrl_svid_to_output_strings[MAX_MENU_ITEMS + 1] = {
+ "Test Pattern Generator",
+ "None"
+};
const struct v4l2_rect vivid_min_rect = {
0, 0, MIN_WIDTH, MIN_HEIGHT
@@ -218,7 +242,7 @@ static const u8 vivid_hdmi_edid[256] = {
0x5e, 0x5d, 0x10, 0x1f, 0x04, 0x13, 0x22, 0x21,
0x20, 0x05, 0x14, 0x02, 0x11, 0x01, 0x23, 0x09,
0x07, 0x07, 0x83, 0x01, 0x00, 0x00, 0x6d, 0x03,
- 0x0c, 0x00, 0x10, 0x00, 0x00, 0x3c, 0x21, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x21, 0x00,
0x60, 0x01, 0x02, 0x03, 0x67, 0xd8, 0x5d, 0xc4,
0x01, 0x78, 0x00, 0x00, 0xe2, 0x00, 0xca, 0xe3,
0x05, 0x00, 0x00, 0xe3, 0x06, 0x01, 0x00, 0x4d,
@@ -229,7 +253,7 @@ static const u8 vivid_hdmi_edid[256] = {
0x00, 0x00, 0x1a, 0x1a, 0x1d, 0x00, 0x80, 0x51,
0xd0, 0x1c, 0x20, 0x40, 0x80, 0x35, 0x00, 0xc0,
0x1c, 0x32, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92,
};
static int vidioc_querycap(struct file *file, void *priv,
@@ -827,6 +851,7 @@ static void vivid_dev_release(struct v4l2_device *v4l2_dev)
{
struct vivid_dev *dev = container_of(v4l2_dev, struct vivid_dev, v4l2_dev);
+ cancel_work_sync(&dev->update_hdmi_ctrl_work);
vivid_free_controls(dev);
v4l2_device_unregister(&dev->v4l2_dev);
#ifdef CONFIG_MEDIA_CONTROLLER
@@ -946,6 +971,7 @@ static int vivid_detect_feature_set(struct vivid_dev *dev, int inst,
dev->num_inputs--;
}
dev->num_hdmi_inputs = in_type_counter[HDMI];
+ dev->num_svid_inputs = in_type_counter[SVID];
/* how many outputs do we have and of what type? */
dev->num_outputs = num_outputs[inst];
@@ -960,7 +986,6 @@ static int vivid_detect_feature_set(struct vivid_dev *dev, int inst,
for (i = 0; i < dev->num_outputs; i++) {
dev->output_type[i] = ((output_types[inst] >> i) & 1) ? HDMI : SVID;
dev->output_name_counter[i] = out_type_counter[dev->output_type[i]]++;
- dev->display_present[i] = true;
}
dev->has_audio_outputs = out_type_counter[SVID];
if (out_type_counter[HDMI] == 16) {
@@ -1393,7 +1418,6 @@ static int vivid_create_queues(struct vivid_dev *dev)
static int vivid_create_devnodes(struct platform_device *pdev,
struct vivid_dev *dev, int inst,
- unsigned int cec_tx_bus_cnt,
v4l2_std_id tvnorms_cap,
v4l2_std_id tvnorms_out,
unsigned in_type_counter[4],
@@ -1437,7 +1461,7 @@ static int vivid_create_devnodes(struct platform_device *pdev,
return ret;
}
cec_s_phys_addr(dev->cec_rx_adap, 0, false);
- v4l2_info(&dev->v4l2_dev, "CEC adapter %s registered for HDMI input 0\n",
+ v4l2_info(&dev->v4l2_dev, "CEC adapter %s registered for HDMI input\n",
dev_name(&dev->cec_rx_adap->devnode.dev));
}
#endif
@@ -1480,10 +1504,10 @@ static int vivid_create_devnodes(struct platform_device *pdev,
#endif
#ifdef CONFIG_VIDEO_VIVID_CEC
- for (i = 0; i < cec_tx_bus_cnt; i++) {
+ for (i = 0; i < dev->num_hdmi_outputs; i++) {
ret = cec_register_adapter(dev->cec_tx_adap[i], &pdev->dev);
if (ret < 0) {
- for (; i < cec_tx_bus_cnt; i++) {
+ for (; i >= 0; i--) {
cec_delete_adapter(dev->cec_tx_adap[i]);
dev->cec_tx_adap[i] = NULL;
}
@@ -1491,10 +1515,6 @@ static int vivid_create_devnodes(struct platform_device *pdev,
}
v4l2_info(&dev->v4l2_dev, "CEC adapter %s registered for HDMI output %d\n",
dev_name(&dev->cec_tx_adap[i]->devnode.dev), i);
- if (i < out_type_counter[HDMI])
- cec_s_phys_addr(dev->cec_tx_adap[i], (i + 1) << 12, false);
- else
- cec_s_phys_addr(dev->cec_tx_adap[i], 0x1000, false);
}
#endif
@@ -1734,6 +1754,47 @@ static int vivid_create_devnodes(struct platform_device *pdev,
return 0;
}
+static void update_hdmi_ctrls_work_handler(struct work_struct *work)
+{
+ u64 skip_mask;
+ u64 update_mask;
+
+ spin_lock(&hdmi_output_skip_mask_lock);
+ skip_mask = hdmi_to_output_menu_skip_mask;
+ update_mask = hdmi_input_update_outputs_mask;
+ hdmi_input_update_outputs_mask = 0;
+ spin_unlock(&hdmi_output_skip_mask_lock);
+ for (int i = 0; i < n_devs && vivid_devs[i]; i++) {
+ if (update_mask & (1 << i))
+ vivid_update_connected_outputs(vivid_devs[i]);
+ for (int j = 0; j < vivid_devs[i]->num_hdmi_inputs; j++) {
+ struct v4l2_ctrl *c = vivid_devs[i]->ctrl_hdmi_to_output[j];
+
+ v4l2_ctrl_modify_range(c, c->minimum, c->maximum,
+ skip_mask & ~(1ULL << c->cur.val),
+ c->default_value);
+ }
+ }
+}
+
+static void update_svid_ctrls_work_handler(struct work_struct *work)
+{
+ u64 skip_mask;
+
+ spin_lock(&svid_output_skip_mask_lock);
+ skip_mask = svid_to_output_menu_skip_mask;
+ spin_unlock(&svid_output_skip_mask_lock);
+ for (int i = 0; i < n_devs && vivid_devs[i]; i++) {
+ for (int j = 0; j < vivid_devs[i]->num_svid_inputs; j++) {
+ struct v4l2_ctrl *c = vivid_devs[i]->ctrl_svid_to_output[j];
+
+ v4l2_ctrl_modify_range(c, c->minimum, c->maximum,
+ skip_mask & ~(1ULL << c->cur.val),
+ c->default_value);
+ }
+ }
+}
+
static int vivid_create_instance(struct platform_device *pdev, int inst)
{
static const struct v4l2_dv_timings def_dv_timings =
@@ -1747,7 +1808,6 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
struct vivid_dev *dev;
unsigned node_type = node_types[inst];
v4l2_std_id tvnorms_cap = 0, tvnorms_out = 0;
- unsigned int cec_tx_bus_cnt = 0;
int ret;
int i;
@@ -1850,6 +1910,22 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
dev->edid_max_blocks = dev->edid_blocks = 2;
memcpy(dev->edid, vivid_hdmi_edid, sizeof(vivid_hdmi_edid));
dev->radio_rds_init_time = ktime_get();
+ INIT_WORK(&dev->update_hdmi_ctrl_work, update_hdmi_ctrls_work_handler);
+ INIT_WORK(&dev->update_svid_ctrl_work, update_svid_ctrls_work_handler);
+ for (int j = 0, k = 0; j < dev->num_inputs; ++j)
+ if (dev->input_type[j] == HDMI)
+ dev->hdmi_index_to_input_index[k++] = j;
+ for (int j = 0, k = 0; j < dev->num_outputs; ++j)
+ if (dev->output_type[j] == HDMI) {
+ dev->output_to_iface_index[j] = k;
+ dev->hdmi_index_to_output_index[k++] = j;
+ }
+ for (int j = 0, k = 0; j < dev->num_inputs; ++j)
+ if (dev->input_type[j] == SVID)
+ dev->svid_index_to_input_index[k++] = j;
+ for (int j = 0, k = 0; j < dev->num_outputs; ++j)
+ if (dev->output_type[j] == SVID)
+ dev->output_to_iface_index[j] = k++;
/* create all controls */
ret = vivid_create_controls(dev, ccs_cap == -1, ccs_out == -1, no_error_inj,
@@ -1860,8 +1936,6 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
goto unreg_dev;
/* enable/disable interface specific controls */
- if (dev->num_outputs && dev->output_type[0] != HDMI)
- v4l2_ctrl_activate(dev->ctrl_display_present, false);
if (dev->num_inputs && dev->input_type[0] != HDMI) {
v4l2_ctrl_activate(dev->ctrl_dv_timings_signal_mode, false);
v4l2_ctrl_activate(dev->ctrl_dv_timings, false);
@@ -1917,27 +1991,27 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
}
if (dev->has_vid_out) {
- for (i = 0; i < dev->num_outputs; i++) {
+ int j;
+
+ for (i = j = 0; i < dev->num_outputs; i++) {
struct cec_adapter *adap;
if (dev->output_type[i] != HDMI)
continue;
- dev->cec_output2bus_map[i] = cec_tx_bus_cnt;
- adap = vivid_cec_alloc_adap(dev, cec_tx_bus_cnt, true);
+ adap = vivid_cec_alloc_adap(dev, j, true);
ret = PTR_ERR_OR_ZERO(adap);
if (ret < 0) {
- for (i = 0; i < dev->num_outputs; i++)
- cec_delete_adapter(dev->cec_tx_adap[i]);
+ while (j--)
+ cec_delete_adapter(dev->cec_tx_adap[j]);
goto unreg_dev;
}
- dev->cec_tx_adap[cec_tx_bus_cnt] = adap;
- cec_tx_bus_cnt++;
+ dev->cec_tx_adap[j++] = adap;
}
}
- if (dev->cec_rx_adap || cec_tx_bus_cnt) {
+ if (dev->cec_rx_adap || dev->num_hdmi_outputs) {
init_waitqueue_head(&dev->kthread_waitq_cec);
dev->kthread_cec = kthread_run(vivid_cec_bus_thread, dev,
"vivid_cec-%s", dev->v4l2_dev.name);
@@ -1963,7 +2037,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
v4l2_ctrl_handler_setup(&dev->ctrl_hdl_touch_cap);
/* finally start creating the device nodes */
- ret = vivid_create_devnodes(pdev, dev, inst, cec_tx_bus_cnt,
+ ret = vivid_create_devnodes(pdev, dev, inst,
tvnorms_cap, tvnorms_out,
in_type_counter, out_type_counter);
if (ret)
@@ -1986,7 +2060,7 @@ unreg_dev:
vb2_video_unregister_device(&dev->vid_out_dev);
vb2_video_unregister_device(&dev->vid_cap_dev);
cec_unregister_adapter(dev->cec_rx_adap);
- for (i = 0; i < MAX_OUTPUTS; i++)
+ for (i = 0; i < MAX_HDMI_OUTPUTS; i++)
cec_unregister_adapter(dev->cec_tx_adap[i]);
if (dev->kthread_cec)
kthread_stop(dev->kthread_cec);
@@ -2033,6 +2107,42 @@ static int vivid_probe(struct platform_device *pdev)
/* n_devs will reflect the actual number of allocated devices */
n_devs = i;
+ /* Determine qmenu items actually in use */
+ int hdmi_count = FIXED_MENU_ITEMS;
+ int svid_count = FIXED_MENU_ITEMS;
+
+ for (int i = 0; i < n_devs; i++) {
+ struct vivid_dev *dev = vivid_devs[i];
+
+ if (!dev->has_vid_out)
+ continue;
+ for (int j = 0; j < dev->num_outputs && hdmi_count < MAX_MENU_ITEMS; ++j) {
+ if (dev->output_type[j] == HDMI) {
+ vivid_ctrl_hdmi_to_output_instance[hdmi_count] = vivid_devs[i];
+ vivid_ctrl_hdmi_to_output_index[hdmi_count++] = j;
+ }
+ }
+ for (int j = 0; j < dev->num_outputs && svid_count < MAX_MENU_ITEMS; ++j) {
+ if (dev->output_type[j] == SVID) {
+ vivid_ctrl_svid_to_output_instance[svid_count] = vivid_devs[i];
+ vivid_ctrl_svid_to_output_index[svid_count++] = j;
+ }
+ }
+ }
+ hdmi_count = min(hdmi_count, MAX_MENU_ITEMS);
+ svid_count = min(svid_count, MAX_MENU_ITEMS);
+ for (int i = 0; i < n_devs; i++) {
+ for (int j = 0; j < vivid_devs[i]->num_hdmi_inputs; j++) {
+ struct v4l2_ctrl *c = vivid_devs[i]->ctrl_hdmi_to_output[j];
+
+ v4l2_ctrl_modify_range(c, c->minimum, hdmi_count - 1, 0, c->default_value);
+ }
+ for (int j = 0; j < vivid_devs[i]->num_svid_inputs; j++) {
+ struct v4l2_ctrl *c = vivid_devs[i]->ctrl_svid_to_output[j];
+
+ v4l2_ctrl_modify_range(c, c->minimum, svid_count - 1, 0, c->default_value);
+ }
+ }
return ret;
}
@@ -2109,7 +2219,7 @@ static void vivid_remove(struct platform_device *pdev)
vb2_video_unregister_device(&dev->touch_cap_dev);
}
cec_unregister_adapter(dev->cec_rx_adap);
- for (j = 0; j < MAX_OUTPUTS; j++)
+ for (j = 0; j < MAX_HDMI_OUTPUTS; j++)
cec_unregister_adapter(dev->cec_tx_adap[j]);
if (dev->kthread_cec)
kthread_stop(dev->kthread_cec);
@@ -2137,21 +2247,91 @@ static struct platform_driver vivid_pdrv = {
static int __init vivid_init(void)
{
- int ret;
-
+ int hdmi_count = FIXED_MENU_ITEMS;
+ int svid_count = FIXED_MENU_ITEMS;
+ int ret = -ENOMEM;
+ unsigned int ndevs;
+
+ /* Sanity check, prevent insane number of vivid instances */
+ if (n_devs > 64)
+ n_devs = 64;
+ ndevs = clamp_t(unsigned int, n_devs, 1, VIVID_MAX_DEVS);
+
+ for (unsigned int i = 0; i < ndevs; i++) {
+ if (!(node_types[i] & (1 << 8)))
+ continue;
+ unsigned int n_outputs = min(num_outputs[i], MAX_OUTPUTS);
+
+ for (u8 j = 0, k = 0; j < n_outputs && hdmi_count < MAX_MENU_ITEMS &&
+ k < MAX_HDMI_OUTPUTS; ++j) {
+ if (output_types[i] & BIT(j)) {
+ vivid_ctrl_hdmi_to_output_strings[hdmi_count] =
+ kmalloc(MAX_STRING_LENGTH, GFP_KERNEL);
+ if (!vivid_ctrl_hdmi_to_output_strings[hdmi_count])
+ goto free_output_strings;
+ snprintf(vivid_ctrl_hdmi_to_output_strings[hdmi_count],
+ MAX_STRING_LENGTH, "Output HDMI %03d-%d",
+ i & 0xff, k);
+ k++;
+ hdmi_count++;
+ }
+ }
+ for (u8 j = 0, k = 0; j < n_outputs && svid_count < MAX_MENU_ITEMS; ++j) {
+ if (!(output_types[i] & BIT(j))) {
+ vivid_ctrl_svid_to_output_strings[svid_count] =
+ kmalloc(MAX_STRING_LENGTH, GFP_KERNEL);
+ if (!vivid_ctrl_svid_to_output_strings[svid_count])
+ goto free_output_strings;
+ snprintf(vivid_ctrl_svid_to_output_strings[svid_count],
+ MAX_STRING_LENGTH, "Output S-Video %03d-%d",
+ i & 0xff, k);
+ k++;
+ svid_count++;
+ }
+ }
+ }
ret = platform_device_register(&vivid_pdev);
if (ret)
- return ret;
-
+ goto free_output_strings;
ret = platform_driver_register(&vivid_pdrv);
if (ret)
- platform_device_unregister(&vivid_pdev);
+ goto unreg_device;
+ /* Initialize workqueue before module is loaded */
+ update_hdmi_ctrls_workqueue = create_workqueue("update_hdmi_ctrls_wq");
+ if (!update_hdmi_ctrls_workqueue) {
+ ret = -ENOMEM;
+ goto unreg_driver;
+ }
+ update_svid_ctrls_workqueue = create_workqueue("update_svid_ctrls_wq");
+ if (!update_svid_ctrls_workqueue) {
+ ret = -ENOMEM;
+ goto destroy_hdmi_wq;
+ }
+ return ret;
+
+destroy_hdmi_wq:
+ destroy_workqueue(update_hdmi_ctrls_workqueue);
+unreg_driver:
+ platform_driver_register(&vivid_pdrv);
+unreg_device:
+ platform_device_unregister(&vivid_pdev);
+free_output_strings:
+ for (int i = FIXED_MENU_ITEMS; i < MAX_MENU_ITEMS; i++) {
+ kfree(vivid_ctrl_hdmi_to_output_strings[i]);
+ kfree(vivid_ctrl_svid_to_output_strings[i]);
+ }
return ret;
}
static void __exit vivid_exit(void)
{
+ for (int i = FIXED_MENU_ITEMS; i < MAX_MENU_ITEMS; i++) {
+ kfree(vivid_ctrl_hdmi_to_output_strings[i]);
+ kfree(vivid_ctrl_svid_to_output_strings[i]);
+ }
+ destroy_workqueue(update_svid_ctrls_workqueue);
+ destroy_workqueue(update_hdmi_ctrls_workqueue);
platform_driver_unregister(&vivid_pdrv);
platform_device_unregister(&vivid_pdev);
}
diff --git a/drivers/media/test-drivers/vivid/vivid-core.h b/drivers/media/test-drivers/vivid/vivid-core.h
index cfb8e66083f6..cc18a3bc6dc0 100644
--- a/drivers/media/test-drivers/vivid/vivid-core.h
+++ b/drivers/media/test-drivers/vivid/vivid-core.h
@@ -50,10 +50,95 @@
#define JIFFIES_PER_DAY (3600U * 24U * HZ)
#define JIFFIES_RESYNC (JIFFIES_PER_DAY * (0xf0000000U / JIFFIES_PER_DAY))
+/*
+ * Maximum number of HDMI inputs allowed by vivid, due to limitations
+ * of the Physical Address in the EDID and used by CEC we stop at 15
+ * inputs and outputs.
+ */
+#define MAX_HDMI_INPUTS 15
+#define MAX_HDMI_OUTPUTS 15
+
+/* Maximum number of S-Video inputs allowed by vivid */
+#define MAX_SVID_INPUTS 16
+
+/* The maximum number of items in a menu control */
+#define MAX_MENU_ITEMS BITS_PER_LONG_LONG
+
+/* Number of fixed menu items in the 'Connected To' menu controls */
+#define FIXED_MENU_ITEMS 2
+
+/* The maximum number of vivid devices */
+#define VIVID_MAX_DEVS CONFIG_VIDEO_VIVID_MAX_DEVS
+
extern const struct v4l2_rect vivid_min_rect;
extern const struct v4l2_rect vivid_max_rect;
extern unsigned vivid_debug;
+/*
+ * NULL-terminated string array for the HDMI 'Connected To' menu controls
+ * with the list of possible HDMI outputs.
+ *
+ * The first two items are fixed ("TPG" and "None").
+ */
+extern char *vivid_ctrl_hdmi_to_output_strings[1 + MAX_MENU_ITEMS];
+/* Menu control skip mask of all HDMI outputs that are in use */
+extern u64 hdmi_to_output_menu_skip_mask;
+/*
+ * Bitmask of which vivid instances need to update any connected
+ * HDMI outputs.
+ */
+extern u64 hdmi_input_update_outputs_mask;
+/*
+ * Spinlock for access to hdmi_to_output_menu_skip_mask and
+ * hdmi_input_update_outputs_mask.
+ */
+extern spinlock_t hdmi_output_skip_mask_lock;
+/*
+ * Workqueue that updates the menu controls whenever the HDMI menu skip mask
+ * changes.
+ */
+extern struct workqueue_struct *update_hdmi_ctrls_workqueue;
+
+/*
+ * The HDMI menu control value (index in the menu list) maps to an HDMI
+ * output that is part of the given vivid_dev instance and has the given
+ * output index (as returned by VIDIOC_G_OUTPUT).
+ *
+ * NULL/0 if not available.
+ */
+extern struct vivid_dev *vivid_ctrl_hdmi_to_output_instance[MAX_MENU_ITEMS];
+extern unsigned int vivid_ctrl_hdmi_to_output_index[MAX_MENU_ITEMS];
+
+/*
+ * NULL-terminated string array for the S-Video 'Connected To' menu controls
+ * with the list of possible S-Video outputs.
+ *
+ * The first two items are fixed ("TPG" and "None").
+ */
+extern char *vivid_ctrl_svid_to_output_strings[1 + MAX_MENU_ITEMS];
+/* Menu control skip mask of all S-Video outputs that are in use */
+extern u64 svid_to_output_menu_skip_mask;
+/* Spinlock for access to svid_to_output_menu_skip_mask */
+extern spinlock_t svid_output_skip_mask_lock;
+/*
+ * Workqueue that updates the menu controls whenever the S-Video menu skip mask
+ * changes.
+ */
+extern struct workqueue_struct *update_svid_ctrls_workqueue;
+
+/*
+ * The S-Video menu control value (index in the menu list) maps to an S-Video
+ * output that is part of the given vivid_dev instance and has the given
+ * output index (as returned by VIDIOC_G_OUTPUT).
+ *
+ * NULL/0 if not available.
+ */
+extern struct vivid_dev *vivid_ctrl_svid_to_output_instance[MAX_MENU_ITEMS];
+extern unsigned int vivid_ctrl_svid_to_output_index[MAX_MENU_ITEMS];
+
+extern struct vivid_dev *vivid_devs[VIVID_MAX_DEVS];
+extern unsigned int n_devs;
+
struct vivid_fmt {
u32 fourcc; /* v4l2 format id */
enum tgp_color_enc color_enc;
@@ -118,7 +203,7 @@ struct vivid_cec_xfer {
};
struct vivid_dev {
- unsigned inst;
+ u8 inst;
struct v4l2_device v4l2_dev;
#ifdef CONFIG_MEDIA_CONTROLLER
struct media_device mdev;
@@ -161,6 +246,8 @@ struct vivid_dev {
spinlock_t slock;
struct mutex mutex;
+ struct work_struct update_hdmi_ctrl_work;
+ struct work_struct update_svid_ctrl_work;
/* capabilities */
u32 vid_cap_caps;
@@ -176,12 +263,13 @@ struct vivid_dev {
/* supported features */
bool multiplanar;
- unsigned num_inputs;
- unsigned int num_hdmi_inputs;
+ u8 num_inputs;
+ u8 num_hdmi_inputs;
+ u8 num_svid_inputs;
u8 input_type[MAX_INPUTS];
u8 input_name_counter[MAX_INPUTS];
- unsigned num_outputs;
- unsigned int num_hdmi_outputs;
+ u8 num_outputs;
+ u8 num_hdmi_outputs;
u8 output_type[MAX_OUTPUTS];
u8 output_name_counter[MAX_OUTPUTS];
bool has_audio_inputs;
@@ -203,7 +291,20 @@ struct vivid_dev {
bool has_tv_tuner;
bool has_touch_cap;
- bool can_loop_video;
+ /* Output index (0-MAX_OUTPUTS) to vivid instance of connected input */
+ struct vivid_dev *output_to_input_instance[MAX_OUTPUTS];
+ /* Output index (0-MAX_OUTPUTS) to input index (0-MAX_INPUTS) of connected input */
+ u8 output_to_input_index[MAX_OUTPUTS];
+ /* Output index (0-MAX_OUTPUTS) to HDMI or S-Video output index (0-MAX_HDMI/SVID_OUTPUTS) */
+ u8 output_to_iface_index[MAX_OUTPUTS];
+ /* ctrl_hdmi_to_output or ctrl_svid_to_output control value for each input */
+ s32 input_is_connected_to_output[MAX_INPUTS];
+ /* HDMI index (0-MAX_HDMI_OUTPUTS) to output index (0-MAX_OUTPUTS) */
+ u8 hdmi_index_to_output_index[MAX_HDMI_OUTPUTS];
+ /* HDMI index (0-MAX_HDMI_INPUTS) to input index (0-MAX_INPUTS) */
+ u8 hdmi_index_to_input_index[MAX_HDMI_INPUTS];
+ /* S-Video index (0-MAX_SVID_INPUTS) to input index (0-MAX_INPUTS) */
+ u8 svid_index_to_input_index[MAX_SVID_INPUTS];
/* controls */
struct v4l2_ctrl *brightness;
@@ -242,7 +343,6 @@ struct vivid_dev {
struct v4l2_ctrl *ctrl_dv_timings_signal_mode;
struct v4l2_ctrl *ctrl_dv_timings;
};
- struct v4l2_ctrl *ctrl_display_present;
struct v4l2_ctrl *ctrl_has_crop_cap;
struct v4l2_ctrl *ctrl_has_compose_cap;
struct v4l2_ctrl *ctrl_has_scaler_cap;
@@ -276,6 +376,11 @@ struct vivid_dev {
struct v4l2_ctrl *radio_rx_rds_psname;
struct v4l2_ctrl *radio_rx_rds_radiotext;
+ struct v4l2_ctrl *ctrl_hdmi_to_output[MAX_HDMI_INPUTS];
+ char ctrl_hdmi_to_output_names[MAX_HDMI_INPUTS][32];
+ struct v4l2_ctrl *ctrl_svid_to_output[MAX_SVID_INPUTS];
+ char ctrl_svid_to_output_names[MAX_SVID_INPUTS][32];
+
unsigned input_brightness[MAX_INPUTS];
unsigned osd_mode;
unsigned button_pressed;
@@ -364,7 +469,6 @@ struct vivid_dev {
u8 *scaled_line;
u8 *blended_line;
unsigned cur_scaled_line;
- bool display_present[MAX_OUTPUTS];
/* Output Overlay */
void *fb_vbase_out;
@@ -544,11 +648,10 @@ struct vivid_dev {
/* CEC */
struct cec_adapter *cec_rx_adap;
- struct cec_adapter *cec_tx_adap[MAX_OUTPUTS];
- u8 cec_output2bus_map[MAX_OUTPUTS];
+ struct cec_adapter *cec_tx_adap[MAX_HDMI_OUTPUTS];
struct task_struct *kthread_cec;
wait_queue_head_t kthread_waitq_cec;
- struct vivid_cec_xfer xfers[MAX_OUTPUTS];
+ struct vivid_cec_xfer xfers[MAX_OUTPUTS];
spinlock_t cec_xfers_slock; /* read and write cec messages */
u32 cec_sft; /* bus signal free time, in bit periods */
u8 last_initiator;
diff --git a/drivers/media/test-drivers/vivid/vivid-ctrls.c b/drivers/media/test-drivers/vivid/vivid-ctrls.c
index f2b20e25a7a4..8bb38bc7b8cc 100644
--- a/drivers/media/test-drivers/vivid/vivid-ctrls.c
+++ b/drivers/media/test-drivers/vivid/vivid-ctrls.c
@@ -18,7 +18,6 @@
#include "vivid-radio-common.h"
#include "vivid-osd.h"
#include "vivid-ctrls.h"
-#include "vivid-cec.h"
#define VIVID_CID_CUSTOM_BASE (V4L2_CID_USER_BASE | 0xf000)
#define VIVID_CID_BUTTON (VIVID_CID_CUSTOM_BASE + 0)
@@ -69,14 +68,12 @@
#define VIVID_CID_HAS_CROP_OUT (VIVID_CID_VIVID_BASE + 34)
#define VIVID_CID_HAS_COMPOSE_OUT (VIVID_CID_VIVID_BASE + 35)
#define VIVID_CID_HAS_SCALER_OUT (VIVID_CID_VIVID_BASE + 36)
-#define VIVID_CID_LOOP_VIDEO (VIVID_CID_VIVID_BASE + 37)
#define VIVID_CID_SEQ_WRAP (VIVID_CID_VIVID_BASE + 38)
#define VIVID_CID_TIME_WRAP (VIVID_CID_VIVID_BASE + 39)
#define VIVID_CID_MAX_EDID_BLOCKS (VIVID_CID_VIVID_BASE + 40)
#define VIVID_CID_PERCENTAGE_FILL (VIVID_CID_VIVID_BASE + 41)
#define VIVID_CID_REDUCED_FPS (VIVID_CID_VIVID_BASE + 42)
#define VIVID_CID_HSV_ENC (VIVID_CID_VIVID_BASE + 43)
-#define VIVID_CID_DISPLAY_PRESENT (VIVID_CID_VIVID_BASE + 44)
#define VIVID_CID_STD_SIGNAL_MODE (VIVID_CID_VIVID_BASE + 60)
#define VIVID_CID_STANDARD (VIVID_CID_VIVID_BASE + 61)
@@ -104,6 +101,12 @@
#define VIVID_CID_META_CAP_GENERATE_PTS (VIVID_CID_VIVID_BASE + 111)
#define VIVID_CID_META_CAP_GENERATE_SCR (VIVID_CID_VIVID_BASE + 112)
+/* HDMI inputs are in the range 0-14. The next available CID is VIVID_CID_VIVID_BASE + 128 */
+#define VIVID_CID_HDMI_IS_CONNECTED_TO_OUTPUT(input) (VIVID_CID_VIVID_BASE + 113 + (input))
+
+/* S-Video inputs are in the range 0-15. The next available CID is VIVID_CID_VIVID_BASE + 144 */
+#define VIVID_CID_SVID_IS_CONNECTED_TO_OUTPUT(input) (VIVID_CID_VIVID_BASE + 128 + (input))
+
/* General User Controls */
static void vivid_unregister_dev(bool valid, struct video_device *vdev)
@@ -439,6 +442,33 @@ static const struct v4l2_ctrl_ops vivid_user_vid_ctrl_ops = {
/* Video Capture Controls */
+static void vivid_update_power_present(struct vivid_dev *dev)
+{
+ unsigned int i, j;
+
+ dev->power_present = 0;
+ for (i = 0, j = 0;
+ i < ARRAY_SIZE(dev->dv_timings_signal_mode); i++) {
+ if (dev->input_type[i] != HDMI)
+ continue;
+ /*
+ * If connected to TPG or HDMI output, and the signal
+ * mode is not NO_SIGNAL, then there is power present.
+ */
+ if (dev->input_is_connected_to_output[i] != 1 &&
+ dev->dv_timings_signal_mode[i] != NO_SIGNAL)
+ dev->power_present |= (1 << j);
+ j++;
+ }
+
+ __v4l2_ctrl_s_ctrl(dev->ctrl_rx_power_present,
+ dev->power_present);
+
+ v4l2_ctrl_activate(dev->ctrl_dv_timings,
+ dev->dv_timings_signal_mode[dev->input] ==
+ SELECTED_DV_TIMINGS);
+}
+
static int vivid_vid_cap_s_ctrl(struct v4l2_ctrl *ctrl)
{
static const u32 colorspaces[] = {
@@ -453,7 +483,11 @@ static int vivid_vid_cap_s_ctrl(struct v4l2_ctrl *ctrl)
V4L2_COLORSPACE_470_SYSTEM_BG,
};
struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_vid_cap);
- unsigned int i, j;
+ unsigned int i;
+ struct vivid_dev *output_inst = NULL;
+ int index = 0;
+ int hdmi_index, svid_index;
+ s32 input_index = 0;
switch (ctrl->id) {
case VIVID_CID_TEST_PATTERN:
@@ -569,25 +603,9 @@ static int vivid_vid_cap_s_ctrl(struct v4l2_ctrl *ctrl)
dev->dv_timings_signal_mode[dev->input] =
dev->ctrl_dv_timings_signal_mode->val;
dev->query_dv_timings[dev->input] = dev->ctrl_dv_timings->val;
-
- dev->power_present = 0;
- for (i = 0, j = 0;
- i < ARRAY_SIZE(dev->dv_timings_signal_mode);
- i++)
- if (dev->input_type[i] == HDMI) {
- if (dev->dv_timings_signal_mode[i] != NO_SIGNAL)
- dev->power_present |= (1 << j);
- j++;
- }
- __v4l2_ctrl_s_ctrl(dev->ctrl_rx_power_present,
- dev->power_present);
-
- v4l2_ctrl_activate(dev->ctrl_dv_timings,
- dev->dv_timings_signal_mode[dev->input] ==
- SELECTED_DV_TIMINGS);
-
+ vivid_update_power_present(dev);
vivid_update_quality(dev);
- vivid_send_source_change(dev, HDMI);
+ vivid_send_input_source_change(dev, dev->input);
break;
case VIVID_CID_DV_TIMINGS_ASPECT_RATIO:
dev->dv_timings_aspect_ratio[dev->input] = ctrl->val;
@@ -604,6 +622,67 @@ static int vivid_vid_cap_s_ctrl(struct v4l2_ctrl *ctrl)
if (dev->edid_blocks > dev->edid_max_blocks)
dev->edid_blocks = dev->edid_max_blocks;
break;
+ case VIVID_CID_HDMI_IS_CONNECTED_TO_OUTPUT(0) ... VIVID_CID_HDMI_IS_CONNECTED_TO_OUTPUT(14):
+ hdmi_index = ctrl->id - VIVID_CID_HDMI_IS_CONNECTED_TO_OUTPUT(0);
+ output_inst = vivid_ctrl_hdmi_to_output_instance[ctrl->cur.val];
+ index = vivid_ctrl_hdmi_to_output_index[ctrl->cur.val];
+ input_index = dev->hdmi_index_to_input_index[hdmi_index];
+ dev->input_is_connected_to_output[input_index] = ctrl->val;
+
+ if (output_inst) {
+ output_inst->output_to_input_instance[index] = NULL;
+ vivid_update_outputs(output_inst);
+ cec_phys_addr_invalidate(output_inst->cec_tx_adap[index]);
+ }
+ if (ctrl->val >= FIXED_MENU_ITEMS) {
+ output_inst = vivid_ctrl_hdmi_to_output_instance[ctrl->val];
+ index = vivid_ctrl_hdmi_to_output_index[ctrl->val];
+ output_inst->output_to_input_instance[index] = dev;
+ output_inst->output_to_input_index[index] =
+ dev->hdmi_index_to_input_index[hdmi_index];
+ }
+ spin_lock(&hdmi_output_skip_mask_lock);
+ hdmi_to_output_menu_skip_mask &= ~(1ULL << ctrl->cur.val);
+ if (ctrl->val >= FIXED_MENU_ITEMS)
+ hdmi_to_output_menu_skip_mask |= 1ULL << ctrl->val;
+ spin_unlock(&hdmi_output_skip_mask_lock);
+ vivid_update_power_present(dev);
+ vivid_update_quality(dev);
+ vivid_send_input_source_change(dev, dev->hdmi_index_to_input_index[hdmi_index]);
+ if (ctrl->val < FIXED_MENU_ITEMS && ctrl->cur.val < FIXED_MENU_ITEMS)
+ break;
+ spin_lock(&hdmi_output_skip_mask_lock);
+ hdmi_input_update_outputs_mask |= 1 << dev->inst;
+ spin_unlock(&hdmi_output_skip_mask_lock);
+ queue_work(update_hdmi_ctrls_workqueue, &dev->update_hdmi_ctrl_work);
+ break;
+ case VIVID_CID_SVID_IS_CONNECTED_TO_OUTPUT(0) ... VIVID_CID_SVID_IS_CONNECTED_TO_OUTPUT(15):
+ svid_index = ctrl->id - VIVID_CID_SVID_IS_CONNECTED_TO_OUTPUT(0);
+ output_inst = vivid_ctrl_svid_to_output_instance[ctrl->cur.val];
+ index = vivid_ctrl_svid_to_output_index[ctrl->cur.val];
+ input_index = dev->svid_index_to_input_index[svid_index];
+ dev->input_is_connected_to_output[input_index] = ctrl->val;
+
+ if (output_inst)
+ output_inst->output_to_input_instance[index] = NULL;
+ if (ctrl->val >= FIXED_MENU_ITEMS) {
+ output_inst = vivid_ctrl_svid_to_output_instance[ctrl->val];
+ index = vivid_ctrl_svid_to_output_index[ctrl->val];
+ output_inst->output_to_input_instance[index] = dev;
+ output_inst->output_to_input_index[index] =
+ dev->svid_index_to_input_index[svid_index];
+ }
+ spin_lock(&svid_output_skip_mask_lock);
+ svid_to_output_menu_skip_mask &= ~(1ULL << ctrl->cur.val);
+ if (ctrl->val >= FIXED_MENU_ITEMS)
+ svid_to_output_menu_skip_mask |= 1ULL << ctrl->val;
+ spin_unlock(&svid_output_skip_mask_lock);
+ vivid_update_quality(dev);
+ vivid_send_input_source_change(dev, dev->svid_index_to_input_index[svid_index]);
+ if (ctrl->val < FIXED_MENU_ITEMS && ctrl->cur.val < FIXED_MENU_ITEMS)
+ break;
+ queue_work(update_svid_ctrls_workqueue, &dev->update_svid_ctrl_work);
+ break;
}
return 0;
}
@@ -966,37 +1045,6 @@ static const struct v4l2_ctrl_config vivid_ctrl_limited_rgb_range = {
};
-/* Video Loop Control */
-
-static int vivid_loop_cap_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_loop_cap);
-
- switch (ctrl->id) {
- case VIVID_CID_LOOP_VIDEO:
- dev->loop_video = ctrl->val;
- vivid_update_quality(dev);
- vivid_send_source_change(dev, SVID);
- vivid_send_source_change(dev, HDMI);
- break;
- }
- return 0;
-}
-
-static const struct v4l2_ctrl_ops vivid_loop_cap_ctrl_ops = {
- .s_ctrl = vivid_loop_cap_s_ctrl,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_loop_video = {
- .ops = &vivid_loop_cap_ctrl_ops,
- .id = VIVID_CID_LOOP_VIDEO,
- .name = "Loop Video",
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .max = 1,
- .step = 1,
-};
-
-
/* VBI Capture Control */
static int vivid_vbi_cap_s_ctrl(struct v4l2_ctrl *ctrl)
@@ -1031,8 +1079,6 @@ static int vivid_vid_out_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_vid_out);
struct v4l2_bt_timings *bt = &dev->dv_timings_out.bt;
- u32 display_present = 0;
- unsigned int i, j, bus_idx;
switch (ctrl->id) {
case VIVID_CID_HAS_CROP_OUT:
@@ -1063,39 +1109,11 @@ static int vivid_vid_out_s_ctrl(struct v4l2_ctrl *ctrl)
V4L2_QUANTIZATION_LIM_RANGE :
V4L2_QUANTIZATION_DEFAULT;
}
- if (dev->loop_video)
- vivid_send_source_change(dev, HDMI);
- break;
- case VIVID_CID_DISPLAY_PRESENT:
- if (dev->output_type[dev->output] != HDMI)
- break;
-
- dev->display_present[dev->output] = ctrl->val;
- 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_rxsense, display_present);
+ if (vivid_output_is_connected_to(dev)) {
+ struct vivid_dev *dev_rx = vivid_output_is_connected_to(dev);
- if (dev->edid_blocks) {
- __v4l2_ctrl_s_ctrl(dev->ctrl_tx_edid_present,
- display_present);
- __v4l2_ctrl_s_ctrl(dev->ctrl_tx_hotplug,
- display_present);
+ vivid_send_source_change(dev_rx, HDMI);
}
-
- bus_idx = dev->cec_output2bus_map[dev->output];
- if (!dev->cec_tx_adap[bus_idx])
- break;
-
- if (ctrl->val && dev->edid_blocks)
- cec_s_phys_addr(dev->cec_tx_adap[bus_idx],
- dev->cec_tx_adap[bus_idx]->phys_addr,
- false);
- else
- cec_phys_addr_invalidate(dev->cec_tx_adap[bus_idx]);
-
break;
}
return 0;
@@ -1135,16 +1153,6 @@ static const struct v4l2_ctrl_config vivid_ctrl_has_scaler_out = {
.step = 1,
};
-static const struct v4l2_ctrl_config vivid_ctrl_display_present = {
- .ops = &vivid_vid_out_ctrl_ops,
- .id = VIVID_CID_DISPLAY_PRESENT,
- .name = "Display Present",
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .max = 1,
- .def = 1,
- .step = 1,
-};
-
/* Streaming Controls */
static int vivid_streaming_s_ctrl(struct v4l2_ctrl *ctrl)
@@ -1710,6 +1718,48 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap,
v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_insert_eav, NULL);
v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_insert_hdmi_video_guard_band, NULL);
v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_reduced_fps, NULL);
+
+ WARN_ON(dev->num_hdmi_inputs > MAX_HDMI_INPUTS);
+ WARN_ON(dev->num_svid_inputs > MAX_SVID_INPUTS);
+
+ for (u8 i = 0; i < dev->num_hdmi_inputs; i++) {
+ snprintf(dev->ctrl_hdmi_to_output_names[i],
+ sizeof(dev->ctrl_hdmi_to_output_names[i]),
+ "HDMI %03u-%u Is Connected To", dev->inst, i);
+ }
+
+ for (u8 i = 0; i < dev->num_hdmi_inputs; i++) {
+ struct v4l2_ctrl_config ctrl_config = {
+ .ops = &vivid_vid_cap_ctrl_ops,
+ .id = VIVID_CID_HDMI_IS_CONNECTED_TO_OUTPUT(i),
+ .name = dev->ctrl_hdmi_to_output_names[i],
+ .type = V4L2_CTRL_TYPE_MENU,
+ .max = 1,
+ .qmenu = (const char * const *)vivid_ctrl_hdmi_to_output_strings,
+ };
+ dev->ctrl_hdmi_to_output[i] = v4l2_ctrl_new_custom(hdl_vid_cap,
+ &ctrl_config, NULL);
+ }
+
+ for (u8 i = 0; i < dev->num_svid_inputs; i++) {
+ snprintf(dev->ctrl_svid_to_output_names[i],
+ sizeof(dev->ctrl_svid_to_output_names[i]),
+ "S-Video %03u-%u Is Connected To", dev->inst, i);
+ }
+
+ for (u8 i = 0; i < dev->num_svid_inputs; i++) {
+ struct v4l2_ctrl_config ctrl_config = {
+ .ops = &vivid_vid_cap_ctrl_ops,
+ .id = VIVID_CID_SVID_IS_CONNECTED_TO_OUTPUT(i),
+ .name = dev->ctrl_svid_to_output_names[i],
+ .type = V4L2_CTRL_TYPE_MENU,
+ .max = 1,
+ .qmenu = (const char * const *)vivid_ctrl_svid_to_output_strings,
+ };
+ dev->ctrl_svid_to_output[i] = v4l2_ctrl_new_custom(hdl_vid_cap,
+ &ctrl_config, NULL);
+ }
+
if (show_ccs_cap) {
dev->ctrl_has_crop_cap = v4l2_ctrl_new_custom(hdl_vid_cap,
&vivid_ctrl_has_crop_cap, NULL);
@@ -1812,21 +1862,13 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap,
dev->ctrl_tx_mode = v4l2_ctrl_new_std_menu(hdl_vid_out, NULL,
V4L2_CID_DV_TX_MODE, V4L2_DV_TX_MODE_HDMI,
0, V4L2_DV_TX_MODE_HDMI);
- dev->ctrl_display_present = v4l2_ctrl_new_custom(hdl_vid_out,
- &vivid_ctrl_display_present, NULL);
- dev->ctrl_tx_hotplug = v4l2_ctrl_new_std(hdl_vid_out,
- NULL, V4L2_CID_DV_TX_HOTPLUG, 0, hdmi_output_mask,
- 0, hdmi_output_mask);
- dev->ctrl_tx_rxsense = v4l2_ctrl_new_std(hdl_vid_out,
- NULL, V4L2_CID_DV_TX_RXSENSE, 0, hdmi_output_mask,
- 0, hdmi_output_mask);
- dev->ctrl_tx_edid_present = v4l2_ctrl_new_std(hdl_vid_out,
- NULL, V4L2_CID_DV_TX_EDID_PRESENT, 0, hdmi_output_mask,
- 0, hdmi_output_mask);
+ dev->ctrl_tx_hotplug = v4l2_ctrl_new_std(hdl_vid_out, NULL,
+ V4L2_CID_DV_TX_HOTPLUG, 0, hdmi_output_mask, 0, 0);
+ dev->ctrl_tx_rxsense = v4l2_ctrl_new_std(hdl_vid_out, NULL,
+ V4L2_CID_DV_TX_RXSENSE, 0, hdmi_output_mask, 0, 0);
+ dev->ctrl_tx_edid_present = v4l2_ctrl_new_std(hdl_vid_out, NULL,
+ V4L2_CID_DV_TX_EDID_PRESENT, 0, hdmi_output_mask, 0, 0);
}
- if ((dev->has_vid_cap && dev->has_vid_out) ||
- (dev->has_vbi_cap && dev->has_vbi_out))
- v4l2_ctrl_new_custom(hdl_loop_cap, &vivid_ctrl_loop_video, NULL);
if (dev->has_fb)
v4l2_ctrl_new_custom(hdl_fb, &vivid_ctrl_clear_fb, NULL);
diff --git a/drivers/media/test-drivers/vivid/vivid-kthread-cap.c b/drivers/media/test-drivers/vivid/vivid-kthread-cap.c
index 42048727d7ff..669bd96da4c7 100644
--- a/drivers/media/test-drivers/vivid/vivid-kthread-cap.c
+++ b/drivers/media/test-drivers/vivid/vivid-kthread-cap.c
@@ -142,7 +142,7 @@ static void scale_line(const u8 *src, u8 *dst, unsigned srcw, unsigned dstw, uns
* (loop_vid_overlay). Finally calculate the part of the capture buffer that
* will receive that overlaid video.
*/
-static void vivid_precalc_copy_rects(struct vivid_dev *dev)
+static void vivid_precalc_copy_rects(struct vivid_dev *dev, struct vivid_dev *out_dev)
{
/* Framebuffer rectangle */
struct v4l2_rect r_fb = {
@@ -150,16 +150,16 @@ static void vivid_precalc_copy_rects(struct vivid_dev *dev)
};
/* Overlay window rectangle in framebuffer coordinates */
struct v4l2_rect r_overlay = {
- dev->overlay_out_left, dev->overlay_out_top,
- dev->compose_out.width, dev->compose_out.height
+ out_dev->overlay_out_left, out_dev->overlay_out_top,
+ out_dev->compose_out.width, out_dev->compose_out.height
};
- v4l2_rect_intersect(&dev->loop_vid_copy, &dev->crop_cap, &dev->compose_out);
+ v4l2_rect_intersect(&dev->loop_vid_copy, &dev->crop_cap, &out_dev->compose_out);
dev->loop_vid_out = dev->loop_vid_copy;
- v4l2_rect_scale(&dev->loop_vid_out, &dev->compose_out, &dev->crop_out);
- dev->loop_vid_out.left += dev->crop_out.left;
- dev->loop_vid_out.top += dev->crop_out.top;
+ v4l2_rect_scale(&dev->loop_vid_out, &out_dev->compose_out, &out_dev->crop_out);
+ dev->loop_vid_out.left += out_dev->crop_out.left;
+ dev->loop_vid_out.top += out_dev->crop_out.top;
dev->loop_vid_cap = dev->loop_vid_copy;
v4l2_rect_scale(&dev->loop_vid_cap, &dev->crop_cap, &dev->compose_cap);
@@ -176,15 +176,15 @@ static void vivid_precalc_copy_rects(struct vivid_dev *dev)
v4l2_rect_intersect(&r_overlay, &r_fb, &r_overlay);
/* shift r_overlay to the same origin as compose_out */
- r_overlay.left += dev->compose_out.left - dev->overlay_out_left;
- r_overlay.top += dev->compose_out.top - dev->overlay_out_top;
+ r_overlay.left += out_dev->compose_out.left - out_dev->overlay_out_left;
+ r_overlay.top += out_dev->compose_out.top - out_dev->overlay_out_top;
v4l2_rect_intersect(&dev->loop_vid_overlay, &r_overlay, &dev->loop_vid_copy);
dev->loop_fb_copy = dev->loop_vid_overlay;
/* shift dev->loop_fb_copy back again to the fb origin */
- dev->loop_fb_copy.left -= dev->compose_out.left - dev->overlay_out_left;
- dev->loop_fb_copy.top -= dev->compose_out.top - dev->overlay_out_top;
+ dev->loop_fb_copy.left -= out_dev->compose_out.left - out_dev->overlay_out_left;
+ dev->loop_fb_copy.top -= out_dev->compose_out.top - out_dev->overlay_out_top;
dev->loop_vid_overlay_cap = dev->loop_vid_overlay;
v4l2_rect_scale(&dev->loop_vid_overlay_cap, &dev->crop_cap, &dev->compose_cap);
@@ -213,24 +213,25 @@ static void *plane_vaddr(struct tpg_data *tpg, struct vivid_buffer *buf,
return vbuf;
}
-static noinline_for_stack int vivid_copy_buffer(struct vivid_dev *dev, unsigned p,
- u8 *vcapbuf, struct vivid_buffer *vid_cap_buf)
+static noinline_for_stack int vivid_copy_buffer(struct vivid_dev *dev,
+ struct vivid_dev *out_dev, unsigned p,
+ u8 *vcapbuf, struct vivid_buffer *vid_cap_buf)
{
bool blank = dev->must_blank[vid_cap_buf->vb.vb2_buf.index];
struct tpg_data *tpg = &dev->tpg;
struct vivid_buffer *vid_out_buf = NULL;
- unsigned vdiv = dev->fmt_out->vdownsampling[p];
+ unsigned vdiv = out_dev->fmt_out->vdownsampling[p];
unsigned twopixsize = tpg_g_twopixelsize(tpg, p);
unsigned img_width = tpg_hdiv(tpg, p, dev->compose_cap.width);
unsigned img_height = dev->compose_cap.height;
unsigned stride_cap = tpg->bytesperline[p];
- unsigned stride_out = dev->bytesperline_out[p];
+ unsigned stride_out = out_dev->bytesperline_out[p];
unsigned stride_osd = dev->display_byte_stride;
unsigned hmax = (img_height * tpg->perc_fill) / 100;
u8 *voutbuf;
u8 *vosdbuf = NULL;
unsigned y;
- bool blend = dev->fbuf_out_flags;
+ bool blend = out_dev->fbuf_out_flags;
/* Coarse scaling with Bresenham */
unsigned vid_out_int_part;
unsigned vid_out_fract_part;
@@ -247,8 +248,8 @@ static noinline_for_stack int vivid_copy_buffer(struct vivid_dev *dev, unsigned
vid_out_int_part = dev->loop_vid_out.height / dev->loop_vid_cap.height;
vid_out_fract_part = dev->loop_vid_out.height % dev->loop_vid_cap.height;
- if (!list_empty(&dev->vid_out_active))
- vid_out_buf = list_entry(dev->vid_out_active.next,
+ if (!list_empty(&out_dev->vid_out_active))
+ vid_out_buf = list_entry(out_dev->vid_out_active.next,
struct vivid_buffer, list);
if (vid_out_buf == NULL)
return -ENODATA;
@@ -256,8 +257,8 @@ static noinline_for_stack int vivid_copy_buffer(struct vivid_dev *dev, unsigned
vid_cap_buf->vb.field = vid_out_buf->vb.field;
voutbuf = plane_vaddr(tpg, vid_out_buf, p,
- dev->bytesperline_out, dev->fmt_out_rect.height);
- if (p < dev->fmt_out->buffers)
+ out_dev->bytesperline_out, out_dev->fmt_out_rect.height);
+ if (p < out_dev->fmt_out->buffers)
voutbuf += vid_out_buf->vb.vb2_buf.planes[p].data_offset;
voutbuf += tpg_hdiv(tpg, p, dev->loop_vid_out.left) +
(dev->loop_vid_out.top / vdiv) * stride_out;
@@ -274,7 +275,7 @@ static noinline_for_stack int vivid_copy_buffer(struct vivid_dev *dev, unsigned
return 0;
}
- if (dev->overlay_out_enabled &&
+ if (out_dev->overlay_out_enabled &&
dev->loop_vid_overlay.width && dev->loop_vid_overlay.height) {
vosdbuf = dev->video_vbase;
vosdbuf += (dev->loop_fb_copy.left * twopixsize) / 2 +
@@ -385,6 +386,7 @@ update_vid_out_y:
static void vivid_fillbuff(struct vivid_dev *dev, struct vivid_buffer *buf)
{
+ struct vivid_dev *out_dev = NULL;
struct tpg_data *tpg = &dev->tpg;
unsigned factor = V4L2_FIELD_HAS_T_OR_B(dev->field_cap) ? 2 : 1;
unsigned line_height = 16 / factor;
@@ -396,14 +398,6 @@ static void vivid_fillbuff(struct vivid_dev *dev, struct vivid_buffer *buf)
unsigned ms;
char str[100];
s32 gain;
- bool is_loop = false;
-
- if (dev->loop_video && dev->can_loop_video &&
- ((vivid_is_svid_cap(dev) &&
- !VIVID_INVALID_SIGNAL(dev->std_signal_mode[dev->input])) ||
- (vivid_is_hdmi_cap(dev) &&
- !VIVID_INVALID_SIGNAL(dev->dv_timings_signal_mode[dev->input]))))
- is_loop = true;
buf->vb.sequence = dev->vid_cap_seq_count;
v4l2_ctrl_s_ctrl(dev->ro_int32, buf->vb.sequence & 0xff);
@@ -428,7 +422,34 @@ static void vivid_fillbuff(struct vivid_dev *dev, struct vivid_buffer *buf)
dev->field_cap == V4L2_FIELD_ALTERNATE);
tpg_s_perc_fill_blank(tpg, dev->must_blank[buf->vb.vb2_buf.index]);
- vivid_precalc_copy_rects(dev);
+ if (vivid_vid_can_loop(dev) &&
+ ((vivid_is_svid_cap(dev) &&
+ !VIVID_INVALID_SIGNAL(dev->std_signal_mode[dev->input])) ||
+ (vivid_is_hdmi_cap(dev) &&
+ !VIVID_INVALID_SIGNAL(dev->dv_timings_signal_mode[dev->input])))) {
+ out_dev = vivid_input_is_connected_to(dev);
+ /*
+ * If the vivid instance of the output device is different
+ * from the vivid instance of this input device, then we
+ * must take care to properly serialize the output device to
+ * prevent that the buffer we are copying from is being freed.
+ *
+ * If the output device is part of the same instance, then the
+ * lock is already taken and there is no need to take the mutex.
+ *
+ * The problem with taking the mutex is that you can get
+ * deadlocked if instance A locks instance B and vice versa.
+ * It is not really worth trying to be very smart about this,
+ * so just try to take the lock, and if you can't, then just
+ * set out_dev to NULL and you will end up with a single frame
+ * of Noise (the default test pattern in this case).
+ */
+ if (out_dev && dev != out_dev && !mutex_trylock(&out_dev->mutex))
+ out_dev = NULL;
+ }
+
+ if (out_dev)
+ vivid_precalc_copy_rects(dev, out_dev);
for (p = 0; p < tpg_g_planes(tpg); p++) {
void *vbuf = plane_vaddr(tpg, buf, p,
@@ -445,10 +466,13 @@ static void vivid_fillbuff(struct vivid_dev *dev, struct vivid_buffer *buf)
vbuf += dev->fmt_cap->data_offset[p];
}
tpg_calc_text_basep(tpg, basep, p, vbuf);
- if (!is_loop || vivid_copy_buffer(dev, p, vbuf, buf))
+ if (!out_dev || vivid_copy_buffer(dev, out_dev, p, vbuf, buf))
tpg_fill_plane_buffer(tpg, vivid_get_std_cap(dev),
p, vbuf);
}
+ if (out_dev && dev != out_dev)
+ mutex_unlock(&out_dev->mutex);
+
dev->must_blank[buf->vb.vb2_buf.index] = false;
/* Updates stream time, only update at the start of a new frame. */
diff --git a/drivers/media/test-drivers/vivid/vivid-sdr-cap.c b/drivers/media/test-drivers/vivid/vivid-sdr-cap.c
index a81f26b76988..38cda33dffb2 100644
--- a/drivers/media/test-drivers/vivid/vivid-sdr-cap.c
+++ b/drivers/media/test-drivers/vivid/vivid-sdr-cap.c
@@ -219,8 +219,13 @@ static int sdr_cap_queue_setup(struct vb2_queue *vq,
unsigned sizes[], struct device *alloc_devs[])
{
/* 2 = max 16-bit sample returned */
- sizes[0] = SDR_CAP_SAMPLES_PER_BUF * 2;
+ u32 size = SDR_CAP_SAMPLES_PER_BUF * 2;
+
+ if (*nplanes)
+ return sizes[0] < size ? -EINVAL : 0;
+
*nplanes = 1;
+ sizes[0] = size;
return 0;
}
diff --git a/drivers/media/test-drivers/vivid/vivid-touch-cap.c b/drivers/media/test-drivers/vivid/vivid-touch-cap.c
index 3888c21b4d0c..3600b084bca5 100644
--- a/drivers/media/test-drivers/vivid/vivid-touch-cap.c
+++ b/drivers/media/test-drivers/vivid/vivid-touch-cap.c
@@ -17,13 +17,13 @@ static int touch_cap_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
unsigned int size = f->sizeimage;
if (*nplanes) {
- if (sizes[0] < size)
+ if (*nplanes != 1)
return -EINVAL;
- } else {
- sizes[0] = size;
+ return sizes[0] < size ? -EINVAL : 0;
}
*nplanes = 1;
+ sizes[0] = size;
return 0;
}
diff --git a/drivers/media/test-drivers/vivid/vivid-vbi-cap.c b/drivers/media/test-drivers/vivid/vivid-vbi-cap.c
index 3840b3a664ac..99138f63585c 100644
--- a/drivers/media/test-drivers/vivid/vivid-vbi-cap.c
+++ b/drivers/media/test-drivers/vivid/vivid-vbi-cap.c
@@ -14,6 +14,7 @@
#include "vivid-kthread-cap.h"
#include "vivid-vbi-cap.h"
#include "vivid-vbi-gen.h"
+#include "vivid-vid-common.h"
static void vivid_sliced_vbi_cap_fill(struct vivid_dev *dev, unsigned seqnr)
{
@@ -23,7 +24,7 @@ static void vivid_sliced_vbi_cap_fill(struct vivid_dev *dev, unsigned seqnr)
vivid_vbi_gen_sliced(vbi_gen, is_60hz, seqnr);
if (!is_60hz) {
- if (dev->loop_video) {
+ if (vivid_vid_can_loop(dev)) {
if (dev->vbi_out_have_wss) {
vbi_gen->data[12].data[0] = dev->vbi_out_wss[0];
vbi_gen->data[12].data[1] = dev->vbi_out_wss[1];
@@ -47,7 +48,7 @@ static void vivid_sliced_vbi_cap_fill(struct vivid_dev *dev, unsigned seqnr)
break;
}
}
- } else if (dev->loop_video && is_60hz) {
+ } else if (vivid_vid_can_loop(dev) && is_60hz) {
if (dev->vbi_out_have_cc[0]) {
vbi_gen->data[0].data[0] = dev->vbi_out_cc[0][0];
vbi_gen->data[0].data[1] = dev->vbi_out_cc[0][1];
@@ -132,6 +133,8 @@ static int vbi_cap_queue_setup(struct vb2_queue *vq,
if (!vivid_is_sdtv_cap(dev))
return -EINVAL;
+ if (*nplanes)
+ return sizes[0] < size ? -EINVAL : 0;
sizes[0] = size;
*nplanes = 1;
diff --git a/drivers/media/test-drivers/vivid/vivid-vbi-out.c b/drivers/media/test-drivers/vivid/vivid-vbi-out.c
index 434a10676417..871a56d93425 100644
--- a/drivers/media/test-drivers/vivid/vivid-vbi-out.c
+++ b/drivers/media/test-drivers/vivid/vivid-vbi-out.c
@@ -28,6 +28,8 @@ static int vbi_out_queue_setup(struct vb2_queue *vq,
if (!vivid_is_svid_out(dev))
return -EINVAL;
+ if (*nplanes)
+ return sizes[0] < size ? -EINVAL : 0;
sizes[0] = size;
*nplanes = 1;
diff --git a/drivers/media/test-drivers/vivid/vivid-vid-cap.c b/drivers/media/test-drivers/vivid/vivid-vid-cap.c
index 2804975fe278..69620e0a35a0 100644
--- a/drivers/media/test-drivers/vivid/vivid-vid-cap.c
+++ b/drivers/media/test-drivers/vivid/vivid-vid-cap.c
@@ -106,8 +106,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 {
@@ -210,9 +211,6 @@ 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++)
@@ -242,7 +240,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)
@@ -273,7 +270,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.
@@ -487,35 +484,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;
}
@@ -1072,13 +1069,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)
@@ -1086,16 +1083,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)
@@ -1537,13 +1534,65 @@ int vidioc_query_dv_timings(struct file *file, void *_fh,
return 0;
}
+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 *_fh,
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));
@@ -1552,11 +1601,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;
@@ -1573,24 +1622,7 @@ 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;
}
diff --git a/drivers/media/test-drivers/vivid/vivid-vid-cap.h b/drivers/media/test-drivers/vivid/vivid-vid-cap.h
index 949768652d38..7a8daf0af2ca 100644
--- a/drivers/media/test-drivers/vivid/vivid-vid-cap.h
+++ b/drivers/media/test-drivers/vivid/vivid-vid-cap.h
@@ -10,6 +10,8 @@
void vivid_update_quality(struct vivid_dev *dev);
void vivid_update_format_cap(struct vivid_dev *dev, bool keep_controls);
+void vivid_update_outputs(struct vivid_dev *dev);
+void vivid_update_connected_outputs(struct vivid_dev *dev);
enum tpg_video_aspect vivid_get_video_aspect(const struct vivid_dev *dev);
extern const v4l2_std_id vivid_standard[];
diff --git a/drivers/media/test-drivers/vivid/vivid-vid-common.c b/drivers/media/test-drivers/vivid/vivid-vid-common.c
index 38d788b5cf19..df7678db67fb 100644
--- a/drivers/media/test-drivers/vivid/vivid-vid-common.c
+++ b/drivers/media/test-drivers/vivid/vivid-vid-common.c
@@ -769,14 +769,55 @@ const struct vivid_fmt *vivid_get_format(struct vivid_dev *dev, u32 pixelformat)
return NULL;
}
+struct vivid_dev *vivid_output_is_connected_to(struct vivid_dev *dev)
+{
+ struct vivid_dev *input_inst = dev->output_to_input_instance[dev->output];
+
+ if (!input_inst)
+ return NULL;
+ if (input_inst->input != dev->output_to_input_index[dev->output])
+ return NULL;
+ return input_inst;
+}
+
+struct vivid_dev *vivid_input_is_connected_to(struct vivid_dev *dev)
+{
+ s32 connected_output = dev->input_is_connected_to_output[dev->input];
+
+ if (connected_output < FIXED_MENU_ITEMS)
+ return NULL;
+ struct vivid_dev *output_inst = NULL;
+
+ if (vivid_is_hdmi_cap(dev)) {
+ output_inst = vivid_ctrl_hdmi_to_output_instance[connected_output];
+ if (vivid_ctrl_hdmi_to_output_index[connected_output] != output_inst->output)
+ return NULL;
+ return output_inst;
+ } else if (vivid_is_svid_cap(dev)) {
+ output_inst = vivid_ctrl_svid_to_output_instance[connected_output];
+ if (vivid_ctrl_svid_to_output_index[connected_output] != output_inst->output)
+ return NULL;
+ return output_inst;
+ } else {
+ return NULL;
+ }
+ return output_inst;
+}
+
bool vivid_vid_can_loop(struct vivid_dev *dev)
{
- if (dev->src_rect.width != dev->sink_rect.width ||
- dev->src_rect.height != dev->sink_rect.height)
+ struct vivid_dev *output_inst = vivid_input_is_connected_to(dev);
+
+ if (!output_inst)
return false;
- if (dev->fmt_cap->fourcc != dev->fmt_out->fourcc)
+ if (!vb2_is_streaming(&output_inst->vb_vid_out_q))
return false;
- if (dev->field_cap != dev->field_out)
+ if (dev->src_rect.width != output_inst->sink_rect.width ||
+ dev->src_rect.height != output_inst->sink_rect.height)
+ return false;
+ if (dev->fmt_cap->fourcc != output_inst->fmt_out->fourcc)
+ return false;
+ if (dev->field_cap != output_inst->field_out)
return false;
/*
* While this can be supported, it is just too much work
@@ -785,34 +826,34 @@ bool vivid_vid_can_loop(struct vivid_dev *dev)
if (dev->field_cap == V4L2_FIELD_SEQ_TB ||
dev->field_cap == V4L2_FIELD_SEQ_BT)
return false;
- if (vivid_is_svid_cap(dev) && vivid_is_svid_out(dev)) {
- if (!(dev->std_cap[dev->input] & V4L2_STD_525_60) !=
- !(dev->std_out & V4L2_STD_525_60))
- return false;
+ if (vivid_is_hdmi_cap(dev))
return true;
- }
- if (vivid_is_hdmi_cap(dev) && vivid_is_hdmi_out(dev))
- return true;
- return false;
+ if (!(dev->std_cap[dev->input] & V4L2_STD_525_60) !=
+ !(output_inst->std_out & V4L2_STD_525_60))
+ return false;
+ return true;
}
-void vivid_send_source_change(struct vivid_dev *dev, unsigned type)
+void vivid_send_input_source_change(struct vivid_dev *dev, unsigned int input_index)
{
struct v4l2_event ev = {
.type = V4L2_EVENT_SOURCE_CHANGE,
.u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION,
};
- unsigned i;
+ ev.id = input_index;
- for (i = 0; i < dev->num_inputs; i++) {
- ev.id = i;
- if (dev->input_type[i] == type) {
- if (video_is_registered(&dev->vid_cap_dev) && dev->has_vid_cap)
- v4l2_event_queue(&dev->vid_cap_dev, &ev);
- if (video_is_registered(&dev->vbi_cap_dev) && dev->has_vbi_cap)
- v4l2_event_queue(&dev->vbi_cap_dev, &ev);
- }
- }
+ if (video_is_registered(&dev->vid_cap_dev) && dev->has_vid_cap)
+ v4l2_event_queue(&dev->vid_cap_dev, &ev);
+ if (dev->input_type[input_index] == TV || dev->input_type[input_index] == SVID)
+ if (video_is_registered(&dev->vbi_cap_dev) && dev->has_vbi_cap)
+ v4l2_event_queue(&dev->vbi_cap_dev, &ev);
+}
+
+void vivid_send_source_change(struct vivid_dev *dev, unsigned int type)
+{
+ for (int i = 0; i < dev->num_inputs; i++)
+ if (dev->input_type[i] == type)
+ vivid_send_input_source_change(dev, i);
}
/*
@@ -1036,8 +1077,10 @@ int vidioc_g_edid(struct file *file, void *_fh,
struct v4l2_edid *edid)
{
struct vivid_dev *dev = video_drvdata(file);
+ struct vivid_dev *dev_rx = dev;
struct video_device *vdev = video_devdata(file);
struct cec_adapter *adap;
+ unsigned int loc;
memset(edid->reserved, 0, sizeof(edid->reserved));
if (vdev->vfl_dir == VFL_DIR_RX) {
@@ -1047,29 +1090,48 @@ int vidioc_g_edid(struct file *file, void *_fh,
return -EINVAL;
adap = dev->cec_rx_adap;
} else {
- unsigned int bus_idx;
-
if (edid->pad >= dev->num_outputs)
return -EINVAL;
if (dev->output_type[edid->pad] != HDMI)
return -EINVAL;
- if (!dev->display_present[edid->pad])
+ dev_rx = dev->output_to_input_instance[edid->pad];
+ if (!dev_rx)
return -ENODATA;
- bus_idx = dev->cec_output2bus_map[edid->pad];
- adap = dev->cec_tx_adap[bus_idx];
+
+ unsigned int hdmi_output = dev->output_to_iface_index[edid->pad];
+
+ adap = dev->cec_tx_adap[hdmi_output];
}
if (edid->start_block == 0 && edid->blocks == 0) {
- edid->blocks = dev->edid_blocks;
+ edid->blocks = dev_rx->edid_blocks;
return 0;
}
- if (dev->edid_blocks == 0)
+ if (dev_rx->edid_blocks == 0)
return -ENODATA;
- if (edid->start_block >= dev->edid_blocks)
+ if (edid->start_block >= dev_rx->edid_blocks)
return -EINVAL;
- if (edid->blocks > dev->edid_blocks - edid->start_block)
- edid->blocks = dev->edid_blocks - edid->start_block;
- if (adap)
- v4l2_set_edid_phys_addr(dev->edid, dev->edid_blocks * 128, adap->phys_addr);
- memcpy(edid->edid, dev->edid + edid->start_block * 128, edid->blocks * 128);
+ if (edid->blocks > dev_rx->edid_blocks - edid->start_block)
+ edid->blocks = dev_rx->edid_blocks - edid->start_block;
+
+ memcpy(edid->edid, dev_rx->edid + edid->start_block * 128, edid->blocks * 128);
+
+ loc = cec_get_edid_spa_location(dev_rx->edid,
+ dev_rx->edid_blocks * 128);
+ if (vdev->vfl_dir == VFL_DIR_TX && adap && loc &&
+ loc >= edid->start_block * 128 &&
+ loc < (edid->start_block + edid->blocks) * 128) {
+ unsigned int i;
+ u8 sum = 0;
+
+ loc -= edid->start_block * 128;
+ edid->edid[loc] = adap->phys_addr >> 8;
+ edid->edid[loc + 1] = adap->phys_addr & 0xff;
+ loc &= ~0x7f;
+
+ /* update the checksum */
+ for (i = loc; i < loc + 127; i++)
+ sum += edid->edid[i];
+ edid->edid[i] = 256 - sum;
+ }
return 0;
}
diff --git a/drivers/media/test-drivers/vivid/vivid-vid-common.h b/drivers/media/test-drivers/vivid/vivid-vid-common.h
index d908d9725283..c49ac85abaed 100644
--- a/drivers/media/test-drivers/vivid/vivid-vid-common.h
+++ b/drivers/media/test-drivers/vivid/vivid-vid-common.h
@@ -22,8 +22,11 @@ extern const struct v4l2_dv_timings_cap vivid_dv_timings_cap;
const struct vivid_fmt *vivid_get_format(struct vivid_dev *dev, u32 pixelformat);
+struct vivid_dev *vivid_input_is_connected_to(struct vivid_dev *dev);
+struct vivid_dev *vivid_output_is_connected_to(struct vivid_dev *dev);
bool vivid_vid_can_loop(struct vivid_dev *dev);
-void vivid_send_source_change(struct vivid_dev *dev, unsigned type);
+void vivid_send_source_change(struct vivid_dev *dev, unsigned int type);
+void vivid_send_input_source_change(struct vivid_dev *dev, unsigned int input_index);
int vivid_vid_adjust_sel(unsigned flags, struct v4l2_rect *r);
diff --git a/drivers/media/test-drivers/vivid/vivid-vid-out.c b/drivers/media/test-drivers/vivid/vivid-vid-out.c
index 1653b2988f7e..60327f3612af 100644
--- a/drivers/media/test-drivers/vivid/vivid-vid-out.c
+++ b/drivers/media/test-drivers/vivid/vivid-vid-out.c
@@ -63,14 +63,16 @@ static int vid_out_queue_setup(struct vb2_queue *vq,
if (sizes[0] < size)
return -EINVAL;
for (p = 1; p < planes; p++) {
- if (sizes[p] < dev->bytesperline_out[p] * h +
- vfmt->data_offset[p])
+ if (sizes[p] < dev->bytesperline_out[p] * h /
+ vfmt->vdownsampling[p] +
+ vfmt->data_offset[p])
return -EINVAL;
}
} else {
for (p = 0; p < planes; p++)
- sizes[p] = p ? dev->bytesperline_out[p] * h +
- vfmt->data_offset[p] : size;
+ sizes[p] = p ? dev->bytesperline_out[p] * h /
+ vfmt->vdownsampling[p] +
+ vfmt->data_offset[p] : size;
}
*nplanes = planes;
@@ -124,7 +126,7 @@ static int vid_out_buf_prepare(struct vb2_buffer *vb)
for (p = 0; p < planes; p++) {
if (p)
- size = dev->bytesperline_out[p] * h;
+ size = dev->bytesperline_out[p] * h / vfmt->vdownsampling[p];
size += vb->planes[p].data_offset;
if (vb2_get_plane_payload(vb, p) < size) {
@@ -155,9 +157,6 @@ static int vid_out_start_streaming(struct vb2_queue *vq, unsigned count)
struct vivid_dev *dev = vb2_get_drv_priv(vq);
int err;
- if (vb2_is_streaming(&dev->vb_vid_cap_q))
- dev->can_loop_video = vivid_vid_can_loop(dev);
-
dev->vid_out_seq_count = 0;
dprintk(dev, 1, "%s\n", __func__);
if (dev->start_streaming_error) {
@@ -185,7 +184,6 @@ static void vid_out_stop_streaming(struct vb2_queue *vq)
dprintk(dev, 1, "%s\n", __func__);
vivid_stop_generating_vid_out(dev, &dev->vid_out_streaming);
- dev->can_loop_video = false;
}
static void vid_out_buf_request_complete(struct vb2_buffer *vb)
@@ -331,8 +329,8 @@ int vivid_g_fmt_vid_out(struct file *file, void *priv,
for (p = 0; p < mp->num_planes; p++) {
mp->plane_fmt[p].bytesperline = dev->bytesperline_out[p];
mp->plane_fmt[p].sizeimage =
- mp->plane_fmt[p].bytesperline * mp->height +
- fmt->data_offset[p];
+ mp->plane_fmt[p].bytesperline * mp->height /
+ fmt->vdownsampling[p] + fmt->data_offset[p];
}
for (p = fmt->buffers; p < fmt->planes; p++) {
unsigned stride = dev->bytesperline_out[p];
@@ -562,9 +560,11 @@ set_colorspace:
dev->xfer_func_out = mp->xfer_func;
dev->ycbcr_enc_out = mp->ycbcr_enc;
dev->quantization_out = mp->quantization;
- if (dev->loop_video) {
- vivid_send_source_change(dev, SVID);
- vivid_send_source_change(dev, HDMI);
+ struct vivid_dev *in_dev = vivid_output_is_connected_to(dev);
+
+ if (in_dev) {
+ vivid_send_source_change(in_dev, SVID);
+ vivid_send_source_change(in_dev, HDMI);
}
return 0;
}
@@ -964,16 +964,16 @@ int vidioc_enum_output(struct file *file, void *priv,
out->type = V4L2_OUTPUT_TYPE_ANALOG;
switch (dev->output_type[out->index]) {
case SVID:
- snprintf(out->name, sizeof(out->name), "S-Video %u",
- dev->output_name_counter[out->index]);
+ snprintf(out->name, sizeof(out->name), "S-Video %03u-%u",
+ dev->inst, dev->output_name_counter[out->index]);
out->std = V4L2_STD_ALL;
if (dev->has_audio_outputs)
out->audioset = (1 << ARRAY_SIZE(vivid_audio_outputs)) - 1;
out->capabilities = V4L2_OUT_CAP_STD;
break;
case HDMI:
- snprintf(out->name, sizeof(out->name), "HDMI %u",
- dev->output_name_counter[out->index]);
+ snprintf(out->name, sizeof(out->name), "HDMI %03u-%u",
+ dev->inst, dev->output_name_counter[out->index]);
out->capabilities = V4L2_OUT_CAP_DV_TIMINGS;
break;
}
@@ -1014,11 +1014,6 @@ int vidioc_s_output(struct file *file, void *priv, unsigned o)
dev->meta_out_dev.tvnorms = dev->vid_out_dev.tvnorms;
vivid_update_format_out(dev);
- v4l2_ctrl_activate(dev->ctrl_display_present, vivid_is_hdmi_out(dev));
- if (vivid_is_hdmi_out(dev))
- v4l2_ctrl_s_ctrl(dev->ctrl_display_present,
- dev->display_present[dev->output]);
-
return 0;
}