summaryrefslogtreecommitdiffstats
path: root/drivers/media/v4l2-core/v4l2-async.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/v4l2-core/v4l2-async.c')
-rw-r--r--drivers/media/v4l2-core/v4l2-async.c83
1 files changed, 75 insertions, 8 deletions
diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
index 8bde33c21ce4..e3ab003a6c85 100644
--- a/drivers/media/v4l2-core/v4l2-async.c
+++ b/drivers/media/v4l2-core/v4l2-async.c
@@ -50,7 +50,8 @@ static int v4l2_async_notifier_call_complete(struct v4l2_async_notifier *n)
return n->ops->complete(n);
}
-static bool match_i2c(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd)
+static bool match_i2c(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd, struct v4l2_async_subdev *asd)
{
#if IS_ENABLED(CONFIG_I2C)
struct i2c_client *client = i2c_verify_client(sd->dev);
@@ -63,18 +64,83 @@ static bool match_i2c(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd)
#endif
}
-static bool match_devname(struct v4l2_subdev *sd,
- struct v4l2_async_subdev *asd)
+static bool match_devname(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd, struct v4l2_async_subdev *asd)
{
return !strcmp(asd->match.device_name, dev_name(sd->dev));
}
-static bool match_fwnode(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd)
+static bool match_fwnode(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd, struct v4l2_async_subdev *asd)
{
- return sd->fwnode == asd->match.fwnode;
+ struct fwnode_handle *other_fwnode;
+ struct fwnode_handle *dev_fwnode;
+ bool asd_fwnode_is_ep;
+ bool sd_fwnode_is_ep;
+ struct device *dev;
+
+ /*
+ * Both the subdev and the async subdev can provide either an endpoint
+ * fwnode or a device fwnode. Start with the simple case of direct
+ * fwnode matching.
+ */
+ if (sd->fwnode == asd->match.fwnode)
+ return true;
+
+ /*
+ * Otherwise, check if the sd fwnode and the asd fwnode refer to an
+ * endpoint or a device. If they're of the same type, there's no match.
+ * Technically speaking this checks if the nodes refer to a connected
+ * endpoint, which is the simplest check that works for both OF and
+ * ACPI. This won't make a difference, as drivers should not try to
+ * match unconnected endpoints.
+ */
+ sd_fwnode_is_ep = fwnode_graph_is_endpoint(sd->fwnode);
+ asd_fwnode_is_ep = fwnode_graph_is_endpoint(asd->match.fwnode);
+
+ if (sd_fwnode_is_ep == asd_fwnode_is_ep)
+ return false;
+
+ /*
+ * The sd and asd fwnodes are of different types. Get the device fwnode
+ * parent of the endpoint fwnode, and compare it with the other fwnode.
+ */
+ if (sd_fwnode_is_ep) {
+ dev_fwnode = fwnode_graph_get_port_parent(sd->fwnode);
+ other_fwnode = asd->match.fwnode;
+ } else {
+ dev_fwnode = fwnode_graph_get_port_parent(asd->match.fwnode);
+ other_fwnode = sd->fwnode;
+ }
+
+ fwnode_handle_put(dev_fwnode);
+
+ if (dev_fwnode != other_fwnode)
+ return false;
+
+ /*
+ * We have a heterogeneous match. Retrieve the struct device of the side
+ * that matched on a device fwnode to print its driver name.
+ */
+ if (sd_fwnode_is_ep)
+ dev = notifier->v4l2_dev ? notifier->v4l2_dev->dev
+ : notifier->sd->dev;
+ else
+ dev = sd->dev;
+
+ if (dev && dev->driver) {
+ if (sd_fwnode_is_ep)
+ dev_warn(dev, "Driver %s uses device fwnode, incorrect match may occur\n",
+ dev->driver->name);
+ dev_notice(dev, "Consider updating driver %s to match on endpoints\n",
+ dev->driver->name);
+ }
+
+ return true;
}
-static bool match_custom(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd)
+static bool match_custom(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd, struct v4l2_async_subdev *asd)
{
if (!asd->match.custom.match)
/* Match always */
@@ -91,7 +157,8 @@ static struct v4l2_async_subdev *
v4l2_async_find_match(struct v4l2_async_notifier *notifier,
struct v4l2_subdev *sd)
{
- bool (*match)(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd);
+ bool (*match)(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd, struct v4l2_async_subdev *asd);
struct v4l2_async_subdev *asd;
list_for_each_entry(asd, &notifier->waiting, list) {
@@ -116,7 +183,7 @@ v4l2_async_find_match(struct v4l2_async_notifier *notifier,
}
/* match cannot be NULL here */
- if (match(sd, asd))
+ if (match(notifier, sd, asd))
return asd;
}