return container_of(sdev, struct csi_priv, sd);
}
+static inline bool is_parallel_bus(struct v4l2_fwnode_endpoint *ep)
+{
+ return ep->bus_type != V4L2_MBUS_CSI2;
+}
+
static inline bool is_parallel_16bit_bus(struct v4l2_fwnode_endpoint *ep)
{
- return ep->bus_type != V4L2_MBUS_CSI2 &&
- ep->bus.parallel.bus_width >= 16;
+ return is_parallel_bus(ep) && ep->bus.parallel.bus_width >= 16;
+}
+
+/*
+ * Check for conditions that require the IPU to handle the
+ * data internally as generic data, aka passthrough mode:
+ * - raw bayer media bus formats, or
+ * - the CSI is receiving from a 16-bit parallel bus, or
+ * - the CSI is receiving from an 8-bit parallel bus and the incoming
+ * media bus format is other than UYVY8_2X8/YUYV8_2X8.
+ */
+static inline bool requires_passthrough(struct v4l2_fwnode_endpoint *ep,
+ struct v4l2_mbus_framefmt *infmt,
+ const struct imx_media_pixfmt *incc)
+{
+ return incc->bayer || is_parallel_16bit_bus(ep) ||
+ (is_parallel_bus(ep) &&
+ infmt->code != MEDIA_BUS_FMT_UYVY8_2X8 &&
+ infmt->code != MEDIA_BUS_FMT_YUYV8_2X8);
}
/*
static int csi_idmac_setup_channel(struct csi_priv *priv)
{
struct imx_media_video_dev *vdev = priv->vdev;
+ const struct imx_media_pixfmt *incc;
struct v4l2_mbus_framefmt *infmt;
struct ipu_image image;
u32 passthrough_bits;
+ u32 passthrough_cycles;
dma_addr_t phys[2];
bool passthrough;
u32 burst_size;
int ret;
infmt = &priv->format_mbus[CSI_SINK_PAD];
+ incc = priv->cc[CSI_SINK_PAD];
ipu_cpmem_zero(priv->idmac_ch);
image.phys0 = phys[0];
image.phys1 = phys[1];
- /*
- * Check for conditions that require the IPU to handle the
- * data internally as generic data, aka passthrough mode:
- * - raw bayer formats
- * - the CSI is receiving from a 16-bit parallel bus
- */
+ passthrough = requires_passthrough(&priv->upstream_ep, infmt, incc);
+ passthrough_cycles = 1;
+
switch (image.pix.pixelformat) {
case V4L2_PIX_FMT_SBGGR8:
case V4L2_PIX_FMT_SGBRG8:
case V4L2_PIX_FMT_SRGGB8:
case V4L2_PIX_FMT_GREY:
burst_size = 16;
- passthrough = true;
passthrough_bits = 8;
break;
case V4L2_PIX_FMT_SBGGR16:
case V4L2_PIX_FMT_SRGGB16:
case V4L2_PIX_FMT_Y16:
burst_size = 8;
- passthrough = true;
passthrough_bits = 16;
break;
case V4L2_PIX_FMT_YUV420:
burst_size = (image.pix.width & 0x3f) ?
((image.pix.width & 0x1f) ?
((image.pix.width & 0xf) ? 8 : 16) : 32) : 64;
- passthrough = is_parallel_16bit_bus(&priv->upstream_ep);
passthrough_bits = 16;
/* Skip writing U and V components to odd rows */
ipu_cpmem_skip_odd_chroma_rows(priv->idmac_ch);
case V4L2_PIX_FMT_UYVY:
burst_size = (image.pix.width & 0x1f) ?
((image.pix.width & 0xf) ? 8 : 16) : 32;
- passthrough = is_parallel_16bit_bus(&priv->upstream_ep);
passthrough_bits = 16;
break;
+ case V4L2_PIX_FMT_RGB565:
+ if (passthrough) {
+ burst_size = 16;
+ passthrough_bits = 8;
+ passthrough_cycles = incc->cycles;
+ break;
+ }
+ /* fallthrough for non-passthrough RGB565 (CSI-2 bus) */
default:
burst_size = (image.pix.width & 0xf) ? 8 : 16;
- passthrough = is_parallel_16bit_bus(&priv->upstream_ep);
passthrough_bits = 16;
break;
}
if (passthrough) {
- ipu_cpmem_set_resolution(priv->idmac_ch, image.rect.width,
+ ipu_cpmem_set_resolution(priv->idmac_ch,
+ image.rect.width * passthrough_cycles,
image.rect.height);
ipu_cpmem_set_stride(priv->idmac_ch, image.pix.bytesperline);
ipu_cpmem_set_buffer(priv->idmac_ch, 0, image.phys0);
static int csi_setup(struct csi_priv *priv)
{
struct v4l2_mbus_framefmt *infmt, *outfmt;
+ const struct imx_media_pixfmt *incc;
struct v4l2_mbus_config mbus_cfg;
struct v4l2_mbus_framefmt if_fmt;
+ struct v4l2_rect crop;
infmt = &priv->format_mbus[CSI_SINK_PAD];
+ incc = priv->cc[CSI_SINK_PAD];
outfmt = &priv->format_mbus[priv->active_output_pad];
/* compose mbus_config from the upstream endpoint */
mbus_cfg.type = priv->upstream_ep.bus_type;
- mbus_cfg.flags = (priv->upstream_ep.bus_type == V4L2_MBUS_CSI2) ?
- priv->upstream_ep.bus.mipi_csi2.flags :
- priv->upstream_ep.bus.parallel.flags;
+ mbus_cfg.flags = is_parallel_bus(&priv->upstream_ep) ?
+ priv->upstream_ep.bus.parallel.flags :
+ priv->upstream_ep.bus.mipi_csi2.flags;
/*
* we need to pass input frame to CSI interface, but
*/
if_fmt = *infmt;
if_fmt.field = outfmt->field;
+ crop = priv->crop;
- ipu_csi_set_window(priv->csi, &priv->crop);
+ /*
+ * if cycles is set, we need to handle this over multiple cycles as
+ * generic/bayer data
+ */
+ if (is_parallel_bus(&priv->upstream_ep) && incc->cycles) {
+ if_fmt.width *= incc->cycles;
+ crop.width *= incc->cycles;
+ }
+
+ ipu_csi_set_window(priv->csi, &crop);
ipu_csi_set_downsize(priv->csi,
priv->crop.width == 2 * priv->compose.width,
{
struct csi_priv *priv = v4l2_get_subdevdata(sd);
struct v4l2_fwnode_endpoint upstream_ep = {};
- const struct imx_media_pixfmt *incc;
bool is_csi2;
int ret;
mutex_lock(&priv->lock);
priv->upstream_ep = upstream_ep;
- is_csi2 = (upstream_ep.bus_type == V4L2_MBUS_CSI2);
- incc = priv->cc[CSI_SINK_PAD];
-
- if (priv->dest != IPU_CSI_DEST_IDMAC &&
- (incc->bayer || is_parallel_16bit_bus(&upstream_ep))) {
- v4l2_err(&priv->sd,
- "bayer/16-bit parallel buses must go to IDMAC pad\n");
- ret = -EINVAL;
- goto out;
- }
-
+ is_csi2 = !is_parallel_bus(&upstream_ep);
if (is_csi2) {
int vc_num = 0;
/*
/* select either parallel or MIPI-CSI2 as input to CSI */
ipu_set_csi_src_mux(priv->ipu, priv->csi_id, is_csi2);
-out:
+
mutex_unlock(&priv->lock);
return ret;
}
struct v4l2_subdev_mbus_code_enum *code)
{
struct csi_priv *priv = v4l2_get_subdevdata(sd);
+ struct v4l2_fwnode_endpoint upstream_ep;
const struct imx_media_pixfmt *incc;
struct v4l2_mbus_framefmt *infmt;
int ret = 0;
break;
case CSI_SRC_PAD_DIRECT:
case CSI_SRC_PAD_IDMAC:
- if (incc->bayer) {
+ ret = csi_get_upstream_endpoint(priv, &upstream_ep);
+ if (ret) {
+ v4l2_err(&priv->sd, "failed to find upstream endpoint\n");
+ goto out;
+ }
+
+ if (requires_passthrough(&upstream_ep, infmt, incc)) {
if (code->index != 0) {
ret = -EINVAL;
goto out;
sdformat->format.width = compose->width;
sdformat->format.height = compose->height;
- if (incc->bayer) {
+ if (requires_passthrough(upstream_ep, infmt, incc)) {
sdformat->format.code = infmt->code;
*cc = incc;
} else {