diff options
Diffstat (limited to 'drivers/gpu/drm/drm_bridge.c')
-rw-r--r-- | drivers/gpu/drm/drm_bridge.c | 294 |
1 files changed, 160 insertions, 134 deletions
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 1545c50fd1c8..c3d69af02e79 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -153,6 +153,45 @@ * situation when probing. */ +/** + * DOC: dsi bridge operations + * + * DSI host interfaces are expected to be implemented as bridges rather than + * encoders, however there are a few aspects of their operation that need to + * be defined in order to provide a consistent interface. + * + * A DSI host should keep the PHY powered down until the pre_enable operation is + * called. All lanes are in an undefined idle state up to this point, and it + * must not be assumed that it is LP-11. + * pre_enable should initialise the PHY, set the data lanes to LP-11, and the + * clock lane to either LP-11 or HS depending on the mode_flag + * %MIPI_DSI_CLOCK_NON_CONTINUOUS. + * + * Ordinarily the downstream bridge DSI peripheral pre_enable will have been + * called before the DSI host. If the DSI peripheral requires LP-11 and/or + * the clock lane to be in HS mode prior to pre_enable, then it can set the + * &pre_enable_prev_first flag to request the pre_enable (and + * post_disable) order to be altered to enable the DSI host first. + * + * Either the CRTC being enabled, or the DSI host enable operation should switch + * the host to actively transmitting video on the data lanes. + * + * The reverse also applies. The DSI host disable operation or stopping the CRTC + * should stop transmitting video, and the data lanes should return to the LP-11 + * state. The DSI host &post_disable operation should disable the PHY. + * If the &pre_enable_prev_first flag is set, then the DSI peripheral's + * bridge &post_disable will be called before the DSI host's post_disable. + * + * Whilst it is valid to call &host_transfer prior to pre_enable or after + * post_disable, the exact state of the lanes is undefined at this point. The + * DSI host should initialise the interface, transmit the data, and then disable + * the interface again. + * + * Ultra Low Power State (ULPS) is not explicitly supported by DRM. If + * implemented, it therefore needs to be handled entirely within the DSI Host + * driver. + */ + static DEFINE_MUTEX(bridge_lock); static LIST_HEAD(bridge_list); @@ -510,61 +549,6 @@ drm_bridge_chain_mode_valid(struct drm_bridge *bridge, EXPORT_SYMBOL(drm_bridge_chain_mode_valid); /** - * drm_bridge_chain_disable - disables all bridges in the encoder chain - * @bridge: bridge control structure - * - * Calls &drm_bridge_funcs.disable op for all the bridges in the encoder - * chain, starting from the last bridge to the first. These are called before - * calling the encoder's prepare op. - * - * Note: the bridge passed should be the one closest to the encoder - */ -void drm_bridge_chain_disable(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->disable) - iter->funcs->disable(iter); - - if (iter == bridge) - break; - } -} -EXPORT_SYMBOL(drm_bridge_chain_disable); - -/** - * drm_bridge_chain_post_disable - cleans up after disabling all bridges in the - * encoder chain - * @bridge: bridge control structure - * - * Calls &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 the encoder's prepare op. - * - * 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); - } -} -EXPORT_SYMBOL(drm_bridge_chain_post_disable); - -/** * drm_bridge_chain_mode_set - set proposed mode for all bridges in the * encoder chain * @bridge: bridge control structure @@ -594,61 +578,6 @@ void drm_bridge_chain_mode_set(struct drm_bridge *bridge, EXPORT_SYMBOL(drm_bridge_chain_mode_set); /** - * drm_bridge_chain_pre_enable - prepares for enabling all bridges in the - * encoder chain - * @bridge: bridge control structure - * - * Calls &drm_bridge_funcs.pre_enable op for all the bridges in the encoder - * chain, starting from the last bridge to the first. These are called - * before calling the encoder's commit op. - * - * 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; - } -} -EXPORT_SYMBOL(drm_bridge_chain_pre_enable); - -/** - * drm_bridge_chain_enable - enables all bridges in the encoder chain - * @bridge: bridge control structure - * - * Calls &drm_bridge_funcs.enable op for all the bridges in the encoder - * chain, starting from the first bridge to the last. These are called - * after completing the encoder's commit op. - * - * Note that the bridge passed should be the one closest to the encoder - */ -void drm_bridge_chain_enable(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->enable) - bridge->funcs->enable(bridge); - } -} -EXPORT_SYMBOL(drm_bridge_chain_enable); - -/** * drm_atomic_bridge_chain_disable - disables all bridges in the encoder chain * @bridge: bridge control structure * @old_state: old atomic state @@ -691,6 +620,25 @@ void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge, } 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 @@ -702,36 +650,86 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_disable); * 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_prev_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_atomic_bridge_chain_post_disable(struct drm_bridge *bridge, 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; + + if (!list_is_last(&bridge->chain_node, &encoder->bridge_chain)) { + next = list_next_entry(bridge, chain_node); + + if (next->pre_enable_prev_first) { + /* next bridge had requested that prev + * was enabled first, so disabled last + */ + limit = next; + + /* Find the next bridge that has NOT requested + * prev to be enabled first / disabled last + */ + list_for_each_entry_from(next, &encoder->bridge_chain, + chain_node) { + if (next->pre_enable_prev_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); + } + } + } - old_bridge_state = - drm_atomic_get_old_bridge_state(old_state, - bridge); - if (WARN_ON(!old_bridge_state)) - return; + drm_atomic_bridge_call_post_disable(bridge, old_state); - bridge->funcs->atomic_post_disable(bridge, - old_bridge_state); - } else if (bridge->funcs->post_disable) { - bridge->funcs->post_disable(bridge); - } + if (limit) + /* Jump all bridges that we have already post_disabled */ + 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 @@ -743,32 +741,60 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable); * 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_prev_first, then the pre_enable for the + * prev 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; + if (iter->pre_enable_prev_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_prev_first) { + /* Found first bridge that does NOT + * request prev 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 prev bridge pre_enable + * in order. + */ + if (next == iter) + /* At the first bridge to request prev + * bridges called first. + */ + break; + + drm_atomic_bridge_call_pre_enable(next, old_state); + } + } - old_bridge_state = - drm_atomic_get_old_bridge_state(old_state, - iter); - if (WARN_ON(!old_bridge_state)) - return; + drm_atomic_bridge_call_pre_enable(iter, old_state); - iter->funcs->atomic_pre_enable(iter, old_bridge_state); - } else if (iter->funcs->pre_enable) { - iter->funcs->pre_enable(iter); - } + if (iter->pre_enable_prev_first) + /* Jump all bridges that we have already pre_enabled */ + iter = limit; if (iter == bridge) break; |