/* Set Wake Up value in the D-PHY */ staticvoid csi2tx_dphy_set_wakeup(struct csi2tx_priv *csi2tx)
{
writel(CSI2TX_DPHY_CLK_WAKEUP_ULPS_CYCLES(32),
csi2tx->base + CSI2TX_DPHY_CLK_WAKEUP_REG);
}
/* * Finishes the D-PHY initialization * reg dphy cfg value to be used
*/ staticvoid csi2tx_dphy_init_finish(struct csi2tx_priv *csi2tx, u32 reg)
{ unsignedint i;
udelay(10);
/* Enable our (clock and data) lanes */
reg |= CSI2TX_DPHY_CFG_CLK_ENABLE; for (i = 0; i < csi2tx->num_lanes; i++)
reg |= CSI2TX_DPHY_CFG_LANE_ENABLE(csi2tx->lanes[i] - 1);
writel(reg, csi2tx->base + CSI2TX_DPHY_CFG_REG);
/* Put our lanes (clock and data) out of reset */
reg = CSI2TX_DPHY_CFG_CLK_RESET | CSI2TX_DPHY_CFG_MODE_LPDT; for (i = 0; i < csi2tx->num_lanes; i++)
reg |= CSI2TX_DPHY_CFG_LANE_RESET(csi2tx->lanes[i] - 1);
writel(reg, csi2tx->base + CSI2TX_DPHY_CFG_REG);
/* Put our lanes (clock and data) out of reset */
reg = CSI2TX_V2_DPHY_CFG_RESET | CSI2TX_V2_DPHY_CFG_MODE_LPDT;
writel(reg, csi2tx->base + CSI2TX_V2_DPHY_CFG_REG);
if (csi2tx->vops && csi2tx->vops->dphy_setup) {
csi2tx->vops->dphy_setup(csi2tx);
udelay(10);
}
/* * Create a static mapping between the CSI virtual channels * and the input streams. * * This should be enhanced, but v4l2 lacks the support for * changing that mapping dynamically at the moment. * * We're protected from the userspace setting up links at the * same time by the upper layer having called * media_pipeline_start().
*/
list_for_each_entry(link, &entity->links, list) { struct v4l2_mbus_framefmt *mfmt; conststruct csi2tx_fmt *fmt; unsignedint stream; int pad_idx = -1;
/* Only consider our enabled input pads */ for (i = CSI2TX_PAD_SINK_STREAM0; i < CSI2TX_PAD_MAX; i++) { struct media_pad *pad = &csi2tx->pads[i];
mfmt = &csi2tx->pad_fmts[pad_idx];
fmt = csi2tx_get_fmt_from_mbus(mfmt->code); if (!fmt) continue;
stream = pad_idx - CSI2TX_PAD_SINK_STREAM0;
/* * We use the stream ID there, but it's wrong. * * A stream could very well send a data type that is * not equal to its stream ID. We need to find a * proper way to address it.
*/
writel(CSI2TX_DT_CFG_DT(fmt->dt),
csi2tx->base + CSI2TX_DT_CFG_REG(stream));
/* * TODO: This needs to be calculated based on the * output CSI2 clock rate.
*/
writel(CSI2TX_STREAM_IF_CFG_FILL_LEVEL(4),
csi2tx->base + CSI2TX_STREAM_IF_CFG_REG(stream));
}
/* Disable the configuration mode */
writel(0, csi2tx->base + CSI2TX_CONFIG_REG);
staticint csi2tx_s_stream(struct v4l2_subdev *subdev, int enable)
{ struct csi2tx_priv *csi2tx = v4l2_subdev_to_csi2tx(subdev); int ret = 0;
mutex_lock(&csi2tx->lock);
if (enable) { /* * If we're not the first users, there's no need to * enable the whole controller.
*/ if (!csi2tx->count) {
ret = csi2tx_start(csi2tx); if (ret) goto out;
}
csi2tx->count++;
} else {
csi2tx->count--;
/* * Let the last user turn off the lights.
*/ if (!csi2tx->count)
csi2tx_stop(csi2tx);
}
csi2tx->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(csi2tx->base)) return PTR_ERR(csi2tx->base);
csi2tx->p_clk = devm_clk_get(&pdev->dev, "p_clk"); if (IS_ERR(csi2tx->p_clk)) {
dev_err(&pdev->dev, "Couldn't get p_clk\n"); return PTR_ERR(csi2tx->p_clk);
}
csi2tx->esc_clk = devm_clk_get(&pdev->dev, "esc_clk"); if (IS_ERR(csi2tx->esc_clk)) {
dev_err(&pdev->dev, "Couldn't get the esc_clk\n"); return PTR_ERR(csi2tx->esc_clk);
}
ret = clk_prepare_enable(csi2tx->p_clk); if (ret) {
dev_err(&pdev->dev, "Couldn't prepare and enable p_clk\n"); return ret;
}
ep = of_graph_get_endpoint_by_regs(csi2tx->dev->of_node, 0, 0); if (!ep) return -EINVAL;
ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &v4l2_ep); if (ret) {
dev_err(csi2tx->dev, "Could not parse v4l2 endpoint\n"); goto out;
}
if (v4l2_ep.bus_type != V4L2_MBUS_CSI2_DPHY) {
dev_err(csi2tx->dev, "Unsupported media bus type: 0x%x\n",
v4l2_ep.bus_type);
ret = -EINVAL; goto out;
}
csi2tx->num_lanes = v4l2_ep.bus.mipi_csi2.num_data_lanes; if (csi2tx->num_lanes > csi2tx->max_lanes) {
dev_err(csi2tx->dev, "Current configuration uses more lanes than supported\n");
ret = -EINVAL; goto out;
}
for (i = 0; i < csi2tx->num_lanes; i++) { if (v4l2_ep.bus.mipi_csi2.data_lanes[i] < 1) {
dev_err(csi2tx->dev, "Invalid lane[%d] number: %u\n",
i, v4l2_ep.bus.mipi_csi2.data_lanes[i]);
ret = -EINVAL; goto out;
}
}
ret = csi2tx_check_lanes(csi2tx); if (ret) goto err_free_priv;
/* Create our media pads */
csi2tx->subdev.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
csi2tx->pads[CSI2TX_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; for (i = CSI2TX_PAD_SINK_STREAM0; i < CSI2TX_PAD_MAX; i++)
csi2tx->pads[i].flags = MEDIA_PAD_FL_SINK;
/* * Only the input pads are considered to have a format at the * moment. The CSI link can multiplex various streams with * different formats, and we can't expose this in v4l2 right * now.
*/ for (i = CSI2TX_PAD_SINK_STREAM0; i < CSI2TX_PAD_MAX; i++)
csi2tx->pad_fmts[i] = fmt_default;
ret = media_entity_pads_init(&csi2tx->subdev.entity, CSI2TX_PAD_MAX,
csi2tx->pads); if (ret) goto err_free_priv;
ret = v4l2_async_register_subdev(&csi2tx->subdev); if (ret < 0) goto err_free_priv;
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.