Hello,
On Mon, Oct 25, 2021 at 01:00:10PM +0200, Andrzej Hajda wrote:
On 21.10.2021 22:21, Sam Ravnborg wrote:And breaks interoperability, because different sources end up calling
On Thu, Oct 21, 2021 at 12:29:01PM -0700, Douglas Anderson wrote:Thanks Sam, but I am not sure about useful feedback - when I see bridge
Right now, the chaining order ofThis makes good sense as you describe it. I hope others can add more
pre_enable/enable/disable/post_disable looks like this:
pre_enable: start from connector and move to encoder
enable: start from encoder and move to connector
disable: start from connector and move to encoder
post_disable: start from encoder and move to connector
In the above, it can be seen that at least pre_enable() and
post_disable() are opposites of each other and enable() and disable()
are opposites. However, it seems broken that pre_enable() and enable()
would not move in the same direction. In other parts of Linux you can
see that various stages move in the same order. For instance, during
system suspend the "early" calls run in the same order as the normal
calls run in the same order as the "late" calls run in the same order
as the "noirq" calls.
Let fix the above so that it makes more sense. Now we'll have:
pre_enable: start from encoder and move to connector
enable: start from encoder and move to connector
disable: start from connector and move to encoder
post_disable: start from connector and move to encoder
This order is chosen because if there are parent-child relationships
anywhere I would expect that the encoder would be a parent and the
connector a child--not the other way around.
useful feedback.
Added Andrzej Hajda <andrzej.hajda@xxxxxxxxx> to the mail, as he have
expressed concerns with the chain of bridges before.
chain issues it automatically triggers "whining mode" in my head :)
It will break for sure. So the question is: if it is worth changing?This can be important when using the DP AUX bus to instantiate a
panel. The DP AUX bus is likely part of a bridge driver and is a
parent of the panel. We'd like the bridge to be pre_enabled before the
panel and the panel to be post_disabled before the
bridge. Specifically, this allows pm_runtime_put_sync_suspend() in a
bridge driver's post_suspend to work properly even a panel is under
it.
NOTE: it's entirely possible that this change could break someone who
was relying on the old order. Hopefully this isn't the case, but if
this does break someone it seems like it's better to do it sonner
rather than later so we can fix everyone to handle the order that
makes the most sense.
New order seems good for eDP, DSI sinks [1], probably other as well.
Old order is better for example for THC63LVD1024 [2 p. 20], I guess for
many other sinks as well.
I am not even sure if it is protocol specific (LVDS, RGB, HDMI,...), or
it depends on specific hw pairs (source->sink).
This is why I complain about the bridge chain - assumption that one
fixed call order will work for all setups seems to me ridiculous.
Going back to the question - changing the order from fixed one to
another fixed one will not solve general issue.
What can we do then?
1. Configurable call order? Probably doable: every chain element should
expose info if it's call should be before or after source, then some
core helper will create queue of callbacks. Seems quite complicated,
hides the logic from implementer and not fully flexible (for example,
there are protocols which require to perform sth on source, then on
sink, then again on the source).
2. Stop using bridge chain and call sink ops directly from the source
(this is what Exynos and VC4 do): is flexible and straightforward, gives
full control to the source.
operations in different orders. We end up having different sinks that
expect calls in different ways, and divide the world in sink/source
groups that don't interoperate :-(
3. Use different abstractions to enforce proper initialization orderA real bus seems overkill, but we could have drm_bridge operations
(like extending mipi_dsi_host_ops): requires existence of transport bus
abstraction (only DSI at the moment(?)).
specific to particular hardware interfaces.
... other ideas?I don't like it because of the amount of work it would require to switch
to such a model, but I'm really starting to think that a variation of
the second option would be best, where the sink controls the source
instead of having the source controlling the sink. It's the sink that
knows about its enabling/disabling sequence, and about how the source
needs to be controlled to match it.
Another idea, connected to the subject - some protocols require somedrm_sink would be equally confusing when used for devices that have a
negotiations between source and sink bus format, or more steps than
pre_enable, enable ops to establish link. I wonder if encapsulating
drm_bridge in some protocol specific struct wouldn't be a solution, it
can be helpful as well in case of the subject.
For example:
struct drm_bridge_edp {
const struct drm_bridge_edp_funcs *funcs;
struct drm_bridge base;
...
};
Then source could promote bridge pointer to bridge_edp pointer (if
applicable) and perform edp specific stuff. To make it working well
pre-enable order should be as proposed in this patchsets (encoder ->
connector), as the source should initiate negotiations.
Btw this encapsulation stuff above asks to rename drm_bridge to
drm_sink, otherwise it would be confusing as bridges have two ends.
sink and a source :-) I'm not against a rename though, if we can find a
better name.
Regards
Andrzej
[1]: I use term sink as short equivalent for 'bridges AND panels'
(another issue in DRMs).
[2]: https://www.mouser.com/datasheet/2/286/THC63LVD1024-1396205.pdf
A FURTHER NOTE: Looking closer at commit 4e5763f03e10 ("drm/bridge:To make the patch complete the descriptions in drm_bridge_funcs
ti-sn65dsi86: Wrap panel with panel-bridge") you can see that patch
inadvertently changed the order of things. The order used to be
correct (panel prepare was at the tail of the bridge enable) but it
became backwards. We'll restore the original order with this patch.
Fixes: 4e5763f03e10 ("drm/bridge: ti-sn65dsi86: Wrap panel with panel-bridge")
Fixes: 05193dc38197 ("drm/bridge: Make the bridge chain a double-linked list")
Signed-off-by: Douglas Anderson <dianders@xxxxxxxxxxxx>
need to be updated to reflect the new reality.
---If you, or someone else, could r-b or ack the pending patches to remove
drivers/gpu/drm/drm_bridge.c | 28 ++++++++++++++--------------
1 file changed, 14 insertions(+), 14 deletions(-)
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index c96847fc0ebc..98808af59afd 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -583,18 +583,14 @@ EXPORT_SYMBOL(drm_bridge_chain_mode_set);
void drm_bridge_chain_pre_enable(struct drm_bridge *bridge)
this function, this part of the patch would no longer be needed.
{s/iter/bridge/ would make the patch simpler
struct drm_encoder *encoder;
- struct drm_bridge *iter;
if (!bridge)
return;
encoder = bridge->encoder;
- list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
- if (iter->funcs->pre_enable)
- iter->funcs->pre_enable(iter);
-
- if (iter == bridge)
- break;
+ list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
+ if (bridge->funcs->pre_enable)
+ bridge->funcs->pre_enable(bridge);
}
}
EXPORT_SYMBOL(drm_bridge_chain_pre_enable);
@@ -684,26 +680,30 @@ void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge,
struct drm_atomic_state *old_state)
{
struct drm_encoder *encoder;
+ struct drm_bridge *iter;
And then the bridge argument could be last_bridge or something.
This would IMO increase readability of the code and make the patch smaller.
if (!bridge)I cannot see why this is needed, we are at the end of the list here
return;
encoder = bridge->encoder;
- list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
- if (bridge->funcs->atomic_post_disable) {
+ list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
+ if (iter->funcs->atomic_post_disable) {
struct drm_bridge_state *old_bridge_state;
old_bridge_state =
drm_atomic_get_old_bridge_state(old_state,
- bridge);
+ iter);
if (WARN_ON(!old_bridge_state))
return;
- bridge->funcs->atomic_post_disable(bridge,
- old_bridge_state);
- } else if (bridge->funcs->post_disable) {
- bridge->funcs->post_disable(bridge);
+ iter->funcs->atomic_post_disable(iter,
+ old_bridge_state);
+ } else if (iter->funcs->post_disable) {
+ iter->funcs->post_disable(iter);
}
+
+ if (iter == bridge)
+ break;
anyway.
}
}
EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable);