diff options
Diffstat (limited to 'drivers/media/test-drivers')
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; } |