/* * The minimum blanking value is one clock cycle for the front porch, one clock * cycle for the sync pulse and one clock cycle for the back porch.
*/ #define XTPG_MIN_HBLANK 3 #define XTPG_MAX_HBLANK (XVTC_MAX_HSIZE - XVIP_MIN_WIDTH) #define XTPG_MIN_VBLANK 3 #define XTPG_MAX_VBLANK (XVTC_MAX_VSIZE - XVIP_MIN_HEIGHT)
/** * struct xtpg_device - Xilinx Test Pattern Generator device structure * @xvip: Xilinx Video IP device * @pads: media pads * @npads: number of pads (1 or 2) * @has_input: whether an input is connected to the sink pad * @formats: active V4L2 media bus format for each pad * @default_format: default V4L2 media bus format * @vip_format: format information corresponding to the active format * @bayer: boolean flag if TPG is set to any bayer format * @ctrl_handler: control handler * @hblank: horizontal blanking control * @vblank: vertical blanking control * @pattern: test pattern control * @streaming: is the video stream active * @vtc: video timing controller * @vtmux_gpio: video timing mux GPIO
*/ struct xtpg_device { struct xvip_device xvip;
/* * If the TPG has no sink pad or no input connected to its sink pad * passthrough mode can't be enabled.
*/ if (xtpg->npads == 1 || !xtpg->has_input)
passthrough = false;
/* If passthrough mode is allowed unmask bit 0. */ if (passthrough)
pattern_mask &= ~1;
/* If test pattern mode is allowed unmask all other bits. */ if (pattern)
pattern_mask &= 1;
/* * Configure the bayer phase and video timing mux based on the * operation mode (passthrough or test pattern generation). The test * pattern can be modified by the control set handler, we thus need to * take the control lock here to avoid races.
*/
mutex_lock(xtpg->ctrl_handler.lock);
/* * Switching between passthrough and test pattern generation modes isn't * allowed during streaming, update the control range accordingly.
*/
passthrough = xtpg->pattern->cur.val == 0;
__xtpg_update_pattern_control(xtpg, passthrough, !passthrough);
xtpg->streaming = true;
mutex_unlock(xtpg->ctrl_handler.lock);
/* * For TPG v5.0, the bayer phase needs to be off for the pass through * mode, otherwise the external input would be subsampled.
*/
bayer_phase = passthrough ? XTPG_BAYER_PHASE_OFF
: xtpg_get_bayer_phase(xtpg->formats[0].code);
xvip_write(&xtpg->xvip, XTPG_BAYER_PHASE, bayer_phase);
if (xtpg->vtmux_gpio)
gpiod_set_value_cansleep(xtpg->vtmux_gpio, !passthrough);
xvip_start(&xtpg->xvip);
return 0;
}
/* ----------------------------------------------------------------------------- * V4L2 Subdevice Pad Operations
*/
/* In two pads mode the source pad format is always identical to the * sink pad format.
*/ if (xtpg->npads == 2 && fmt->pad == 1) {
fmt->format = *__format; return 0;
}
/* Bayer phase is configurable at runtime */ if (xtpg->bayer) {
bayer_phase = xtpg_get_bayer_phase(fmt->format.code); if (bayer_phase != XTPG_BAYER_PHASE_OFF)
__format->code = fmt->format.code;
}
xvip_set_format_size(__format, fmt);
fmt->format = *__format;
/* Propagate the format to the source pad. */ if (xtpg->npads == 2) {
__format = __xtpg_get_pad_format(xtpg, sd_state, 1,
fmt->which);
*__format = fmt->format;
}
format = v4l2_subdev_state_get_format(sd_state, fse->pad);
if (fse->index || fse->code != format->code) return -EINVAL;
/* Min / max values for pad 0 is always fixed in both one and two pads * modes. In two pads mode, the source pad(= 1) size is identical to
* the sink pad size */ if (fse->pad == 0) {
fse->min_width = XVIP_MIN_WIDTH;
fse->max_width = XVIP_MAX_WIDTH;
fse->min_height = XVIP_MIN_HEIGHT;
fse->max_height = XVIP_MAX_HEIGHT;
} else {
fse->min_width = format->width;
fse->max_width = format->width;
fse->min_height = format->height;
fse->max_height = format->height;
}
format = xvip_of_get_format(port); if (IS_ERR(format)) {
dev_err(dev, "invalid format in DT"); return PTR_ERR(format);
}
/* Get and check the format description */ if (!xtpg->vip_format) {
xtpg->vip_format = format;
} elseif (xtpg->vip_format != format) {
dev_err(dev, "in/out format mismatch in DT"); return -EINVAL;
}
if (nports == 0) {
endpoint = of_graph_get_next_port_endpoint(port, NULL); if (endpoint)
has_endpoint = true;
of_node_put(endpoint);
}
/* Count the number of ports. */
nports++;
}
if (nports != 1 && nports != 2) {
dev_err(dev, "invalid number of ports %u\n", nports); return -EINVAL;
}
staticint xtpg_probe(struct platform_device *pdev)
{ struct v4l2_subdev *subdev; struct xtpg_device *xtpg;
u32 i, bayer_phase; int ret;
xtpg = devm_kzalloc(&pdev->dev, sizeof(*xtpg), GFP_KERNEL); if (!xtpg) return -ENOMEM;
xtpg->xvip.dev = &pdev->dev;
ret = xtpg_parse_of(xtpg); if (ret < 0) return ret;
ret = xvip_init_resources(&xtpg->xvip); if (ret < 0) return ret;
xtpg->vtmux_gpio = devm_gpiod_get_optional(&pdev->dev, "timing",
GPIOD_OUT_HIGH); if (IS_ERR(xtpg->vtmux_gpio)) {
ret = PTR_ERR(xtpg->vtmux_gpio); goto error_resource;
}
xtpg->vtc = xvtc_of_get(pdev->dev.of_node); if (IS_ERR(xtpg->vtc)) {
ret = PTR_ERR(xtpg->vtc); goto error_resource;
}
/* Reset and initialize the core */
xvip_reset(&xtpg->xvip);
/* Initialize V4L2 subdevice and media entity. Pad numbers depend on the * number of pads.
*/ if (xtpg->npads == 2) {
xtpg->pads[0].flags = MEDIA_PAD_FL_SINK;
xtpg->pads[1].flags = MEDIA_PAD_FL_SOURCE;
} else {
xtpg->pads[0].flags = MEDIA_PAD_FL_SOURCE;
}
/* Initialize the default format */
xtpg->default_format.code = xtpg->vip_format->code;
xtpg->default_format.field = V4L2_FIELD_NONE;
xtpg->default_format.colorspace = V4L2_COLORSPACE_SRGB;
xvip_get_frame_size(&xtpg->xvip, &xtpg->default_format);
bayer_phase = xtpg_get_bayer_phase(xtpg->vip_format->code); if (bayer_phase != XTPG_BAYER_PHASE_OFF)
xtpg->bayer = true;
xtpg->formats[0] = xtpg->default_format; if (xtpg->npads == 2)
xtpg->formats[1] = xtpg->default_format;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.