/* * v4l2_get_link_freq() uses V4L2_CID_LINK_FREQ first, and falls back * to V4L2_CID_PIXEL_RATE if V4L2_CID_LINK_FREQ is not available. * * With multistream input there is no single pixel rate, and thus we * cannot use V4L2_CID_PIXEL_RATE, so we pass 0 as the bpp which * causes v4l2_get_link_freq() to return an error if it falls back to * V4L2_CID_PIXEL_RATE.
*/
state = v4l2_subdev_get_locked_active_state(&phy->subdev);
fmtinfo = cal_format_by_code(fmt->code); if (!fmtinfo) return -EINVAL;
bpp = fmtinfo->bpp;
}
freq = v4l2_get_link_freq(&phy->source->entity.pads[phy->source_pad],
bpp, 2 * num_lanes); if (freq < 0) {
phy_err(phy, "failed to get link freq for subdev '%s'\n",
phy->source->name); return freq;
}
phy_dbg(3, phy, "Source Link Freq: %llu\n", freq);
cal_set_field(&val, mipi_csi2->clock_lane + 1, lane_mask);
cal_set_field(&val, mipi_csi2->lane_polarities[0], polarity_mask); for (lane = 0; lane < mipi_csi2->num_data_lanes; lane++) { /* * Every lane are one nibble apart starting with the * clock followed by the data lanes so shift masks by 4.
*/
lane_mask <<= 4;
polarity_mask <<= 4;
cal_set_field(&val, mipi_csi2->data_lanes[lane] + 1, lane_mask);
cal_set_field(&val, mipi_csi2->lane_polarities[lane + 1],
polarity_mask);
}
regmap_field_write(phy->fields[F_CAMMODE], 0); /* Always enable all lanes at the phy control level */
regmap_field_write(phy->fields[F_LANEENABLE], (1 << num_lanes) - 1); /* F_CSI_MODE is not present on every architecture */ if (phy->fields[F_CSI_MODE])
regmap_field_write(phy->fields[F_CSI_MODE], 1);
regmap_field_write(phy->fields[F_CTRLCLKEN], 1);
}
/* * We need to enable the PHY hardware when enabling the first stream, * but for the following streams we just propagate the enable_streams * to the source.
*/
if (phy->enable_count > 0) {
ret = v4l2_subdev_enable_streams(phy->source, remote_pad->index,
BIT(sink_stream)); if (ret) {
phy_err(phy, "enable streams failed in source: %d\n", ret); return ret;
}
phy->enable_count++;
return 0;
}
link_freq = cal_camerarx_get_ext_link_freq(phy); if (link_freq < 0) return link_freq;
ret = v4l2_subdev_call(phy->source, core, s_power, 1); if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) {
phy_err(phy, "power on failed in subdev\n"); return ret;
}
cal_camerarx_enable_irqs(phy);
/* * CSI-2 PHY Link Initialization Sequence, according to the DRA74xP / * DRA75xP / DRA76xP / DRA77xP TRM. The DRA71x / DRA72x and the AM65x / * DRA80xM TRMs have a slightly simplified sequence.
*/
/* * 1. Configure all CSI-2 low level protocol registers to be ready to * receive signals/data from the CSI-2 PHY. * * i.-v. Configure the lanes position and polarity.
*/
cal_camerarx_lane_config(phy);
/* * vi.-vii. Configure D-PHY mode, enable the required lanes and * enable the CAMERARX clock.
*/
cal_camerarx_enable(phy);
/* * 2. CSI PHY and link initialization sequence. * * a. Deassert the CSI-2 PHY reset. Do not wait for reset completion * at this point, as it requires the external source to send the * CSI-2 HS clock.
*/
cal_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance),
CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_OPERATIONAL,
CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK);
phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x De-assert Complex IO Reset\n",
phy->instance,
cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance)));
/* Dummy read to allow SCP reset to complete. */
camerarx_read(phy, CAL_CSI2_PHY_REG0);
/* Program the PHY timing parameters. */
cal_camerarx_config(phy, link_freq);
/* * b. Assert the FORCERXMODE signal. * * The stop-state-counter is based on fclk cycles, and we always use * the x16 and x4 settings, so stop-state-timeout = * fclk-cycle * 16 * 4 * counter. * * Stop-state-timeout must be more than 100us as per CSI-2 spec, so we * calculate a timeout that's 100us (rounding up).
*/
sscounter = DIV_ROUND_UP(clk_get_rate(phy->cal->fclk), 10000 * 16 * 4);
/* Assert the FORCERXMODE signal. */
cal_write_field(phy->cal, CAL_CSI2_TIMING(phy->instance),
1, CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK);
phy_dbg(3, phy, "CAL_CSI2_TIMING(%d) = 0x%08x Force RXMODE\n",
phy->instance,
cal_read(phy->cal, CAL_CSI2_TIMING(phy->instance)));
/* * c. Connect pull-down on CSI-2 PHY link (using pad control). * * This is not required on DRA71x, DRA72x, AM65x and DRA80xM. Not * implemented.
*/
/* * d. Power up the CSI-2 PHY. * e. Check whether the state status reaches the ON state.
*/
cal_camerarx_power(phy, true);
/* * Start the source to enable the CSI-2 HS clock. We can now wait for * CSI-2 PHY reset to complete.
*/
ret = v4l2_subdev_enable_streams(phy->source, remote_pad->index,
BIT(sink_stream)); if (ret) {
v4l2_subdev_call(phy->source, core, s_power, 0);
cal_camerarx_disable_irqs(phy);
phy_err(phy, "stream on failed in subdev\n"); return ret;
}
cal_camerarx_wait_reset(phy);
/* f. Wait for STOPSTATE=1 for all enabled lane modules. */
cal_camerarx_wait_stop_state(phy);
phy_dbg(1, phy, "CSI2_%u_REG1 = 0x%08x (bits 31-28 should be set)\n",
phy->instance, camerarx_read(phy, CAL_CSI2_PHY_REG1));
/* * g. Disable pull-down on CSI-2 PHY link (using pad control). * * This is not required on DRA71x, DRA72x, AM65x and DRA80xM. Not * implemented.
*/
/* Finally, enable the PHY Protocol Interface (PPI). */
cal_camerarx_ppi_enable(phy);
if (--phy->enable_count > 0) {
ret = v4l2_subdev_disable_streams(phy->source,
remote_pad->index,
BIT(sink_stream)); if (ret)
phy_err(phy, "stream off failed in subdev\n");
ret = v4l2_subdev_disable_streams(phy->source, remote_pad->index,
BIT(sink_stream)); if (ret)
phy_err(phy, "stream off failed in subdev\n");
ret = v4l2_subdev_call(phy->source, core, s_power, 0); if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
phy_err(phy, "power off failed in subdev\n");
}
/* * Errata i913: CSI2 LDO Needs to be disabled when module is powered on * * Enabling CSI2 LDO shorts it to core supply. It is crucial the 2 CSI2 * LDOs on the device are disabled if CSI-2 module is powered on * (0x4845 B304 | 0x4845 B384 [28:27] = 0x1) or in ULPS (0x4845 B304 * | 0x4845 B384 [28:27] = 0x2) mode. Common concerns include: high * current draw on the module supply in active mode. * * Errata does not apply when CSI-2 module is powered off * (0x4845 B304 | 0x4845 B384 [28:27] = 0x0). * * SW Workaround: * Set the following register bits to disable the LDO, * which is essentially CSI2 REG10 bit 6: * * Core 0: 0x4845 B828 = 0x0000 0040 * Core 1: 0x4845 B928 = 0x0000 0040
*/ void cal_camerarx_i913_errata(struct cal_camerarx *phy)
{
u32 reg10 = camerarx_read(phy, CAL_CSI2_PHY_REG10);
for (i = 0; i < F_MAX_FIELDS; i++) { struct reg_field field = {
.reg = cal->syscon_camerrx_offset,
.lsb = phy_data->fields[i].lsb,
.msb = phy_data->fields[i].msb,
};
/* * Here we update the reg offset with the * value found in DT
*/
phy->fields[i] = devm_regmap_field_alloc(cal->dev,
cal->syscon_camerrx,
field); if (IS_ERR(phy->fields[i])) {
cal_err(cal, "Unable to allocate regmap fields\n"); return PTR_ERR(phy->fields[i]);
}
}
/* * Find the endpoint node for the port corresponding to the PHY * instance, and parse its CSI-2-related properties.
*/
ep_node = of_graph_get_endpoint_by_regs(phy->cal->dev->of_node,
phy->instance, 0); if (!ep_node) { /* * The endpoint is not mandatory, not all PHY instances need to * be connected in DT.
*/
phy_dbg(3, phy, "Port has no endpoint\n"); return 0;
}
endpoint->bus_type = V4L2_MBUS_CSI2_DPHY;
ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep_node), endpoint); if (ret < 0) {
phy_err(phy, "Failed to parse endpoint\n"); goto done;
}
for (i = 0; i < endpoint->bus.mipi_csi2.num_data_lanes; i++) { unsignedint lane = endpoint->bus.mipi_csi2.data_lanes[i];
if (lane > 4) {
phy_err(phy, "Invalid position %u for data lane %u\n",
lane, i);
ret = -EINVAL; goto done;
}
phy_dbg(3, phy, "CSI-2 bus: clock lane <%u>, data lanes <%s>, flags 0x%08x\n",
endpoint->bus.mipi_csi2.clock_lane, data_lanes,
endpoint->bus.mipi_csi2.flags);
/* Retrieve the connected device and store it for later use. */
phy->source_ep_node = of_graph_get_remote_endpoint(ep_node);
phy->source_node = of_graph_get_port_parent(phy->source_ep_node); if (!phy->source_node) {
phy_dbg(3, phy, "Can't get remote parent\n");
of_node_put(phy->source_ep_node);
ret = -EINVAL; goto done;
}
/* No transcoding, source and sink formats must match. */ if (cal_rx_pad_is_source(format->pad)) return v4l2_subdev_get_fmt(sd, state, format);
/* * Default to the first format if the requested media bus code isn't * supported.
*/
fmtinfo = cal_format_by_code(format->format.code); if (!fmtinfo)
fmtinfo = &cal_formats[0];
/* Clamp the size, update the code. The colorspace is accepted as-is. */
bpp = ALIGN(fmtinfo->bpp, 8);
state = v4l2_subdev_lock_and_get_active_state(sd);
ret = v4l2_subdev_routing_find_opposite_end(&state->routing,
pad, 0,
NULL, &sink_stream); if (ret) goto out_unlock;
remote_pad = media_pad_remote_pad_first(&phy->pads[CAL_CAMERARX_PAD_SINK]); if (!remote_pad) {
ret = -EPIPE; goto out_unlock;
}
ret = v4l2_subdev_call(phy->source, pad, get_frame_desc,
remote_pad->index, &remote_desc); if (ret) goto out_unlock;
if (remote_desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) {
cal_err(phy->cal, "Frame descriptor does not describe CSI-2 link");
ret = -EINVAL; goto out_unlock;
}
for (i = 0; i < remote_desc.num_entries; i++) { if (remote_desc.entry[i].stream == sink_stream) break;
}
if (i == remote_desc.num_entries) {
cal_err(phy->cal, "Stream %u not found in remote frame desc\n",
sink_stream);
ret = -EINVAL; goto out_unlock;
}
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.