diff mbox series

media: v4l2-async: Accept endpoints and devices for fwnode matching

Message ID 20200315125511.25756-1-laurent.pinchart+renesas@ideasonboard.com (mailing list archive)
State New, archived
Headers show
Series media: v4l2-async: Accept endpoints and devices for fwnode matching | expand

Commit Message

Laurent Pinchart March 15, 2020, 12:55 p.m. UTC
fwnode matching was designed to match on nodes corresponding to a
device. Some drivers, however, needed to match on endpoints, and have
passed endpoint fwnodes to v4l2-async. This works when both the subdev
and the notifier use the same fwnode types (endpoint or device), but
makes drivers that use different types incompatible.

Fix this by extending the fwnode match to handle fwnodes of different
types. When the types (deduced from the node name) are different,
retrieve the device fwnode for the side that provides an endpoint
fwnode, and compare it with the device fwnode provided by the other
side. This allows interoperability between all drivers, regardless of
which type of fwnode they use for matching.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
---
This has been compile-tested only. Prabhakar, could you check if it
fixes your issue ?

 drivers/media/v4l2-core/v4l2-async.c | 42 +++++++++++++++++++++++++++-
 1 file changed, 41 insertions(+), 1 deletion(-)

Comments

Lad, Prabhakar March 15, 2020, 1:32 p.m. UTC | #1
Hi Laurent,

Thank you for the patch.

On Sun, Mar 15, 2020 at 12:55 PM Laurent Pinchart
<laurent.pinchart+renesas@ideasonboard.com> wrote:
>
> fwnode matching was designed to match on nodes corresponding to a
> device. Some drivers, however, needed to match on endpoints, and have
> passed endpoint fwnodes to v4l2-async. This works when both the subdev
> and the notifier use the same fwnode types (endpoint or device), but
> makes drivers that use different types incompatible.
>
> Fix this by extending the fwnode match to handle fwnodes of different
> types. When the types (deduced from the node name) are different,
> retrieve the device fwnode for the side that provides an endpoint
> fwnode, and compare it with the device fwnode provided by the other
> side. This allows interoperability between all drivers, regardless of
> which type of fwnode they use for matching.
>
> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> ---
> This has been compile-tested only. Prabhakar, could you check if it
> fixes your issue ?
>
Yes it does handle all the case.

Tested-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>

Cheers,
--Prabhakar

>  drivers/media/v4l2-core/v4l2-async.c | 42 +++++++++++++++++++++++++++-
>  1 file changed, 41 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
> index 8bde33c21ce4..995e5464cba7 100644
> --- a/drivers/media/v4l2-core/v4l2-async.c
> +++ b/drivers/media/v4l2-core/v4l2-async.c
> @@ -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)
> --
> Regards,
>
> Laurent Pinchart
>
Jacopo Mondi March 15, 2020, 9:47 p.m. UTC | #2
Hi Laurent,

On Sun, Mar 15, 2020 at 02:55:11PM +0200, Laurent Pinchart wrote:
> fwnode matching was designed to match on nodes corresponding to a
> device. Some drivers, however, needed to match on endpoints, and have
> passed endpoint fwnodes to v4l2-async. This works when both the subdev
> and the notifier use the same fwnode types (endpoint or device), but
> makes drivers that use different types incompatible.
>
> Fix this by extending the fwnode match to handle fwnodes of different
> types. When the types (deduced from the node name) are different,
> retrieve the device fwnode for the side that provides an endpoint
> fwnode, and compare it with the device fwnode provided by the other
> side. This allows interoperability between all drivers, regardless of
> which type of fwnode they use for matching.
>

I'm sorry but I'm not sure why would make a difference compared to
what Kieran's patch did.
https://lore.kernel.org/patchwork/patch/788637/

If the bridge matches on device node, and the remote registers more
than one endpoints it is possible to get a false match.

I'm not sure how that would happen in practice, as the bridge would be
registering the fwnode of the remote device twice, but I think that
was the reason kieran's patch has not been collected or am I
mistaken ?

> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> ---
> This has been compile-tested only. Prabhakar, could you check if it
> fixes your issue ?
>
>  drivers/media/v4l2-core/v4l2-async.c | 42 +++++++++++++++++++++++++++-
>  1 file changed, 41 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
> index 8bde33c21ce4..995e5464cba7 100644
> --- a/drivers/media/v4l2-core/v4l2-async.c
> +++ b/drivers/media/v4l2-core/v4l2-async.c
> @@ -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)
> --
> Regards,
>
> Laurent Pinchart
>
Laurent Pinchart March 16, 2020, 6:34 a.m. UTC | #3
Hi Jacopo,

On Sun, Mar 15, 2020 at 10:47:07PM +0100, Jacopo Mondi wrote:
> On Sun, Mar 15, 2020 at 02:55:11PM +0200, Laurent Pinchart wrote:
> > fwnode matching was designed to match on nodes corresponding to a
> > device. Some drivers, however, needed to match on endpoints, and have
> > passed endpoint fwnodes to v4l2-async. This works when both the subdev
> > and the notifier use the same fwnode types (endpoint or device), but
> > makes drivers that use different types incompatible.
> >
> > Fix this by extending the fwnode match to handle fwnodes of different
> > types. When the types (deduced from the node name) are different,
> > retrieve the device fwnode for the side that provides an endpoint
> > fwnode, and compare it with the device fwnode provided by the other
> > side. This allows interoperability between all drivers, regardless of
> > which type of fwnode they use for matching.
> 
> I'm sorry but I'm not sure why would make a difference compared to
> what Kieran's patch did.
> https://lore.kernel.org/patchwork/patch/788637/
> 
> If the bridge matches on device node, and the remote registers more
> than one endpoints it is possible to get a false match.

How so ? If a notifier entry points to a device node, and two subdevs
are registered with different endpoint nodes that are both part of the
same device node, the notifier will get either of them. Which subdev
match the notifier won't be guaranteed, but that's what the notifier
asked for if it contains a device node and not an endpoint node: any
subdev corresponding to the device node.

In practice notifiers will need to move to endpoint matching if they
want to get a particular subdev of a device, and this change allows
doing so without mass-patching every driver. It allows a notifier to
switch to endpoint nodes, while subdevs still use device nodes and are
gradually ported.

> I'm not sure how that would happen in practice, as the bridge would be
> registering the fwnode of the remote device twice, but I think that
> was the reason kieran's patch has not been collected or am I
> mistaken ?
> 
> > Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> > ---
> > This has been compile-tested only. Prabhakar, could you check if it
> > fixes your issue ?
> >
> >  drivers/media/v4l2-core/v4l2-async.c | 42 +++++++++++++++++++++++++++-
> >  1 file changed, 41 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
> > index 8bde33c21ce4..995e5464cba7 100644
> > --- a/drivers/media/v4l2-core/v4l2-async.c
> > +++ b/drivers/media/v4l2-core/v4l2-async.c
> > @@ -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)
Jacopo Mondi March 16, 2020, 8:59 a.m. UTC | #4
Hi Laurent,

On Mon, Mar 16, 2020 at 08:34:44AM +0200, Laurent Pinchart wrote:
> Hi Jacopo,
>
> On Sun, Mar 15, 2020 at 10:47:07PM +0100, Jacopo Mondi wrote:
> > On Sun, Mar 15, 2020 at 02:55:11PM +0200, Laurent Pinchart wrote:
> > > fwnode matching was designed to match on nodes corresponding to a
> > > device. Some drivers, however, needed to match on endpoints, and have
> > > passed endpoint fwnodes to v4l2-async. This works when both the subdev
> > > and the notifier use the same fwnode types (endpoint or device), but
> > > makes drivers that use different types incompatible.
> > >
> > > Fix this by extending the fwnode match to handle fwnodes of different
> > > types. When the types (deduced from the node name) are different,
> > > retrieve the device fwnode for the side that provides an endpoint
> > > fwnode, and compare it with the device fwnode provided by the other
> > > side. This allows interoperability between all drivers, regardless of
> > > which type of fwnode they use for matching.
> >
> > I'm sorry but I'm not sure why would make a difference compared to
> > what Kieran's patch did.
> > https://lore.kernel.org/patchwork/patch/788637/
> >
> > If the bridge matches on device node, and the remote registers more
> > than one endpoints it is possible to get a false match.
>
> How so ? If a notifier entry points to a device node, and two subdevs
> are registered with different endpoint nodes that are both part of the
> same device node, the notifier will get either of them. Which subdev
> match the notifier won't be guaranteed, but that's what the notifier
> asked for if it contains a device node and not an endpoint node: any
> subdev corresponding to the device node.
>
> In practice notifiers will need to move to endpoint matching if they
> want to get a particular subdev of a device, and this change allows
> doing so without mass-patching every driver. It allows a notifier to
> switch to endpoint nodes, while subdevs still use device nodes and are
> gradually ported.
>

In case a device has two CSI-2 receivers, different IPs, different
drivers which register different notifiers, and they are connected in
DTS to a device like adv748x which registers two async
devices for its two CSI-2 transmitter on endpoints, depending on which
CSI-2 receiver gets probed first, it matches any of the two CSI-2 Tx.
The media graph would complete but it won't be what's described in
dts.

I agree it's unlikely, and having something like this or what kieran
did in is better than the current situation, so I'm not pushing this
back, at all. Just pointing possible reasons why we still don't have
any solution to this issue in mainline.

Thanks
   j

> > I'm not sure how that would happen in practice, as the bridge would be
> > registering the fwnode of the remote device twice, but I think that
> > was the reason kieran's patch has not been collected or am I
> > mistaken ?
> >
> > > Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> > > ---
> > > This has been compile-tested only. Prabhakar, could you check if it
> > > fixes your issue ?
> > >
> > >  drivers/media/v4l2-core/v4l2-async.c | 42 +++++++++++++++++++++++++++-
> > >  1 file changed, 41 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
> > > index 8bde33c21ce4..995e5464cba7 100644
> > > --- a/drivers/media/v4l2-core/v4l2-async.c
> > > +++ b/drivers/media/v4l2-core/v4l2-async.c
> > > @@ -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)
>
> --
> Regards,
>
> Laurent Pinchart
Laurent Pinchart March 16, 2020, 9:56 a.m. UTC | #5
Hi Jacopo,

On Mon, Mar 16, 2020 at 09:59:34AM +0100, Jacopo Mondi wrote:
> On Mon, Mar 16, 2020 at 08:34:44AM +0200, Laurent Pinchart wrote:
> > On Sun, Mar 15, 2020 at 10:47:07PM +0100, Jacopo Mondi wrote:
> >> On Sun, Mar 15, 2020 at 02:55:11PM +0200, Laurent Pinchart wrote:
> >>> fwnode matching was designed to match on nodes corresponding to a
> >>> device. Some drivers, however, needed to match on endpoints, and have
> >>> passed endpoint fwnodes to v4l2-async. This works when both the subdev
> >>> and the notifier use the same fwnode types (endpoint or device), but
> >>> makes drivers that use different types incompatible.
> >>>
> >>> Fix this by extending the fwnode match to handle fwnodes of different
> >>> types. When the types (deduced from the node name) are different,
> >>> retrieve the device fwnode for the side that provides an endpoint
> >>> fwnode, and compare it with the device fwnode provided by the other
> >>> side. This allows interoperability between all drivers, regardless of
> >>> which type of fwnode they use for matching.
> >>
> >> I'm sorry but I'm not sure why would make a difference compared to
> >> what Kieran's patch did.
> >> https://lore.kernel.org/patchwork/patch/788637/
> >>
> >> If the bridge matches on device node, and the remote registers more
> >> than one endpoints it is possible to get a false match.
> >
> > How so ? If a notifier entry points to a device node, and two subdevs
> > are registered with different endpoint nodes that are both part of the
> > same device node, the notifier will get either of them. Which subdev
> > match the notifier won't be guaranteed, but that's what the notifier
> > asked for if it contains a device node and not an endpoint node: any
> > subdev corresponding to the device node.
> >
> > In practice notifiers will need to move to endpoint matching if they
> > want to get a particular subdev of a device, and this change allows
> > doing so without mass-patching every driver. It allows a notifier to
> > switch to endpoint nodes, while subdevs still use device nodes and are
> > gradually ported.
> 
> In case a device has two CSI-2 receivers, different IPs, different
> drivers which register different notifiers, and they are connected in
> DTS to a device like adv748x which registers two async
> devices for its two CSI-2 transmitter on endpoints, depending on which
> CSI-2 receiver gets probed first, it matches any of the two CSI-2 Tx.
> The media graph would complete but it won't be what's described in
> dts.
> 
> I agree it's unlikely, and having something like this or what kieran
> did in is better than the current situation, so I'm not pushing this
> back, at all. Just pointing possible reasons why we still don't have
> any solution to this issue in mainline.

Regardless of whether it's likely or not, to support this correctly,
endpoint matching is required, there's no way around that. This change
doesn't introduce any regression, and allows migrating subdevs and
subdevs users independently from each other, so I see no drawback :-)

> >> I'm not sure how that would happen in practice, as the bridge would be
> >> registering the fwnode of the remote device twice, but I think that
> >> was the reason kieran's patch has not been collected or am I
> >> mistaken ?
> >>
> >>> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> >>> ---
> >>> This has been compile-tested only. Prabhakar, could you check if it
> >>> fixes your issue ?
> >>>
> >>>  drivers/media/v4l2-core/v4l2-async.c | 42 +++++++++++++++++++++++++++-
> >>>  1 file changed, 41 insertions(+), 1 deletion(-)
> >>>
> >>> diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
> >>> index 8bde33c21ce4..995e5464cba7 100644
> >>> --- a/drivers/media/v4l2-core/v4l2-async.c
> >>> +++ b/drivers/media/v4l2-core/v4l2-async.c
> >>> @@ -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)
Niklas Söderlund March 16, 2020, 9:40 p.m. UTC | #6
Hi Laurent,

Thanks for your work.

On 2020-03-15 14:55:11 +0200, Laurent Pinchart wrote:
> fwnode matching was designed to match on nodes corresponding to a
> device. Some drivers, however, needed to match on endpoints, and have
> passed endpoint fwnodes to v4l2-async. This works when both the subdev
> and the notifier use the same fwnode types (endpoint or device), but
> makes drivers that use different types incompatible.
> 
> Fix this by extending the fwnode match to handle fwnodes of different
> types. When the types (deduced from the node name) are different,
> retrieve the device fwnode for the side that provides an endpoint
> fwnode, and compare it with the device fwnode provided by the other
> side. This allows interoperability between all drivers, regardless of
> which type of fwnode they use for matching.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>

I tested this on R-Car CSI-2 and ADV748x without any regressions. As 
Jacopo already pointed out it's similar to what have been tried before 
and have the potential problem for new transmitters registering multiple 
endpoints (like ADV748x) being used together with older receivers who 
register a single device node in v4l-async. But this do not introduce 
any regressions and is a good first step to move everything to endpoint 
matching.

Tested-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>

Maybe a info message should be logged if a match is made between 
endpoint and node? It would make it easy to spot if one needs to debug a 
miss match and would be a clear message one driver should be moved to 
endpoint matching. Maybe adding such a log message would count as a 
regression for some.

> ---
> This has been compile-tested only. Prabhakar, could you check if it
> fixes your issue ?
> 
>  drivers/media/v4l2-core/v4l2-async.c | 42 +++++++++++++++++++++++++++-
>  1 file changed, 41 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
> index 8bde33c21ce4..995e5464cba7 100644
> --- a/drivers/media/v4l2-core/v4l2-async.c
> +++ b/drivers/media/v4l2-core/v4l2-async.c
> @@ -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)
> -- 
> Regards,
> 
> Laurent Pinchart
>
Laurent Pinchart March 16, 2020, 9:47 p.m. UTC | #7
Hi Niklas,

On Mon, Mar 16, 2020 at 10:40:12PM +0100, Niklas Söderlund wrote:
> On 2020-03-15 14:55:11 +0200, Laurent Pinchart wrote:
> > fwnode matching was designed to match on nodes corresponding to a
> > device. Some drivers, however, needed to match on endpoints, and have
> > passed endpoint fwnodes to v4l2-async. This works when both the subdev
> > and the notifier use the same fwnode types (endpoint or device), but
> > makes drivers that use different types incompatible.
> > 
> > Fix this by extending the fwnode match to handle fwnodes of different
> > types. When the types (deduced from the node name) are different,
> > retrieve the device fwnode for the side that provides an endpoint
> > fwnode, and compare it with the device fwnode provided by the other
> > side. This allows interoperability between all drivers, regardless of
> > which type of fwnode they use for matching.
> > 
> > Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> 
> I tested this on R-Car CSI-2 and ADV748x without any regressions. As 
> Jacopo already pointed out it's similar to what have been tried before 
> and have the potential problem for new transmitters registering multiple 
> endpoints (like ADV748x) being used together with older receivers who 
> register a single device node in v4l-async. But this do not introduce 
> any regressions and is a good first step to move everything to endpoint 
> matching.
> 
> Tested-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
> 
> Maybe a info message should be logged if a match is made between 
> endpoint and node? It would make it easy to spot if one needs to debug a 
> miss match and would be a clear message one driver should be moved to 
> endpoint matching. Maybe adding such a log message would count as a 
> regression for some.

WARN("FIX YOUR DRIVER TO USE ENDPOINT MATCHING") ? :-)

Jokes aside, something a bit less harsh such as "Matching endpoint with
device node, consider fixing driver %s to use endpoints" wouldn't be a
bad idea.

> > ---
> > This has been compile-tested only. Prabhakar, could you check if it
> > fixes your issue ?
> > 
> >  drivers/media/v4l2-core/v4l2-async.c | 42 +++++++++++++++++++++++++++-
> >  1 file changed, 41 insertions(+), 1 deletion(-)
> > 
> > diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
> > index 8bde33c21ce4..995e5464cba7 100644
> > --- a/drivers/media/v4l2-core/v4l2-async.c
> > +++ b/drivers/media/v4l2-core/v4l2-async.c
> > @@ -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)
Kieran Bingham March 17, 2020, 12:33 p.m. UTC | #8
Hi Laurent,

On 16/03/2020 21:47, Laurent Pinchart wrote:
> Hi Niklas,
> 
> On Mon, Mar 16, 2020 at 10:40:12PM +0100, Niklas Söderlund wrote:
>> On 2020-03-15 14:55:11 +0200, Laurent Pinchart wrote:
>>> fwnode matching was designed to match on nodes corresponding to a
>>> device. Some drivers, however, needed to match on endpoints, and have
>>> passed endpoint fwnodes to v4l2-async. This works when both the subdev
>>> and the notifier use the same fwnode types (endpoint or device), but
>>> makes drivers that use different types incompatible.
>>>
>>> Fix this by extending the fwnode match to handle fwnodes of different
>>> types. When the types (deduced from the node name) are different,
>>> retrieve the device fwnode for the side that provides an endpoint
>>> fwnode, and compare it with the device fwnode provided by the other
>>> side. This allows interoperability between all drivers, regardless of
>>> which type of fwnode they use for matching.
>>>
>>> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
>>
>> I tested this on R-Car CSI-2 and ADV748x without any regressions. As 
>> Jacopo already pointed out it's similar to what have been tried before 

This was the patch that I had believed was accepted, but ended up stuck
in Sakari's tree:

https://git.linuxtv.org/sailus/media_tree.git/commit/?h=fwnode-const&id=35c32d99b2c3f5086b911ec817926de9b7bc3b41

(it's already a little bit-rotted though)


>> and have the potential problem for new transmitters registering multiple 
>> endpoints (like ADV748x) being used together with older receivers who 
>> register a single device node in v4l-async. But this do not introduce 

So if an 'old' receiver wants to use the 'new' features, it must upgrade
to endpoint matching.

I think that's fine.

>> any regressions and is a good first step to move everything to endpoint 
>> matching.
>>
>> Tested-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
>>
>> Maybe a info message should be logged if a match is made between 
>> endpoint and node? It would make it easy to spot if one needs to debug a 
>> miss match and would be a clear message one driver should be moved to 
>> endpoint matching. Maybe adding such a log message would count as a 
>> regression for some.
> 
> WARN("FIX YOUR DRIVER TO USE ENDPOINT MATCHING") ? :-)

Indeed, a notification that they need to update their matching would be
useful in that scenario.

I believe we need to move forward with this somehow, as we have Xilinx
trying to use MAX9286 with Xilinx drivers, (endpoint matching subdev
with dev node matching receiver) and Renesas trying to use non-endpoint
subdevices against an endpoint matched RCar-VIN ...?


> Jokes aside, something a bit less harsh such as "Matching endpoint with
> device node, consider fixing driver %s to use endpoints" wouldn't be a
> bad idea.

Yes, Is there anything else we can do? Even if we 'started' converting
other receivers to match on endpoints, it would take time - so I think
an intermediate stage like this is still very useful.

Of course this patch also lets us push the updates back to those who
care about those drivers too ...

>>> ---
>>> This has been compile-tested only. Prabhakar, could you check if it
>>> fixes your issue ?
>>>
>>>  drivers/media/v4l2-core/v4l2-async.c | 42 +++++++++++++++++++++++++++-
>>>  1 file changed, 41 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
>>> index 8bde33c21ce4..995e5464cba7 100644
>>> --- a/drivers/media/v4l2-core/v4l2-async.c
>>> +++ b/drivers/media/v4l2-core/v4l2-async.c
>>> @@ -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;

Ok, so this looks like a good safety check for edge cases which would
potentially have got through in my version.

>>> +
>>> +	/*
>>> +	 * 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);

It seems in my implementation these got leaked too :-)

I'm sold. This one is better than the old version I had.

Hopefully we can get this moving so that we can progress towards
endpoint matching throughout next.

(Ideally with a warning to convert non-endpoint matching drivers...)

Reviewed-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>


>>> +
>>> +	return dev_fwnode == other_fwnode;
>>>  }
>>>  
>>>  static bool match_custom(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd)
>
Sakari Ailus March 17, 2020, 12:44 p.m. UTC | #9
Hi Laurent,

Thanks for the patch.

On Sun, Mar 15, 2020 at 02:55:11PM +0200, Laurent Pinchart wrote:
> fwnode matching was designed to match on nodes corresponding to a
> device. Some drivers, however, needed to match on endpoints, and have
> passed endpoint fwnodes to v4l2-async. This works when both the subdev
> and the notifier use the same fwnode types (endpoint or device), but
> makes drivers that use different types incompatible.
> 
> Fix this by extending the fwnode match to handle fwnodes of different
> types. When the types (deduced from the node name) are different,
> retrieve the device fwnode for the side that provides an endpoint
> fwnode, and compare it with the device fwnode provided by the other
> side. This allows interoperability between all drivers, regardless of
> which type of fwnode they use for matching.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> ---
> This has been compile-tested only. Prabhakar, could you check if it
> fixes your issue ?
> 
>  drivers/media/v4l2-core/v4l2-async.c | 42 +++++++++++++++++++++++++++-
>  1 file changed, 41 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
> index 8bde33c21ce4..995e5464cba7 100644
> --- a/drivers/media/v4l2-core/v4l2-async.c
> +++ b/drivers/media/v4l2-core/v4l2-async.c
> @@ -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");

Apart from the fact that you're parsing graph node names here, this looks
good.

How about checking instead that calling
fwnode_graph_get_remote_endpoint(fwnode_graph_get_remote_endpoint()) yields
the same node? That would ensure you're dealing with endpoint nodes without
explicitly parsing the graph in any way.

Just remember to drop the references.

> +
> +	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)
Laurent Pinchart March 17, 2020, 11:04 p.m. UTC | #10
Hi Sakari,

On Tue, Mar 17, 2020 at 02:44:56PM +0200, Sakari Ailus wrote:
> On Sun, Mar 15, 2020 at 02:55:11PM +0200, Laurent Pinchart wrote:
> > fwnode matching was designed to match on nodes corresponding to a
> > device. Some drivers, however, needed to match on endpoints, and have
> > passed endpoint fwnodes to v4l2-async. This works when both the subdev
> > and the notifier use the same fwnode types (endpoint or device), but
> > makes drivers that use different types incompatible.
> > 
> > Fix this by extending the fwnode match to handle fwnodes of different
> > types. When the types (deduced from the node name) are different,
> > retrieve the device fwnode for the side that provides an endpoint
> > fwnode, and compare it with the device fwnode provided by the other
> > side. This allows interoperability between all drivers, regardless of
> > which type of fwnode they use for matching.
> > 
> > Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> > ---
> > This has been compile-tested only. Prabhakar, could you check if it
> > fixes your issue ?
> > 
> >  drivers/media/v4l2-core/v4l2-async.c | 42 +++++++++++++++++++++++++++-
> >  1 file changed, 41 insertions(+), 1 deletion(-)
> > 
> > diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
> > index 8bde33c21ce4..995e5464cba7 100644
> > --- a/drivers/media/v4l2-core/v4l2-async.c
> > +++ b/drivers/media/v4l2-core/v4l2-async.c
> > @@ -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");
> 
> Apart from the fact that you're parsing graph node names here, this looks
> good.
> 
> How about checking instead that calling
> fwnode_graph_get_remote_endpoint(fwnode_graph_get_remote_endpoint()) yields
> the same node? That would ensure you're dealing with endpoint nodes without
> explicitly parsing the graph in any way.

Would it be simpler to check for the presence of an endpoint property ?

> Just remember to drop the references.
> 
> > +
> > +	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)
Laurent Pinchart March 17, 2020, 11:08 p.m. UTC | #11
Hi Kieran,

On Tue, Mar 17, 2020 at 12:33:07PM +0000, Kieran Bingham wrote:
> On 16/03/2020 21:47, Laurent Pinchart wrote:
> > On Mon, Mar 16, 2020 at 10:40:12PM +0100, Niklas Söderlund wrote:
> >> On 2020-03-15 14:55:11 +0200, Laurent Pinchart wrote:
> >>> fwnode matching was designed to match on nodes corresponding to a
> >>> device. Some drivers, however, needed to match on endpoints, and have
> >>> passed endpoint fwnodes to v4l2-async. This works when both the subdev
> >>> and the notifier use the same fwnode types (endpoint or device), but
> >>> makes drivers that use different types incompatible.
> >>>
> >>> Fix this by extending the fwnode match to handle fwnodes of different
> >>> types. When the types (deduced from the node name) are different,
> >>> retrieve the device fwnode for the side that provides an endpoint
> >>> fwnode, and compare it with the device fwnode provided by the other
> >>> side. This allows interoperability between all drivers, regardless of
> >>> which type of fwnode they use for matching.
> >>>
> >>> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> >>
> >> I tested this on R-Car CSI-2 and ADV748x without any regressions. As 
> >> Jacopo already pointed out it's similar to what have been tried before 
> 
> This was the patch that I had believed was accepted, but ended up stuck
> in Sakari's tree:
> 
> https://git.linuxtv.org/sailus/media_tree.git/commit/?h=fwnode-const&id=35c32d99b2c3f5086b911ec817926de9b7bc3b41
> 
> (it's already a little bit-rotted though)

Yes, I noticed that. I don't mind dropping this patch if you rebase
yours, as long as we merge a fix :-)

> >> and have the potential problem for new transmitters registering multiple 
> >> endpoints (like ADV748x) being used together with older receivers who 
> >> register a single device node in v4l-async. But this do not introduce 
> 
> So if an 'old' receiver wants to use the 'new' features, it must upgrade
> to endpoint matching.
> 
> I think that's fine.

Yes that's the idea. It will however not have to upgrade all the subdev
drivers it uses at the same time.

> >> any regressions and is a good first step to move everything to endpoint 
> >> matching.
> >>
> >> Tested-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
> >>
> >> Maybe a info message should be logged if a match is made between 
> >> endpoint and node? It would make it easy to spot if one needs to debug a 
> >> miss match and would be a clear message one driver should be moved to 
> >> endpoint matching. Maybe adding such a log message would count as a 
> >> regression for some.
> > 
> > WARN("FIX YOUR DRIVER TO USE ENDPOINT MATCHING") ? :-)
> 
> Indeed, a notification that they need to update their matching would be
> useful in that scenario.
> 
> I believe we need to move forward with this somehow, as we have Xilinx
> trying to use MAX9286 with Xilinx drivers, (endpoint matching subdev
> with dev node matching receiver) and Renesas trying to use non-endpoint
> subdevices against an endpoint matched RCar-VIN ...?
> 
> > Jokes aside, something a bit less harsh such as "Matching endpoint with
> > device node, consider fixing driver %s to use endpoints" wouldn't be a
> > bad idea.
> 
> Yes, Is there anything else we can do? Even if we 'started' converting
> other receivers to match on endpoints, it would take time - so I think
> an intermediate stage like this is still very useful.
> 
> Of course this patch also lets us push the updates back to those who
> care about those drivers too ...

Exactly :-)

> >>> ---
> >>> This has been compile-tested only. Prabhakar, could you check if it
> >>> fixes your issue ?
> >>>
> >>>  drivers/media/v4l2-core/v4l2-async.c | 42 +++++++++++++++++++++++++++-
> >>>  1 file changed, 41 insertions(+), 1 deletion(-)
> >>>
> >>> diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
> >>> index 8bde33c21ce4..995e5464cba7 100644
> >>> --- a/drivers/media/v4l2-core/v4l2-async.c
> >>> +++ b/drivers/media/v4l2-core/v4l2-async.c
> >>> @@ -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;
> 
> Ok, so this looks like a good safety check for edge cases which would
> potentially have got through in my version.
> 
> >>> +
> >>> +	/*
> >>> +	 * 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);
> 
> It seems in my implementation these got leaked too :-)
> 
> I'm sold. This one is better than the old version I had.
> 
> Hopefully we can get this moving so that we can progress towards
> endpoint matching throughout next.
> 
> (Ideally with a warning to convert non-endpoint matching drivers...)
> 
> Reviewed-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>

Thank you. I'll add a warning and agree with Sakari on the best method
to check if a node is an endpoint node, and will then resubmit.

> >>> +
> >>> +	return dev_fwnode == other_fwnode;
> >>>  }
> >>>  
> >>>  static bool match_custom(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd)
Laurent Pinchart March 18, 2020, 12:17 a.m. UTC | #12
Hi Sakari,

On Wed, Mar 18, 2020 at 01:04:32AM +0200, Laurent Pinchart wrote:
> On Tue, Mar 17, 2020 at 02:44:56PM +0200, Sakari Ailus wrote:
> > On Sun, Mar 15, 2020 at 02:55:11PM +0200, Laurent Pinchart wrote:
> > > fwnode matching was designed to match on nodes corresponding to a
> > > device. Some drivers, however, needed to match on endpoints, and have
> > > passed endpoint fwnodes to v4l2-async. This works when both the subdev
> > > and the notifier use the same fwnode types (endpoint or device), but
> > > makes drivers that use different types incompatible.
> > > 
> > > Fix this by extending the fwnode match to handle fwnodes of different
> > > types. When the types (deduced from the node name) are different,
> > > retrieve the device fwnode for the side that provides an endpoint
> > > fwnode, and compare it with the device fwnode provided by the other
> > > side. This allows interoperability between all drivers, regardless of
> > > which type of fwnode they use for matching.
> > > 
> > > Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> > > ---
> > > This has been compile-tested only. Prabhakar, could you check if it
> > > fixes your issue ?
> > > 
> > >  drivers/media/v4l2-core/v4l2-async.c | 42 +++++++++++++++++++++++++++-
> > >  1 file changed, 41 insertions(+), 1 deletion(-)
> > > 
> > > diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
> > > index 8bde33c21ce4..995e5464cba7 100644
> > > --- a/drivers/media/v4l2-core/v4l2-async.c
> > > +++ b/drivers/media/v4l2-core/v4l2-async.c
> > > @@ -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");
> > 
> > Apart from the fact that you're parsing graph node names here, this looks
> > good.

And why is that an issue btw ? the ACPI fwnode ops seem to provide a
.get_name() operation, would it return the ACPI bus ID here ?

> > How about checking instead that calling
> > fwnode_graph_get_remote_endpoint(fwnode_graph_get_remote_endpoint()) yields
> > the same node? That would ensure you're dealing with endpoint nodes without
> > explicitly parsing the graph in any way.
> 
> Would it be simpler to check for the presence of an endpoint property ?
> 
> > Just remember to drop the references.
> > 
> > > +
> > > +	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)
Sakari Ailus March 18, 2020, 7:52 a.m. UTC | #13
Hi Laurent,

On Wed, Mar 18, 2020 at 02:17:26AM +0200, Laurent Pinchart wrote:
> Hi Sakari,
> 
> On Wed, Mar 18, 2020 at 01:04:32AM +0200, Laurent Pinchart wrote:
> > On Tue, Mar 17, 2020 at 02:44:56PM +0200, Sakari Ailus wrote:
> > > On Sun, Mar 15, 2020 at 02:55:11PM +0200, Laurent Pinchart wrote:
> > > > fwnode matching was designed to match on nodes corresponding to a
> > > > device. Some drivers, however, needed to match on endpoints, and have
> > > > passed endpoint fwnodes to v4l2-async. This works when both the subdev
> > > > and the notifier use the same fwnode types (endpoint or device), but
> > > > makes drivers that use different types incompatible.
> > > > 
> > > > Fix this by extending the fwnode match to handle fwnodes of different
> > > > types. When the types (deduced from the node name) are different,
> > > > retrieve the device fwnode for the side that provides an endpoint
> > > > fwnode, and compare it with the device fwnode provided by the other
> > > > side. This allows interoperability between all drivers, regardless of
> > > > which type of fwnode they use for matching.
> > > > 
> > > > Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> > > > ---
> > > > This has been compile-tested only. Prabhakar, could you check if it
> > > > fixes your issue ?
> > > > 
> > > >  drivers/media/v4l2-core/v4l2-async.c | 42 +++++++++++++++++++++++++++-
> > > >  1 file changed, 41 insertions(+), 1 deletion(-)
> > > > 
> > > > diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
> > > > index 8bde33c21ce4..995e5464cba7 100644
> > > > --- a/drivers/media/v4l2-core/v4l2-async.c
> > > > +++ b/drivers/media/v4l2-core/v4l2-async.c
> > > > @@ -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");
> > > 
> > > Apart from the fact that you're parsing graph node names here, this looks
> > > good.
> 
> And why is that an issue btw ? the ACPI fwnode ops seem to provide a
> .get_name() operation, would it return the ACPI bus ID here ?

I'd really prefer not to do graph parsing outside the main parser(s), OF,
ACPI and property frameworks.

Just for an example, the v4l2_fwnode_link_parse() was broken for ACPI for a
long time just because it did not use the graph parsing functions, but
implemented graph parsing on its own.

> 
> > > How about checking instead that calling
> > > fwnode_graph_get_remote_endpoint(fwnode_graph_get_remote_endpoint()) yields
> > > the same node? That would ensure you're dealing with endpoint nodes without
> > > explicitly parsing the graph in any way.
> > 
> > Would it be simpler to check for the presence of an endpoint property ?

There's no endpoint property, apart from an old ACPI definition.

There are differences in the implementations and this is not the best place
to try to take them all into account.
Laurent Pinchart March 18, 2020, 11:22 a.m. UTC | #14
Hi Sakari,

On Wed, Mar 18, 2020 at 09:52:25AM +0200, Sakari Ailus wrote:
> On Wed, Mar 18, 2020 at 02:17:26AM +0200, Laurent Pinchart wrote:
> > On Wed, Mar 18, 2020 at 01:04:32AM +0200, Laurent Pinchart wrote:
> >> On Tue, Mar 17, 2020 at 02:44:56PM +0200, Sakari Ailus wrote:
> >>> On Sun, Mar 15, 2020 at 02:55:11PM +0200, Laurent Pinchart wrote:
> >>>> fwnode matching was designed to match on nodes corresponding to a
> >>>> device. Some drivers, however, needed to match on endpoints, and have
> >>>> passed endpoint fwnodes to v4l2-async. This works when both the subdev
> >>>> and the notifier use the same fwnode types (endpoint or device), but
> >>>> makes drivers that use different types incompatible.
> >>>> 
> >>>> Fix this by extending the fwnode match to handle fwnodes of different
> >>>> types. When the types (deduced from the node name) are different,
> >>>> retrieve the device fwnode for the side that provides an endpoint
> >>>> fwnode, and compare it with the device fwnode provided by the other
> >>>> side. This allows interoperability between all drivers, regardless of
> >>>> which type of fwnode they use for matching.
> >>>> 
> >>>> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> >>>> ---
> >>>> This has been compile-tested only. Prabhakar, could you check if it
> >>>> fixes your issue ?
> >>>> 
> >>>>  drivers/media/v4l2-core/v4l2-async.c | 42 +++++++++++++++++++++++++++-
> >>>>  1 file changed, 41 insertions(+), 1 deletion(-)
> >>>> 
> >>>> diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
> >>>> index 8bde33c21ce4..995e5464cba7 100644
> >>>> --- a/drivers/media/v4l2-core/v4l2-async.c
> >>>> +++ b/drivers/media/v4l2-core/v4l2-async.c
> >>>> @@ -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");
> >>> 
> >>> Apart from the fact that you're parsing graph node names here, this looks
> >>> good.
> > 
> > And why is that an issue btw ? the ACPI fwnode ops seem to provide a
> > .get_name() operation, would it return the ACPI bus ID here ?
> 
> I'd really prefer not to do graph parsing outside the main parser(s), OF,
> ACPI and property frameworks.
> 
> Just for an example, the v4l2_fwnode_link_parse() was broken for ACPI for a
> long time just because it did not use the graph parsing functions, but
> implemented graph parsing on its own.
> 
> >>> How about checking instead that calling
> >>> fwnode_graph_get_remote_endpoint(fwnode_graph_get_remote_endpoint()) yields
> >>> the same node? That would ensure you're dealing with endpoint nodes without
> >>> explicitly parsing the graph in any way.
> >> 
> >> Would it be simpler to check for the presence of an endpoint property ?
> 
> There's no endpoint property, apart from an old ACPI definition.

There isn't ? How does it work on ACPI then ?
acpi_graph_get_remote_endpoint() starts with

	ret = acpi_node_get_property_reference(__fwnode, "remote-endpoint", 0,
					       &args);

> There are differences in the implementations and this is not the best place
> to try to take them all into account.

OK, but in that case I think we need an fwnode_graph_is_endpoint().
Sakari Ailus March 18, 2020, 1:35 p.m. UTC | #15
Hi Laurent,

On Wed, Mar 18, 2020 at 01:22:16PM +0200, Laurent Pinchart wrote:
> Hi Sakari,
> 
> On Wed, Mar 18, 2020 at 09:52:25AM +0200, Sakari Ailus wrote:
> > On Wed, Mar 18, 2020 at 02:17:26AM +0200, Laurent Pinchart wrote:
> > > On Wed, Mar 18, 2020 at 01:04:32AM +0200, Laurent Pinchart wrote:
> > >> On Tue, Mar 17, 2020 at 02:44:56PM +0200, Sakari Ailus wrote:
> > >>> On Sun, Mar 15, 2020 at 02:55:11PM +0200, Laurent Pinchart wrote:
> > >>>> fwnode matching was designed to match on nodes corresponding to a
> > >>>> device. Some drivers, however, needed to match on endpoints, and have
> > >>>> passed endpoint fwnodes to v4l2-async. This works when both the subdev
> > >>>> and the notifier use the same fwnode types (endpoint or device), but
> > >>>> makes drivers that use different types incompatible.
> > >>>> 
> > >>>> Fix this by extending the fwnode match to handle fwnodes of different
> > >>>> types. When the types (deduced from the node name) are different,
> > >>>> retrieve the device fwnode for the side that provides an endpoint
> > >>>> fwnode, and compare it with the device fwnode provided by the other
> > >>>> side. This allows interoperability between all drivers, regardless of
> > >>>> which type of fwnode they use for matching.
> > >>>> 
> > >>>> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> > >>>> ---
> > >>>> This has been compile-tested only. Prabhakar, could you check if it
> > >>>> fixes your issue ?
> > >>>> 
> > >>>>  drivers/media/v4l2-core/v4l2-async.c | 42 +++++++++++++++++++++++++++-
> > >>>>  1 file changed, 41 insertions(+), 1 deletion(-)
> > >>>> 
> > >>>> diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
> > >>>> index 8bde33c21ce4..995e5464cba7 100644
> > >>>> --- a/drivers/media/v4l2-core/v4l2-async.c
> > >>>> +++ b/drivers/media/v4l2-core/v4l2-async.c
> > >>>> @@ -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");
> > >>> 
> > >>> Apart from the fact that you're parsing graph node names here, this looks
> > >>> good.
> > > 
> > > And why is that an issue btw ? the ACPI fwnode ops seem to provide a
> > > .get_name() operation, would it return the ACPI bus ID here ?
> > 
> > I'd really prefer not to do graph parsing outside the main parser(s), OF,
> > ACPI and property frameworks.
> > 
> > Just for an example, the v4l2_fwnode_link_parse() was broken for ACPI for a
> > long time just because it did not use the graph parsing functions, but
> > implemented graph parsing on its own.
> > 
> > >>> How about checking instead that calling
> > >>> fwnode_graph_get_remote_endpoint(fwnode_graph_get_remote_endpoint()) yields
> > >>> the same node? That would ensure you're dealing with endpoint nodes without
> > >>> explicitly parsing the graph in any way.
> > >> 
> > >> Would it be simpler to check for the presence of an endpoint property ?
> > 
> > There's no endpoint property, apart from an old ACPI definition.
> 
> There isn't ? How does it work on ACPI then ?
> acpi_graph_get_remote_endpoint() starts with
> 
> 	ret = acpi_node_get_property_reference(__fwnode, "remote-endpoint", 0,
> 					       &args);

The remote-endpoint property is used in ACPI, too, yes. But the question
was about a property named "endpoint".

> 
> > There are differences in the implementations and this is not the best place
> > to try to take them all into account.
> 
> OK, but in that case I think we need an fwnode_graph_is_endpoint().

Thinking about this --- could you check fwnode_graph_get_remote_endpoint()
returns non-NULL, and then put the remote? That would be more simple as you
are only interested in knowing you're dealing with an endpoint. I don't
object having a little helper for this though.
diff mbox series

Patch

diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
index 8bde33c21ce4..995e5464cba7 100644
--- a/drivers/media/v4l2-core/v4l2-async.c
+++ b/drivers/media/v4l2-core/v4l2-async.c
@@ -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)