@@ -71,7 +71,47 @@ static bool match_devname(struct v4l2_subdev *sd,
static bool match_fwnode(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;
+ const char *name;
+
+ /*
+ * 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.
+ */
+ name = fwnode_get_name(sd->fwnode);
+ sd_fwnode_is_ep = name && strstarts(name, "endpoint");
+ name = fwnode_get_name(asd->match.fwnode);
+ asd_fwnode_is_ep = name && strstarts(name, "endpoint");
+
+ 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);
+
+ return dev_fwnode == other_fwnode;
}
static bool match_custom(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd)