Re: [PATCH v4 15/18] dt-bindings: usb: Add ports to google,cros-ec-typec for DP altmode

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue, Oct 29, 2024 at 01:15:51PM -0700, Stephen Boyd wrote:
> Quoting Dmitry Baryshkov (2024-10-25 03:49:36)
> > On Tue, Oct 22, 2024 at 06:15:47PM -0700, Stephen Boyd wrote:
> > > Quoting Dmitry Baryshkov (2024-09-20 02:38:53)
> > > > On Sat, Aug 31, 2024 at 09:06:53PM GMT, Stephen Boyd wrote:
> >
> > >
> > > Either way the problem seems to be that I need to associate one
> > > drm_bridge with two displayport altmode drivers and pass some fwnode
> > > handle to drm_connector_oob_hotplug_event() in a way that we can map
> > > that back to the right output endpoint in the DP bridge graph. That
> > > seems to imply that we need to pass the fwnode for the usb-c-connector
> > > in addition to the fwnode for the drm_bridge, so that the drm_bridge
> > > code can look at its DT graph and find the remote node connected.
> > > Basically something like this:
> > >
> > >   void drm_connector_oob_hotplug_event(struct fwnode_handle *connector_fwnode,
> > >                                        struct fwnode_handle
> > > *usb_connector_fwnode,
> > >                                        enum drm_connector_status status)
> > >
> > > (We might as well also pass the number of lanes here)
> >
> > I think this is a bit of an overkill.
> >
> > The drm_connector_oob_hotplug_event() is fine as it is, it gets
> > "fwnode_handle to report the event on".
> 
> Ok. I understand that drm_*() shouldn't know about USB or type-c in
> general.
> 
> >
> > What needs to be changed (in my humble opinion) is the
> > drm_connector_find_by_fwnode() function (or likely a new function is to
> > be added): if it can not find drm_connector for the passed fwnode, it
> > should look it up on the parent, then on parent's parent, etc, until we
> > actually find the drm_connector (good) or we reach the root (sad).
> >
> > And finally after getting the drm_connector, the oob_hotplug_event()
> > callback should also receive the fwnode argument. This way the connector
> > (or the bridge) can identify the fwnode (aka usb-c-connector in our
> > case) that caused the event.
> >
> > WDYT?
> 
> Ok I think I'm following along. The dp->connector_fwnode in
> displayport.c will always be the usb-c-connector node in your example?
> And that will search for the connector or bridge associated with that
> usb-c-connector node. Then when it comes time to call
> drm_connector_oob_hotplug_event() it will take the usb-c-connector
> fwnode as 'connector_fwnode' and in that function we'll make
> drm_connector_find_by_fwnode() implement the parent walk? The
> cros-ec-typec compatible node will register a drm_bridge in all cases,
> and that is the parent of the usb-c-connector node, so the walk will end
> there.
> 
> Then you want to pass the usb-c-connector fwnode to
> connector->funcs->oob_hotplug_event()? So
> drm_bridge_connector_oob_hotplug_event() changes to also get the
> usb-c-connector fwnode. This plan should work.

Yes, that was my proposal. This way we know the origin device (bridge /
connector) and let it take care of any details on its own.

> At this point we need to tell the DP bridge, like IT6505, that it's one
> or the other output endpoints that it should use, but we haven't
> directly connected the usb-c-connector to the output ports of IT6505
> because drm_of_find_panel_or_bridge() can't find the parent of the
> usb-c-connector if we connect the DP bridge to the usb-c-connector
> graphs. We'll need a way for the bridge to know which output port is
> connected to a usb-c-connector fwnode. Some sort of API like

I think that the final bridge should be the IT6505. It can save you from
some troubles, like the inter-bridge lane negotiation. Please remember
that using lanes 2-3 as primary lanes doesn't seem to fall into the
standard DisplayPort usage. It is documented by USB-C and only because
of the orientation switching.

Maybe that's just it? Register DP_bridge (or QMP PHY) as
orientation-switch? Then you don't need any extra API for the lane
mapping? The cross-ec-typec can provide orientation information and the
USB-C-aware controller will follow the lane mapping.

> 
>  fwnode_graph_get_endpoint_connected_to_fwnode(bridge_fwnode, usb_c_fwnode)
> 
> that takes the bridge fwnode and traverses the graph to find the
> endpoint in that's connected to 'usb_c_fwnode'. That traversal API will
> need help from the intermediate node, cros-ec-typec, so maybe it is
> better as a drm_bridge API that uses some new drm_bridge_funcs callback.
> This way IT6505 can ask the bridge chain which output DP endpoint is
> actually associated with the connector fwnode it gets from the
> oob_hotplug_event() callback.
> 
> Here's the two DT snippets that I've ended up with:
> 
> typec {
>         compatible = "google,cros-ec-typec";
> 
>         ports {
>                 port@0 {
>                         reg = <0>;
>                         typec_dp_in: endpoint {
>                                  remote-endpoint = <&usb_1_qmp_phy_out_dp>;
>                         };
>                 };
> 
>                 port@1 {
>                         reg = <1>;
>                         typec_usb0_in: endpoint@0 {
>                                  reg = <0>;
>                                  remote-endpoint = <&usb_hub_dfp1_ss>;
>                         };
>                         typec_usb1_in: endpoint@1 {
>                                  reg = <1>;
>                                  remote-endpoint = <&usb_hub_dfp2_ss>;
>                         };
>                 }
> 
>                 // This port is not really needed because we know the
> 		// mapping from input ports to usb-c-connectors
>                 port@2 {
>                         reg = <2>;
>                         typec0_out: endpoint@0 {
>                                  reg = <0>;
>                                  remote-endpoint = <&usb_c0_ss_in>;
>                         };
>                         typec1_out: endpoint@1 {
>                                  reg = <1>;
>                                  remote-endpoint = <&usb_c1_ss_in>;
>                         };
>                 }

Why do we need these two ports? Can't &usb_hub_dfpN_ss be connected
directly to the usb_cN_ss_in? I understand that you probably want to
express the internal structure of the lane switching, but I think that's
a bit of the overkill. Leaving this to the other commenters / DT
maintainers.

>         };
> 
>         usb_c0: connector@0 {
>                 compatible = "usb-c-connector";
>                 reg = <0>;
> 
>                 ports {
>                         port@0 {
>                                 reg = <0>;
>                                 usb_c0_hs_in: endpoint {
>                                         remote-endpoint = <&usb_hub_dfp1_hs>;
>                                 };
>                         };
> 
>                         port@1 {
>                                 reg = <1>;
>                                 usb_c0_ss_in: endpoint {
>                                         remote-endpoint = <&typec0_out>;
>                                 };
>                         };
>                 };
>         };
> 
>         usb_c1: connector@1 {
>                 compatible = "usb-c-connector";
>                 reg = <1>;
> 
>                 ports {
>                         port@0 {
>                                 reg = <0>;
>                                 usb_c1_hs_in: endpoint {
>                                         remote-endpoint = <&usb_hub_dfp2_hs>;
>                                 };
>                         };
> 
>                         port@1 {
>                                 reg = <1>;
>                                 usb_c1_ss_in: endpoint {
>                                         remote-endpoint = <&typec1_out>;
>                                 };
>                         };
>                 };
>         };
> };
> 
> &usb_1_qmpphy {
>         ports {
>                 port@0 {
>                         endpoint@0 {
>                                 data-lanes = <0 1>;
>                                 // this might go to USB-3 hub
>                         };
> 
>                         usb_1_qmp_phy_out_dp: endpoint@1 {
>                                 remote-endpoint = <&typec_dp_in>;
>                                 data-lanes = <2 3>;
>                         };
>                 }
>         };
> };
> 
> -------
> 
> typec {
>         ports {
>                 port@0 {
>                         reg = <0>;
>                         typec_dp0_in: endpoint@0 {
>                                  reg = <0>;
>                                  remote-endpoint = <&dp_bridge_out_0>;
>                         };
>                         typec_dp1_in: endpoint@1 {
>                                  reg = <1>;
>                                  remote-endpoint = <&dp_bridge_out_1>;
>                         };
>                 };
> 
>                 port@1 {
>                         reg = <1>;
>                         typec_usb0_in: endpoint@0 {
>                                  reg = <0>;
>                                  remote-endpoint = <&usb_hub_0_ss>;
>                         };
>                         typec_usb1_in: endpoint@1 {
>                                  reg = <1>;
>                                  remote-endpoint = <&usb_hub_1_ss>;
>                         };
>                 }
>         };
> 
>         connector@0 {
>                 port@1 {
>                         endpoint@0 {
>                                 remote-endpoint = <&usb_hub_0_hs>;

port@1 is for SS lanes, so something is wrong here.

>                         };
>                 };
>         };
> 
>         connector@1 {
>                 port@1 {
>                         endpoint@0 {
>                                 remote-endpoint = <&usb_hub_1_hs>;
>                         };
>                 };
>         };
> };
> 
> dp_bridge {
>         ports {
>                 port@1 {
>                         dp_bridge_out_0: endpoint@0 {
>                                 remote-endpoint = <&typec_dp0_in>;
>                                 data-lanes = <0 1>;
>                         };
> 
>                         dp_bridge_out_1: endpoint@1 {
>                                 remote-endpoint = <&typec_dp1_in>;
>                                 data-lanes = <2 3>;
>                         };
>                 };
>         };
> };
> 
> -------
> 
> I wonder about a case where we may take two lanes and connect them to a
> usb-c-connector and then take the other two lanes and send them through
> a mux to two more usb-c-connectors. I think we'll need another property
> in that case that indicates which input DP endpoints correspond to the
> usb-c-connector nodes.
> 
> typec {
>         ports {
>                 port@0 {
>                         reg = <0>;
>                         typec_dp0_in: endpoint@0 {
>                                  reg = <0>;
>                                  remote-endpoint = <&dp_bridge_out_0>;
>                         };
>                         typec_dp1_in: endpoint@1 {
>                                  reg = <1>;
>                                  remote-endpoint = <&dp_bridge_out_1>;
>                         };
>                 };
> 
>                 port@1 {
>                         reg = <1>;
>                         typec_usb0_in: endpoint@0 {
>                                  reg = <0>;
>                                  remote-endpoint = <&usb_hub_0_ss>;
>                         };
>                         typec_usb1_in: endpoint@1 {
>                                  reg = <1>;
>                                  remote-endpoint = <&usb_hub_1_ss>;
>                         };
>                         typec_usb2_in: endpoint@2 {
>                                  reg = <2>;
>                                  remote-endpoint = <&usb_hub_2_ss>;
>                         };
>                 }
>         };
> 
> 	dp-2-usb-mapping = <0 0>, <1 1>, <1 2>;

dp-to-typec-mapping?

> };
> 
> This property would indicate dp endpoint 0 goes to usb-c-connector 0
> while dp endpoint 1 goes to usb-c-connector 1 and 2. I don't have this
> hardware but I could see how someone might do this by adding another mux
> that the EC controls. I don't want to design a binding and have to
> rework it in the future to handle this new case. I hope adding a new
> property, or getting more information from the EC firmware, will be
> sufficient to describe the linkage between the DP endpoint and the
> connectors managed by the cros-ec-typec device.

Does it change anything from the DP point of view? It is still either
lanes 0-1 or lanes 2-3? I'd really like to inject the hotplug OOB event
to the dp_bridge letting it get one of the endpoints as a HPD even
source.

> > > Corsola could work with this design, but we'll need to teach
> > > dp_altmode_probe() to look for the drm_bridge elsewhere besides as the
> > > parent of the usb-c-connector node. That implies using the 'displayport'
> > > property in the cros-ec-typec node or teaching dp_altmode_probe() to
> > > look for the port@1/endpoint@1 remote-endpoint handle in the
> > > usb-c-connector graph.
> > >
> > > Assuming the bindings you've presented here are fine and good and I got
> > > over the differences between Trogdor and Corsola, then I can make mostly
> > > everything work with the drm_connector_oob_hotplug_event() signature
> > > change from above and some tweaks to dp_altmode_probe() to look for
> > > port@1/endpoint@1 first because that's the "logical" DP input endpoint
> > > in the usb-c-connector binding's graph. Great! The final roadblock I'm
> > > at is that HPD doesn't work on Trogdor, so I can't signal HPD through
> > > the typec framework.
> >
> > Hmm, I thought that a normal DP's HPD GPIO works on the trogdor. Was I
> > misunderstanding it? But then we don't know, which USB-C connector
> > triggered the HPD...
> 
> By HPD not working on Trogdor I mean that the EC doesn't tell the kernel
> about the state of HPD for a usb-c-connector in software. Instead, HPD
> is signaled directly to the DP controller in hardware via a GPIO. It is
> as you suspect, we don't know which USB-C connector has HPD unless we
> read the mux controlled by the EC and combine that with what the DP
> driver knows about the state of the HPD pin.

I see. So the HPD event gets delivered to the DP controller, but we
really need some API to read the port? If it's not the
orientation-switch, of course.

> 
> >
> > > This series fixes that problem by "capturing" HPD state from the
> > > upstream drm_bridge, e.g. msm_dp, by hooking the struct
> > > drm_bridge_funcs::hpd_notify() path and injecting HPD into the typec
> > > messages received from the EC. That's a workaround to make the typec
> > > framework see HPD state changes that are otherwise invisible to the
> > > kernel. Newer firmwares actually tell us the state of HPD properly, but
> > > even then we have to read a gpio mux controlled by the EC to figure out
> > > which usb-c-connector is actually muxing DP when HPD changes on either
> > > typec_port. Having a drm_bridge in cros-ec-typec helped here because we
> > > could hook this path and signal HPD if we knew the firmware was fixed.
> > > If we don't have the drm_bridge anymore, I'm lost how to do this.
> >
> > It's probably okay to add one, but let me think if we can work without
> > it. Can we make EC driver listen for that single HPD GPIO (by making it
> > an actual GPIO rather than "dp_hot") and then react to it?
> 
> I don't know how we handle the attention message, HPD_IRQ, because
> that's a short pulse that the kernel may miss when this is a GPIO that
> has to be triggered when both falling and rising. When the pin goes
> directly to the DP controller this is fine because the hardware can
> detect that. Similarly, when the EC sends the message about an HPD_IRQ
> we can replay that into the type-c framework and signal attention
> through drm_connector_oob_hotplug_event()/hpd_notify() paths.

Ack.

> 
> >
> > >
> > > Maybe the right answer here is to introduce a drm_connector_dp_typec
> > > structure that is created by the TCPM (cros-ec-typec) that sets a new
> > > DRM_BRIDGE_OP_DP_TYPEC bridge op flag? And then teach
> > > drm_bridge_connector about this new flag, similar to the HDMI one. The
> > > drm_bridge could implement some function that maps the typec_port
> > > (usb-c-connector) to the upstream drm_bridge (ANX7625) graph port,
> > > possibly all in drm_bridge_connector_oob_hotplug_event() so that nothing
> > > else really changes. It could also let us keep hooking the hpd_notify()
> > > path for the workaround needed on Trogdor. And finally it may let us
> > > harmonize the two DT bindings so that we only have one port@1/endpoint
> > > node in the usb-c-connector.
> >
> > I think we have lightly discussed adding drm_connector_displayport, so
> > that part is okay. But my gut feeling is that there should be no _typec
> > part in thart picture. The DRM framework shouldn't need to know all the
> > Type-C details.
> >
> 
> Alright, got it.

-- 
With best wishes
Dmitry




[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]


  Powered by Linux