mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-23 15:32:33 +00:00
20ea6adbf1
Build system: x86_64 Build-tested: bcm2708, bcm2709, bcm2710, bcm2711 Run-tested: bcm2708/RPiB+, bcm2709/RPi3B, bcm2710/RPi3B, bcm2711/RPi4B Signed-off-by: Marty Jones <mj8263788@gmail.com> Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
309 lines
9.5 KiB
Diff
309 lines
9.5 KiB
Diff
From 2c8b6954e6168d56cc6d35f5f6d075c826a9b8e1 Mon Sep 17 00:00:00 2001
|
|
From: Dave Stevenson <dave.stevenson@raspberrypi.com>
|
|
Date: Thu, 16 Dec 2021 15:25:35 +0000
|
|
Subject: [PATCH] drm/bridge: Introduce pre_enable_upstream_first to
|
|
alter bridge init order
|
|
|
|
DSI sink devices typically want the DSI host powered up and configured
|
|
before they are powered up. pre_enable is the place this would normally
|
|
happen, but they are called in reverse order from panel/connector towards
|
|
the encoder, which is the "wrong" order.
|
|
|
|
Add a new flag pre_enable_upstream_first that any bridge can set
|
|
to swap the order of pre_enable (and post_disable) for that and the
|
|
immediately upstream bridge.
|
|
Should the immediately upstream bridge also set the
|
|
pre_enable_upstream_first flag, the bridge upstream of that will be called
|
|
before either of those which requested pre_enable_upstream_first.
|
|
|
|
eg:
|
|
- Panel
|
|
- Bridge 1
|
|
- Bridge 2 pre_enable_upstream_first
|
|
- Bridge 3
|
|
- Bridge 4 pre_enable_upstream_first
|
|
- Bridge 5 pre_enable_upstream_first
|
|
- Bridge 6
|
|
- Encoder
|
|
Would result in pre_enable's being called as Panel, Bridge 1, Bridge 3,
|
|
Bridge 2, Bridge 6, Bridge 5, Bridge 4, Encoder.
|
|
|
|
Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
|
|
---
|
|
drivers/gpu/drm/drm_bridge.c | 177 +++++++++++++++++++++++++----------
|
|
include/drm/drm_bridge.h | 8 ++
|
|
2 files changed, 137 insertions(+), 48 deletions(-)
|
|
|
|
--- a/drivers/gpu/drm/drm_bridge.c
|
|
+++ b/drivers/gpu/drm/drm_bridge.c
|
|
@@ -463,20 +463,15 @@ EXPORT_SYMBOL(drm_bridge_chain_disable);
|
|
* encoder chain, starting from the first bridge to the last. These are called
|
|
* after completing the encoder's prepare op.
|
|
*
|
|
+ * If a bridge sets @pre_enable_upstream_first, then the @post_disable for that
|
|
+ * bridge will be called before the previous one to reverse the @pre_enable
|
|
+ * calling direction.
|
|
+ *
|
|
* Note: the bridge passed should be the one closest to the encoder
|
|
*/
|
|
void drm_bridge_chain_post_disable(struct drm_bridge *bridge)
|
|
{
|
|
- struct drm_encoder *encoder;
|
|
-
|
|
- if (!bridge)
|
|
- return;
|
|
-
|
|
- encoder = bridge->encoder;
|
|
- list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
|
|
- if (bridge->funcs->post_disable)
|
|
- bridge->funcs->post_disable(bridge);
|
|
- }
|
|
+ drm_atomic_bridge_chain_post_disable(bridge, NULL);
|
|
}
|
|
EXPORT_SYMBOL(drm_bridge_chain_post_disable);
|
|
|
|
@@ -518,24 +513,14 @@ EXPORT_SYMBOL(drm_bridge_chain_mode_set)
|
|
* chain, starting from the last bridge to the first. These are called
|
|
* before calling the encoder's commit op.
|
|
*
|
|
+ * If a bridge sets @pre_enable_upstream_first, then the @pre_enable for the
|
|
+ * previous bridge will be called before @pre_enable of this bridge.
|
|
+ *
|
|
* Note: the bridge passed should be the one closest to the encoder
|
|
*/
|
|
void drm_bridge_chain_pre_enable(struct drm_bridge *bridge)
|
|
{
|
|
- 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;
|
|
- }
|
|
+ drm_atomic_bridge_chain_pre_enable(bridge, NULL);
|
|
}
|
|
EXPORT_SYMBOL(drm_bridge_chain_pre_enable);
|
|
|
|
@@ -607,6 +592,25 @@ void drm_atomic_bridge_chain_disable(str
|
|
}
|
|
EXPORT_SYMBOL(drm_atomic_bridge_chain_disable);
|
|
|
|
+static void drm_atomic_bridge_call_post_disable(struct drm_bridge *bridge,
|
|
+ struct drm_atomic_state *old_state)
|
|
+{
|
|
+ if (old_state && bridge->funcs->atomic_post_disable) {
|
|
+ struct drm_bridge_state *old_bridge_state;
|
|
+
|
|
+ old_bridge_state =
|
|
+ drm_atomic_get_old_bridge_state(old_state,
|
|
+ bridge);
|
|
+ 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);
|
|
+ }
|
|
+}
|
|
+
|
|
/**
|
|
* drm_atomic_bridge_chain_post_disable - cleans up after disabling all bridges
|
|
* in the encoder chain
|
|
@@ -617,6 +621,9 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_di
|
|
* &drm_bridge_funcs.post_disable) op for all the bridges in the encoder chain,
|
|
* starting from the first bridge to the last. These are called after completing
|
|
* &drm_encoder_helper_funcs.atomic_disable
|
|
+ * If a bridge sets @pre_enable_upstream_first, then the @post_disable for that
|
|
+ * bridge will be called before the previous one to reverse the @pre_enable
|
|
+ * calling direction.
|
|
*
|
|
* Note: the bridge passed should be the one closest to the encoder
|
|
*/
|
|
@@ -624,30 +631,75 @@ void drm_atomic_bridge_chain_post_disabl
|
|
struct drm_atomic_state *old_state)
|
|
{
|
|
struct drm_encoder *encoder;
|
|
+ struct drm_bridge *next, *limit;
|
|
|
|
if (!bridge)
|
|
return;
|
|
|
|
encoder = bridge->encoder;
|
|
+
|
|
list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
|
|
- if (bridge->funcs->atomic_post_disable) {
|
|
- struct drm_bridge_state *old_bridge_state;
|
|
+ limit = NULL;
|
|
|
|
- old_bridge_state =
|
|
- drm_atomic_get_old_bridge_state(old_state,
|
|
- bridge);
|
|
- if (WARN_ON(!old_bridge_state))
|
|
- return;
|
|
+ if (!list_is_last(&bridge->chain_node, &encoder->bridge_chain)) {
|
|
+ next = list_next_entry(bridge, chain_node);
|
|
|
|
- bridge->funcs->atomic_post_disable(bridge,
|
|
- old_bridge_state);
|
|
- } else if (bridge->funcs->post_disable) {
|
|
- bridge->funcs->post_disable(bridge);
|
|
+ if (next->pre_enable_upstream_first) {
|
|
+ /* Downstream bridge had requested that upstream
|
|
+ * was enabled first, so disabled last
|
|
+ */
|
|
+ limit = next;
|
|
+
|
|
+ /* Find the next bridge that has NOT requested
|
|
+ * upstream to be enabled first / disabled last
|
|
+ */
|
|
+ list_for_each_entry_from(next, &encoder->bridge_chain,
|
|
+ chain_node) {
|
|
+ if (next->pre_enable_upstream_first) {
|
|
+ next = list_prev_entry(next, chain_node);
|
|
+ limit = next;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Call these bridges in reverse order */
|
|
+ list_for_each_entry_from_reverse(next, &encoder->bridge_chain,
|
|
+ chain_node) {
|
|
+ if (next == bridge)
|
|
+ break;
|
|
+
|
|
+ drm_atomic_bridge_call_post_disable(next,
|
|
+ old_state);
|
|
+ }
|
|
+ }
|
|
}
|
|
+
|
|
+ drm_atomic_bridge_call_post_disable(bridge, old_state);
|
|
+
|
|
+ if (limit)
|
|
+ bridge = limit;
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable);
|
|
|
|
+static void drm_atomic_bridge_call_pre_enable(struct drm_bridge *bridge,
|
|
+ struct drm_atomic_state *old_state)
|
|
+{
|
|
+ if (old_state && bridge->funcs->atomic_pre_enable) {
|
|
+ struct drm_bridge_state *old_bridge_state;
|
|
+
|
|
+ old_bridge_state =
|
|
+ drm_atomic_get_old_bridge_state(old_state,
|
|
+ bridge);
|
|
+ if (WARN_ON(!old_bridge_state))
|
|
+ return;
|
|
+
|
|
+ bridge->funcs->atomic_pre_enable(bridge, old_bridge_state);
|
|
+ } else if (bridge->funcs->pre_enable) {
|
|
+ bridge->funcs->pre_enable(bridge);
|
|
+ }
|
|
+}
|
|
+
|
|
/**
|
|
* drm_atomic_bridge_chain_pre_enable - prepares for enabling all bridges in
|
|
* the encoder chain
|
|
@@ -659,33 +711,62 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_po
|
|
* starting from the last bridge to the first. These are called before calling
|
|
* &drm_encoder_helper_funcs.atomic_enable
|
|
*
|
|
+ * If a bridge sets @pre_enable_upstream_first, then the pre_enable for the
|
|
+ * upstream bridge will be called before pre_enable of this bridge.
|
|
+ *
|
|
* Note: the bridge passed should be the one closest to the encoder
|
|
*/
|
|
void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge,
|
|
struct drm_atomic_state *old_state)
|
|
{
|
|
struct drm_encoder *encoder;
|
|
- struct drm_bridge *iter;
|
|
+ struct drm_bridge *iter, *next, *limit;
|
|
|
|
if (!bridge)
|
|
return;
|
|
|
|
encoder = bridge->encoder;
|
|
- list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
|
|
- if (iter->funcs->atomic_pre_enable) {
|
|
- struct drm_bridge_state *old_bridge_state;
|
|
|
|
- old_bridge_state =
|
|
- drm_atomic_get_old_bridge_state(old_state,
|
|
- iter);
|
|
- if (WARN_ON(!old_bridge_state))
|
|
- return;
|
|
+ list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
|
|
+ if (iter->pre_enable_upstream_first) {
|
|
+ next = iter;
|
|
+ limit = bridge;
|
|
+ list_for_each_entry_from_reverse(next,
|
|
+ &encoder->bridge_chain,
|
|
+ chain_node) {
|
|
+ if (next == bridge)
|
|
+ break;
|
|
+
|
|
+ if (!next->pre_enable_upstream_first) {
|
|
+ /* Found first bridge that does NOT
|
|
+ * request upstream to be enabled first
|
|
+ */
|
|
+ limit = list_prev_entry(next, chain_node);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ list_for_each_entry_from(next, &encoder->bridge_chain, chain_node) {
|
|
+ /* Call requested upstream bridge pre_enable
|
|
+ * in order.
|
|
+ */
|
|
+ if (next == iter)
|
|
+ /* At the first bridgge to request upstream
|
|
+ * bridges called first.
|
|
+ */
|
|
+ break;
|
|
|
|
- iter->funcs->atomic_pre_enable(iter, old_bridge_state);
|
|
- } else if (iter->funcs->pre_enable) {
|
|
- iter->funcs->pre_enable(iter);
|
|
+ drm_atomic_bridge_call_pre_enable(next, old_state);
|
|
+ }
|
|
}
|
|
|
|
+ drm_atomic_bridge_call_pre_enable(iter, old_state);
|
|
+
|
|
+ if (iter->pre_enable_upstream_first)
|
|
+ /* Jump all bridges that we have already pre_enabled
|
|
+ */
|
|
+ iter = limit;
|
|
+
|
|
if (iter == bridge)
|
|
break;
|
|
}
|
|
--- a/include/drm/drm_bridge.h
|
|
+++ b/include/drm/drm_bridge.h
|
|
@@ -762,6 +762,14 @@ struct drm_bridge {
|
|
*/
|
|
bool interlace_allowed;
|
|
/**
|
|
+ * @pre_enable_upstream_first: The bridge requires that the upstream
|
|
+ * bridge @pre_enable function is called before its @pre_enable,
|
|
+ * and conversely for post_disable. This is most frequently a
|
|
+ * requirement for DSI devices which need the host to be initialised
|
|
+ * before the peripheral.
|
|
+ */
|
|
+ bool pre_enable_upstream_first;
|
|
+ /**
|
|
* @ddc: Associated I2C adapter for DDC access, if any.
|
|
*/
|
|
struct i2c_adapter *ddc;
|