[PATCH v2 2/9] media: staging/imx: remove static media link arrays

From: Steve Longerbeam
Date: Thu Dec 14 2017 - 20:05:58 EST


Remove the static list of media links that were formed at probe time.
These links can instead be created after all registered async subdevices
have been bound in imx_media_probe_complete().

The media links between subdevices that exist in the device tree, can
be created post-async completion by using v4l2_fwnode_parse_link() for
each endpoint node of that subdevice. Note this approach assumes
device-tree ports are equivalent to media pads (pad index equals
port id), and that device-tree endpoints are equivalent to media
links between pads.

Because links are no longer parsed by imx_media_of_parse(), its sole
function is now only to add subdevices that it encounters by walking
the OF graph to the async list, so the function has been renamed
imx_media_add_of_subdevs().

Similarly, the media links between the IPU-internal subdevice pads (the
CSI source pads, and all pads between the vdic, ic-prp, ic-prpenc, and
ic-prpvf subdevices), can be created post-async completion by looping
through the subdevice's media pads and using the const internal_subdev
table.

Because links are no longer parsed by imx_media_add_internal_subdevs(),
this function no longer needs an array of CSI subdevs to form links
from.

In summary, the following functions, which were used to form a list
of media links at probe time, are removed:

imx_media_add_pad_link()
add_internal_links()
of_add_pad_link()

replaced by these functions, called at probe time, which only populate
the async subdev list:

imx_media_add_of_subdevs()
imx_media_add_internal_subdevs()

and these functions, called at async completion, which create the
media links:

imx_media_create_of_links()
imx_media_create_csi_of_links()
imx_media_create_internal_links()

Signed-off-by: Steve Longerbeam <steve_longerbeam@xxxxxxxxxx>
---
drivers/staging/media/imx/imx-media-dev.c | 130 +++----------
drivers/staging/media/imx/imx-media-internal-sd.c | 219 +++++++++++++---------
drivers/staging/media/imx/imx-media-of.c | 189 ++++++++++++-------
drivers/staging/media/imx/imx-media.h | 39 +---
4 files changed, 281 insertions(+), 296 deletions(-)

diff --git a/drivers/staging/media/imx/imx-media-dev.c b/drivers/staging/media/imx/imx-media-dev.c
index c483b1d..09d2deb 100644
--- a/drivers/staging/media/imx/imx-media-dev.c
+++ b/drivers/staging/media/imx/imx-media-dev.c
@@ -11,6 +11,7 @@
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/module.h>
+#include <linux/of_graph.h>
#include <linux/of_platform.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
@@ -128,50 +129,6 @@ imx_media_add_async_subdev(struct imx_media_dev *imxmd,
}

/*
- * Adds an imx-media link to a subdev pad's link list. This is called
- * during driver load when forming the links between subdevs.
- *
- * @pad: the local pad
- * @remote_node: the device node of the remote subdev
- * @remote_devname: the device name of the remote subdev
- * @local_pad: local pad index
- * @remote_pad: remote pad index
- */
-int imx_media_add_pad_link(struct imx_media_dev *imxmd,
- struct imx_media_pad *pad,
- struct device_node *remote_node,
- const char *remote_devname,
- int local_pad, int remote_pad)
-{
- struct imx_media_link *link;
- int link_idx, ret = 0;
-
- mutex_lock(&imxmd->mutex);
-
- link_idx = pad->num_links;
- if (link_idx >= IMX_MEDIA_MAX_LINKS) {
- dev_err(imxmd->md.dev, "%s: too many links!\n", __func__);
- ret = -ENOSPC;
- goto out;
- }
-
- link = &pad->link[link_idx];
-
- link->remote_sd_node = remote_node;
- if (remote_devname)
- strncpy(link->remote_devname, remote_devname,
- sizeof(link->remote_devname));
-
- link->local_pad = local_pad;
- link->remote_pad = remote_pad;
-
- pad->num_links++;
-out:
- mutex_unlock(&imxmd->mutex);
- return ret;
-}
-
-/*
* get IPU from this CSI and add it to the list of IPUs
* the media driver will control.
*/
@@ -240,76 +197,38 @@ static int imx_media_subdev_bound(struct v4l2_async_notifier *notifier,
}

/*
- * Create a single source->sink media link given a subdev and a single
- * link from one of its source pads. Called after all subdevs have
- * registered.
- */
-static int imx_media_create_link(struct imx_media_dev *imxmd,
- struct imx_media_subdev *src,
- struct imx_media_link *link)
-{
- struct imx_media_subdev *sink;
- u16 source_pad, sink_pad;
- int ret;
-
- sink = imx_media_find_async_subdev(imxmd, link->remote_sd_node,
- link->remote_devname);
- if (!sink) {
- v4l2_warn(&imxmd->v4l2_dev, "%s: no sink for %s:%d\n",
- __func__, src->sd->name, link->local_pad);
- return 0;
- }
-
- source_pad = link->local_pad;
- sink_pad = link->remote_pad;
-
- v4l2_info(&imxmd->v4l2_dev, "%s: %s:%d -> %s:%d\n", __func__,
- src->sd->name, source_pad, sink->sd->name, sink_pad);
-
- ret = media_create_pad_link(&src->sd->entity, source_pad,
- &sink->sd->entity, sink_pad, 0);
- if (ret)
- v4l2_err(&imxmd->v4l2_dev,
- "create_pad_link failed: %d\n", ret);
-
- return ret;
-}
-
-/*
- * create the media links from all imx-media pads and their links.
+ * create the media links from all pads and their links.
* Called after all subdevs have registered.
*/
static int imx_media_create_links(struct imx_media_dev *imxmd)
{
struct imx_media_subdev *imxsd;
- struct imx_media_link *link;
- struct imx_media_pad *pad;
- int num_pads, i, j, k;
- int ret = 0;
+ struct v4l2_subdev *sd;
+ int i, ret;

for (i = 0; i < imxmd->num_subdevs; i++) {
imxsd = &imxmd->subdev[i];
- num_pads = imxsd->num_sink_pads + imxsd->num_src_pads;
-
- for (j = 0; j < num_pads; j++) {
- pad = &imxsd->pad[j];
-
- /* only create the source->sink links */
- if (!(pad->pad.flags & MEDIA_PAD_FL_SOURCE))
- continue;
-
- for (k = 0; k < pad->num_links; k++) {
- link = &pad->link[k];
+ sd = imxsd->sd;

- ret = imx_media_create_link(imxmd, imxsd, link);
- if (ret)
- goto out;
- }
+ if (((sd->grp_id & IMX_MEDIA_GRP_ID_CSI) || imxsd->pdev)) {
+ /* this is an internal subdev or a CSI */
+ ret = imx_media_create_internal_links(imxmd, imxsd);
+ if (ret)
+ return ret;
+ /*
+ * the CSIs straddle between the external and the IPU
+ * internal entities, so create the external links
+ * to the CSI sink pads.
+ */
+ if (sd->grp_id & IMX_MEDIA_GRP_ID_CSI)
+ imx_media_create_csi_of_links(imxmd, imxsd);
+ } else {
+ /* this is an external fwnode subdev */
+ imx_media_create_of_links(imxmd, imxsd);
}
}

-out:
- return ret;
+ return 0;
}

/*
@@ -550,7 +469,6 @@ static int imx_media_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *node = dev->of_node;
- struct imx_media_subdev *csi[4] = {0};
struct imx_media_dev *imxmd;
int ret;

@@ -581,14 +499,14 @@ static int imx_media_probe(struct platform_device *pdev)

dev_set_drvdata(imxmd->v4l2_dev.dev, imxmd);

- ret = imx_media_of_parse(imxmd, &csi, node);
+ ret = imx_media_add_of_subdevs(imxmd, node);
if (ret) {
v4l2_err(&imxmd->v4l2_dev,
- "imx_media_of_parse failed with %d\n", ret);
+ "add_of_subdevs failed with %d\n", ret);
goto unreg_dev;
}

- ret = imx_media_add_internal_subdevs(imxmd, csi);
+ ret = imx_media_add_internal_subdevs(imxmd);
if (ret) {
v4l2_err(&imxmd->v4l2_dev,
"add_internal_subdevs failed with %d\n", ret);
diff --git a/drivers/staging/media/imx/imx-media-internal-sd.c b/drivers/staging/media/imx/imx-media-internal-sd.c
index cdfbf40..3e60df5 100644
--- a/drivers/staging/media/imx/imx-media-internal-sd.c
+++ b/drivers/staging/media/imx/imx-media-internal-sd.c
@@ -60,14 +60,19 @@ static const struct internal_subdev_id {
},
};

+struct internal_subdev;
+
struct internal_link {
- const struct internal_subdev_id *remote_id;
+ const struct internal_subdev *remote;
+ int local_pad;
int remote_pad;
};

+/* max links per internal-sd pad */
+#define MAX_INTERNAL_LINKS 8
+
struct internal_pad {
- bool devnode; /* does this pad link to a device node */
- struct internal_link link[IMX_MEDIA_MAX_LINKS];
+ struct internal_link link[MAX_INTERNAL_LINKS];
};

static const struct internal_subdev {
@@ -75,7 +80,7 @@ static const struct internal_subdev {
struct internal_pad pad[IMX_MEDIA_MAX_PADS];
int num_sink_pads;
int num_src_pads;
-} internal_subdev[num_isd] = {
+} int_subdev[num_isd] = {
[isd_csi0] = {
.id = &isd_id[isd_csi0],
.num_sink_pads = CSI_NUM_SINK_PADS,
@@ -83,17 +88,16 @@ static const struct internal_subdev {
.pad[CSI_SRC_PAD_DIRECT] = {
.link = {
{
- .remote_id = &isd_id[isd_ic_prp],
+ .local_pad = CSI_SRC_PAD_DIRECT,
+ .remote = &int_subdev[isd_ic_prp],
.remote_pad = PRP_SINK_PAD,
}, {
- .remote_id = &isd_id[isd_vdic],
+ .local_pad = CSI_SRC_PAD_DIRECT,
+ .remote = &int_subdev[isd_vdic],
.remote_pad = VDIC_SINK_PAD_DIRECT,
},
},
},
- .pad[CSI_SRC_PAD_IDMAC] = {
- .devnode = true,
- },
},

[isd_csi1] = {
@@ -103,30 +107,27 @@ static const struct internal_subdev {
.pad[CSI_SRC_PAD_DIRECT] = {
.link = {
{
- .remote_id = &isd_id[isd_ic_prp],
+ .local_pad = CSI_SRC_PAD_DIRECT,
+ .remote = &int_subdev[isd_ic_prp],
.remote_pad = PRP_SINK_PAD,
}, {
- .remote_id = &isd_id[isd_vdic],
+ .local_pad = CSI_SRC_PAD_DIRECT,
+ .remote = &int_subdev[isd_vdic],
.remote_pad = VDIC_SINK_PAD_DIRECT,
},
},
},
- .pad[CSI_SRC_PAD_IDMAC] = {
- .devnode = true,
- },
},

[isd_vdic] = {
.id = &isd_id[isd_vdic],
.num_sink_pads = VDIC_NUM_SINK_PADS,
.num_src_pads = VDIC_NUM_SRC_PADS,
- .pad[VDIC_SINK_PAD_IDMAC] = {
- .devnode = true,
- },
.pad[VDIC_SRC_PAD_DIRECT] = {
.link = {
{
- .remote_id = &isd_id[isd_ic_prp],
+ .local_pad = VDIC_SRC_PAD_DIRECT,
+ .remote = &int_subdev[isd_ic_prp],
.remote_pad = PRP_SINK_PAD,
},
},
@@ -140,7 +141,8 @@ static const struct internal_subdev {
.pad[PRP_SRC_PAD_PRPENC] = {
.link = {
{
- .remote_id = &isd_id[isd_ic_prpenc],
+ .local_pad = PRP_SRC_PAD_PRPENC,
+ .remote = &int_subdev[isd_ic_prpenc],
.remote_pad = 0,
},
},
@@ -148,7 +150,8 @@ static const struct internal_subdev {
.pad[PRP_SRC_PAD_PRPVF] = {
.link = {
{
- .remote_id = &isd_id[isd_ic_prpvf],
+ .local_pad = PRP_SRC_PAD_PRPVF,
+ .remote = &int_subdev[isd_ic_prpvf],
.remote_pad = 0,
},
},
@@ -159,68 +162,114 @@ static const struct internal_subdev {
.id = &isd_id[isd_ic_prpenc],
.num_sink_pads = PRPENCVF_NUM_SINK_PADS,
.num_src_pads = PRPENCVF_NUM_SRC_PADS,
- .pad[PRPENCVF_SRC_PAD] = {
- .devnode = true,
- },
},

[isd_ic_prpvf] = {
.id = &isd_id[isd_ic_prpvf],
.num_sink_pads = PRPENCVF_NUM_SINK_PADS,
.num_src_pads = PRPENCVF_NUM_SRC_PADS,
- .pad[PRPENCVF_SRC_PAD] = {
- .devnode = true,
- },
},
};

-/* form a device name given a group id and ipu id */
-static inline void isd_id_to_devname(char *devname, int sz,
- const struct internal_subdev_id *id,
- int ipu_id)
+/* form a device name given an internal subdev and ipu id */
+static inline void isd_to_devname(char *devname, int sz,
+ const struct internal_subdev *isd,
+ int ipu_id)
{
- int pdev_id = ipu_id * num_isd + id->index;
+ int pdev_id = ipu_id * num_isd + isd->id->index;

- snprintf(devname, sz, "%s.%d", id->name, pdev_id);
+ snprintf(devname, sz, "%s.%d", isd->id->name, pdev_id);
}

-/* adds the links from given internal subdev */
-static int add_internal_links(struct imx_media_dev *imxmd,
- const struct internal_subdev *isd,
- struct imx_media_subdev *imxsd,
- int ipu_id)
+static const struct internal_subdev *find_intsd_by_grp_id(u32 grp_id)
{
- int i, num_pads, ret;
+ enum isd_enum i;
+
+ for (i = 0; i < num_isd; i++) {
+ const struct internal_subdev *isd = &int_subdev[i];

- num_pads = isd->num_sink_pads + isd->num_src_pads;
+ if (isd->id->grp_id == grp_id)
+ return isd;
+ }

- for (i = 0; i < num_pads; i++) {
- const struct internal_pad *intpad = &isd->pad[i];
- struct imx_media_pad *pad = &imxsd->pad[i];
- int j;
+ return NULL;
+}

- /* init the pad flags for this internal subdev */
- pad->pad.flags = (i < isd->num_sink_pads) ?
- MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE;
- /* export devnode pad flag to the subdevs */
- pad->devnode = intpad->devnode;
+static struct imx_media_subdev *find_sink(struct imx_media_dev *imxmd,
+ struct imx_media_subdev *src,
+ const struct internal_link *link)
+{
+ char sink_devname[32];
+ int ipu_id;
+
+ /*
+ * retrieve IPU id from subdev name, note: can't get this from
+ * struct imx_media_internal_sd_platformdata because if src is
+ * a CSI, it has different struct ipu_client_platformdata which
+ * does not contain IPU id.
+ */
+ if (sscanf(src->sd->name, "ipu%d", &ipu_id) != 1)
+ return NULL;
+
+ isd_to_devname(sink_devname, sizeof(sink_devname),
+ link->remote, ipu_id - 1);
+
+ return imx_media_find_async_subdev(imxmd, NULL, sink_devname);
+}

- for (j = 0; ; j++) {
- const struct internal_link *link;
- char remote_devname[32];
+static int create_ipu_internal_link(struct imx_media_dev *imxmd,
+ struct imx_media_subdev *src,
+ const struct internal_link *link)
+{
+ struct imx_media_subdev *sink;
+ int ret;
+
+ sink = find_sink(imxmd, src, link);
+ if (!sink)
+ return -ENODEV;

+ v4l2_info(&imxmd->v4l2_dev, "%s:%d -> %s:%d\n",
+ src->sd->name, link->local_pad,
+ sink->sd->name, link->remote_pad);
+
+ ret = media_create_pad_link(&src->sd->entity, link->local_pad,
+ &sink->sd->entity, link->remote_pad, 0);
+ if (ret)
+ v4l2_err(&imxmd->v4l2_dev,
+ "create_pad_link failed: %d\n", ret);
+
+ return ret;
+}
+
+int imx_media_create_internal_links(struct imx_media_dev *imxmd,
+ struct imx_media_subdev *imxsd)
+{
+ struct v4l2_subdev *sd = imxsd->sd;
+ const struct internal_subdev *intsd;
+ const struct internal_pad *intpad;
+ const struct internal_link *link;
+ struct media_pad *pad;
+ int i, j, ret;
+
+ intsd = find_intsd_by_grp_id(imxsd->sd->grp_id);
+ if (!intsd)
+ return -ENODEV;
+
+ /* create the source->sink links */
+ for (i = 0; i < sd->entity.num_pads; i++) {
+ intpad = &intsd->pad[i];
+ pad = &sd->entity.pads[i];
+
+ if (!(pad->flags & MEDIA_PAD_FL_SOURCE))
+ continue;
+
+ for (j = 0; ; j++) {
link = &intpad->link[j];

- if (!link->remote_id)
+ if (!link->remote)
break;

- isd_id_to_devname(remote_devname,
- sizeof(remote_devname),
- link->remote_id, ipu_id);
-
- ret = imx_media_add_pad_link(imxmd, pad,
- NULL, remote_devname,
- i, link->remote_pad);
+ ret = create_ipu_internal_link(imxmd, imxsd, link);
if (ret)
return ret;
}
@@ -230,10 +279,9 @@ static int add_internal_links(struct imx_media_dev *imxmd,
}

/* register an internal subdev as a platform device */
-static struct imx_media_subdev *
-add_internal_subdev(struct imx_media_dev *imxmd,
- const struct internal_subdev *isd,
- int ipu_id)
+static int add_internal_subdev(struct imx_media_dev *imxmd,
+ const struct internal_subdev *isd,
+ int ipu_id)
{
struct imx_media_internal_sd_platformdata pdata;
struct platform_device_info pdevinfo = {0};
@@ -258,73 +306,58 @@ add_internal_subdev(struct imx_media_dev *imxmd,

pdev = platform_device_register_full(&pdevinfo);
if (IS_ERR(pdev))
- return ERR_CAST(pdev);
+ return PTR_ERR(pdev);

imxsd = imx_media_add_async_subdev(imxmd, NULL, pdev);
if (IS_ERR(imxsd))
- return imxsd;
+ return PTR_ERR(imxsd);

imxsd->num_sink_pads = isd->num_sink_pads;
imxsd->num_src_pads = isd->num_src_pads;

- return imxsd;
+ return 0;
}

/* adds the internal subdevs in one ipu */
-static int add_ipu_internal_subdevs(struct imx_media_dev *imxmd,
- struct imx_media_subdev *csi0,
- struct imx_media_subdev *csi1,
- int ipu_id)
+static int add_ipu_internal_subdevs(struct imx_media_dev *imxmd, int ipu_id)
{
enum isd_enum i;
- int ret;

for (i = 0; i < num_isd; i++) {
- const struct internal_subdev *isd = &internal_subdev[i];
- struct imx_media_subdev *imxsd;
+ const struct internal_subdev *isd = &int_subdev[i];
+ int ret;

/*
* the CSIs are represented in the device-tree, so those
- * devices are added already, and are added to the async
- * subdev list by of_parse_subdev(), so we are given those
- * subdevs as csi0 and csi1.
+ * devices are already added to the async subdev list by
+ * of_parse_subdev().
*/
switch (isd->id->grp_id) {
case IMX_MEDIA_GRP_ID_CSI0:
- imxsd = csi0;
- break;
case IMX_MEDIA_GRP_ID_CSI1:
- imxsd = csi1;
+ ret = 0;
break;
default:
- imxsd = add_internal_subdev(imxmd, isd, ipu_id);
+ ret = add_internal_subdev(imxmd, isd, ipu_id);
break;
}

- if (IS_ERR(imxsd))
- return PTR_ERR(imxsd);
-
- /* add the links from this subdev */
- if (imxsd) {
- ret = add_internal_links(imxmd, isd, imxsd, ipu_id);
- if (ret)
- return ret;
- }
+ if (ret)
+ return ret;
}

return 0;
}

-int imx_media_add_internal_subdevs(struct imx_media_dev *imxmd,
- struct imx_media_subdev *csi[4])
+int imx_media_add_internal_subdevs(struct imx_media_dev *imxmd)
{
int ret;

- ret = add_ipu_internal_subdevs(imxmd, csi[0], csi[1], 0);
+ ret = add_ipu_internal_subdevs(imxmd, 0);
if (ret)
goto remove;

- ret = add_ipu_internal_subdevs(imxmd, csi[2], csi[3], 1);
+ ret = add_ipu_internal_subdevs(imxmd, 1);
if (ret)
goto remove;

diff --git a/drivers/staging/media/imx/imx-media-of.c b/drivers/staging/media/imx/imx-media-of.c
index 883ad85..d35c99e 100644
--- a/drivers/staging/media/imx/imx-media-of.c
+++ b/drivers/staging/media/imx/imx-media-of.c
@@ -20,20 +20,6 @@
#include <video/imx-ipu-v3.h>
#include "imx-media.h"

-static int of_add_pad_link(struct imx_media_dev *imxmd,
- struct imx_media_pad *pad,
- struct device_node *local_sd_node,
- struct device_node *remote_sd_node,
- int local_pad, int remote_pad)
-{
- dev_dbg(imxmd->md.dev, "%s: adding %s:%d -> %s:%d\n", __func__,
- local_sd_node->name, local_pad,
- remote_sd_node->name, remote_pad);
-
- return imx_media_add_pad_link(imxmd, pad, remote_sd_node, NULL,
- local_pad, remote_pad);
-}
-
static int of_get_port_count(const struct device_node *np)
{
struct device_node *ports, *child;
@@ -53,12 +39,10 @@ static int of_get_port_count(const struct device_node *np)
}

/*
- * find the remote device node and remote port id (remote pad #)
- * given local endpoint node
+ * find the remote device node given local endpoint node
*/
-static void of_get_remote_pad(struct device_node *epnode,
- struct device_node **remote_node,
- int *remote_pad)
+static void of_get_remote(struct device_node *epnode,
+ struct device_node **remote_node)
{
struct device_node *rp, *rpp;
struct device_node *remote;
@@ -69,12 +53,9 @@ static void of_get_remote_pad(struct device_node *epnode,
if (of_device_is_compatible(rpp, "fsl,imx6q-ipu")) {
/* the remote is one of the CSI ports */
remote = rp;
- *remote_pad = 0;
of_node_put(rpp);
} else {
remote = rpp;
- if (of_property_read_u32(rp, "reg", remote_pad))
- *remote_pad = 0;
of_node_put(rp);
}

@@ -88,7 +69,7 @@ static void of_get_remote_pad(struct device_node *epnode,

static int
of_parse_subdev(struct imx_media_dev *imxmd, struct device_node *sd_np,
- bool is_csi_port, struct imx_media_subdev **subdev)
+ bool is_csi_port)
{
struct imx_media_subdev *imxsd;
int i, num_pads, ret;
@@ -96,7 +77,6 @@ of_parse_subdev(struct imx_media_dev *imxmd, struct device_node *sd_np,
if (!of_device_is_available(sd_np)) {
dev_dbg(imxmd->md.dev, "%s: %s not enabled\n", __func__,
sd_np->name);
- *subdev = NULL;
/* unavailable is not an error */
return 0;
}
@@ -107,14 +87,12 @@ of_parse_subdev(struct imx_media_dev *imxmd, struct device_node *sd_np,
if (ret) {
if (ret == -EEXIST) {
/* already added, everything is fine */
- *subdev = NULL;
return 0;
}

/* other error, can't continue */
return ret;
}
- *subdev = imxsd;

if (is_csi_port) {
/*
@@ -160,14 +138,6 @@ of_parse_subdev(struct imx_media_dev *imxmd, struct device_node *sd_np,

for (i = 0; i < num_pads; i++) {
struct device_node *epnode = NULL, *port, *remote_np;
- struct imx_media_subdev *remote_imxsd;
- struct imx_media_pad *pad;
- int remote_pad;
-
- /* init this pad */
- pad = &imxsd->pad[i];
- pad->pad.flags = (i < imxsd->num_sink_pads) ?
- MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE;

if (is_csi_port)
port = (i < imxsd->num_sink_pads) ? sd_np : NULL;
@@ -177,19 +147,13 @@ of_parse_subdev(struct imx_media_dev *imxmd, struct device_node *sd_np,
continue;

for_each_child_of_node(port, epnode) {
- of_get_remote_pad(epnode, &remote_np, &remote_pad);
+ of_get_remote(epnode, &remote_np);
if (!remote_np)
continue;

- ret = of_add_pad_link(imxmd, pad, sd_np, remote_np,
- i, remote_pad);
- if (ret)
- break;
-
if (i < imxsd->num_sink_pads) {
/* follow sink endpoints upstream */
- ret = of_parse_subdev(imxmd, remote_np,
- false, &remote_imxsd);
+ ret = of_parse_subdev(imxmd, remote_np, false);
if (ret)
break;
}
@@ -209,13 +173,10 @@ of_parse_subdev(struct imx_media_dev *imxmd, struct device_node *sd_np,
return ret;
}

-int imx_media_of_parse(struct imx_media_dev *imxmd,
- struct imx_media_subdev *(*csi)[4],
- struct device_node *np)
+int imx_media_add_of_subdevs(struct imx_media_dev *imxmd,
+ struct device_node *np)
{
- struct imx_media_subdev *lcsi;
struct device_node *csi_np;
- u32 ipu_id, csi_id;
int i, ret;

for (i = 0; ; i++) {
@@ -223,33 +184,125 @@ int imx_media_of_parse(struct imx_media_dev *imxmd,
if (!csi_np)
break;

- ret = of_parse_subdev(imxmd, csi_np, true, &lcsi);
+ ret = of_parse_subdev(imxmd, csi_np, true);
+ of_node_put(csi_np);
if (ret)
- goto err_put;
+ return ret;
+ }

- ret = of_property_read_u32(csi_np, "reg", &csi_id);
- if (ret) {
- dev_err(imxmd->md.dev,
- "%s: csi port missing reg property!\n",
- __func__);
- goto err_put;
- }
+ return 0;
+}

- ipu_id = of_alias_get_id(csi_np->parent, "ipu");
- of_node_put(csi_np);
+/*
+ * Create a single media link to/from imxsd 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 imx_media_subdev *imxsd,
+ struct v4l2_fwnode_link *link)
+{
+ struct v4l2_subdev *sd = imxsd->sd;
+ struct imx_media_subdev *remote;
+ struct v4l2_subdev *src, *sink;
+ int src_pad, sink_pad;

- if (ipu_id > 1 || csi_id > 1) {
- dev_err(imxmd->md.dev,
- "%s: invalid ipu/csi id (%u/%u)\n",
- __func__, ipu_id, csi_id);
- return -EINVAL;
- }
+ if (link->local_port >= sd->entity.num_pads)
+ return -EINVAL;
+
+ remote = imx_media_find_async_subdev(imxmd,
+ to_of_node(link->remote_node),
+ NULL);
+ if (!remote)
+ return 0;

- (*csi)[ipu_id * 2 + csi_id] = lcsi;
+ if (sd->entity.pads[link->local_port].flags & MEDIA_PAD_FL_SINK) {
+ src = remote->sd;
+ src_pad = link->remote_port;
+ sink = sd;
+ sink_pad = link->local_port;
+ } else {
+ src = sd;
+ src_pad = link->local_port;
+ sink = remote->sd;
+ 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 imxsd using its device-tree endpoints.
+ */
+int imx_media_create_of_links(struct imx_media_dev *imxmd,
+ struct imx_media_subdev *imxsd)
+{
+ struct v4l2_subdev *sd = imxsd->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, imxsd, &link);
+ v4l2_fwnode_put_link(&link);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * 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 imx_media_subdev *csi)
+{
+ struct device_node *csi_np = csi->sd->dev->of_node;
+ struct fwnode_handle *fwnode, *csi_ep;
+ struct v4l2_fwnode_link link;
+ struct device_node *ep;
+ int ret;
+
+ link.local_node = of_fwnode_handle(csi_np);
+ link.local_port = CSI_SINK_PAD;
+
+ for_each_child_of_node(csi_np, ep) {
+ 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_cmp(to_of_node(fwnode)->name, "ports") == 0)
+ 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;
-err_put:
- of_node_put(csi_np);
- return ret;
}
diff --git a/drivers/staging/media/imx/imx-media.h b/drivers/staging/media/imx/imx-media.h
index 2cc19da..c05deb4 100644
--- a/drivers/staging/media/imx/imx-media.h
+++ b/drivers/staging/media/imx/imx-media.h
@@ -35,8 +35,6 @@
#define IMX_MEDIA_MAX_SUBDEVS 32
/* max pads per subdev */
#define IMX_MEDIA_MAX_PADS 16
-/* max links per pad */
-#define IMX_MEDIA_MAX_LINKS 8

/*
* Pad definitions for the subdevs with multiple source or
@@ -119,19 +117,7 @@ static inline struct imx_media_buffer *to_imx_media_vb(struct vb2_buffer *vb)
return container_of(vbuf, struct imx_media_buffer, vbuf);
}

-struct imx_media_link {
- struct device_node *remote_sd_node;
- char remote_devname[32];
- int local_pad;
- int remote_pad;
-};
-
struct imx_media_pad {
- struct media_pad pad;
- struct imx_media_link link[IMX_MEDIA_MAX_LINKS];
- bool devnode; /* does this pad link to a device node */
- int num_links;
-
/*
* list of video devices that can be reached from this pad,
* list is only valid for source pads.
@@ -154,7 +140,7 @@ struct imx_media_subdev {
int num_sink_pads;
int num_src_pads;

- /* the platform device if this is an internal subdev */
+ /* the platform device if this is an IPU-internal subdev */
struct platform_device *pdev;
/* the devname is needed for async devname match */
char devname[32];
@@ -225,17 +211,13 @@ struct imx_media_subdev *
imx_media_add_async_subdev(struct imx_media_dev *imxmd,
struct device_node *np,
struct platform_device *pdev);
-int imx_media_add_pad_link(struct imx_media_dev *imxmd,
- struct imx_media_pad *pad,
- struct device_node *remote_node,
- const char *remote_devname,
- int local_pad, int remote_pad);

void imx_media_grp_id_to_sd_name(char *sd_name, int sz,
u32 grp_id, int ipu_id);

-int imx_media_add_internal_subdevs(struct imx_media_dev *imxmd,
- struct imx_media_subdev *csi[4]);
+int imx_media_add_internal_subdevs(struct imx_media_dev *imxmd);
+int imx_media_create_internal_links(struct imx_media_dev *imxmd,
+ struct imx_media_subdev *imxsd);
void imx_media_remove_internal_subdevs(struct imx_media_dev *imxmd);

struct imx_media_subdev *
@@ -284,13 +266,12 @@ struct imx_media_fim *imx_media_fim_init(struct v4l2_subdev *sd);
void imx_media_fim_free(struct imx_media_fim *fim);

/* imx-media-of.c */
-struct imx_media_subdev *
-imx_media_of_find_subdev(struct imx_media_dev *imxmd,
- struct device_node *np,
- const char *name);
-int imx_media_of_parse(struct imx_media_dev *dev,
- struct imx_media_subdev *(*csi)[4],
- struct device_node *np);
+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 imx_media_subdev *imxsd);
+int imx_media_create_csi_of_links(struct imx_media_dev *imxmd,
+ struct imx_media_subdev *csi);

/* imx-media-capture.c */
struct imx_media_video_dev *
--
2.7.4