From 9d77db3c730780c8ef5ccd4b66403ff5675cfe4e Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Mon, 13 May 2024 10:30:14 +0000
Subject: [PATCH] modify sin led gpio
---
kernel/drivers/staging/media/imx/imx-media-csi.c | 492 ++++++++++++++++++++++++++++++++++++------------------
1 files changed, 325 insertions(+), 167 deletions(-)
diff --git a/kernel/drivers/staging/media/imx/imx-media-csi.c b/kernel/drivers/staging/media/imx/imx-media-csi.c
index c7df0ff..d9a8667 100644
--- a/kernel/drivers/staging/media/imx/imx-media-csi.c
+++ b/kernel/drivers/staging/media/imx/imx-media-csi.c
@@ -1,13 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* V4L2 Capture CSI Subdev for Freescale i.MX5/6 SOC
*
* Copyright (c) 2014-2017 Mentor Graphics Inc.
* Copyright (C) 2017 Pengutronix, Philipp Zabel <kernel@pengutronix.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
*/
#include <linux/delay.h>
#include <linux/gcd.h>
@@ -37,11 +33,11 @@
* has not requested planar formats, we should allow 8 pixel
* alignment.
*/
-#define MIN_W 176
-#define MIN_H 144
+#define MIN_W 32
+#define MIN_H 32
#define MAX_W 4096
#define MAX_H 4096
-#define W_ALIGN 4 /* multiple of 16 pixels */
+#define W_ALIGN 1 /* multiple of 2 pixels */
#define H_ALIGN 1 /* multiple of 2 lines */
#define S_ALIGN 1 /* multiple of 2 */
@@ -60,9 +56,10 @@
struct csi_priv {
struct device *dev;
struct ipu_soc *ipu;
- struct imx_media_dev *md;
struct v4l2_subdev sd;
struct media_pad pad[CSI_NUM_PADS];
+ struct v4l2_async_notifier notifier;
+
/* the video device at IDMAC output pad */
struct imx_media_video_dev *vdev;
struct imx_media_fim *fim;
@@ -114,6 +111,7 @@
u32 frame_sequence; /* frame sequence counter */
bool last_eof; /* waiting for last EOF at stream off */
bool nfb4eof; /* NFB4EOF encountered during streaming */
+ bool interweave_swap; /* swap top/bottom lines when interweaving */
struct completion last_eof_comp;
};
@@ -122,9 +120,14 @@
return container_of(sdev, struct csi_priv, sd);
}
+static inline struct csi_priv *notifier_to_dev(struct v4l2_async_notifier *n)
+{
+ return container_of(n, struct csi_priv, notifier);
+}
+
static inline bool is_parallel_bus(struct v4l2_fwnode_endpoint *ep)
{
- return ep->bus_type != V4L2_MBUS_CSI2;
+ return ep->bus_type != V4L2_MBUS_CSI2_DPHY;
}
static inline bool is_parallel_16bit_bus(struct v4l2_fwnode_endpoint *ep)
@@ -161,8 +164,7 @@
static int csi_get_upstream_endpoint(struct csi_priv *priv,
struct v4l2_fwnode_endpoint *ep)
{
- struct device_node *endpoint, *port;
- struct media_entity *src;
+ struct fwnode_handle *endpoint;
struct v4l2_subdev *sd;
struct media_pad *pad;
@@ -173,50 +175,43 @@
return -EPIPE;
sd = priv->src_sd;
- src = &sd->entity;
- if (src->function == MEDIA_ENT_F_VID_MUX) {
+ switch (sd->grp_id) {
+ case IMX_MEDIA_GRP_ID_CSI_MUX:
/*
- * CSI is connected directly to video mux, skip up to
+ * CSI is connected directly to CSI mux, skip up to
* CSI-2 receiver if it is in the path, otherwise stay
- * with video mux.
+ * with the CSI mux.
*/
- sd = imx_media_find_upstream_subdev(priv->md, src,
- IMX_MEDIA_GRP_ID_CSI2);
- if (!IS_ERR(sd))
- src = &sd->entity;
+ sd = imx_media_pipeline_subdev(&sd->entity,
+ IMX_MEDIA_GRP_ID_CSI2,
+ true);
+ if (IS_ERR(sd))
+ sd = priv->src_sd;
+ break;
+ case IMX_MEDIA_GRP_ID_CSI2:
+ break;
+ default:
+ /*
+ * the source is neither the CSI mux nor the CSI-2 receiver,
+ * get the source pad directly upstream from CSI itself.
+ */
+ sd = &priv->sd;
+ break;
}
- /*
- * If the source is neither the video mux nor the CSI-2 receiver,
- * get the source pad directly upstream from CSI itself.
- */
- if (src->function != MEDIA_ENT_F_VID_MUX &&
- sd->grp_id != IMX_MEDIA_GRP_ID_CSI2)
- src = &priv->sd.entity;
-
- /* get source pad of entity directly upstream from src */
- pad = imx_media_find_upstream_pad(priv->md, src, 0);
- if (IS_ERR(pad))
- return PTR_ERR(pad);
-
- sd = media_entity_to_v4l2_subdev(pad->entity);
-
- /*
- * NOTE: this assumes an OF-graph port id is the same as a
- * media pad index.
- */
- port = of_graph_get_port_by_id(sd->dev->of_node, pad->index);
- if (!port)
+ /* get source pad of entity directly upstream from sd */
+ pad = imx_media_pipeline_pad(&sd->entity, 0, 0, true);
+ if (!pad)
return -ENODEV;
- endpoint = of_get_next_child(port, NULL);
- of_node_put(port);
- if (!endpoint)
- return -ENODEV;
+ endpoint = imx_media_get_pad_fwnode(pad);
+ if (IS_ERR(endpoint))
+ return PTR_ERR(endpoint);
- v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint), ep);
- of_node_put(endpoint);
+ v4l2_fwnode_endpoint_parse(endpoint, ep);
+
+ fwnode_handle_put(endpoint);
return 0;
}
@@ -295,6 +290,9 @@
if (ipu_idmac_buffer_is_ready(priv->idmac_ch, priv->ipu_buf_num))
ipu_idmac_clear_buffer(priv->idmac_ch, priv->ipu_buf_num);
+
+ if (priv->interweave_swap)
+ phys += vdev->fmt.fmt.pix.bytesperline;
ipu_cpmem_set_buffer(priv->idmac_ch, priv->ipu_buf_num, phys);
}
@@ -408,23 +406,24 @@
struct imx_media_video_dev *vdev = priv->vdev;
const struct imx_media_pixfmt *incc;
struct v4l2_mbus_framefmt *infmt;
+ struct v4l2_mbus_framefmt *outfmt;
+ bool passthrough, interweave;
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];
+ outfmt = &priv->format_mbus[CSI_SRC_PAD_IDMAC];
ipu_cpmem_zero(priv->idmac_ch);
memset(&image, 0, sizeof(image));
image.pix = vdev->fmt.fmt.pix;
- image.rect.width = image.pix.width;
- image.rect.height = image.pix.height;
+ image.rect = vdev->compose;
csi_idmac_setup_vb2_buf(priv, phys);
@@ -433,6 +432,16 @@
passthrough = requires_passthrough(&priv->upstream_ep, infmt, incc);
passthrough_cycles = 1;
+
+ /*
+ * If the field type at capture interface is interlaced, and
+ * the output IDMAC pad is sequential, enable interweave at
+ * the IDMAC output channel.
+ */
+ interweave = V4L2_FIELD_IS_INTERLACED(image.pix.field) &&
+ V4L2_FIELD_IS_SEQUENTIAL(outfmt->field);
+ priv->interweave_swap = interweave &&
+ image.pix.field == V4L2_FIELD_INTERLACED_BT;
switch (image.pix.pixelformat) {
case V4L2_PIX_FMT_SBGGR8:
@@ -447,18 +456,24 @@
case V4L2_PIX_FMT_SGBRG16:
case V4L2_PIX_FMT_SGRBG16:
case V4L2_PIX_FMT_SRGGB16:
- case V4L2_PIX_FMT_Y16:
+ case V4L2_PIX_FMT_Y10:
+ case V4L2_PIX_FMT_Y12:
burst_size = 8;
passthrough_bits = 16;
break;
case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
case V4L2_PIX_FMT_NV12:
burst_size = (image.pix.width & 0x3f) ?
((image.pix.width & 0x1f) ?
((image.pix.width & 0xf) ? 8 : 16) : 32) : 64;
passthrough_bits = 16;
- /* Skip writing U and V components to odd rows */
- ipu_cpmem_skip_odd_chroma_rows(priv->idmac_ch);
+ /*
+ * Skip writing U and V components to odd rows (but not
+ * when enabling IDMAC interweaving, they are incompatible).
+ */
+ if (!interweave)
+ ipu_cpmem_skip_odd_chroma_rows(priv->idmac_ch);
break;
case V4L2_PIX_FMT_YUYV:
case V4L2_PIX_FMT_UYVY:
@@ -473,7 +488,7 @@
passthrough_cycles = incc->cycles;
break;
}
- /* fallthrough - non-passthrough RGB565 (CSI-2 bus) */
+ fallthrough; /* non-passthrough RGB565 (CSI-2 bus) */
default:
burst_size = (image.pix.width & 0xf) ? 8 : 16;
passthrough_bits = 16;
@@ -481,6 +496,12 @@
}
if (passthrough) {
+ if (priv->interweave_swap) {
+ /* start interweave scan at 1st top line (2nd line) */
+ image.phys0 += image.pix.bytesperline;
+ image.phys1 += image.pix.bytesperline;
+ }
+
ipu_cpmem_set_resolution(priv->idmac_ch,
image.rect.width * passthrough_cycles,
image.rect.height);
@@ -490,6 +511,11 @@
ipu_cpmem_set_format_passthrough(priv->idmac_ch,
passthrough_bits);
} else {
+ if (priv->interweave_swap) {
+ /* start interweave scan at 1st top line (2nd line) */
+ image.rect.top = 1;
+ }
+
ret = ipu_cpmem_set_image(priv->idmac_ch, &image);
if (ret)
goto unsetup_vb2;
@@ -519,10 +545,12 @@
ipu_smfc_set_burstsize(priv->smfc, burst_size);
- if (image.pix.field == V4L2_FIELD_NONE &&
- V4L2_FIELD_HAS_BOTH(infmt->field))
+ if (interweave)
ipu_cpmem_interlaced_scan(priv->idmac_ch,
- image.pix.bytesperline);
+ priv->interweave_swap ?
+ -image.pix.bytesperline :
+ image.pix.bytesperline,
+ image.pix.pixelformat);
ipu_idmac_set_double_buffer(priv->idmac_ch, true);
@@ -579,7 +607,7 @@
outfmt = &vdev->fmt.fmt.pix;
- ret = imx_media_alloc_dma_buf(priv->md, &priv->underrun_buf,
+ ret = imx_media_alloc_dma_buf(priv->dev, &priv->underrun_buf,
outfmt->sizeimage);
if (ret)
goto out_put_ipu;
@@ -599,8 +627,8 @@
}
priv->nfb4eof_irq = ipu_idmac_channel_irq(priv->ipu,
- priv->idmac_ch,
- IPU_IRQ_NFB4EOF);
+ priv->idmac_ch,
+ IPU_IRQ_NFB4EOF);
ret = devm_request_irq(priv->dev, priv->nfb4eof_irq,
csi_idmac_nfb4eof_interrupt, 0,
"imx-smfc-nfb4eof", priv);
@@ -633,7 +661,7 @@
out_unsetup:
csi_idmac_unsetup(priv, VB2_BUF_STATE_QUEUED);
out_free_dma_buf:
- imx_media_free_dma_buf(priv->md, &priv->underrun_buf);
+ imx_media_free_dma_buf(priv->dev, &priv->underrun_buf);
out_put_ipu:
csi_idmac_put_ipu_resources(priv);
return ret;
@@ -665,7 +693,7 @@
csi_idmac_unsetup(priv, VB2_BUF_STATE_ERROR);
- imx_media_free_dma_buf(priv->md, &priv->underrun_buf);
+ imx_media_free_dma_buf(priv->dev, &priv->underrun_buf);
/* cancel the EOF timeout timer */
del_timer_sync(&priv->eof_timeout_timer);
@@ -692,12 +720,7 @@
priv->upstream_ep.bus.parallel.flags :
priv->upstream_ep.bus.mipi_csi2.flags;
- /*
- * we need to pass input frame to CSI interface, but
- * with translated field type from output format
- */
if_fmt = *infmt;
- if_fmt.field = outfmt->field;
crop = priv->crop;
/*
@@ -715,7 +738,7 @@
priv->crop.width == 2 * priv->compose.width,
priv->crop.height == 2 * priv->compose.height);
- ipu_csi_init_interface(priv->csi, &mbus_cfg, &if_fmt);
+ ipu_csi_init_interface(priv->csi, &mbus_cfg, &if_fmt, outfmt);
ipu_csi_set_dest(priv->csi, priv->dest);
@@ -920,7 +943,10 @@
switch (fi->pad) {
case CSI_SINK_PAD:
- /* No limits on input frame interval */
+ /* No limits on valid input frame intervals */
+ if (fi->interval.numerator == 0 ||
+ fi->interval.denominator == 0)
+ fi->interval = *input_fi;
/* Reset output intervals and frame skipping ratio to 1:1 */
priv->frame_interval[CSI_SRC_PAD_IDMAC] = fi->interval;
priv->frame_interval[CSI_SRC_PAD_DIRECT] = fi->interval;
@@ -1035,6 +1061,8 @@
v4l2_ctrl_handler_free(&priv->ctrl_hdlr);
v4l2_ctrl_handler_init(&priv->ctrl_hdlr, 0);
priv->sink = NULL;
+ /* do not apply IC burst alignment in csi_try_crop */
+ priv->active_output_pad = CSI_SRC_PAD_IDMAC;
goto out;
}
@@ -1063,10 +1091,10 @@
remote_sd = media_entity_to_v4l2_subdev(remote->entity);
switch (remote_sd->grp_id) {
- case IMX_MEDIA_GRP_ID_VDIC:
+ case IMX_MEDIA_GRP_ID_IPU_VDIC:
priv->dest = IPU_CSI_DEST_VDIC;
break;
- case IMX_MEDIA_GRP_ID_IC_PRP:
+ case IMX_MEDIA_GRP_ID_IPU_IC_PRP:
priv->dest = IPU_CSI_DEST_IC;
break;
default:
@@ -1087,7 +1115,7 @@
struct v4l2_subdev_format *sink_fmt)
{
struct csi_priv *priv = v4l2_get_subdevdata(sd);
- struct v4l2_fwnode_endpoint upstream_ep;
+ struct v4l2_fwnode_endpoint upstream_ep = { .bus_type = 0 };
bool is_csi2;
int ret;
@@ -1117,8 +1145,7 @@
*/
#if 0
mutex_unlock(&priv->lock);
- vc_num = imx_media_find_mipi_csi2_channel(priv->md,
- &priv->sd.entity);
+ vc_num = imx_media_find_mipi_csi2_channel(&priv->sd.entity);
if (vc_num < 0)
return vc_num;
mutex_lock(&priv->lock);
@@ -1171,12 +1198,21 @@
struct v4l2_mbus_framefmt *infmt,
struct v4l2_fwnode_endpoint *upstream_ep)
{
+ u32 in_height;
+
crop->width = min_t(__u32, infmt->width, crop->width);
if (crop->left + crop->width > infmt->width)
crop->left = infmt->width - crop->width;
/* adjust crop left/width to h/w alignment restrictions */
crop->left &= ~0x3;
- crop->width &= ~0x7;
+ if (priv->active_output_pad == CSI_SRC_PAD_DIRECT)
+ crop->width &= ~0x7; /* multiple of 8 pixels (IC burst) */
+ else
+ crop->width &= ~0x1; /* multiple of 2 pixels */
+
+ in_height = infmt->height;
+ if (infmt->field == V4L2_FIELD_ALTERNATE)
+ in_height *= 2;
/*
* FIXME: not sure why yet, but on interlaced bt.656,
@@ -1187,12 +1223,12 @@
if (upstream_ep->bus_type == V4L2_MBUS_BT656 &&
(V4L2_FIELD_HAS_BOTH(infmt->field) ||
infmt->field == V4L2_FIELD_ALTERNATE)) {
- crop->height = infmt->height;
- crop->top = (infmt->height == 480) ? 2 : 0;
+ crop->height = in_height;
+ crop->top = (in_height == 480) ? 2 : 0;
} else {
- crop->height = min_t(__u32, infmt->height, crop->height);
- if (crop->top + crop->height > infmt->height)
- crop->top = infmt->height - crop->height;
+ crop->height = min_t(__u32, in_height, crop->height);
+ if (crop->top + crop->height > in_height)
+ crop->top = in_height - crop->height;
}
}
@@ -1201,7 +1237,7 @@
struct v4l2_subdev_mbus_code_enum *code)
{
struct csi_priv *priv = v4l2_get_subdevdata(sd);
- struct v4l2_fwnode_endpoint upstream_ep;
+ struct v4l2_fwnode_endpoint upstream_ep = { .bus_type = 0 };
const struct imx_media_pixfmt *incc;
struct v4l2_mbus_framefmt *infmt;
int ret = 0;
@@ -1209,12 +1245,12 @@
mutex_lock(&priv->lock);
infmt = __csi_get_fmt(priv, cfg, CSI_SINK_PAD, code->which);
- incc = imx_media_find_mbus_format(infmt->code, CS_SEL_ANY, true);
+ incc = imx_media_find_mbus_format(infmt->code, PIXFMT_SEL_ANY);
switch (code->pad) {
case CSI_SINK_PAD:
- ret = imx_media_enum_mbus_format(&code->code, code->index,
- CS_SEL_ANY, true);
+ ret = imx_media_enum_mbus_formats(&code->code, code->index,
+ PIXFMT_SEL_ANY);
break;
case CSI_SRC_PAD_DIRECT:
case CSI_SRC_PAD_IDMAC:
@@ -1231,11 +1267,13 @@
}
code->code = infmt->code;
} else {
- u32 cs_sel = (incc->cs == IPUV3_COLORSPACE_YUV) ?
- CS_SEL_YUV : CS_SEL_RGB;
- ret = imx_media_enum_ipu_format(&code->code,
- code->index,
- cs_sel);
+ enum imx_pixfmt_sel fmt_sel =
+ (incc->cs == IPUV3_COLORSPACE_YUV) ?
+ PIXFMT_SEL_YUV : PIXFMT_SEL_RGB;
+
+ ret = imx_media_enum_ipu_formats(&code->code,
+ code->index,
+ fmt_sel);
}
break;
default:
@@ -1342,6 +1380,55 @@
return ret;
}
+static void csi_try_field(struct csi_priv *priv,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *sdformat)
+{
+ struct v4l2_mbus_framefmt *infmt =
+ __csi_get_fmt(priv, cfg, CSI_SINK_PAD, sdformat->which);
+
+ /*
+ * no restrictions on sink pad field type except must
+ * be initialized.
+ */
+ if (sdformat->pad == CSI_SINK_PAD) {
+ if (sdformat->format.field == V4L2_FIELD_ANY)
+ sdformat->format.field = V4L2_FIELD_NONE;
+ return;
+ }
+
+ switch (infmt->field) {
+ case V4L2_FIELD_SEQ_TB:
+ case V4L2_FIELD_SEQ_BT:
+ /*
+ * If the user requests sequential at the source pad,
+ * allow it (along with possibly inverting field order).
+ * Otherwise passthrough the field type.
+ */
+ if (!V4L2_FIELD_IS_SEQUENTIAL(sdformat->format.field))
+ sdformat->format.field = infmt->field;
+ break;
+ case V4L2_FIELD_ALTERNATE:
+ /*
+ * This driver does not support alternate field mode, and
+ * the CSI captures a whole frame, so the CSI never presents
+ * alternate mode at its source pads. If user has not
+ * already requested sequential, translate ALTERNATE at
+ * sink pad to SEQ_TB or SEQ_BT at the source pad depending
+ * on input height (assume NTSC BT order if 480 total active
+ * frame lines, otherwise PAL TB order).
+ */
+ if (!V4L2_FIELD_IS_SEQUENTIAL(sdformat->format.field))
+ sdformat->format.field = (infmt->height == 480 / 2) ?
+ V4L2_FIELD_SEQ_BT : V4L2_FIELD_SEQ_TB;
+ break;
+ default:
+ /* Passthrough for all other input field types */
+ sdformat->format.field = infmt->field;
+ break;
+ }
+}
+
static void csi_try_fmt(struct csi_priv *priv,
struct v4l2_fwnode_endpoint *upstream_ep,
struct v4l2_subdev_pad_config *cfg,
@@ -1359,8 +1446,7 @@
switch (sdformat->pad) {
case CSI_SRC_PAD_DIRECT:
case CSI_SRC_PAD_IDMAC:
- incc = imx_media_find_mbus_format(infmt->code,
- CS_SEL_ANY, true);
+ incc = imx_media_find_mbus_format(infmt->code, PIXFMT_SEL_ANY);
sdformat->format.width = compose->width;
sdformat->format.height = compose->height;
@@ -1369,69 +1455,63 @@
sdformat->format.code = infmt->code;
*cc = incc;
} else {
- u32 cs_sel = (incc->cs == IPUV3_COLORSPACE_YUV) ?
- CS_SEL_YUV : CS_SEL_RGB;
+ enum imx_pixfmt_sel fmt_sel =
+ (incc->cs == IPUV3_COLORSPACE_YUV) ?
+ PIXFMT_SEL_YUV : PIXFMT_SEL_RGB;
*cc = imx_media_find_ipu_format(sdformat->format.code,
- cs_sel);
+ fmt_sel);
if (!*cc) {
- imx_media_enum_ipu_format(&code, 0, cs_sel);
- *cc = imx_media_find_ipu_format(code, cs_sel);
+ imx_media_enum_ipu_formats(&code, 0, fmt_sel);
+ *cc = imx_media_find_ipu_format(code, fmt_sel);
sdformat->format.code = (*cc)->codes[0];
}
}
- if (sdformat->pad == CSI_SRC_PAD_DIRECT ||
- sdformat->format.field != V4L2_FIELD_NONE)
- sdformat->format.field = infmt->field;
-
- /*
- * translate V4L2_FIELD_ALTERNATE to SEQ_TB or SEQ_BT
- * depending on input height (assume NTSC top-bottom
- * order if 480 lines, otherwise PAL bottom-top order).
- */
- if (sdformat->format.field == V4L2_FIELD_ALTERNATE) {
- sdformat->format.field = (infmt->height == 480) ?
- V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT;
- }
+ csi_try_field(priv, cfg, sdformat);
/* propagate colorimetry from sink */
sdformat->format.colorspace = infmt->colorspace;
sdformat->format.xfer_func = infmt->xfer_func;
sdformat->format.quantization = infmt->quantization;
sdformat->format.ycbcr_enc = infmt->ycbcr_enc;
+
break;
case CSI_SINK_PAD:
v4l_bound_align_image(&sdformat->format.width, MIN_W, MAX_W,
W_ALIGN, &sdformat->format.height,
MIN_H, MAX_H, H_ALIGN, S_ALIGN);
+ *cc = imx_media_find_mbus_format(sdformat->format.code,
+ PIXFMT_SEL_ANY);
+ if (!*cc) {
+ imx_media_enum_mbus_formats(&code, 0,
+ PIXFMT_SEL_YUV_RGB);
+ *cc = imx_media_find_mbus_format(code,
+ PIXFMT_SEL_YUV_RGB);
+ sdformat->format.code = (*cc)->codes[0];
+ }
+
+ csi_try_field(priv, cfg, sdformat);
+
/* Reset crop and compose rectangles */
crop->left = 0;
crop->top = 0;
crop->width = sdformat->format.width;
crop->height = sdformat->format.height;
+ if (sdformat->format.field == V4L2_FIELD_ALTERNATE)
+ crop->height *= 2;
csi_try_crop(priv, crop, cfg, &sdformat->format, upstream_ep);
compose->left = 0;
compose->top = 0;
compose->width = crop->width;
compose->height = crop->height;
- *cc = imx_media_find_mbus_format(sdformat->format.code,
- CS_SEL_ANY, true);
- if (!*cc) {
- imx_media_enum_mbus_format(&code, 0,
- CS_SEL_ANY, false);
- *cc = imx_media_find_mbus_format(code,
- CS_SEL_ANY, false);
- sdformat->format.code = (*cc)->codes[0];
- }
-
- imx_media_fill_default_mbus_fields(
- &sdformat->format, infmt,
- priv->active_output_pad == CSI_SRC_PAD_DIRECT);
break;
}
+
+ imx_media_try_colorimetry(&sdformat->format,
+ priv->active_output_pad == CSI_SRC_PAD_DIRECT);
}
static int csi_set_fmt(struct v4l2_subdev *sd,
@@ -1439,10 +1519,8 @@
struct v4l2_subdev_format *sdformat)
{
struct csi_priv *priv = v4l2_get_subdevdata(sd);
- struct imx_media_video_dev *vdev = priv->vdev;
- struct v4l2_fwnode_endpoint upstream_ep;
+ struct v4l2_fwnode_endpoint upstream_ep = { .bus_type = 0 };
const struct imx_media_pixfmt *cc;
- struct v4l2_pix_format vdev_fmt;
struct v4l2_mbus_framefmt *fmt;
struct v4l2_rect *crop, *compose;
int ret;
@@ -1494,19 +1572,9 @@
}
}
- if (sdformat->which == V4L2_SUBDEV_FORMAT_TRY)
- goto out;
+ if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ priv->cc[sdformat->pad] = cc;
- priv->cc[sdformat->pad] = cc;
-
- /* propagate IDMAC output pad format to capture device */
- imx_media_mbus_fmt_to_pix_fmt(&vdev_fmt,
- &priv->format_mbus[CSI_SRC_PAD_IDMAC],
- priv->cc[CSI_SRC_PAD_IDMAC]);
- mutex_unlock(&priv->lock);
- imx_media_capture_device_set_format(vdev, &vdev_fmt);
-
- return 0;
out:
mutex_unlock(&priv->lock);
return ret;
@@ -1536,6 +1604,8 @@
sel->r.top = 0;
sel->r.width = infmt->width;
sel->r.height = infmt->height;
+ if (infmt->field == V4L2_FIELD_ALTERNATE)
+ sel->r.height *= 2;
break;
case V4L2_SEL_TGT_CROP:
sel->r = *crop;
@@ -1579,7 +1649,7 @@
struct v4l2_subdev_selection *sel)
{
struct csi_priv *priv = v4l2_get_subdevdata(sd);
- struct v4l2_fwnode_endpoint upstream_ep;
+ struct v4l2_fwnode_endpoint upstream_ep = { .bus_type = 0 };
struct v4l2_mbus_framefmt *infmt;
struct v4l2_rect *crop, *compose;
int pad, ret;
@@ -1686,18 +1756,12 @@
return v4l2_event_unsubscribe(fh, sub);
}
-/*
- * retrieve our pads parsed from the OF graph by the media device
- */
static int csi_registered(struct v4l2_subdev *sd)
{
struct csi_priv *priv = v4l2_get_subdevdata(sd);
struct ipu_csi *csi;
int i, ret;
u32 code;
-
- /* get media device */
- priv->md = dev_get_drvdata(sd->v4l2_dev->dev);
/* get handle to IPU CSI */
csi = ipu_csi_get(priv->ipu, priv->csi_id);
@@ -1708,12 +1772,9 @@
priv->csi = csi;
for (i = 0; i < CSI_NUM_PADS; i++) {
- priv->pad[i].flags = (i == CSI_SINK_PAD) ?
- MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE;
-
code = 0;
if (i != CSI_SINK_PAD)
- imx_media_enum_ipu_format(&code, 0, CS_SEL_YUV);
+ imx_media_enum_ipu_formats(&code, 0, PIXFMT_SEL_YUV);
/* set a default mbus format */
ret = imx_media_init_mbus_fmt(&priv->format_mbus[i],
@@ -1742,21 +1803,22 @@
goto put_csi;
}
- ret = media_entity_pads_init(&sd->entity, CSI_NUM_PADS, priv->pad);
- if (ret)
+ priv->vdev = imx_media_capture_device_init(priv->sd.dev,
+ &priv->sd,
+ CSI_SRC_PAD_IDMAC);
+ if (IS_ERR(priv->vdev)) {
+ ret = PTR_ERR(priv->vdev);
goto free_fim;
+ }
ret = imx_media_capture_device_register(priv->vdev);
if (ret)
- goto free_fim;
-
- ret = imx_media_add_video_device(priv->md, priv->vdev);
- if (ret)
- goto unreg;
+ goto remove_vdev;
return 0;
-unreg:
- imx_media_capture_device_unregister(priv->vdev);
+
+remove_vdev:
+ imx_media_capture_device_remove(priv->vdev);
free_fim:
if (priv->fim)
imx_media_fim_free(priv->fim);
@@ -1770,6 +1832,7 @@
struct csi_priv *priv = v4l2_get_subdevdata(sd);
imx_media_capture_device_unregister(priv->vdev);
+ imx_media_capture_device_remove(priv->vdev);
if (priv->fim)
imx_media_fim_free(priv->fim);
@@ -1778,9 +1841,32 @@
ipu_csi_put(priv->csi);
}
+/*
+ * The CSI has only one fwnode endpoint, at the sink pad. Verify the
+ * endpoint belongs to us, and return CSI_SINK_PAD.
+ */
+static int csi_get_fwnode_pad(struct media_entity *entity,
+ struct fwnode_endpoint *endpoint)
+{
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
+ struct csi_priv *priv = v4l2_get_subdevdata(sd);
+ struct fwnode_handle *csi_port = dev_fwnode(priv->dev);
+ struct fwnode_handle *csi_ep;
+ int ret;
+
+ csi_ep = fwnode_get_next_child_node(csi_port, NULL);
+
+ ret = endpoint->local_fwnode == csi_ep ? CSI_SINK_PAD : -ENXIO;
+
+ fwnode_handle_put(csi_ep);
+
+ return ret;
+}
+
static const struct media_entity_operations csi_entity_ops = {
.link_setup = csi_link_setup,
.link_validate = v4l2_subdev_link_validate,
+ .get_fwnode_pad = csi_get_fwnode_pad,
};
static const struct v4l2_subdev_core_ops csi_core_ops = {
@@ -1817,12 +1903,74 @@
.unregistered = csi_unregistered,
};
+static int imx_csi_notify_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd,
+ struct v4l2_async_subdev *asd)
+{
+ struct csi_priv *priv = notifier_to_dev(notifier);
+ struct media_pad *sink = &priv->sd.entity.pads[CSI_SINK_PAD];
+
+ /*
+ * If the subdev is a video mux, it must be one of the CSI
+ * muxes. Mark it as such via its group id.
+ */
+ if (sd->entity.function == MEDIA_ENT_F_VID_MUX)
+ sd->grp_id = IMX_MEDIA_GRP_ID_CSI_MUX;
+
+ return v4l2_create_fwnode_links_to_pad(sd, sink);
+}
+
+static const struct v4l2_async_notifier_operations csi_notify_ops = {
+ .bound = imx_csi_notify_bound,
+};
+
+static int imx_csi_async_register(struct csi_priv *priv)
+{
+ struct v4l2_async_subdev *asd = NULL;
+ struct fwnode_handle *ep;
+ unsigned int port;
+ int ret;
+
+ v4l2_async_notifier_init(&priv->notifier);
+
+ /* get this CSI's port id */
+ ret = fwnode_property_read_u32(dev_fwnode(priv->dev), "reg", &port);
+ if (ret < 0)
+ return ret;
+
+ ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(priv->dev->parent),
+ port, 0,
+ FWNODE_GRAPH_ENDPOINT_NEXT);
+ if (ep) {
+ asd = v4l2_async_notifier_add_fwnode_remote_subdev(
+ &priv->notifier, ep, sizeof(*asd));
+
+ fwnode_handle_put(ep);
+
+ if (IS_ERR(asd)) {
+ ret = PTR_ERR(asd);
+ /* OK if asd already exists */
+ if (ret != -EEXIST)
+ return ret;
+ }
+ }
+
+ priv->notifier.ops = &csi_notify_ops;
+
+ ret = v4l2_async_subdev_notifier_register(&priv->sd,
+ &priv->notifier);
+ if (ret)
+ return ret;
+
+ return v4l2_async_register_subdev(&priv->sd);
+}
+
static int imx_csi_probe(struct platform_device *pdev)
{
struct ipu_client_platformdata *pdata;
struct pinctrl *pinctrl;
struct csi_priv *priv;
- int ret;
+ int i, ret;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@@ -1843,6 +1991,8 @@
priv->csi_id = pdata->csi;
priv->smfc_id = (priv->csi_id == 0) ? 0 : 2;
+ priv->active_output_pad = CSI_SRC_PAD_IDMAC;
+
timer_setup(&priv->eof_timeout_timer, csi_idmac_eof_timeout, 0);
spin_lock_init(&priv->irqlock);
@@ -1856,14 +2006,18 @@
priv->sd.owner = THIS_MODULE;
priv->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
priv->sd.grp_id = priv->csi_id ?
- IMX_MEDIA_GRP_ID_CSI1 : IMX_MEDIA_GRP_ID_CSI0;
+ IMX_MEDIA_GRP_ID_IPU_CSI1 : IMX_MEDIA_GRP_ID_IPU_CSI0;
imx_media_grp_id_to_sd_name(priv->sd.name, sizeof(priv->sd.name),
priv->sd.grp_id, ipu_get_num(priv->ipu));
- priv->vdev = imx_media_capture_device_init(&priv->sd,
- CSI_SRC_PAD_IDMAC);
- if (IS_ERR(priv->vdev))
- return PTR_ERR(priv->vdev);
+ for (i = 0; i < CSI_NUM_PADS; i++)
+ priv->pad[i].flags = (i == CSI_SINK_PAD) ?
+ MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE;
+
+ ret = media_entity_pads_init(&priv->sd.entity, CSI_NUM_PADS,
+ priv->pad);
+ if (ret)
+ return ret;
mutex_init(&priv->lock);
@@ -1886,15 +2040,18 @@
goto free;
}
- ret = v4l2_async_register_subdev(&priv->sd);
+ ret = imx_csi_async_register(priv);
if (ret)
- goto free;
+ goto cleanup;
return 0;
+
+cleanup:
+ v4l2_async_notifier_unregister(&priv->notifier);
+ v4l2_async_notifier_cleanup(&priv->notifier);
free:
v4l2_ctrl_handler_free(&priv->ctrl_hdlr);
mutex_destroy(&priv->lock);
- imx_media_capture_device_remove(priv->vdev);
return ret;
}
@@ -1905,7 +2062,8 @@
v4l2_ctrl_handler_free(&priv->ctrl_hdlr);
mutex_destroy(&priv->lock);
- imx_media_capture_device_remove(priv->vdev);
+ v4l2_async_notifier_unregister(&priv->notifier);
+ v4l2_async_notifier_cleanup(&priv->notifier);
v4l2_async_unregister_subdev(sd);
media_entity_cleanup(&sd->entity);
--
Gitblit v1.6.2