summaryrefslogtreecommitdiff
path: root/drivers/staging/media/imx
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/media/imx')
-rw-r--r--drivers/staging/media/imx/Kconfig5
-rw-r--r--drivers/staging/media/imx/TODO29
-rw-r--r--drivers/staging/media/imx/imx-ic-prp.c15
-rw-r--r--drivers/staging/media/imx/imx-ic-prpencvf.c14
-rw-r--r--drivers/staging/media/imx/imx-media-capture.c42
-rw-r--r--drivers/staging/media/imx/imx-media-csc-scaler.c13
-rw-r--r--drivers/staging/media/imx/imx-media-csi.c223
-rw-r--r--drivers/staging/media/imx/imx-media-dev-common.c50
-rw-r--r--drivers/staging/media/imx/imx-media-dev.c2
-rw-r--r--drivers/staging/media/imx/imx-media-internal-sd.c6
-rw-r--r--drivers/staging/media/imx/imx-media-of.c114
-rw-r--r--drivers/staging/media/imx/imx-media-utils.c550
-rw-r--r--drivers/staging/media/imx/imx-media-vdic.c12
-rw-r--r--drivers/staging/media/imx/imx-media.h63
-rw-r--r--drivers/staging/media/imx/imx6-mipi-csi2.c93
-rw-r--r--drivers/staging/media/imx/imx7-media-csi.c177
-rw-r--r--drivers/staging/media/imx/imx7-mipi-csis.c582
17 files changed, 1030 insertions, 960 deletions
diff --git a/drivers/staging/media/imx/Kconfig b/drivers/staging/media/imx/Kconfig
index 8f1ae50a4abd..f555aac8a9d5 100644
--- a/drivers/staging/media/imx/Kconfig
+++ b/drivers/staging/media/imx/Kconfig
@@ -2,8 +2,9 @@
config VIDEO_IMX_MEDIA
tristate "i.MX5/6 V4L2 media core driver"
depends on ARCH_MXC || COMPILE_TEST
- depends on MEDIA_CONTROLLER && VIDEO_V4L2 && IMX_IPUV3_CORE
- depends on VIDEO_V4L2_SUBDEV_API
+ depends on VIDEO_V4L2 && IMX_IPUV3_CORE
+ select MEDIA_CONTROLLER
+ select VIDEO_V4L2_SUBDEV_API
depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
select V4L2_FWNODE
diff --git a/drivers/staging/media/imx/TODO b/drivers/staging/media/imx/TODO
index 6f29b5ca5324..a371cdedcdb0 100644
--- a/drivers/staging/media/imx/TODO
+++ b/drivers/staging/media/imx/TODO
@@ -17,35 +17,6 @@
decided whether this feature is useful enough to make it generally
available by exporting to v4l2-core.
-- After all async subdevices have been bound, v4l2_fwnode_parse_link()
- is used to form the media links between the devices discovered in
- the OF graph.
-
- While this approach allows support for arbitrary OF graphs, there
- are some assumptions for this to work:
-
- 1. If a port owned by a device in the graph has endpoint nodes, the
- port is treated as a media pad.
-
- This presents problems for devices that don't make this port = pad
- assumption. Examples are SMIAPP compatible cameras which define only
- a single output port node, but which define multiple pads owned
- by multiple subdevices (pixel-array, binner, scaler). Or video
- decoders (entity function MEDIA_ENT_F_ATV_DECODER), which also define
- only a single output port node, but define multiple pads for video,
- VBI, and audio out.
-
- A workaround at present is to set the port reg properties to
- correspond to the media pad index that the port represents. A
- possible long-term solution is to implement a subdev API that
- maps a port id to a media pad index.
-
- 2. Every endpoint of a port owned by a device in the graph is treated
- as a media link.
-
- Which means a port must not contain mixed-use endpoints, they
- must all refer to media links between V4L2 subdevices.
-
- i.MX7: all of the above, since it uses the imx media core
- i.MX7: use Frame Interval Monitor
diff --git a/drivers/staging/media/imx/imx-ic-prp.c b/drivers/staging/media/imx/imx-ic-prp.c
index 2a4f77e83ed3..7658b83466a7 100644
--- a/drivers/staging/media/imx/imx-ic-prp.c
+++ b/drivers/staging/media/imx/imx-ic-prp.c
@@ -106,8 +106,8 @@ static int prp_enum_mbus_code(struct v4l2_subdev *sd,
switch (code->pad) {
case PRP_SINK_PAD:
- ret = imx_media_enum_ipu_format(&code->code, code->index,
- CS_SEL_ANY);
+ ret = imx_media_enum_ipu_formats(&code->code, code->index,
+ PIXFMT_SEL_YUV_RGB);
break;
case PRP_SRC_PAD_PRPENC:
case PRP_SRC_PAD_PRPVF:
@@ -180,10 +180,12 @@ static int prp_set_fmt(struct v4l2_subdev *sd,
MIN_H, MAX_H, H_ALIGN, S_ALIGN);
cc = imx_media_find_ipu_format(sdformat->format.code,
- CS_SEL_ANY);
+ PIXFMT_SEL_YUV_RGB);
if (!cc) {
- imx_media_enum_ipu_format(&code, 0, CS_SEL_ANY);
- cc = imx_media_find_ipu_format(code, CS_SEL_ANY);
+ imx_media_enum_ipu_formats(&code, 0,
+ PIXFMT_SEL_YUV_RGB);
+ cc = imx_media_find_ipu_format(code,
+ PIXFMT_SEL_YUV_RGB);
sdformat->format.code = cc->codes[0];
}
@@ -438,7 +440,8 @@ static int prp_registered(struct v4l2_subdev *sd)
priv->frame_interval.denominator = 30;
/* set a default mbus format */
- imx_media_enum_ipu_format(&code, 0, CS_SEL_YUV);
+ imx_media_enum_ipu_formats(&code, 0, PIXFMT_SEL_YUV);
+
return imx_media_init_mbus_fmt(&priv->format_mbus, 640, 480, code,
V4L2_FIELD_NONE, NULL);
}
diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c
index 09c4e3f33807..b1f84e0ac486 100644
--- a/drivers/staging/media/imx/imx-ic-prpencvf.c
+++ b/drivers/staging/media/imx/imx-ic-prpencvf.c
@@ -850,7 +850,8 @@ static int prp_enum_mbus_code(struct v4l2_subdev *sd,
if (code->pad >= PRPENCVF_NUM_PADS)
return -EINVAL;
- return imx_media_enum_ipu_format(&code->code, code->index, CS_SEL_ANY);
+ return imx_media_enum_ipu_formats(&code->code, code->index,
+ PIXFMT_SEL_YUV_RGB);
}
static int prp_get_fmt(struct v4l2_subdev *sd,
@@ -885,12 +886,14 @@ static void prp_try_fmt(struct prp_priv *priv,
{
struct v4l2_mbus_framefmt *infmt;
- *cc = imx_media_find_ipu_format(sdformat->format.code, CS_SEL_ANY);
+ *cc = imx_media_find_ipu_format(sdformat->format.code,
+ PIXFMT_SEL_YUV_RGB);
if (!*cc) {
u32 code;
- imx_media_enum_ipu_format(&code, 0, CS_SEL_ANY);
- *cc = imx_media_find_ipu_format(code, CS_SEL_ANY);
+ imx_media_enum_ipu_formats(&code, 0, PIXFMT_SEL_YUV_RGB);
+ *cc = imx_media_find_ipu_format(code, PIXFMT_SEL_YUV_RGB);
+
sdformat->format.code = (*cc)->codes[0];
}
@@ -1248,7 +1251,8 @@ static int prp_registered(struct v4l2_subdev *sd)
u32 code;
/* set a default mbus format */
- imx_media_enum_ipu_format(&code, 0, CS_SEL_YUV);
+ imx_media_enum_ipu_formats(&code, 0, PIXFMT_SEL_YUV);
+
for (i = 0; i < PRPENCVF_NUM_PADS; i++) {
ret = imx_media_init_mbus_fmt(&priv->format_mbus[i],
640, 480, code, V4L2_FIELD_NONE,
diff --git a/drivers/staging/media/imx/imx-media-capture.c b/drivers/staging/media/imx/imx-media-capture.c
index d37b776ff86d..c1931eb2540e 100644
--- a/drivers/staging/media/imx/imx-media-capture.c
+++ b/drivers/staging/media/imx/imx-media-capture.c
@@ -91,11 +91,11 @@ static int capture_enum_framesizes(struct file *file, void *fh,
};
int ret;
- cc = imx_media_find_format(fsize->pixel_format, CS_SEL_ANY, true);
+ cc = imx_media_find_pixel_format(fsize->pixel_format, PIXFMT_SEL_ANY);
if (!cc)
return -EINVAL;
- fse.code = cc->codes[0];
+ fse.code = cc->codes ? cc->codes[0] : 0;
ret = v4l2_subdev_call(priv->src_sd, pad, enum_frame_size, NULL, &fse);
if (ret)
@@ -133,11 +133,11 @@ static int capture_enum_frameintervals(struct file *file, void *fh,
};
int ret;
- cc = imx_media_find_format(fival->pixel_format, CS_SEL_ANY, true);
+ cc = imx_media_find_pixel_format(fival->pixel_format, PIXFMT_SEL_ANY);
if (!cc)
return -EINVAL;
- fie.code = cc->codes[0];
+ fie.code = cc->codes ? cc->codes[0] : 0;
ret = v4l2_subdev_call(priv->src_sd, pad, enum_frame_interval,
NULL, &fie);
@@ -167,17 +167,19 @@ static int capture_enum_fmt_vid_cap(struct file *file, void *fh,
return ret;
}
- cc_src = imx_media_find_ipu_format(fmt_src.format.code, CS_SEL_ANY);
+ cc_src = imx_media_find_ipu_format(fmt_src.format.code,
+ PIXFMT_SEL_YUV_RGB);
if (cc_src) {
- u32 cs_sel = (cc_src->cs == IPUV3_COLORSPACE_YUV) ?
- CS_SEL_YUV : CS_SEL_RGB;
+ enum imx_pixfmt_sel fmt_sel =
+ (cc_src->cs == IPUV3_COLORSPACE_YUV) ?
+ PIXFMT_SEL_YUV : PIXFMT_SEL_RGB;
- ret = imx_media_enum_format(&fourcc, f->index, cs_sel);
+ ret = imx_media_enum_pixel_formats(&fourcc, f->index, fmt_sel);
if (ret)
return ret;
} else {
cc_src = imx_media_find_mbus_format(fmt_src.format.code,
- CS_SEL_ANY, true);
+ PIXFMT_SEL_ANY);
if (WARN_ON(!cc_src))
return -EINVAL;
@@ -209,22 +211,24 @@ static int __capture_try_fmt_vid_cap(struct capture_priv *priv,
{
const struct imx_media_pixfmt *cc, *cc_src;
- cc_src = imx_media_find_ipu_format(fmt_src->format.code, CS_SEL_ANY);
+ cc_src = imx_media_find_ipu_format(fmt_src->format.code,
+ PIXFMT_SEL_YUV_RGB);
if (cc_src) {
- u32 fourcc, cs_sel;
+ enum imx_pixfmt_sel fmt_sel;
+ u32 fourcc;
- cs_sel = (cc_src->cs == IPUV3_COLORSPACE_YUV) ?
- CS_SEL_YUV : CS_SEL_RGB;
+ fmt_sel = (cc_src->cs == IPUV3_COLORSPACE_YUV) ?
+ PIXFMT_SEL_YUV : PIXFMT_SEL_RGB;
fourcc = f->fmt.pix.pixelformat;
- cc = imx_media_find_format(fourcc, cs_sel, false);
+ cc = imx_media_find_pixel_format(fourcc, fmt_sel);
if (!cc) {
- imx_media_enum_format(&fourcc, 0, cs_sel);
- cc = imx_media_find_format(fourcc, cs_sel, false);
+ imx_media_enum_pixel_formats(&fourcc, 0, fmt_sel);
+ cc = imx_media_find_pixel_format(fourcc, fmt_sel);
}
} else {
cc_src = imx_media_find_mbus_format(fmt_src->format.code,
- CS_SEL_ANY, true);
+ PIXFMT_SEL_ANY);
if (WARN_ON(!cc_src))
return -EINVAL;
@@ -789,8 +793,8 @@ int imx_media_capture_device_register(struct imx_media_video_dev *vdev)
&fmt_src.format, NULL);
vdev->compose.width = fmt_src.format.width;
vdev->compose.height = fmt_src.format.height;
- vdev->cc = imx_media_find_format(vdev->fmt.fmt.pix.pixelformat,
- CS_SEL_ANY, false);
+ vdev->cc = imx_media_find_pixel_format(vdev->fmt.fmt.pix.pixelformat,
+ PIXFMT_SEL_ANY);
v4l2_info(sd, "Registered %s as /dev/%s\n", vfd->name,
video_device_node_name(vfd));
diff --git a/drivers/staging/media/imx/imx-media-csc-scaler.c b/drivers/staging/media/imx/imx-media-csc-scaler.c
index 2cc77f6e84b6..fab1155a5958 100644
--- a/drivers/staging/media/imx/imx-media-csc-scaler.c
+++ b/drivers/staging/media/imx/imx-media-csc-scaler.c
@@ -25,6 +25,8 @@
#define fh_to_ctx(__fh) container_of(__fh, struct ipu_csc_scaler_ctx, fh)
+#define IMX_CSC_SCALER_NAME "imx-csc-scaler"
+
enum {
V4L2_M2M_SRC = 0,
V4L2_M2M_DST = 1,
@@ -150,10 +152,10 @@ err:
static int ipu_csc_scaler_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
- strscpy(cap->driver, "imx-media-csc-scaler", sizeof(cap->driver));
- strscpy(cap->card, "imx-media-csc-scaler", sizeof(cap->card));
- strscpy(cap->bus_info, "platform:imx-media-csc-scaler",
- sizeof(cap->bus_info));
+ strscpy(cap->driver, IMX_CSC_SCALER_NAME, sizeof(cap->driver));
+ strscpy(cap->card, IMX_CSC_SCALER_NAME, sizeof(cap->card));
+ snprintf(cap->bus_info, sizeof(cap->bus_info),
+ "platform:%s", IMX_CSC_SCALER_NAME);
return 0;
}
@@ -164,7 +166,8 @@ static int ipu_csc_scaler_enum_fmt(struct file *file, void *fh,
u32 fourcc;
int ret;
- ret = imx_media_enum_format(&fourcc, f->index, CS_SEL_ANY);
+ ret = imx_media_enum_pixel_formats(&fourcc, f->index,
+ PIXFMT_SEL_YUV_RGB);
if (ret)
return ret;
diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c
index e76a6a85baa3..d7e5b9ed27b8 100644
--- a/drivers/staging/media/imx/imx-media-csi.c
+++ b/drivers/staging/media/imx/imx-media-csi.c
@@ -58,6 +58,8 @@ struct csi_priv {
struct ipu_soc *ipu;
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;
@@ -118,6 +120,11 @@ static inline struct csi_priv *sd_to_dev(struct v4l2_subdev *sdev)
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_DPHY;
@@ -157,8 +164,7 @@ static inline bool requires_passthrough(struct v4l2_fwnode_endpoint *ep,
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;
@@ -169,50 +175,43 @@ static int csi_get_upstream_endpoint(struct csi_priv *priv,
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_pipeline_subdev(src, IMX_MEDIA_GRP_ID_CSI2,
+ sd = imx_media_pipeline_subdev(&sd->entity,
+ IMX_MEDIA_GRP_ID_CSI2,
true);
- if (!IS_ERR(sd))
- src = &sd->entity;
+ 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_pipeline_pad(src, 0, 0, true);
+ /* get source pad of entity directly upstream from sd */
+ pad = imx_media_pipeline_pad(&sd->entity, 0, 0, true);
if (!pad)
return -ENODEV;
- sd = media_entity_to_v4l2_subdev(pad->entity);
+ endpoint = imx_media_get_pad_fwnode(pad);
+ if (IS_ERR(endpoint))
+ return PTR_ERR(endpoint);
- /*
- * 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)
- return -ENODEV;
-
- endpoint = of_get_next_child(port, NULL);
- of_node_put(port);
- if (!endpoint)
- return -ENODEV;
+ v4l2_fwnode_endpoint_parse(endpoint, ep);
- v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint), ep);
- of_node_put(endpoint);
+ fwnode_handle_put(endpoint);
return 0;
}
@@ -1234,12 +1233,12 @@ static int csi_enum_mbus_code(struct v4l2_subdev *sd,
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:
@@ -1256,11 +1255,13 @@ static int csi_enum_mbus_code(struct v4l2_subdev *sd,
}
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:
@@ -1433,8 +1434,7 @@ static void csi_try_fmt(struct csi_priv *priv,
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;
@@ -1443,14 +1443,15 @@ static void csi_try_fmt(struct csi_priv *priv,
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];
}
}
@@ -1470,12 +1471,12 @@ static void csi_try_fmt(struct csi_priv *priv,
MIN_H, MAX_H, H_ALIGN, S_ALIGN);
*cc = imx_media_find_mbus_format(sdformat->format.code,
- CS_SEL_ANY, true);
+ PIXFMT_SEL_ANY);
if (!*cc) {
- imx_media_enum_mbus_format(&code, 0,
- CS_SEL_ANY, false);
+ imx_media_enum_mbus_formats(&code, 0,
+ PIXFMT_SEL_YUV_RGB);
*cc = imx_media_find_mbus_format(code,
- CS_SEL_ANY, false);
+ PIXFMT_SEL_YUV_RGB);
sdformat->format.code = (*cc)->codes[0];
}
@@ -1761,7 +1762,7 @@ static int csi_registered(struct v4l2_subdev *sd)
for (i = 0; i < CSI_NUM_PADS; i++) {
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],
@@ -1828,9 +1829,32 @@ static void csi_unregistered(struct v4l2_subdev *sd)
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 = {
@@ -1867,59 +1891,72 @@ static const struct v4l2_subdev_internal_ops csi_internal_ops = {
.unregistered = csi_unregistered,
};
-static int imx_csi_parse_endpoint(struct device *dev,
- struct v4l2_fwnode_endpoint *vep,
- struct v4l2_async_subdev *asd)
+static int imx_csi_notify_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd,
+ struct v4l2_async_subdev *asd)
{
- return fwnode_device_is_available(asd->match.fwnode) ? 0 : -ENOTCONN;
+ 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_notifier *notifier;
- struct fwnode_handle *fwnode;
+ struct v4l2_async_subdev *asd = NULL;
+ struct fwnode_handle *ep;
unsigned int port;
int ret;
- notifier = kzalloc(sizeof(*notifier), GFP_KERNEL);
- if (!notifier)
- return -ENOMEM;
-
- v4l2_async_notifier_init(notifier);
-
- fwnode = dev_fwnode(priv->dev);
+ v4l2_async_notifier_init(&priv->notifier);
/* get this CSI's port id */
- ret = fwnode_property_read_u32(fwnode, "reg", &port);
+ ret = fwnode_property_read_u32(dev_fwnode(priv->dev), "reg", &port);
if (ret < 0)
- goto out_free;
+ return ret;
- ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port(
- priv->dev->parent, notifier, sizeof(struct v4l2_async_subdev),
- port, imx_csi_parse_endpoint);
- if (ret < 0)
- goto out_cleanup;
+ ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(priv->dev->parent),
+ port, 0,
+ FWNODE_GRAPH_ENDPOINT_NEXT);
+ if (ep) {
+ asd = kzalloc(sizeof(*asd), GFP_KERNEL);
+ if (!asd) {
+ fwnode_handle_put(ep);
+ return -ENOMEM;
+ }
- ret = v4l2_async_subdev_notifier_register(&priv->sd, notifier);
- if (ret < 0)
- goto out_cleanup;
+ ret = v4l2_async_notifier_add_fwnode_remote_subdev(
+ &priv->notifier, ep, asd);
- ret = v4l2_async_register_subdev(&priv->sd);
- if (ret < 0)
- goto out_unregister;
+ fwnode_handle_put(ep);
- priv->sd.subdev_notifier = notifier;
+ if (ret) {
+ kfree(asd);
+ /* OK if asd already exists */
+ if (ret != -EEXIST)
+ return ret;
+ }
+ }
- return 0;
+ priv->notifier.ops = &csi_notify_ops;
-out_unregister:
- v4l2_async_notifier_unregister(notifier);
-out_cleanup:
- v4l2_async_notifier_cleanup(notifier);
-out_free:
- kfree(notifier);
+ ret = v4l2_async_subdev_notifier_register(&priv->sd,
+ &priv->notifier);
+ if (ret)
+ return ret;
- return ret;
+ return v4l2_async_register_subdev(&priv->sd);
}
static int imx_csi_probe(struct platform_device *pdev)
@@ -1999,9 +2036,13 @@ static int imx_csi_probe(struct platform_device *pdev)
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);
@@ -2015,6 +2056,8 @@ static int imx_csi_remove(struct platform_device *pdev)
v4l2_ctrl_handler_free(&priv->ctrl_hdlr);
mutex_destroy(&priv->lock);
+ v4l2_async_notifier_unregister(&priv->notifier);
+ v4l2_async_notifier_cleanup(&priv->notifier);
v4l2_async_unregister_subdev(sd);
media_entity_cleanup(&sd->entity);
diff --git a/drivers/staging/media/imx/imx-media-dev-common.c b/drivers/staging/media/imx/imx-media-dev-common.c
index 66b505f7e8df..5fe4b22ab847 100644
--- a/drivers/staging/media/imx/imx-media-dev-common.c
+++ b/drivers/staging/media/imx/imx-media-dev-common.c
@@ -24,47 +24,39 @@ static int imx_media_subdev_bound(struct v4l2_async_notifier *notifier,
struct v4l2_subdev *sd,
struct v4l2_async_subdev *asd)
{
- v4l2_info(sd->v4l2_dev, "subdev %s bound\n", sd->name);
+ struct imx_media_dev *imxmd = notifier2dev(notifier);
+
+ dev_dbg(imxmd->md.dev, "subdev %s bound\n", sd->name);
return 0;
}
/*
- * Create the media links for all subdevs that registered.
+ * Create the missing media links from the CSI-2 receiver.
* Called after all async subdevs have bound.
*/
-static int imx_media_create_links(struct v4l2_async_notifier *notifier)
+static void imx_media_create_csi2_links(struct imx_media_dev *imxmd)
{
- struct imx_media_dev *imxmd = notifier2dev(notifier);
- struct v4l2_subdev *sd;
+ struct v4l2_subdev *sd, *csi2 = NULL;
list_for_each_entry(sd, &imxmd->v4l2_dev.subdevs, list) {
- switch (sd->grp_id) {
- case IMX_MEDIA_GRP_ID_IPU_VDIC:
- case IMX_MEDIA_GRP_ID_IPU_IC_PRP:
- case IMX_MEDIA_GRP_ID_IPU_IC_PRPENC:
- case IMX_MEDIA_GRP_ID_IPU_IC_PRPVF:
- /*
- * links have already been created for the
- * sync-registered subdevs.
- */
- break;
- case IMX_MEDIA_GRP_ID_IPU_CSI0:
- case IMX_MEDIA_GRP_ID_IPU_CSI1:
- case IMX_MEDIA_GRP_ID_CSI:
- imx_media_create_csi_of_links(imxmd, sd);
- break;
- default:
- /*
- * if this subdev has fwnode links, create media
- * links for them.
- */
- imx_media_create_of_links(imxmd, sd);
+ if (sd->grp_id == IMX_MEDIA_GRP_ID_CSI2) {
+ csi2 = sd;
break;
}
}
+ if (!csi2)
+ return;
- return 0;
+ list_for_each_entry(sd, &imxmd->v4l2_dev.subdevs, list) {
+ /* skip if not a CSI or a CSI mux */
+ if (!(sd->grp_id & IMX_MEDIA_GRP_ID_IPU_CSI) &&
+ !(sd->grp_id & IMX_MEDIA_GRP_ID_CSI) &&
+ !(sd->grp_id & IMX_MEDIA_GRP_ID_CSI_MUX))
+ continue;
+
+ v4l2_create_fwnode_links(csi2, sd);
+ }
}
/*
@@ -196,9 +188,7 @@ int imx_media_probe_complete(struct v4l2_async_notifier *notifier)
mutex_lock(&imxmd->mutex);
- ret = imx_media_create_links(notifier);
- if (ret)
- goto unlock;
+ imx_media_create_csi2_links(imxmd);
ret = imx_media_create_pad_vdev_lists(imxmd);
if (ret)
diff --git a/drivers/staging/media/imx/imx-media-dev.c b/drivers/staging/media/imx/imx-media-dev.c
index 2c3c2adca683..6d2205461e56 100644
--- a/drivers/staging/media/imx/imx-media-dev.c
+++ b/drivers/staging/media/imx/imx-media-dev.c
@@ -32,7 +32,7 @@ static int imx_media_subdev_bound(struct v4l2_async_notifier *notifier,
return ret;
}
- v4l2_info(&imxmd->v4l2_dev, "subdev %s bound\n", sd->name);
+ dev_dbg(imxmd->md.dev, "subdev %s bound\n", sd->name);
return 0;
}
diff --git a/drivers/staging/media/imx/imx-media-internal-sd.c b/drivers/staging/media/imx/imx-media-internal-sd.c
index d4237e1a4241..da4109b2fd13 100644
--- a/drivers/staging/media/imx/imx-media-internal-sd.c
+++ b/drivers/staging/media/imx/imx-media-internal-sd.c
@@ -142,9 +142,9 @@ static int create_internal_link(struct imx_media_dev *imxmd,
&sink->entity.pads[link->remote_pad]))
return 0;
- v4l2_info(&imxmd->v4l2_dev, "%s:%d -> %s:%d\n",
- src->name, link->local_pad,
- sink->name, link->remote_pad);
+ dev_dbg(imxmd->md.dev, "%s:%d -> %s:%d\n",
+ src->name, link->local_pad,
+ sink->name, link->remote_pad);
ret = media_create_pad_link(&src->entity, link->local_pad,
&sink->entity, link->remote_pad, 0);
diff --git a/drivers/staging/media/imx/imx-media-of.c b/drivers/staging/media/imx/imx-media-of.c
index 2d3efd2a6dde..82e13e972e23 100644
--- a/drivers/staging/media/imx/imx-media-of.c
+++ b/drivers/staging/media/imx/imx-media-of.c
@@ -74,117 +74,3 @@ err_out:
return ret;
}
EXPORT_SYMBOL_GPL(imx_media_add_of_subdevs);
-
-/*
- * Create a single media link to/from sd using a fwnode link.
- *
- * NOTE: this function assumes an OF port node is equivalent to
- * a media pad (port id equal to media pad index), and that an
- * OF endpoint node is equivalent to a media link.
- */
-static int create_of_link(struct imx_media_dev *imxmd,
- struct v4l2_subdev *sd,
- struct v4l2_fwnode_link *link)
-{
- struct v4l2_subdev *remote, *src, *sink;
- int src_pad, sink_pad;
-
- if (link->local_port >= sd->entity.num_pads)
- return -EINVAL;
-
- remote = imx_media_find_subdev_by_fwnode(imxmd, link->remote_node);
- if (!remote)
- return 0;
-
- if (sd->entity.pads[link->local_port].flags & MEDIA_PAD_FL_SINK) {
- src = remote;
- src_pad = link->remote_port;
- sink = sd;
- sink_pad = link->local_port;
- } else {
- src = sd;
- src_pad = link->local_port;
- sink = remote;
- sink_pad = link->remote_port;
- }
-
- /* make sure link doesn't already exist before creating */
- if (media_entity_find_link(&src->entity.pads[src_pad],
- &sink->entity.pads[sink_pad]))
- return 0;
-
- v4l2_info(sd->v4l2_dev, "%s:%d -> %s:%d\n",
- src->name, src_pad, sink->name, sink_pad);
-
- return media_create_pad_link(&src->entity, src_pad,
- &sink->entity, sink_pad, 0);
-}
-
-/*
- * Create media links to/from sd using its device-tree endpoints.
- */
-int imx_media_create_of_links(struct imx_media_dev *imxmd,
- struct v4l2_subdev *sd)
-{
- struct v4l2_fwnode_link link;
- struct device_node *ep;
- int ret;
-
- for_each_endpoint_of_node(sd->dev->of_node, ep) {
- ret = v4l2_fwnode_parse_link(of_fwnode_handle(ep), &link);
- if (ret)
- continue;
-
- ret = create_of_link(imxmd, sd, &link);
- v4l2_fwnode_put_link(&link);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(imx_media_create_of_links);
-
-/*
- * Create media links to the given CSI subdevice's sink pads,
- * using its device-tree endpoints.
- */
-int imx_media_create_csi_of_links(struct imx_media_dev *imxmd,
- struct v4l2_subdev *csi)
-{
- struct device_node *csi_np = csi->dev->of_node;
- struct device_node *ep;
-
- for_each_child_of_node(csi_np, ep) {
- struct fwnode_handle *fwnode, *csi_ep;
- struct v4l2_fwnode_link link;
- int ret;
-
- memset(&link, 0, sizeof(link));
-
- link.local_node = of_fwnode_handle(csi_np);
- link.local_port = CSI_SINK_PAD;
-
- csi_ep = of_fwnode_handle(ep);
-
- fwnode = fwnode_graph_get_remote_endpoint(csi_ep);
- if (!fwnode)
- continue;
-
- fwnode = fwnode_get_parent(fwnode);
- fwnode_property_read_u32(fwnode, "reg", &link.remote_port);
- fwnode = fwnode_get_next_parent(fwnode);
- if (is_of_node(fwnode) &&
- of_node_name_eq(to_of_node(fwnode), "ports"))
- fwnode = fwnode_get_next_parent(fwnode);
- link.remote_node = fwnode;
-
- ret = create_of_link(imxmd, csi, &link);
- fwnode_handle_put(link.remote_node);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(imx_media_create_csi_of_links);
diff --git a/drivers/staging/media/imx/imx-media-utils.c b/drivers/staging/media/imx/imx-media-utils.c
index fae981698c49..c2088f7ceef5 100644
--- a/drivers/staging/media/imx/imx-media-utils.c
+++ b/drivers/staging/media/imx/imx-media-utils.c
@@ -7,36 +7,30 @@
#include <linux/module.h>
#include "imx-media.h"
+#define IMX_BUS_FMTS(fmt...) (const u32[]) {fmt, 0}
+
/*
* List of supported pixel formats for the subdevs.
- *
- * In all of these tables, the non-mbus formats (with no
- * mbus codes) must all fall at the end of the table.
*/
-
-static const struct imx_media_pixfmt yuv_formats[] = {
+static const struct imx_media_pixfmt pixel_formats[] = {
+ /*** YUV formats start here ***/
{
.fourcc = V4L2_PIX_FMT_UYVY,
- .codes = {
+ .codes = IMX_BUS_FMTS(
MEDIA_BUS_FMT_UYVY8_2X8,
MEDIA_BUS_FMT_UYVY8_1X16
- },
+ ),
.cs = IPUV3_COLORSPACE_YUV,
.bpp = 16,
}, {
.fourcc = V4L2_PIX_FMT_YUYV,
- .codes = {
+ .codes = IMX_BUS_FMTS(
MEDIA_BUS_FMT_YUYV8_2X8,
MEDIA_BUS_FMT_YUYV8_1X16
- },
+ ),
.cs = IPUV3_COLORSPACE_YUV,
.bpp = 16,
- },
- /***
- * non-mbus YUV formats start here. NOTE! when adding non-mbus
- * formats, NUM_NON_MBUS_YUV_FORMATS must be updated below.
- ***/
- {
+ }, {
.fourcc = V4L2_PIX_FMT_YUV420,
.cs = IPUV3_COLORSPACE_YUV,
.bpp = 12,
@@ -61,213 +55,212 @@ static const struct imx_media_pixfmt yuv_formats[] = {
.cs = IPUV3_COLORSPACE_YUV,
.bpp = 16,
.planar = true,
+ }, {
+ .fourcc = V4L2_PIX_FMT_YUV32,
+ .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_AYUV8_1X32),
+ .cs = IPUV3_COLORSPACE_YUV,
+ .bpp = 32,
+ .ipufmt = true,
},
-};
-
-#define NUM_NON_MBUS_YUV_FORMATS 5
-#define NUM_YUV_FORMATS ARRAY_SIZE(yuv_formats)
-#define NUM_MBUS_YUV_FORMATS (NUM_YUV_FORMATS - NUM_NON_MBUS_YUV_FORMATS)
-
-static const struct imx_media_pixfmt rgb_formats[] = {
+ /*** RGB formats start here ***/
{
.fourcc = V4L2_PIX_FMT_RGB565,
- .codes = {MEDIA_BUS_FMT_RGB565_2X8_LE},
+ .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_RGB565_2X8_LE),
.cs = IPUV3_COLORSPACE_RGB,
.bpp = 16,
.cycles = 2,
}, {
.fourcc = V4L2_PIX_FMT_RGB24,
- .codes = {
+ .codes = IMX_BUS_FMTS(
MEDIA_BUS_FMT_RGB888_1X24,
MEDIA_BUS_FMT_RGB888_2X12_LE
- },
+ ),
+ .cs = IPUV3_COLORSPACE_RGB,
+ .bpp = 24,
+ }, {
+ .fourcc = V4L2_PIX_FMT_BGR24,
.cs = IPUV3_COLORSPACE_RGB,
.bpp = 24,
}, {
.fourcc = V4L2_PIX_FMT_XRGB32,
- .codes = {MEDIA_BUS_FMT_ARGB8888_1X32},
+ .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_ARGB8888_1X32),
+ .cs = IPUV3_COLORSPACE_RGB,
+ .bpp = 32,
+ }, {
+ .fourcc = V4L2_PIX_FMT_XRGB32,
+ .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_ARGB8888_1X32),
.cs = IPUV3_COLORSPACE_RGB,
.bpp = 32,
.ipufmt = true,
+ }, {
+ .fourcc = V4L2_PIX_FMT_XBGR32,
+ .cs = IPUV3_COLORSPACE_RGB,
+ .bpp = 32,
+ }, {
+ .fourcc = V4L2_PIX_FMT_BGRX32,
+ .cs = IPUV3_COLORSPACE_RGB,
+ .bpp = 32,
+ }, {
+ .fourcc = V4L2_PIX_FMT_RGBX32,
+ .cs = IPUV3_COLORSPACE_RGB,
+ .bpp = 32,
},
/*** raw bayer and grayscale formats start here ***/
{
.fourcc = V4L2_PIX_FMT_SBGGR8,
- .codes = {MEDIA_BUS_FMT_SBGGR8_1X8},
+ .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SBGGR8_1X8),
.cs = IPUV3_COLORSPACE_RGB,
.bpp = 8,
.bayer = true,
}, {
.fourcc = V4L2_PIX_FMT_SGBRG8,
- .codes = {MEDIA_BUS_FMT_SGBRG8_1X8},
+ .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SGBRG8_1X8),
.cs = IPUV3_COLORSPACE_RGB,
.bpp = 8,
.bayer = true,
}, {
.fourcc = V4L2_PIX_FMT_SGRBG8,
- .codes = {MEDIA_BUS_FMT_SGRBG8_1X8},
+ .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SGRBG8_1X8),
.cs = IPUV3_COLORSPACE_RGB,
.bpp = 8,
.bayer = true,
}, {
.fourcc = V4L2_PIX_FMT_SRGGB8,
- .codes = {MEDIA_BUS_FMT_SRGGB8_1X8},
+ .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SRGGB8_1X8),
.cs = IPUV3_COLORSPACE_RGB,
.bpp = 8,
.bayer = true,
}, {
.fourcc = V4L2_PIX_FMT_SBGGR16,
- .codes = {
+ .codes = IMX_BUS_FMTS(
MEDIA_BUS_FMT_SBGGR10_1X10,
MEDIA_BUS_FMT_SBGGR12_1X12,
MEDIA_BUS_FMT_SBGGR14_1X14,
MEDIA_BUS_FMT_SBGGR16_1X16
- },
+ ),
.cs = IPUV3_COLORSPACE_RGB,
.bpp = 16,
.bayer = true,
}, {
.fourcc = V4L2_PIX_FMT_SGBRG16,
- .codes = {
+ .codes = IMX_BUS_FMTS(
MEDIA_BUS_FMT_SGBRG10_1X10,
MEDIA_BUS_FMT_SGBRG12_1X12,
MEDIA_BUS_FMT_SGBRG14_1X14,
- MEDIA_BUS_FMT_SGBRG16_1X16,
- },
+ MEDIA_BUS_FMT_SGBRG16_1X16
+ ),
.cs = IPUV3_COLORSPACE_RGB,
.bpp = 16,
.bayer = true,
}, {
.fourcc = V4L2_PIX_FMT_SGRBG16,
- .codes = {
+ .codes = IMX_BUS_FMTS(
MEDIA_BUS_FMT_SGRBG10_1X10,
MEDIA_BUS_FMT_SGRBG12_1X12,
MEDIA_BUS_FMT_SGRBG14_1X14,
- MEDIA_BUS_FMT_SGRBG16_1X16,
- },
+ MEDIA_BUS_FMT_SGRBG16_1X16
+ ),
.cs = IPUV3_COLORSPACE_RGB,
.bpp = 16,
.bayer = true,
}, {
.fourcc = V4L2_PIX_FMT_SRGGB16,
- .codes = {
+ .codes = IMX_BUS_FMTS(
MEDIA_BUS_FMT_SRGGB10_1X10,
MEDIA_BUS_FMT_SRGGB12_1X12,
MEDIA_BUS_FMT_SRGGB14_1X14,
- MEDIA_BUS_FMT_SRGGB16_1X16,
- },
+ MEDIA_BUS_FMT_SRGGB16_1X16
+ ),
.cs = IPUV3_COLORSPACE_RGB,
.bpp = 16,
.bayer = true,
}, {
.fourcc = V4L2_PIX_FMT_GREY,
- .codes = {
+ .codes = IMX_BUS_FMTS(
MEDIA_BUS_FMT_Y8_1X8,
MEDIA_BUS_FMT_Y10_1X10,
- MEDIA_BUS_FMT_Y12_1X12,
- },
+ MEDIA_BUS_FMT_Y12_1X12
+ ),
.cs = IPUV3_COLORSPACE_RGB,
.bpp = 8,
.bayer = true,
}, {
.fourcc = V4L2_PIX_FMT_Y10,
- .codes = {MEDIA_BUS_FMT_Y10_1X10},
+ .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_Y10_1X10),
.cs = IPUV3_COLORSPACE_RGB,
.bpp = 16,
.bayer = true,
}, {
.fourcc = V4L2_PIX_FMT_Y12,
- .codes = {MEDIA_BUS_FMT_Y12_1X12},
+ .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_Y12_1X12),
.cs = IPUV3_COLORSPACE_RGB,
.bpp = 16,
.bayer = true,
},
- /***
- * non-mbus RGB formats start here. NOTE! when adding non-mbus
- * formats, NUM_NON_MBUS_RGB_FORMATS must be updated below.
- ***/
- {
- .fourcc = V4L2_PIX_FMT_BGR24,
- .cs = IPUV3_COLORSPACE_RGB,
- .bpp = 24,
- }, {
- .fourcc = V4L2_PIX_FMT_XBGR32,
- .cs = IPUV3_COLORSPACE_RGB,
- .bpp = 32,
- }, {
- .fourcc = V4L2_PIX_FMT_BGRX32,
- .cs = IPUV3_COLORSPACE_RGB,
- .bpp = 32,
- }, {
- .fourcc = V4L2_PIX_FMT_RGBX32,
- .cs = IPUV3_COLORSPACE_RGB,
- .bpp = 32,
- },
};
-#define NUM_NON_MBUS_RGB_FORMATS 2
-#define NUM_RGB_FORMATS ARRAY_SIZE(rgb_formats)
-#define NUM_MBUS_RGB_FORMATS (NUM_RGB_FORMATS - NUM_NON_MBUS_RGB_FORMATS)
+/*
+ * Search in the pixel_formats[] array for an entry with the given fourcc
+ * that matches the requested selection criteria and return it.
+ *
+ * @fourcc: Search for an entry with the given fourcc pixel format.
+ * @fmt_sel: Allow entries only with the given selection criteria.
+ */
+const struct imx_media_pixfmt *
+imx_media_find_pixel_format(u32 fourcc, enum imx_pixfmt_sel fmt_sel)
+{
+ bool sel_ipu = fmt_sel & PIXFMT_SEL_IPU;
+ unsigned int i;
-static const struct imx_media_pixfmt ipu_yuv_formats[] = {
- {
- .fourcc = V4L2_PIX_FMT_YUV32,
- .codes = {MEDIA_BUS_FMT_AYUV8_1X32},
- .cs = IPUV3_COLORSPACE_YUV,
- .bpp = 32,
- .ipufmt = true,
- },
-};
+ fmt_sel &= ~PIXFMT_SEL_IPU;
-#define NUM_IPU_YUV_FORMATS ARRAY_SIZE(ipu_yuv_formats)
+ for (i = 0; i < ARRAY_SIZE(pixel_formats); i++) {
+ const struct imx_media_pixfmt *fmt = &pixel_formats[i];
+ enum imx_pixfmt_sel sel;
-static const struct imx_media_pixfmt ipu_rgb_formats[] = {
- {
- .fourcc = V4L2_PIX_FMT_XRGB32,
- .codes = {MEDIA_BUS_FMT_ARGB8888_1X32},
- .cs = IPUV3_COLORSPACE_RGB,
- .bpp = 32,
- .ipufmt = true,
- },
-};
+ if (sel_ipu != fmt->ipufmt)
+ continue;
-#define NUM_IPU_RGB_FORMATS ARRAY_SIZE(ipu_rgb_formats)
+ sel = fmt->bayer ? PIXFMT_SEL_BAYER :
+ ((fmt->cs == IPUV3_COLORSPACE_YUV) ?
+ PIXFMT_SEL_YUV : PIXFMT_SEL_RGB);
-static void init_mbus_colorimetry(struct v4l2_mbus_framefmt *mbus,
- const struct imx_media_pixfmt *fmt)
-{
- mbus->colorspace = (fmt->cs == IPUV3_COLORSPACE_RGB) ?
- V4L2_COLORSPACE_SRGB : V4L2_COLORSPACE_SMPTE170M;
- mbus->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(mbus->colorspace);
- mbus->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(mbus->colorspace);
- mbus->quantization =
- V4L2_MAP_QUANTIZATION_DEFAULT(fmt->cs == IPUV3_COLORSPACE_RGB,
- mbus->colorspace,
- mbus->ycbcr_enc);
+ if ((fmt_sel & sel) && fmt->fourcc == fourcc)
+ return fmt;
+ }
+
+ return NULL;
}
+EXPORT_SYMBOL_GPL(imx_media_find_pixel_format);
-static const
-struct imx_media_pixfmt *__find_format(u32 fourcc,
- u32 code,
- bool allow_non_mbus,
- bool allow_bayer,
- const struct imx_media_pixfmt *array,
- u32 array_size)
+/*
+ * Search in the pixel_formats[] array for an entry with the given media
+ * bus code that matches the requested selection criteria and return it.
+ *
+ * @code: Search for an entry with the given media-bus code.
+ * @fmt_sel: Allow entries only with the given selection criteria.
+ */
+const struct imx_media_pixfmt *
+imx_media_find_mbus_format(u32 code, enum imx_pixfmt_sel fmt_sel)
{
- const struct imx_media_pixfmt *fmt;
- int i, j;
+ bool sel_ipu = fmt_sel & PIXFMT_SEL_IPU;
+ unsigned int i;
+
+ fmt_sel &= ~PIXFMT_SEL_IPU;
- for (i = 0; i < array_size; i++) {
- fmt = &array[i];
+ for (i = 0; i < ARRAY_SIZE(pixel_formats); i++) {
+ const struct imx_media_pixfmt *fmt = &pixel_formats[i];
+ enum imx_pixfmt_sel sel;
+ unsigned int j;
- if ((!allow_non_mbus && !fmt->codes[0]) ||
- (!allow_bayer && fmt->bayer))
+ if (sel_ipu != fmt->ipufmt)
continue;
- if (fourcc && fmt->fourcc == fourcc)
- return fmt;
+ sel = fmt->bayer ? PIXFMT_SEL_BAYER :
+ ((fmt->cs == IPUV3_COLORSPACE_YUV) ?
+ PIXFMT_SEL_YUV : PIXFMT_SEL_RGB);
- if (!code)
+ if (!(fmt_sel & sel) || !fmt->codes)
continue;
for (j = 0; fmt->codes[j]; j++) {
@@ -275,199 +268,103 @@ struct imx_media_pixfmt *__find_format(u32 fourcc,
return fmt;
}
}
+
return NULL;
}
+EXPORT_SYMBOL_GPL(imx_media_find_mbus_format);
-static const struct imx_media_pixfmt *find_format(u32 fourcc,
- u32 code,
- enum codespace_sel cs_sel,
- bool allow_non_mbus,
- bool allow_bayer)
+/*
+ * Enumerate entries in the pixel_formats[] array that match the
+ * requested selection criteria. Return the fourcc that matches the
+ * selection criteria at the requested match index.
+ *
+ * @fourcc: The returned fourcc that matches the search criteria at
+ * the requested match index.
+ * @index: The requested match index.
+ * @fmt_sel: Include in the enumeration entries with the given selection
+ * criteria.
+ */
+int imx_media_enum_pixel_formats(u32 *fourcc, u32 index,
+ enum imx_pixfmt_sel fmt_sel)
{
- const struct imx_media_pixfmt *ret;
-
- switch (cs_sel) {
- case CS_SEL_YUV:
- return __find_format(fourcc, code, allow_non_mbus, allow_bayer,
- yuv_formats, NUM_YUV_FORMATS);
- case CS_SEL_RGB:
- return __find_format(fourcc, code, allow_non_mbus, allow_bayer,
- rgb_formats, NUM_RGB_FORMATS);
- case CS_SEL_ANY:
- ret = __find_format(fourcc, code, allow_non_mbus, allow_bayer,
- yuv_formats, NUM_YUV_FORMATS);
- if (ret)
- return ret;
- return __find_format(fourcc, code, allow_non_mbus, allow_bayer,
- rgb_formats, NUM_RGB_FORMATS);
- default:
- return NULL;
- }
-}
+ bool sel_ipu = fmt_sel & PIXFMT_SEL_IPU;
+ unsigned int i;
-static int enum_format(u32 *fourcc, u32 *code, u32 index,
- enum codespace_sel cs_sel,
- bool allow_non_mbus,
- bool allow_bayer)
-{
- const struct imx_media_pixfmt *fmt;
- u32 mbus_yuv_sz = NUM_MBUS_YUV_FORMATS;
- u32 mbus_rgb_sz = NUM_MBUS_RGB_FORMATS;
- u32 yuv_sz = NUM_YUV_FORMATS;
- u32 rgb_sz = NUM_RGB_FORMATS;
-
- switch (cs_sel) {
- case CS_SEL_YUV:
- if (index >= yuv_sz ||
- (!allow_non_mbus && index >= mbus_yuv_sz))
- return -EINVAL;
- fmt = &yuv_formats[index];
- break;
- case CS_SEL_RGB:
- if (index >= rgb_sz ||
- (!allow_non_mbus && index >= mbus_rgb_sz))
- return -EINVAL;
- fmt = &rgb_formats[index];
- if (!allow_bayer && fmt->bayer)
- return -EINVAL;
- break;
- case CS_SEL_ANY:
- if (!allow_non_mbus) {
- if (index >= mbus_yuv_sz) {
- index -= mbus_yuv_sz;
- if (index >= mbus_rgb_sz)
- return -EINVAL;
- fmt = &rgb_formats[index];
- if (!allow_bayer && fmt->bayer)
- return -EINVAL;
- } else {
- fmt = &yuv_formats[index];
- }
- } else {
- if (index >= yuv_sz + rgb_sz)
- return -EINVAL;
- if (index >= yuv_sz) {
- fmt = &rgb_formats[index - yuv_sz];
- if (!allow_bayer && fmt->bayer)
- return -EINVAL;
- } else {
- fmt = &yuv_formats[index];
- }
+ fmt_sel &= ~PIXFMT_SEL_IPU;
+
+ for (i = 0; i < ARRAY_SIZE(pixel_formats); i++) {
+ const struct imx_media_pixfmt *fmt = &pixel_formats[i];
+ enum imx_pixfmt_sel sel;
+
+ if (sel_ipu != fmt->ipufmt)
+ continue;
+
+ sel = fmt->bayer ? PIXFMT_SEL_BAYER :
+ ((fmt->cs == IPUV3_COLORSPACE_YUV) ?
+ PIXFMT_SEL_YUV : PIXFMT_SEL_RGB);
+
+ if (!(fmt_sel & sel))
+ continue;
+
+ if (index == 0) {
+ *fourcc = fmt->fourcc;
+ return 0;
}
- break;
- default:
- return -EINVAL;
- }
- if (fourcc)
- *fourcc = fmt->fourcc;
- if (code)
- *code = fmt->codes[0];
+ index--;
+ }
- return 0;
+ return -EINVAL;
}
+EXPORT_SYMBOL_GPL(imx_media_enum_pixel_formats);
-const struct imx_media_pixfmt *
-imx_media_find_format(u32 fourcc, enum codespace_sel cs_sel, bool allow_bayer)
+/*
+ * Enumerate entries in the pixel_formats[] array that match the
+ * requested search criteria. Return the media-bus code that matches
+ * the search criteria at the requested match index.
+ *
+ * @code: The returned media-bus code that matches the search criteria at
+ * the requested match index.
+ * @index: The requested match index.
+ * @fmt_sel: Include in the enumeration entries with the given selection
+ * criteria.
+ */
+int imx_media_enum_mbus_formats(u32 *code, u32 index,
+ enum imx_pixfmt_sel fmt_sel)
{
- return find_format(fourcc, 0, cs_sel, true, allow_bayer);
-}
-EXPORT_SYMBOL_GPL(imx_media_find_format);
+ bool sel_ipu = fmt_sel & PIXFMT_SEL_IPU;
+ unsigned int i;
-int imx_media_enum_format(u32 *fourcc, u32 index, enum codespace_sel cs_sel)
-{
- return enum_format(fourcc, NULL, index, cs_sel, true, false);
-}
-EXPORT_SYMBOL_GPL(imx_media_enum_format);
+ fmt_sel &= ~PIXFMT_SEL_IPU;
-const struct imx_media_pixfmt *
-imx_media_find_mbus_format(u32 code, enum codespace_sel cs_sel,
- bool allow_bayer)
-{
- return find_format(0, code, cs_sel, false, allow_bayer);
-}
-EXPORT_SYMBOL_GPL(imx_media_find_mbus_format);
+ for (i = 0; i < ARRAY_SIZE(pixel_formats); i++) {
+ const struct imx_media_pixfmt *fmt = &pixel_formats[i];
+ enum imx_pixfmt_sel sel;
+ unsigned int j;
-int imx_media_enum_mbus_format(u32 *code, u32 index, enum codespace_sel cs_sel,
- bool allow_bayer)
-{
- return enum_format(NULL, code, index, cs_sel, false, allow_bayer);
-}
-EXPORT_SYMBOL_GPL(imx_media_enum_mbus_format);
+ if (sel_ipu != fmt->ipufmt)
+ continue;
-const struct imx_media_pixfmt *
-imx_media_find_ipu_format(u32 code, enum codespace_sel cs_sel)
-{
- const struct imx_media_pixfmt *array, *fmt, *ret = NULL;
- u32 array_size;
- int i, j;
-
- switch (cs_sel) {
- case CS_SEL_YUV:
- array_size = NUM_IPU_YUV_FORMATS;
- array = ipu_yuv_formats;
- break;
- case CS_SEL_RGB:
- array_size = NUM_IPU_RGB_FORMATS;
- array = ipu_rgb_formats;
- break;
- case CS_SEL_ANY:
- array_size = NUM_IPU_YUV_FORMATS + NUM_IPU_RGB_FORMATS;
- array = ipu_yuv_formats;
- break;
- default:
- return NULL;
- }
+ sel = fmt->bayer ? PIXFMT_SEL_BAYER :
+ ((fmt->cs == IPUV3_COLORSPACE_YUV) ?
+ PIXFMT_SEL_YUV : PIXFMT_SEL_RGB);
- for (i = 0; i < array_size; i++) {
- if (cs_sel == CS_SEL_ANY && i >= NUM_IPU_YUV_FORMATS)
- fmt = &ipu_rgb_formats[i - NUM_IPU_YUV_FORMATS];
- else
- fmt = &array[i];
+ if (!(fmt_sel & sel) || !fmt->codes)
+ continue;
- for (j = 0; code && fmt->codes[j]; j++) {
- if (code == fmt->codes[j]) {
- ret = fmt;
- goto out;
+ for (j = 0; fmt->codes[j]; j++) {
+ if (index == 0) {
+ *code = fmt->codes[j];
+ return 0;
}
- }
- }
-out:
- return ret;
-}
-EXPORT_SYMBOL_GPL(imx_media_find_ipu_format);
-
-int imx_media_enum_ipu_format(u32 *code, u32 index, enum codespace_sel cs_sel)
-{
- switch (cs_sel) {
- case CS_SEL_YUV:
- if (index >= NUM_IPU_YUV_FORMATS)
- return -EINVAL;
- *code = ipu_yuv_formats[index].codes[0];
- break;
- case CS_SEL_RGB:
- if (index >= NUM_IPU_RGB_FORMATS)
- return -EINVAL;
- *code = ipu_rgb_formats[index].codes[0];
- break;
- case CS_SEL_ANY:
- if (index >= NUM_IPU_YUV_FORMATS + NUM_IPU_RGB_FORMATS)
- return -EINVAL;
- if (index >= NUM_IPU_YUV_FORMATS) {
- index -= NUM_IPU_YUV_FORMATS;
- *code = ipu_rgb_formats[index].codes[0];
- } else {
- *code = ipu_yuv_formats[index].codes[0];
+ index--;
}
- break;
- default:
- return -EINVAL;
}
- return 0;
+ return -EINVAL;
}
-EXPORT_SYMBOL_GPL(imx_media_enum_ipu_format);
+EXPORT_SYMBOL_GPL(imx_media_enum_mbus_formats);
int imx_media_init_mbus_fmt(struct v4l2_mbus_framefmt *mbus,
u32 width, u32 height, u32 code, u32 field,
@@ -478,17 +375,27 @@ int imx_media_init_mbus_fmt(struct v4l2_mbus_framefmt *mbus,
mbus->width = width;
mbus->height = height;
mbus->field = field;
+
if (code == 0)
- imx_media_enum_mbus_format(&code, 0, CS_SEL_YUV, false);
- lcc = imx_media_find_mbus_format(code, CS_SEL_ANY, false);
+ imx_media_enum_mbus_formats(&code, 0, PIXFMT_SEL_YUV);
+
+ lcc = imx_media_find_mbus_format(code, PIXFMT_SEL_ANY);
if (!lcc) {
- lcc = imx_media_find_ipu_format(code, CS_SEL_ANY);
+ lcc = imx_media_find_ipu_format(code, PIXFMT_SEL_YUV_RGB);
if (!lcc)
return -EINVAL;
}
mbus->code = code;
- init_mbus_colorimetry(mbus, lcc);
+
+ mbus->colorspace = V4L2_COLORSPACE_SRGB;
+ mbus->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(mbus->colorspace);
+ mbus->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(mbus->colorspace);
+ mbus->quantization =
+ V4L2_MAP_QUANTIZATION_DEFAULT(lcc->cs == IPUV3_COLORSPACE_RGB,
+ mbus->colorspace,
+ mbus->ycbcr_enc);
+
if (cc)
*cc = lcc;
@@ -542,9 +449,11 @@ void imx_media_try_colorimetry(struct v4l2_mbus_framefmt *tryfmt,
const struct imx_media_pixfmt *cc;
bool is_rgb = false;
- cc = imx_media_find_mbus_format(tryfmt->code, CS_SEL_ANY, true);
+ cc = imx_media_find_mbus_format(tryfmt->code, PIXFMT_SEL_ANY);
if (!cc)
- cc = imx_media_find_ipu_format(tryfmt->code, CS_SEL_ANY);
+ cc = imx_media_find_ipu_format(tryfmt->code,
+ PIXFMT_SEL_YUV_RGB);
+
if (cc && cc->cs == IPUV3_COLORSPACE_RGB)
is_rgb = true;
@@ -587,17 +496,18 @@ void imx_media_try_colorimetry(struct v4l2_mbus_framefmt *tryfmt,
EXPORT_SYMBOL_GPL(imx_media_try_colorimetry);
int imx_media_mbus_fmt_to_pix_fmt(struct v4l2_pix_format *pix,
- struct v4l2_mbus_framefmt *mbus,
+ const struct v4l2_mbus_framefmt *mbus,
const struct imx_media_pixfmt *cc)
{
u32 width;
u32 stride;
if (!cc) {
- cc = imx_media_find_ipu_format(mbus->code, CS_SEL_ANY);
+ cc = imx_media_find_ipu_format(mbus->code,
+ PIXFMT_SEL_YUV_RGB);
if (!cc)
- cc = imx_media_find_mbus_format(mbus->code, CS_SEL_ANY,
- true);
+ cc = imx_media_find_mbus_format(mbus->code,
+ PIXFMT_SEL_ANY);
if (!cc)
return -EINVAL;
}
@@ -609,8 +519,8 @@ int imx_media_mbus_fmt_to_pix_fmt(struct v4l2_pix_format *pix,
if (cc->ipufmt && cc->cs == IPUV3_COLORSPACE_YUV) {
u32 code;
- imx_media_enum_mbus_format(&code, 0, CS_SEL_YUV, false);
- cc = imx_media_find_mbus_format(code, CS_SEL_YUV, false);
+ imx_media_enum_mbus_formats(&code, 0, PIXFMT_SEL_YUV);
+ cc = imx_media_find_mbus_format(code, PIXFMT_SEL_YUV);
}
/* Round up width for minimum burst size */
@@ -639,7 +549,7 @@ int imx_media_mbus_fmt_to_pix_fmt(struct v4l2_pix_format *pix,
EXPORT_SYMBOL_GPL(imx_media_mbus_fmt_to_pix_fmt);
int imx_media_mbus_fmt_to_ipu_image(struct ipu_image *image,
- struct v4l2_mbus_framefmt *mbus)
+ const struct v4l2_mbus_framefmt *mbus)
{
int ret;
@@ -657,12 +567,13 @@ int imx_media_mbus_fmt_to_ipu_image(struct ipu_image *image,
EXPORT_SYMBOL_GPL(imx_media_mbus_fmt_to_ipu_image);
int imx_media_ipu_image_to_mbus_fmt(struct v4l2_mbus_framefmt *mbus,
- struct ipu_image *image)
+ const struct ipu_image *image)
{
const struct imx_media_pixfmt *fmt;
- fmt = imx_media_find_format(image->pix.pixelformat, CS_SEL_ANY, true);
- if (!fmt)
+ fmt = imx_media_find_pixel_format(image->pix.pixelformat,
+ PIXFMT_SEL_ANY);
+ if (!fmt || !fmt->codes || !fmt->codes[0])
return -EINVAL;
memset(mbus, 0, sizeof(*mbus));
@@ -924,6 +835,39 @@ imx_media_pipeline_video_device(struct media_entity *start_entity,
EXPORT_SYMBOL_GPL(imx_media_pipeline_video_device);
/*
+ * Find a fwnode endpoint that maps to the given subdevice's pad.
+ * If there are multiple endpoints that map to the pad, only the
+ * first endpoint encountered is returned.
+ *
+ * On success the refcount of the returned fwnode endpoint is
+ * incremented.
+ */
+struct fwnode_handle *imx_media_get_pad_fwnode(struct media_pad *pad)
+{
+ struct fwnode_handle *endpoint;
+ struct v4l2_subdev *sd;
+
+ if (!is_media_entity_v4l2_subdev(pad->entity))
+ return ERR_PTR(-ENODEV);
+
+ sd = media_entity_to_v4l2_subdev(pad->entity);
+
+ fwnode_graph_for_each_endpoint(dev_fwnode(sd->dev), endpoint) {
+ int pad_idx = media_entity_get_fwnode_pad(&sd->entity,
+ endpoint,
+ pad->flags);
+ if (pad_idx < 0)
+ continue;
+
+ if (pad_idx == pad->index)
+ return endpoint;
+ }
+
+ return ERR_PTR(-ENODEV);
+}
+EXPORT_SYMBOL_GPL(imx_media_get_pad_fwnode);
+
+/*
* Turn current pipeline streaming on/off starting from entity.
*/
int imx_media_pipeline_set_stream(struct imx_media_dev *imxmd,
diff --git a/drivers/staging/media/imx/imx-media-vdic.c b/drivers/staging/media/imx/imx-media-vdic.c
index 0d83c2c41606..303b5407fb64 100644
--- a/drivers/staging/media/imx/imx-media-vdic.c
+++ b/drivers/staging/media/imx/imx-media-vdic.c
@@ -548,7 +548,8 @@ static int vdic_enum_mbus_code(struct v4l2_subdev *sd,
if (code->pad >= VDIC_NUM_PADS)
return -EINVAL;
- return imx_media_enum_ipu_format(&code->code, code->index, CS_SEL_YUV);
+ return imx_media_enum_ipu_formats(&code->code, code->index,
+ PIXFMT_SEL_YUV);
}
static int vdic_get_fmt(struct v4l2_subdev *sd,
@@ -583,12 +584,13 @@ static void vdic_try_fmt(struct vdic_priv *priv,
{
struct v4l2_mbus_framefmt *infmt;
- *cc = imx_media_find_ipu_format(sdformat->format.code, CS_SEL_YUV);
+ *cc = imx_media_find_ipu_format(sdformat->format.code,
+ PIXFMT_SEL_YUV);
if (!*cc) {
u32 code;
- imx_media_enum_ipu_format(&code, 0, CS_SEL_YUV);
- *cc = imx_media_find_ipu_format(code, CS_SEL_YUV);
+ imx_media_enum_ipu_formats(&code, 0, PIXFMT_SEL_YUV);
+ *cc = imx_media_find_ipu_format(code, PIXFMT_SEL_YUV);
sdformat->format.code = (*cc)->codes[0];
}
@@ -850,7 +852,7 @@ static int vdic_registered(struct v4l2_subdev *sd)
for (i = 0; i < VDIC_NUM_PADS; i++) {
code = 0;
if (i != VDIC_SINK_PAD_IDMAC)
- 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],
diff --git a/drivers/staging/media/imx/imx-media.h b/drivers/staging/media/imx/imx-media.h
index 11861191324a..f17135158029 100644
--- a/drivers/staging/media/imx/imx-media.h
+++ b/drivers/staging/media/imx/imx-media.h
@@ -68,8 +68,13 @@ enum {
#define IMX_MEDIA_EOF_TIMEOUT 1000
struct imx_media_pixfmt {
+ /* the in-memory FourCC pixel format */
u32 fourcc;
- u32 codes[4];
+ /*
+ * the set of equivalent media bus codes for the fourcc.
+ * NOTE! codes pointer is NULL for in-memory-only formats.
+ */
+ const u32 *codes;
int bpp; /* total bpp */
/* cycles per pixel for generic (bayer) formats for the parallel bus */
int cycles;
@@ -79,6 +84,15 @@ struct imx_media_pixfmt {
bool ipufmt; /* is one of the IPU internal formats */
};
+enum imx_pixfmt_sel {
+ PIXFMT_SEL_YUV = BIT(0), /* select YUV formats */
+ PIXFMT_SEL_RGB = BIT(1), /* select RGB formats */
+ PIXFMT_SEL_BAYER = BIT(2), /* select BAYER formats */
+ PIXFMT_SEL_IPU = BIT(3), /* select IPU-internal formats */
+ PIXFMT_SEL_YUV_RGB = PIXFMT_SEL_YUV | PIXFMT_SEL_RGB,
+ PIXFMT_SEL_ANY = PIXFMT_SEL_YUV | PIXFMT_SEL_RGB | PIXFMT_SEL_BAYER,
+};
+
struct imx_media_buffer {
struct vb2_v4l2_buffer vbuf; /* v4l buffer must be first */
struct list_head list;
@@ -149,24 +163,29 @@ struct imx_media_dev {
struct v4l2_subdev *sync_sd[2][NUM_IPU_SUBDEVS];
};
-enum codespace_sel {
- CS_SEL_YUV = 0,
- CS_SEL_RGB,
- CS_SEL_ANY,
-};
-
/* imx-media-utils.c */
const struct imx_media_pixfmt *
-imx_media_find_format(u32 fourcc, enum codespace_sel cs_sel, bool allow_bayer);
-int imx_media_enum_format(u32 *fourcc, u32 index, enum codespace_sel cs_sel);
-const struct imx_media_pixfmt *
-imx_media_find_mbus_format(u32 code, enum codespace_sel cs_sel,
- bool allow_bayer);
-int imx_media_enum_mbus_format(u32 *code, u32 index, enum codespace_sel cs_sel,
- bool allow_bayer);
+imx_media_find_pixel_format(u32 fourcc, enum imx_pixfmt_sel sel);
+int imx_media_enum_pixel_formats(u32 *fourcc, u32 index,
+ enum imx_pixfmt_sel sel);
const struct imx_media_pixfmt *
-imx_media_find_ipu_format(u32 code, enum codespace_sel cs_sel);
-int imx_media_enum_ipu_format(u32 *code, u32 index, enum codespace_sel cs_sel);
+imx_media_find_mbus_format(u32 code, enum imx_pixfmt_sel sel);
+int imx_media_enum_mbus_formats(u32 *code, u32 index,
+ enum imx_pixfmt_sel sel);
+
+static inline const struct imx_media_pixfmt *
+imx_media_find_ipu_format(u32 code, enum imx_pixfmt_sel fmt_sel)
+{
+ return imx_media_find_mbus_format(code, fmt_sel | PIXFMT_SEL_IPU);
+}
+
+static inline int imx_media_enum_ipu_formats(u32 *code, u32 index,
+ enum imx_pixfmt_sel fmt_sel)
+{
+ return imx_media_enum_mbus_formats(code, index,
+ fmt_sel | PIXFMT_SEL_IPU);
+}
+
int imx_media_init_mbus_fmt(struct v4l2_mbus_framefmt *mbus,
u32 width, u32 height, u32 code, u32 field,
const struct imx_media_pixfmt **cc);
@@ -175,12 +194,12 @@ int imx_media_init_cfg(struct v4l2_subdev *sd,
void imx_media_try_colorimetry(struct v4l2_mbus_framefmt *tryfmt,
bool ic_route);
int imx_media_mbus_fmt_to_pix_fmt(struct v4l2_pix_format *pix,
- struct v4l2_mbus_framefmt *mbus,
+ const struct v4l2_mbus_framefmt *mbus,
const struct imx_media_pixfmt *cc);
int imx_media_mbus_fmt_to_ipu_image(struct ipu_image *image,
- struct v4l2_mbus_framefmt *mbus);
+ const struct v4l2_mbus_framefmt *mbus);
int imx_media_ipu_image_to_mbus_fmt(struct v4l2_mbus_framefmt *mbus,
- struct ipu_image *image);
+ const struct ipu_image *image);
void imx_media_grp_id_to_sd_name(char *sd_name, int sz,
u32 grp_id, int ipu_id);
struct v4l2_subdev *
@@ -201,6 +220,7 @@ imx_media_pipeline_subdev(struct media_entity *start_entity, u32 grp_id,
struct video_device *
imx_media_pipeline_video_device(struct media_entity *start_entity,
enum v4l2_buf_type buftype, bool upstream);
+struct fwnode_handle *imx_media_get_pad_fwnode(struct media_pad *pad);
struct imx_media_dma_buf {
void *virt;
@@ -243,10 +263,6 @@ void imx_media_unregister_ipu_internal_subdevs(struct imx_media_dev *imxmd);
/* imx-media-of.c */
int imx_media_add_of_subdevs(struct imx_media_dev *dev,
struct device_node *np);
-int imx_media_create_of_links(struct imx_media_dev *imxmd,
- struct v4l2_subdev *sd);
-int imx_media_create_csi_of_links(struct imx_media_dev *imxmd,
- struct v4l2_subdev *csi);
int imx_media_of_add_csi(struct imx_media_dev *imxmd,
struct device_node *csi_np);
@@ -292,5 +308,6 @@ void imx_media_csc_scaler_device_unregister(struct imx_media_video_dev *vdev);
#define IMX_MEDIA_GRP_ID_IPU_IC_PRP BIT(13)
#define IMX_MEDIA_GRP_ID_IPU_IC_PRPENC BIT(14)
#define IMX_MEDIA_GRP_ID_IPU_IC_PRPVF BIT(15)
+#define IMX_MEDIA_GRP_ID_CSI_MUX BIT(16)
#endif
diff --git a/drivers/staging/media/imx/imx6-mipi-csi2.c b/drivers/staging/media/imx/imx6-mipi-csi2.c
index 8ab823042c09..94d87d27d389 100644
--- a/drivers/staging/media/imx/imx6-mipi-csi2.c
+++ b/drivers/staging/media/imx/imx6-mipi-csi2.c
@@ -14,6 +14,7 @@
#include <linux/platform_device.h>
#include <media/v4l2-device.h>
#include <media/v4l2-fwnode.h>
+#include <media/v4l2-mc.h>
#include <media/v4l2-subdev.h>
#include "imx-media.h"
@@ -35,6 +36,7 @@
struct csi2_dev {
struct device *dev;
struct v4l2_subdev sd;
+ struct v4l2_async_notifier notifier;
struct media_pad pad[CSI2_NUM_PADS];
struct clk *dphy_clk;
struct clk *pllref_clk;
@@ -90,6 +92,11 @@ static inline struct csi2_dev *sd_to_dev(struct v4l2_subdev *sdev)
return container_of(sdev, struct csi2_dev, sd);
}
+static inline struct csi2_dev *notifier_to_dev(struct v4l2_async_notifier *n)
+{
+ return container_of(n, struct csi2_dev, notifier);
+}
+
/*
* The required sequence of MIPI CSI-2 startup as specified in the i.MX6
* reference manual is as follows:
@@ -509,6 +516,7 @@ static int csi2_registered(struct v4l2_subdev *sd)
static const struct media_entity_operations csi2_entity_ops = {
.link_setup = csi2_link_setup,
.link_validate = v4l2_subdev_link_validate,
+ .get_fwnode_pad = v4l2_subdev_get_fwnode_pad_1_to_1,
};
static const struct v4l2_subdev_video_ops csi2_video_ops = {
@@ -530,34 +538,75 @@ static const struct v4l2_subdev_internal_ops csi2_internal_ops = {
.registered = csi2_registered,
};
-static int csi2_parse_endpoint(struct device *dev,
- struct v4l2_fwnode_endpoint *vep,
- struct v4l2_async_subdev *asd)
+static int csi2_notify_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd,
+ struct v4l2_async_subdev *asd)
{
- struct v4l2_subdev *sd = dev_get_drvdata(dev);
- struct csi2_dev *csi2 = sd_to_dev(sd);
+ struct csi2_dev *csi2 = notifier_to_dev(notifier);
+ struct media_pad *sink = &csi2->sd.entity.pads[CSI2_SINK_PAD];
- if (!fwnode_device_is_available(asd->match.fwnode)) {
- v4l2_err(&csi2->sd, "remote is not available\n");
- return -EINVAL;
- }
+ return v4l2_create_fwnode_links_to_pad(sd, sink);
+}
- if (vep->bus_type != V4L2_MBUS_CSI2_DPHY) {
- v4l2_err(&csi2->sd, "invalid bus type, must be MIPI CSI2\n");
- return -EINVAL;
- }
+static const struct v4l2_async_notifier_operations csi2_notify_ops = {
+ .bound = csi2_notify_bound,
+};
- csi2->bus = vep->bus.mipi_csi2;
+static int csi2_async_register(struct csi2_dev *csi2)
+{
+ struct v4l2_fwnode_endpoint vep = {
+ .bus_type = V4L2_MBUS_CSI2_DPHY,
+ };
+ struct v4l2_async_subdev *asd = NULL;
+ struct fwnode_handle *ep;
+ int ret;
+
+ v4l2_async_notifier_init(&csi2->notifier);
+
+ ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(csi2->dev), 0, 0,
+ FWNODE_GRAPH_ENDPOINT_NEXT);
+ if (!ep)
+ return -ENOTCONN;
+
+ ret = v4l2_fwnode_endpoint_parse(ep, &vep);
+ if (ret)
+ goto err_parse;
+
+ csi2->bus = vep.bus.mipi_csi2;
dev_dbg(csi2->dev, "data lanes: %d\n", csi2->bus.num_data_lanes);
dev_dbg(csi2->dev, "flags: 0x%08x\n", csi2->bus.flags);
- return 0;
+ asd = kzalloc(sizeof(*asd), GFP_KERNEL);
+ if (!asd) {
+ ret = -ENOMEM;
+ goto err_parse;
+ }
+
+ ret = v4l2_async_notifier_add_fwnode_remote_subdev(
+ &csi2->notifier, ep, asd);
+ if (ret)
+ goto err_parse;
+
+ fwnode_handle_put(ep);
+
+ csi2->notifier.ops = &csi2_notify_ops;
+
+ ret = v4l2_async_subdev_notifier_register(&csi2->sd,
+ &csi2->notifier);
+ if (ret)
+ return ret;
+
+ return v4l2_async_register_subdev(&csi2->sd);
+
+err_parse:
+ fwnode_handle_put(ep);
+ kfree(asd);
+ return ret;
}
static int csi2_probe(struct platform_device *pdev)
{
- unsigned int sink_port = 0;
struct csi2_dev *csi2;
struct resource *res;
int i, ret;
@@ -633,15 +682,15 @@ static int csi2_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, &csi2->sd);
- ret = v4l2_async_register_fwnode_subdev(
- &csi2->sd, sizeof(struct v4l2_async_subdev),
- &sink_port, 1, csi2_parse_endpoint);
+ ret = csi2_async_register(csi2);
if (ret)
- goto dphy_off;
+ goto clean_notifier;
return 0;
-dphy_off:
+clean_notifier:
+ v4l2_async_notifier_unregister(&csi2->notifier);
+ v4l2_async_notifier_cleanup(&csi2->notifier);
clk_disable_unprepare(csi2->dphy_clk);
pllref_off:
clk_disable_unprepare(csi2->pllref_clk);
@@ -655,6 +704,8 @@ static int csi2_remove(struct platform_device *pdev)
struct v4l2_subdev *sd = platform_get_drvdata(pdev);
struct csi2_dev *csi2 = sd_to_dev(sd);
+ v4l2_async_notifier_unregister(&csi2->notifier);
+ v4l2_async_notifier_cleanup(&csi2->notifier);
v4l2_async_unregister_subdev(sd);
clk_disable_unprepare(csi2->dphy_clk);
clk_disable_unprepare(csi2->pllref_clk);
diff --git a/drivers/staging/media/imx/imx7-media-csi.c b/drivers/staging/media/imx/imx7-media-csi.c
index acbdffb77668..a3f3df901704 100644
--- a/drivers/staging/media/imx/imx7-media-csi.c
+++ b/drivers/staging/media/imx/imx7-media-csi.c
@@ -155,6 +155,7 @@
struct imx7_csi {
struct device *dev;
struct v4l2_subdev sd;
+ struct v4l2_async_notifier notifier;
struct imx_media_video_dev *vdev;
struct imx_media_dev *imxmd;
struct media_pad pad[IMX7_CSI_PADS_NUM];
@@ -168,8 +169,6 @@ struct imx7_csi {
struct media_entity *sink;
- struct v4l2_fwnode_endpoint upstream_ep;
-
struct v4l2_mbus_framefmt format_mbus[IMX7_CSI_PADS_NUM];
const struct imx_media_pixfmt *cc[IMX7_CSI_PADS_NUM];
struct v4l2_fract frame_interval[IMX7_CSI_PADS_NUM];
@@ -195,6 +194,12 @@ struct imx7_csi {
struct completion last_eof_completion;
};
+static struct imx7_csi *
+imx7_csi_notifier_to_dev(struct v4l2_async_notifier *n)
+{
+ return container_of(n, struct imx7_csi, notifier);
+}
+
static u32 imx7_csi_reg_read(struct imx7_csi *csi, unsigned int offset)
{
return readl(csi->regbase + offset);
@@ -428,61 +433,6 @@ static void imx7_csi_deinit(struct imx7_csi *csi)
csi->is_init = false;
}
-static int imx7_csi_get_upstream_endpoint(struct imx7_csi *csi,
- struct v4l2_fwnode_endpoint *ep,
- bool skip_mux)
-{
- struct device_node *endpoint, *port;
- struct media_entity *src;
- struct v4l2_subdev *sd;
- struct media_pad *pad;
-
- if (!csi->src_sd)
- return -EPIPE;
-
- src = &csi->src_sd->entity;
-
- /*
- * if the source is neither a mux or csi2 get the one directly upstream
- * from this csi
- */
- if (src->function != MEDIA_ENT_F_VID_IF_BRIDGE &&
- src->function != MEDIA_ENT_F_VID_MUX)
- src = &csi->sd.entity;
-
-skip_video_mux:
- /* get source pad of entity directly upstream from src */
- pad = imx_media_pipeline_pad(src, 0, 0, true);
- if (!pad)
- return -ENODEV;
-
- sd = media_entity_to_v4l2_subdev(pad->entity);
-
- /* To get bus type we may need to skip video mux */
- if (skip_mux && src->function == MEDIA_ENT_F_VID_MUX) {
- src = &sd->entity;
- goto skip_video_mux;
- }
-
- /*
- * 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)
- return -ENODEV;
-
- endpoint = of_get_next_child(port, NULL);
- of_node_put(port);
- if (!endpoint)
- return -ENODEV;
-
- v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint), ep);
- of_node_put(endpoint);
-
- return 0;
-}
-
static int imx7_csi_link_setup(struct media_entity *entity,
const struct media_pad *local,
const struct media_pad *remote, u32 flags)
@@ -549,23 +499,27 @@ static int imx7_csi_pad_link_validate(struct v4l2_subdev *sd,
struct v4l2_subdev_format *sink_fmt)
{
struct imx7_csi *csi = v4l2_get_subdevdata(sd);
- struct v4l2_fwnode_endpoint upstream_ep = {};
+ struct media_pad *pad;
int ret;
ret = v4l2_subdev_link_validate_default(sd, link, source_fmt, sink_fmt);
if (ret)
return ret;
- ret = imx7_csi_get_upstream_endpoint(csi, &upstream_ep, true);
- if (ret) {
- v4l2_err(&csi->sd, "failed to find upstream endpoint\n");
- return ret;
- }
+ if (!csi->src_sd)
+ return -EPIPE;
+
+ /*
+ * find the entity that is selected by the CSI mux. This is needed
+ * to distinguish between a parallel or CSI-2 pipeline.
+ */
+ pad = imx_media_pipeline_pad(&csi->src_sd->entity, 0, 0, true);
+ if (!pad)
+ return -ENODEV;
mutex_lock(&csi->lock);
- csi->upstream_ep = upstream_ep;
- csi->is_csi2 = (upstream_ep.bus_type == V4L2_MBUS_CSI2_DPHY);
+ csi->is_csi2 = (pad->entity->function == MEDIA_ENT_F_VID_IF_BRIDGE);
mutex_unlock(&csi->lock);
@@ -958,8 +912,8 @@ static int imx7_csi_enum_mbus_code(struct v4l2_subdev *sd,
switch (code->pad) {
case IMX7_CSI_PAD_SINK:
- 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 IMX7_CSI_PAD_SRC:
if (code->index != 0) {
@@ -1019,8 +973,8 @@ static int imx7_csi_try_fmt(struct imx7_csi *csi,
switch (sdformat->pad) {
case IMX7_CSI_PAD_SRC:
- in_cc = imx_media_find_mbus_format(in_fmt->code, CS_SEL_ANY,
- true);
+ in_cc = imx_media_find_mbus_format(in_fmt->code,
+ PIXFMT_SEL_ANY);
sdformat->format.width = in_fmt->width;
sdformat->format.height = in_fmt->height;
@@ -1035,11 +989,12 @@ static int imx7_csi_try_fmt(struct imx7_csi *csi,
break;
case IMX7_CSI_PAD_SINK:
*cc = imx_media_find_mbus_format(sdformat->format.code,
- CS_SEL_ANY, true);
+ PIXFMT_SEL_ANY);
if (!*cc) {
- imx_media_enum_mbus_format(&code, 0, CS_SEL_ANY, false);
- *cc = imx_media_find_mbus_format(code, CS_SEL_ANY,
- false);
+ 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];
}
@@ -1177,6 +1132,7 @@ static int imx7_csi_init_cfg(struct v4l2_subdev *sd,
static const struct media_entity_operations imx7_csi_entity_ops = {
.link_setup = imx7_csi_link_setup,
.link_validate = v4l2_subdev_link_validate,
+ .get_fwnode_pad = v4l2_subdev_get_fwnode_pad_1_to_1,
};
static const struct v4l2_subdev_video_ops imx7_csi_video_ops = {
@@ -1201,11 +1157,64 @@ static const struct v4l2_subdev_internal_ops imx7_csi_internal_ops = {
.unregistered = imx7_csi_unregistered,
};
-static int imx7_csi_parse_endpoint(struct device *dev,
- struct v4l2_fwnode_endpoint *vep,
- struct v4l2_async_subdev *asd)
+static int imx7_csi_notify_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd,
+ struct v4l2_async_subdev *asd)
+{
+ struct imx7_csi *csi = imx7_csi_notifier_to_dev(notifier);
+ struct media_pad *sink = &csi->sd.entity.pads[IMX7_CSI_PAD_SINK];
+
+ /* The bound subdev must always be the CSI mux */
+ if (WARN_ON(sd->entity.function != MEDIA_ENT_F_VID_MUX))
+ return -ENXIO;
+
+ /* Mark it as such via its group id */
+ 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 imx7_csi_notify_ops = {
+ .bound = imx7_csi_notify_bound,
+};
+
+static int imx7_csi_async_register(struct imx7_csi *csi)
{
- return fwnode_device_is_available(asd->match.fwnode) ? 0 : -EINVAL;
+ struct v4l2_async_subdev *asd = NULL;
+ struct fwnode_handle *ep;
+ int ret;
+
+ v4l2_async_notifier_init(&csi->notifier);
+
+ ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(csi->dev), 0, 0,
+ FWNODE_GRAPH_ENDPOINT_NEXT);
+ if (ep) {
+ asd = kzalloc(sizeof(*asd), GFP_KERNEL);
+ if (!asd) {
+ fwnode_handle_put(ep);
+ return -ENOMEM;
+ }
+
+ ret = v4l2_async_notifier_add_fwnode_remote_subdev(
+ &csi->notifier, ep, asd);
+
+ fwnode_handle_put(ep);
+
+ if (ret) {
+ kfree(asd);
+ /* OK if asd already exists */
+ if (ret != -EEXIST)
+ return ret;
+ }
+ }
+
+ csi->notifier.ops = &imx7_csi_notify_ops;
+
+ ret = v4l2_async_subdev_notifier_register(&csi->sd, &csi->notifier);
+ if (ret)
+ return ret;
+
+ return v4l2_async_register_subdev(&csi->sd);
}
static int imx7_csi_probe(struct platform_device *pdev)
@@ -1288,19 +1297,21 @@ static int imx7_csi_probe(struct platform_device *pdev)
if (ret < 0)
goto free;
- ret = v4l2_async_register_fwnode_subdev(&csi->sd,
- sizeof(struct v4l2_async_subdev),
- NULL, 0,
- imx7_csi_parse_endpoint);
+ ret = imx7_csi_async_register(csi);
if (ret)
- goto free;
+ goto subdev_notifier_cleanup;
return 0;
+subdev_notifier_cleanup:
+ v4l2_async_notifier_unregister(&csi->notifier);
+ v4l2_async_notifier_cleanup(&csi->notifier);
+
free:
v4l2_ctrl_handler_free(&csi->ctrl_hdlr);
cleanup:
+ v4l2_async_notifier_unregister(&imxmd->notifier);
v4l2_async_notifier_cleanup(&imxmd->notifier);
v4l2_device_unregister(&imxmd->v4l2_dev);
media_device_unregister(&imxmd->md);
@@ -1325,6 +1336,8 @@ static int imx7_csi_remove(struct platform_device *pdev)
v4l2_device_unregister(&imxmd->v4l2_dev);
media_device_cleanup(&imxmd->md);
+ v4l2_async_notifier_unregister(&csi->notifier);
+ v4l2_async_notifier_cleanup(&csi->notifier);
v4l2_async_unregister_subdev(sd);
v4l2_ctrl_handler_free(&csi->ctrl_hdlr);
diff --git a/drivers/staging/media/imx/imx7-mipi-csis.c b/drivers/staging/media/imx/imx7-mipi-csis.c
index fbc1a924652a..7612993cc1d6 100644
--- a/drivers/staging/media/imx/imx7-mipi-csis.c
+++ b/drivers/staging/media/imx/imx7-mipi-csis.c
@@ -14,32 +14,30 @@
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/io.h>
-#include <linux/irq.h>
#include <linux/kernel.h>
-#include <linux/mfd/syscon.h>
#include <linux/module.h>
-#include <linux/of_graph.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
-#include <linux/reset.h>
#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
#include <linux/spinlock.h>
#include <media/v4l2-device.h>
#include <media/v4l2-fwnode.h>
+#include <media/v4l2-mc.h>
#include <media/v4l2-subdev.h>
-#include "imx-media.h"
-
-#define CSIS_DRIVER_NAME "imx7-mipi-csis"
-#define CSIS_SUBDEV_NAME CSIS_DRIVER_NAME
+#define CSIS_DRIVER_NAME "imx7-mipi-csis"
+#define CSIS_SUBDEV_NAME CSIS_DRIVER_NAME
-#define CSIS_PAD_SINK 0
-#define CSIS_PAD_SOURCE 1
-#define CSIS_PADS_NUM 2
+#define CSIS_PAD_SINK 0
+#define CSIS_PAD_SOURCE 1
+#define CSIS_PADS_NUM 2
-#define MIPI_CSIS_DEF_PIX_WIDTH 640
-#define MIPI_CSIS_DEF_PIX_HEIGHT 480
+#define MIPI_CSIS_DEF_PIX_WIDTH 640
+#define MIPI_CSIS_DEF_PIX_HEIGHT 480
/* Register map definition */
@@ -64,42 +62,42 @@
#define MIPI_CSIS_CLK_CTRL_WCLK_SRC BIT(0)
/* CSIS Interrupt mask */
-#define MIPI_CSIS_INTMSK 0x10
-#define MIPI_CSIS_INTMSK_EVEN_BEFORE BIT(31)
-#define MIPI_CSIS_INTMSK_EVEN_AFTER BIT(30)
-#define MIPI_CSIS_INTMSK_ODD_BEFORE BIT(29)
-#define MIPI_CSIS_INTMSK_ODD_AFTER BIT(28)
-#define MIPI_CSIS_INTMSK_FRAME_START BIT(24)
-#define MIPI_CSIS_INTMSK_FRAME_END BIT(20)
-#define MIPI_CSIS_INTMSK_ERR_SOT_HS BIT(16)
-#define MIPI_CSIS_INTMSK_ERR_LOST_FS BIT(12)
-#define MIPI_CSIS_INTMSK_ERR_LOST_FE BIT(8)
-#define MIPI_CSIS_INTMSK_ERR_OVER BIT(4)
-#define MIPI_CSIS_INTMSK_ERR_WRONG_CFG BIT(3)
-#define MIPI_CSIS_INTMSK_ERR_ECC BIT(2)
-#define MIPI_CSIS_INTMSK_ERR_CRC BIT(1)
-#define MIPI_CSIS_INTMSK_ERR_UNKNOWN BIT(0)
+#define MIPI_CSIS_INTMSK 0x10
+#define MIPI_CSIS_INTMSK_EVEN_BEFORE BIT(31)
+#define MIPI_CSIS_INTMSK_EVEN_AFTER BIT(30)
+#define MIPI_CSIS_INTMSK_ODD_BEFORE BIT(29)
+#define MIPI_CSIS_INTMSK_ODD_AFTER BIT(28)
+#define MIPI_CSIS_INTMSK_FRAME_START BIT(24)
+#define MIPI_CSIS_INTMSK_FRAME_END BIT(20)
+#define MIPI_CSIS_INTMSK_ERR_SOT_HS BIT(16)
+#define MIPI_CSIS_INTMSK_ERR_LOST_FS BIT(12)
+#define MIPI_CSIS_INTMSK_ERR_LOST_FE BIT(8)
+#define MIPI_CSIS_INTMSK_ERR_OVER BIT(4)
+#define MIPI_CSIS_INTMSK_ERR_WRONG_CFG BIT(3)
+#define MIPI_CSIS_INTMSK_ERR_ECC BIT(2)
+#define MIPI_CSIS_INTMSK_ERR_CRC BIT(1)
+#define MIPI_CSIS_INTMSK_ERR_UNKNOWN BIT(0)
/* CSIS Interrupt source */
-#define MIPI_CSIS_INTSRC 0x14
-#define MIPI_CSIS_INTSRC_EVEN_BEFORE BIT(31)
-#define MIPI_CSIS_INTSRC_EVEN_AFTER BIT(30)
-#define MIPI_CSIS_INTSRC_EVEN BIT(30)
-#define MIPI_CSIS_INTSRC_ODD_BEFORE BIT(29)
-#define MIPI_CSIS_INTSRC_ODD_AFTER BIT(28)
-#define MIPI_CSIS_INTSRC_ODD (0x3 << 28)
-#define MIPI_CSIS_INTSRC_NON_IMAGE_DATA (0xf << 28)
-#define MIPI_CSIS_INTSRC_FRAME_START BIT(24)
-#define MIPI_CSIS_INTSRC_FRAME_END BIT(20)
-#define MIPI_CSIS_INTSRC_ERR_SOT_HS BIT(16)
-#define MIPI_CSIS_INTSRC_ERR_LOST_FS BIT(12)
-#define MIPI_CSIS_INTSRC_ERR_LOST_FE BIT(8)
-#define MIPI_CSIS_INTSRC_ERR_OVER BIT(4)
-#define MIPI_CSIS_INTSRC_ERR_WRONG_CFG BIT(3)
-#define MIPI_CSIS_INTSRC_ERR_ECC BIT(2)
-#define MIPI_CSIS_INTSRC_ERR_CRC BIT(1)
-#define MIPI_CSIS_INTSRC_ERR_UNKNOWN BIT(0)
-#define MIPI_CSIS_INTSRC_ERRORS 0xfffff
+#define MIPI_CSIS_INTSRC 0x14
+#define MIPI_CSIS_INTSRC_EVEN_BEFORE BIT(31)
+#define MIPI_CSIS_INTSRC_EVEN_AFTER BIT(30)
+#define MIPI_CSIS_INTSRC_EVEN BIT(30)
+#define MIPI_CSIS_INTSRC_ODD_BEFORE BIT(29)
+#define MIPI_CSIS_INTSRC_ODD_AFTER BIT(28)
+#define MIPI_CSIS_INTSRC_ODD (0x3 << 28)
+#define MIPI_CSIS_INTSRC_NON_IMAGE_DATA (0xf << 28)
+#define MIPI_CSIS_INTSRC_FRAME_START BIT(24)
+#define MIPI_CSIS_INTSRC_FRAME_END BIT(20)
+#define MIPI_CSIS_INTSRC_ERR_SOT_HS BIT(16)
+#define MIPI_CSIS_INTSRC_ERR_LOST_FS BIT(12)
+#define MIPI_CSIS_INTSRC_ERR_LOST_FE BIT(8)
+#define MIPI_CSIS_INTSRC_ERR_OVER BIT(4)
+#define MIPI_CSIS_INTSRC_ERR_WRONG_CFG BIT(3)
+#define MIPI_CSIS_INTSRC_ERR_ECC BIT(2)
+#define MIPI_CSIS_INTSRC_ERR_CRC BIT(1)
+#define MIPI_CSIS_INTSRC_ERR_UNKNOWN BIT(0)
+#define MIPI_CSIS_INTSRC_ERRORS 0xfffff
/* D-PHY status control */
#define MIPI_CSIS_DPHYSTATUS 0x20
@@ -121,19 +119,19 @@
#define MIPI_CSIS_DPHYCTRL_ENABLE (0x1f << 0)
/* D-PHY Master and Slave Control register Low */
-#define MIPI_CSIS_DPHYBCTRL_L 0x30
+#define MIPI_CSIS_DPHYBCTRL_L 0x30
/* D-PHY Master and Slave Control register High */
-#define MIPI_CSIS_DPHYBCTRL_H 0x34
+#define MIPI_CSIS_DPHYBCTRL_H 0x34
/* D-PHY Slave Control register Low */
-#define MIPI_CSIS_DPHYSCTRL_L 0x38
+#define MIPI_CSIS_DPHYSCTRL_L 0x38
/* D-PHY Slave Control register High */
-#define MIPI_CSIS_DPHYSCTRL_H 0x3c
+#define MIPI_CSIS_DPHYSCTRL_H 0x3c
/* ISP Configuration register */
-#define MIPI_CSIS_ISPCONFIG_CH0 0x40
-#define MIPI_CSIS_ISPCONFIG_CH1 0x50
-#define MIPI_CSIS_ISPCONFIG_CH2 0x60
-#define MIPI_CSIS_ISPCONFIG_CH3 0x70
+#define MIPI_CSIS_ISPCONFIG_CH0 0x40
+#define MIPI_CSIS_ISPCONFIG_CH1 0x50
+#define MIPI_CSIS_ISPCONFIG_CH2 0x60
+#define MIPI_CSIS_ISPCONFIG_CH3 0x70
#define MIPI_CSIS_ISPCFG_MEM_FULL_GAP_MSK (0xff << 24)
#define MIPI_CSIS_ISPCFG_MEM_FULL_GAP(x) ((x) << 24)
@@ -143,35 +141,36 @@
#define MIPI_CSIS_ISPCFG_FMT_RAW8 (0x2a << 2)
#define MIPI_CSIS_ISPCFG_FMT_RAW10 (0x2b << 2)
#define MIPI_CSIS_ISPCFG_FMT_RAW12 (0x2c << 2)
+#define MIPI_CSIS_ISPCFG_FMT_RAW14 (0x2d << 2)
/* User defined formats, x = 1...4 */
-#define MIPI_CSIS_ISPCFG_FMT_USER(x) ((0x30 + (x) - 1) << 2)
-#define MIPI_CSIS_ISPCFG_FMT_MASK (0x3f << 2)
+#define MIPI_CSIS_ISPCFG_FMT_USER(x) ((0x30 + (x) - 1) << 2)
+#define MIPI_CSIS_ISPCFG_FMT_MASK (0x3f << 2)
/* ISP Image Resolution register */
-#define MIPI_CSIS_ISPRESOL_CH0 0x44
-#define MIPI_CSIS_ISPRESOL_CH1 0x54
-#define MIPI_CSIS_ISPRESOL_CH2 0x64
-#define MIPI_CSIS_ISPRESOL_CH3 0x74
-#define CSIS_MAX_PIX_WIDTH 0xffff
-#define CSIS_MAX_PIX_HEIGHT 0xffff
+#define MIPI_CSIS_ISPRESOL_CH0 0x44
+#define MIPI_CSIS_ISPRESOL_CH1 0x54
+#define MIPI_CSIS_ISPRESOL_CH2 0x64
+#define MIPI_CSIS_ISPRESOL_CH3 0x74
+#define CSIS_MAX_PIX_WIDTH 0xffff
+#define CSIS_MAX_PIX_HEIGHT 0xffff
/* ISP SYNC register */
-#define MIPI_CSIS_ISPSYNC_CH0 0x48
-#define MIPI_CSIS_ISPSYNC_CH1 0x58
-#define MIPI_CSIS_ISPSYNC_CH2 0x68
-#define MIPI_CSIS_ISPSYNC_CH3 0x78
+#define MIPI_CSIS_ISPSYNC_CH0 0x48
+#define MIPI_CSIS_ISPSYNC_CH1 0x58
+#define MIPI_CSIS_ISPSYNC_CH2 0x68
+#define MIPI_CSIS_ISPSYNC_CH3 0x78
#define MIPI_CSIS_ISPSYNC_HSYNC_LINTV_OFFSET 18
#define MIPI_CSIS_ISPSYNC_VSYNC_SINTV_OFFSET 12
#define MIPI_CSIS_ISPSYNC_VSYNC_EINTV_OFFSET 0
/* Non-image packet data buffers */
-#define MIPI_CSIS_PKTDATA_ODD 0x2000
-#define MIPI_CSIS_PKTDATA_EVEN 0x3000
-#define MIPI_CSIS_PKTDATA_SIZE SZ_4K
+#define MIPI_CSIS_PKTDATA_ODD 0x2000
+#define MIPI_CSIS_PKTDATA_EVEN 0x3000
+#define MIPI_CSIS_PKTDATA_SIZE SZ_4K
-#define DEFAULT_SCLK_CSIS_FREQ 166000000UL
+#define DEFAULT_SCLK_CSIS_FREQ 166000000UL
enum {
ST_POWERED = 1,
@@ -223,6 +222,7 @@ struct csi_state {
struct device *dev;
struct media_pad pads[CSIS_PADS_NUM];
struct v4l2_subdev mipi_sd;
+ struct v4l2_async_notifier notifier;
struct v4l2_subdev *src_sd;
u8 index;
@@ -253,45 +253,102 @@ struct csi_state {
struct csis_hw_reset hw_reset;
struct regulator *mipi_phy_regulator;
- bool sink_linked;
};
struct csis_pix_format {
- unsigned int pix_width_alignment;
u32 code;
u32 fmt_reg;
- u8 data_alignment;
+ u8 width;
};
static const struct csis_pix_format mipi_csis_formats[] = {
+ /* YUV formats. */
{
- .code = MEDIA_BUS_FMT_SBGGR10_1X10,
- .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW10,
- .data_alignment = 16,
- }, {
- .code = MEDIA_BUS_FMT_VYUY8_2X8,
+ .code = MEDIA_BUS_FMT_UYVY8_2X8,
.fmt_reg = MIPI_CSIS_ISPCFG_FMT_YCBCR422_8BIT,
- .data_alignment = 16,
+ .width = 8,
}, {
+ .code = MEDIA_BUS_FMT_UYVY10_2X10,
+ .fmt_reg = MIPI_CSIS_ISPCFG_FMT_YCBCR422_8BIT,
+ .width = 10,
+ },
+ /* RAW (Bayer and greyscale) formats. */
+ {
.code = MEDIA_BUS_FMT_SBGGR8_1X8,
.fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW8,
- .data_alignment = 8,
+ .width = 8,
}, {
- .code = MEDIA_BUS_FMT_YUYV8_2X8,
- .fmt_reg = MIPI_CSIS_ISPCFG_FMT_YCBCR422_8BIT,
- .data_alignment = 16,
+ .code = MEDIA_BUS_FMT_SGBRG8_1X8,
+ .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW8,
+ .width = 8,
+ }, {
+ .code = MEDIA_BUS_FMT_SGRBG8_1X8,
+ .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW8,
+ .width = 8,
+ }, {
+ .code = MEDIA_BUS_FMT_SRGGB8_1X8,
+ .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW8,
+ .width = 8,
}, {
.code = MEDIA_BUS_FMT_Y8_1X8,
.fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW8,
- .data_alignment = 8,
+ .width = 8,
+ }, {
+ .code = MEDIA_BUS_FMT_SBGGR10_1X10,
+ .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW10,
+ .width = 10,
+ }, {
+ .code = MEDIA_BUS_FMT_SGBRG10_1X10,
+ .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW10,
+ .width = 10,
+ }, {
+ .code = MEDIA_BUS_FMT_SGRBG10_1X10,
+ .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW10,
+ .width = 10,
+ }, {
+ .code = MEDIA_BUS_FMT_SRGGB10_1X10,
+ .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW10,
+ .width = 10,
}, {
.code = MEDIA_BUS_FMT_Y10_1X10,
.fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW10,
- .data_alignment = 16,
+ .width = 10,
+ }, {
+ .code = MEDIA_BUS_FMT_SBGGR12_1X12,
+ .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW12,
+ .width = 12,
+ }, {
+ .code = MEDIA_BUS_FMT_SGBRG12_1X12,
+ .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW12,
+ .width = 12,
+ }, {
+ .code = MEDIA_BUS_FMT_SGRBG12_1X12,
+ .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW12,
+ .width = 12,
+ }, {
+ .code = MEDIA_BUS_FMT_SRGGB12_1X12,
+ .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW12,
+ .width = 12,
}, {
.code = MEDIA_BUS_FMT_Y12_1X12,
.fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW12,
- .data_alignment = 16,
+ .width = 12,
+ }, {
+ .code = MEDIA_BUS_FMT_SBGGR14_1X14,
+ .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW14,
+ .width = 14,
+ }, {
+ .code = MEDIA_BUS_FMT_SGBRG14_1X14,
+ .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW14,
+ .width = 14,
+ }, {
+ .code = MEDIA_BUS_FMT_SGRBG14_1X14,
+ .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW14,
+ .width = 14,
+ }, {
+ .code = MEDIA_BUS_FMT_SRGGB14_1X14,
+ .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW14,
+ .width = 14,
}
};
@@ -329,6 +386,12 @@ static int mipi_csis_dump_regs(struct csi_state *state)
return 0;
}
+static struct csi_state *
+mipi_notifier_to_csis_state(struct v4l2_async_notifier *n)
+{
+ return container_of(n, struct csi_state, notifier);
+}
+
static struct csi_state *mipi_sd_to_csis_state(struct v4l2_subdev *sdev)
{
return container_of(sdev, struct csi_state, mipi_sd);
@@ -405,7 +468,8 @@ static void __mipi_csis_set_format(struct csi_state *state)
/* Color format */
val = mipi_csis_read(state, MIPI_CSIS_ISPCONFIG_CH0);
- val = (val & ~MIPI_CSIS_ISPCFG_FMT_MASK) | state->csis_fmt->fmt_reg;
+ val &= ~(MIPI_CSIS_ISPCFG_ALIGN_32BIT | MIPI_CSIS_ISPCFG_FMT_MASK);
+ val |= state->csis_fmt->fmt_reg;
mipi_csis_write(state, MIPI_CSIS_ISPCONFIG_CH0, val);
/* Pixel resolution */
@@ -437,13 +501,6 @@ static void mipi_csis_set_params(struct csi_state *state)
mipi_csis_set_hsync_settle(state, state->hs_settle);
- val = mipi_csis_read(state, MIPI_CSIS_ISPCONFIG_CH0);
- if (state->csis_fmt->data_alignment == 32)
- val |= MIPI_CSIS_ISPCFG_ALIGN_32BIT;
- else
- val &= ~MIPI_CSIS_ISPCFG_ALIGN_32BIT;
- mipi_csis_write(state, MIPI_CSIS_ISPCONFIG_CH0, val);
-
val = (0 << MIPI_CSIS_ISPSYNC_HSYNC_LINTV_OFFSET) |
(0 << MIPI_CSIS_ISPSYNC_VSYNC_SINTV_OFFSET) |
(0 << MIPI_CSIS_ISPSYNC_VSYNC_EINTV_OFFSET);
@@ -622,17 +679,7 @@ static int mipi_csis_link_setup(struct media_entity *entity,
mutex_lock(&state->lock);
- if (local_pad->flags & MEDIA_PAD_FL_SOURCE) {
- if (flags & MEDIA_LNK_FL_ENABLED) {
- if (state->sink_linked) {
- ret = -EBUSY;
- goto out;
- }
- state->sink_linked = true;
- } else {
- state->sink_linked = false;
- }
- } else {
+ if (local_pad->flags & MEDIA_PAD_FL_SINK) {
if (flags & MEDIA_LNK_FL_ENABLED) {
if (state->src_sd) {
ret = -EBUSY;
@@ -649,48 +696,6 @@ out:
return ret;
}
-static int mipi_csis_init_cfg(struct v4l2_subdev *mipi_sd,
- struct v4l2_subdev_pad_config *cfg)
-{
- struct v4l2_mbus_framefmt *mf;
- unsigned int i;
- int ret;
-
- for (i = 0; i < CSIS_PADS_NUM; i++) {
- mf = v4l2_subdev_get_try_format(mipi_sd, cfg, i);
-
- ret = imx_media_init_mbus_fmt(mf, MIPI_CSIS_DEF_PIX_HEIGHT,
- MIPI_CSIS_DEF_PIX_WIDTH, 0,
- V4L2_FIELD_NONE, NULL);
- if (ret < 0)
- return ret;
- }
-
- return 0;
-}
-
-static struct csis_pix_format const *
-mipi_csis_try_format(struct v4l2_subdev *mipi_sd, struct v4l2_mbus_framefmt *mf)
-{
- struct csi_state *state = mipi_sd_to_csis_state(mipi_sd);
- struct csis_pix_format const *csis_fmt;
-
- csis_fmt = find_csis_format(mf->code);
- if (!csis_fmt)
- csis_fmt = &mipi_csis_formats[0];
-
- v4l_bound_align_image(&mf->width, 1, CSIS_MAX_PIX_WIDTH,
- csis_fmt->pix_width_alignment,
- &mf->height, 1, CSIS_MAX_PIX_HEIGHT, 1,
- 0);
-
- state->format_mbus.code = csis_fmt->code;
- state->format_mbus.width = mf->width;
- state->format_mbus.height = mf->height;
-
- return csis_fmt;
-}
-
static struct v4l2_mbus_framefmt *
mipi_csis_get_format(struct csi_state *state,
struct v4l2_subdev_pad_config *cfg,
@@ -703,53 +708,160 @@ mipi_csis_get_format(struct csi_state *state,
return &state->format_mbus;
}
-static int mipi_csis_set_fmt(struct v4l2_subdev *mipi_sd,
+static int mipi_csis_init_cfg(struct v4l2_subdev *mipi_sd,
+ struct v4l2_subdev_pad_config *cfg)
+{
+ struct csi_state *state = mipi_sd_to_csis_state(mipi_sd);
+ struct v4l2_mbus_framefmt *fmt_sink;
+ struct v4l2_mbus_framefmt *fmt_source;
+ enum v4l2_subdev_format_whence which;
+
+ which = cfg ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
+ fmt_sink = mipi_csis_get_format(state, cfg, which, CSIS_PAD_SINK);
+
+ fmt_sink->code = MEDIA_BUS_FMT_UYVY8_2X8;
+ fmt_sink->width = MIPI_CSIS_DEF_PIX_WIDTH;
+ fmt_sink->height = MIPI_CSIS_DEF_PIX_HEIGHT;
+ fmt_sink->field = V4L2_FIELD_NONE;
+
+ fmt_sink->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ fmt_sink->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt_sink->colorspace);
+ fmt_sink->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt_sink->colorspace);
+ fmt_sink->quantization =
+ V4L2_MAP_QUANTIZATION_DEFAULT(false, fmt_sink->colorspace,
+ fmt_sink->ycbcr_enc);
+
+ /*
+ * When called from mipi_csis_subdev_init() to initialize the active
+ * configuration, cfg is NULL, which indicates there's no source pad
+ * configuration to set.
+ */
+ if (!cfg)
+ return 0;
+
+ fmt_source = mipi_csis_get_format(state, cfg, which, CSIS_PAD_SOURCE);
+ *fmt_source = *fmt_sink;
+
+ return 0;
+}
+
+static int mipi_csis_get_fmt(struct v4l2_subdev *mipi_sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *sdformat)
{
struct csi_state *state = mipi_sd_to_csis_state(mipi_sd);
- struct csis_pix_format const *csis_fmt;
struct v4l2_mbus_framefmt *fmt;
- if (sdformat->pad >= CSIS_PADS_NUM)
- return -EINVAL;
-
+ mutex_lock(&state->lock);
fmt = mipi_csis_get_format(state, cfg, sdformat->which, sdformat->pad);
+ sdformat->format = *fmt;
+ mutex_unlock(&state->lock);
- mutex_lock(&state->lock);
- if (sdformat->pad == CSIS_PAD_SOURCE) {
- sdformat->format = *fmt;
- goto unlock;
- }
+ return 0;
+}
+
+static int mipi_csis_enum_mbus_code(struct v4l2_subdev *mipi_sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct csi_state *state = mipi_sd_to_csis_state(mipi_sd);
- csis_fmt = mipi_csis_try_format(mipi_sd, &sdformat->format);
+ /*
+ * The CSIS can't transcode in any way, the source format is identical
+ * to the sink format.
+ */
+ if (code->pad == CSIS_PAD_SOURCE) {
+ struct v4l2_mbus_framefmt *fmt;
- sdformat->format = *fmt;
+ if (code->index > 0)
+ return -EINVAL;
- if (csis_fmt && sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE)
- state->csis_fmt = csis_fmt;
- else
- cfg->try_fmt = sdformat->format;
+ fmt = mipi_csis_get_format(state, cfg, code->which, code->pad);
+ code->code = fmt->code;
+ return 0;
+ }
-unlock:
- mutex_unlock(&state->lock);
+ if (code->pad != CSIS_PAD_SINK)
+ return -EINVAL;
+
+ if (code->index >= ARRAY_SIZE(mipi_csis_formats))
+ return -EINVAL;
+
+ code->code = mipi_csis_formats[code->index].code;
return 0;
}
-static int mipi_csis_get_fmt(struct v4l2_subdev *mipi_sd,
+static int mipi_csis_set_fmt(struct v4l2_subdev *mipi_sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *sdformat)
{
struct csi_state *state = mipi_sd_to_csis_state(mipi_sd);
+ struct csis_pix_format const *csis_fmt;
struct v4l2_mbus_framefmt *fmt;
+ unsigned int align;
- mutex_lock(&state->lock);
+ /*
+ * The CSIS can't transcode in any way, the source format can't be
+ * modified.
+ */
+ if (sdformat->pad == CSIS_PAD_SOURCE)
+ return mipi_csis_get_fmt(mipi_sd, cfg, sdformat);
+
+ if (sdformat->pad != CSIS_PAD_SINK)
+ return -EINVAL;
fmt = mipi_csis_get_format(state, cfg, sdformat->which, sdformat->pad);
+ mutex_lock(&state->lock);
+
+ /* Validate the media bus code and clamp the size. */
+ csis_fmt = find_csis_format(sdformat->format.code);
+ if (!csis_fmt)
+ csis_fmt = &mipi_csis_formats[0];
+
+ fmt->code = csis_fmt->code;
+ fmt->width = sdformat->format.width;
+ fmt->height = sdformat->format.height;
+
+ /*
+ * The total number of bits per line must be a multiple of 8. We thus
+ * need to align the width for formats that are not multiples of 8
+ * bits.
+ */
+ switch (csis_fmt->width % 8) {
+ case 0:
+ align = 1;
+ break;
+ case 4:
+ align = 2;
+ break;
+ case 2:
+ case 6:
+ align = 4;
+ break;
+ case 1:
+ case 3:
+ case 5:
+ case 7:
+ align = 8;
+ break;
+ }
+
+ v4l_bound_align_image(&fmt->width, 1, CSIS_MAX_PIX_WIDTH, align,
+ &fmt->height, 1, CSIS_MAX_PIX_HEIGHT, 1, 0);
+
sdformat->format = *fmt;
+ /* Propagate the format from sink to source. */
+ fmt = mipi_csis_get_format(state, cfg, sdformat->which,
+ CSIS_PAD_SOURCE);
+ *fmt = sdformat->format;
+
+ /* Store the CSIS format descriptor for active formats. */
+ if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ state->csis_fmt = csis_fmt;
+
mutex_unlock(&state->lock);
return 0;
@@ -801,6 +913,7 @@ static const struct v4l2_subdev_core_ops mipi_csis_core_ops = {
static const struct media_entity_operations mipi_csis_entity_ops = {
.link_setup = mipi_csis_link_setup,
.link_validate = v4l2_subdev_link_validate,
+ .get_fwnode_pad = v4l2_subdev_get_fwnode_pad_1_to_1,
};
static const struct v4l2_subdev_video_ops mipi_csis_video_ops = {
@@ -809,6 +922,7 @@ static const struct v4l2_subdev_video_ops mipi_csis_video_ops = {
static const struct v4l2_subdev_pad_ops mipi_csis_pad_ops = {
.init_cfg = mipi_csis_init_cfg,
+ .enum_mbus_code = mipi_csis_enum_mbus_code,
.get_fmt = mipi_csis_get_fmt,
.set_fmt = mipi_csis_set_fmt,
};
@@ -841,33 +955,25 @@ static int mipi_csis_parse_dt(struct platform_device *pdev,
static int mipi_csis_pm_resume(struct device *dev, bool runtime);
-static int mipi_csis_parse_endpoint(struct device *dev,
- struct v4l2_fwnode_endpoint *ep,
- struct v4l2_async_subdev *asd)
+static int mipi_csis_notify_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd,
+ struct v4l2_async_subdev *asd)
{
- struct v4l2_subdev *mipi_sd = dev_get_drvdata(dev);
- struct csi_state *state = mipi_sd_to_csis_state(mipi_sd);
-
- if (ep->bus_type != V4L2_MBUS_CSI2_DPHY) {
- dev_err(dev, "invalid bus type, must be MIPI CSI2\n");
- return -EINVAL;
- }
-
- state->bus = ep->bus.mipi_csi2;
-
- dev_dbg(state->dev, "data lanes: %d\n", state->bus.num_data_lanes);
- dev_dbg(state->dev, "flags: 0x%08x\n", state->bus.flags);
+ struct csi_state *state = mipi_notifier_to_csis_state(notifier);
+ struct media_pad *sink = &state->mipi_sd.entity.pads[CSIS_PAD_SINK];
- return 0;
+ return v4l2_create_fwnode_links_to_pad(sd, sink);
}
+static const struct v4l2_async_notifier_operations mipi_csis_notify_ops = {
+ .bound = mipi_csis_notify_bound,
+};
+
static int mipi_csis_subdev_init(struct v4l2_subdev *mipi_sd,
struct platform_device *pdev,
const struct v4l2_subdev_ops *ops)
{
struct csi_state *state = mipi_sd_to_csis_state(mipi_sd);
- unsigned int sink_port = 0;
- int ret;
v4l2_subdev_init(mipi_sd, ops);
mipi_sd->owner = THIS_MODULE;
@@ -883,26 +989,66 @@ static int mipi_csis_subdev_init(struct v4l2_subdev *mipi_sd,
mipi_sd->dev = &pdev->dev;
state->csis_fmt = &mipi_csis_formats[0];
- state->format_mbus.code = mipi_csis_formats[0].code;
- state->format_mbus.width = MIPI_CSIS_DEF_PIX_WIDTH;
- state->format_mbus.height = MIPI_CSIS_DEF_PIX_HEIGHT;
- state->format_mbus.field = V4L2_FIELD_NONE;
+ mipi_csis_init_cfg(mipi_sd, NULL);
v4l2_set_subdevdata(mipi_sd, &pdev->dev);
state->pads[CSIS_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
state->pads[CSIS_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
- ret = media_entity_pads_init(&mipi_sd->entity, CSIS_PADS_NUM,
- state->pads);
+ return media_entity_pads_init(&mipi_sd->entity, CSIS_PADS_NUM,
+ state->pads);
+}
+
+static int mipi_csis_async_register(struct csi_state *state)
+{
+ struct v4l2_fwnode_endpoint vep = {
+ .bus_type = V4L2_MBUS_CSI2_DPHY,
+ };
+ struct v4l2_async_subdev *asd = NULL;
+ struct fwnode_handle *ep;
+ int ret;
+
+ v4l2_async_notifier_init(&state->notifier);
+
+ ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(state->dev), 0, 0,
+ FWNODE_GRAPH_ENDPOINT_NEXT);
+ if (!ep)
+ return -ENOTCONN;
+
+ ret = v4l2_fwnode_endpoint_parse(ep, &vep);
+ if (ret)
+ goto err_parse;
+
+ state->bus = vep.bus.mipi_csi2;
+
+ dev_dbg(state->dev, "data lanes: %d\n", state->bus.num_data_lanes);
+ dev_dbg(state->dev, "flags: 0x%08x\n", state->bus.flags);
+
+ asd = kzalloc(sizeof(*asd), GFP_KERNEL);
+ if (!asd) {
+ ret = -ENOMEM;
+ goto err_parse;
+ }
+
+ ret = v4l2_async_notifier_add_fwnode_remote_subdev(
+ &state->notifier, ep, asd);
+ if (ret)
+ goto err_parse;
+
+ fwnode_handle_put(ep);
+
+ state->notifier.ops = &mipi_csis_notify_ops;
+
+ ret = v4l2_async_subdev_notifier_register(&state->mipi_sd,
+ &state->notifier);
if (ret)
return ret;
- ret = v4l2_async_register_fwnode_subdev(mipi_sd,
- sizeof(struct v4l2_async_subdev),
- &sink_port, 1,
- mipi_csis_parse_endpoint);
- if (ret < 0)
- dev_err(&pdev->dev, "async fwnode register failed: %d\n", ret);
+ return v4l2_async_register_subdev(&state->mipi_sd);
+
+err_parse:
+ fwnode_handle_put(ep);
+ kfree(asd);
return ret;
}
@@ -915,33 +1061,14 @@ static int mipi_csis_dump_regs_show(struct seq_file *m, void *private)
}
DEFINE_SHOW_ATTRIBUTE(mipi_csis_dump_regs);
-static int mipi_csis_debugfs_init(struct csi_state *state)
+static void mipi_csis_debugfs_init(struct csi_state *state)
{
- struct dentry *d;
-
- if (!debugfs_initialized())
- return -ENODEV;
-
state->debugfs_root = debugfs_create_dir(dev_name(state->dev), NULL);
- if (!state->debugfs_root)
- return -ENOMEM;
-
- d = debugfs_create_bool("debug_enable", 0600, state->debugfs_root,
- &state->debug);
- if (!d)
- goto remove_debugfs;
-
- d = debugfs_create_file("dump_regs", 0600, state->debugfs_root,
- state, &mipi_csis_dump_regs_fops);
- if (!d)
- goto remove_debugfs;
-
- return 0;
-remove_debugfs:
- debugfs_remove_recursive(state->debugfs_root);
-
- return -ENOMEM;
+ debugfs_create_bool("debug_enable", 0600, state->debugfs_root,
+ &state->debug);
+ debugfs_create_file("dump_regs", 0600, state->debugfs_root, state,
+ &mipi_csis_dump_regs_fops);
}
static void mipi_csis_debugfs_exit(struct csi_state *state)
@@ -1009,6 +1136,12 @@ static int mipi_csis_probe(struct platform_device *pdev)
if (ret < 0)
goto disable_clock;
+ ret = mipi_csis_async_register(state);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "async register failed: %d\n", ret);
+ goto cleanup;
+ }
+
memcpy(state->events, mipi_csis_events, sizeof(state->events));
mipi_csis_debugfs_init(state);
@@ -1027,7 +1160,10 @@ static int mipi_csis_probe(struct platform_device *pdev)
unregister_all:
mipi_csis_debugfs_exit(state);
+cleanup:
media_entity_cleanup(&state->mipi_sd.entity);
+ v4l2_async_notifier_unregister(&state->notifier);
+ v4l2_async_notifier_cleanup(&state->notifier);
v4l2_async_unregister_subdev(&state->mipi_sd);
disable_clock:
mipi_csis_clk_disable(state);
@@ -1115,6 +1251,8 @@ static int mipi_csis_remove(struct platform_device *pdev)
struct csi_state *state = mipi_sd_to_csis_state(mipi_sd);
mipi_csis_debugfs_exit(state);
+ v4l2_async_notifier_unregister(&state->notifier);
+ v4l2_async_notifier_cleanup(&state->notifier);
v4l2_async_unregister_subdev(&state->mipi_sd);
pm_runtime_disable(&pdev->dev);