summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/i915/display/intel_tc.c
diff options
context:
space:
mode:
authorImre Deak <imre.deak@intel.com>2019-06-28 17:36:32 +0300
committerImre Deak <imre.deak@intel.com>2019-07-01 15:02:34 +0300
commit24a7bfe0c2d7aec06956d48808cdfe2756f618ad (patch)
treead4023c08bd5d50eb94b9676bd12c66ade539a3e /drivers/gpu/drm/i915/display/intel_tc.c
parenteea72c4c2161269a9046184607b71c5cfaabe477 (diff)
drm/i915: Keep the TypeC port mode fixed when the port is active
The TypeC port mode needs to stay fixed whenever the port is active. Do that by introducing a tc_link_refcount to account for active ports, avoiding changing the port mode if a reference is held. During the modeset commit phase we also have to reset the port mode and update the active PLL reflecting the new port mode. We can do this only once the port and its old PLL has been already disabled. Add the new encoder update_prepare/complete hooks that are called around the whole enabling sequence. The TypeC specific hooks of these will reset the port mode, update the active PLL if the port will be active and ensure that the port mode will stay fixed for the duration of the whole enabling sequence by holding a tc_link_refcount. During the port enabling, the pre_pll_enable/post_pll_disable hooks will take/release a tc_link_refcount to ensure the port mode stays fixed while the port is active. Changing the port mode should also be avoided during connector detection and AUX transfers if the port is active, we'll do that by checking the port's tc_link_refcount. When resetting the port mode we also have to take into account the maximum lanes provided by the FIA. It's guaranteed to be 4 in TBT-alt and legacy modes, but there may be less lanes available in DP-alt mode, in which case we have to fall back to TBT-alt mode. While at it also update icl_tc_phy_connect()'s code comment, reflecting the current way of switching the port mode. v2: - Add the update_prepare/complete hooks to the encoder instead of the connector. (Ville) - Simplify intel_connector_needs_modeset() by removing redundant if. (Ville) v3: - Fix sparse warning, marking static functions as such. v4: - Rebase on drm-tip. Cc: Ville Syrjälä <ville.syrjala@linux.intel.com> Cc: Daniel Vetter <daniel.vetter@ffwll.ch> Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com> Signed-off-by: Imre Deak <imre.deak@intel.com> Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com> Link: https://patchwork.freedesktop.org/patch/msgid/20190628143635.22066-21-imre.deak@intel.com
Diffstat (limited to 'drivers/gpu/drm/i915/display/intel_tc.c')
-rw-r--r--drivers/gpu/drm/i915/display/intel_tc.c94
1 files changed, 70 insertions, 24 deletions
diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c
index 58a228ba7696..ba6492bc0ee0 100644
--- a/drivers/gpu/drm/i915/display/intel_tc.c
+++ b/drivers/gpu/drm/i915/display/intel_tc.c
@@ -188,19 +188,12 @@ static bool icl_tc_phy_is_in_safe_mode(struct intel_digital_port *dig_port)
* display, USB, etc. As a result, handshaking through FIA is required around
* connect and disconnect to cleanly transfer ownership with the controller and
* set the type-C power state.
- *
- * We could opt to only do the connect flow when we actually try to use the AUX
- * channels or do a modeset, then immediately run the disconnect flow after
- * usage, but there are some implications on this for a dynamic environment:
- * things may go away or change behind our backs. So for now our driver is
- * always trying to acquire ownership of the controller as soon as it gets an
- * interrupt (or polls state and sees a port is connected) and only gives it
- * back when it sees a disconnect. Implementation of a more fine-grained model
- * will require a lot of coordination with user space and thorough testing for
- * the extra possible cases.
*/
-static void icl_tc_phy_connect(struct intel_digital_port *dig_port)
+static void icl_tc_phy_connect(struct intel_digital_port *dig_port,
+ int required_lanes)
{
+ int max_lanes;
+
if (!icl_tc_phy_status_complete(dig_port)) {
DRM_DEBUG_KMS("Port %s: PHY not ready\n",
dig_port->tc_port_name);
@@ -211,8 +204,9 @@ static void icl_tc_phy_connect(struct intel_digital_port *dig_port)
!WARN_ON(dig_port->tc_legacy_port))
goto out_set_tbt_alt_mode;
+ max_lanes = intel_tc_port_fia_max_lane_count(dig_port);
if (dig_port->tc_legacy_port) {
- WARN_ON(intel_tc_port_fia_max_lane_count(dig_port) != 4);
+ WARN_ON(max_lanes != 4);
dig_port->tc_mode = TC_PORT_LEGACY;
return;
@@ -228,6 +222,13 @@ static void icl_tc_phy_connect(struct intel_digital_port *dig_port)
goto out_set_safe_mode;
}
+ if (max_lanes < required_lanes) {
+ DRM_DEBUG_KMS("Port %s: PHY max lanes %d < required lanes %d\n",
+ dig_port->tc_port_name,
+ max_lanes, required_lanes);
+ goto out_set_safe_mode;
+ }
+
dig_port->tc_mode = TC_PORT_DP_ALT;
return;
@@ -311,7 +312,8 @@ intel_tc_port_get_target_mode(struct intel_digital_port *dig_port)
TC_PORT_TBT_ALT;
}
-static void intel_tc_port_reset_mode(struct intel_digital_port *dig_port)
+static void intel_tc_port_reset_mode(struct intel_digital_port *dig_port,
+ int required_lanes)
{
struct drm_i915_private *dev_priv = to_i915(dig_port->base.base.dev);
enum tc_port_mode old_tc_mode = dig_port->tc_mode;
@@ -319,7 +321,7 @@ static void intel_tc_port_reset_mode(struct intel_digital_port *dig_port)
intel_display_power_flush_work(dev_priv);
icl_tc_phy_disconnect(dig_port);
- icl_tc_phy_connect(dig_port);
+ icl_tc_phy_connect(dig_port, required_lanes);
DRM_DEBUG_KMS("Port %s: TC port mode reset (%s -> %s)\n",
dig_port->tc_port_name,
@@ -327,6 +329,14 @@ static void intel_tc_port_reset_mode(struct intel_digital_port *dig_port)
tc_port_mode_name(dig_port->tc_mode));
}
+static void
+intel_tc_port_link_init_refcount(struct intel_digital_port *dig_port,
+ int refcount)
+{
+ WARN_ON(dig_port->tc_link_refcount);
+ dig_port->tc_link_refcount = refcount;
+}
+
void intel_tc_port_sanitize(struct intel_digital_port *dig_port)
{
struct intel_encoder *encoder = &dig_port->base;
@@ -344,11 +354,13 @@ void intel_tc_port_sanitize(struct intel_digital_port *dig_port)
if (!icl_tc_phy_is_connected(dig_port))
DRM_DEBUG_KMS("Port %s: PHY disconnected with %d active link(s)\n",
dig_port->tc_port_name, active_links);
+ intel_tc_port_link_init_refcount(dig_port, active_links);
+
goto out;
}
if (dig_port->tc_legacy_port)
- icl_tc_phy_connect(dig_port);
+ icl_tc_phy_connect(dig_port, 1);
out:
DRM_DEBUG_KMS("Port %s: sanitize mode (%s)\n",
@@ -377,27 +389,60 @@ bool intel_tc_port_connected(struct intel_digital_port *dig_port)
{
bool is_connected;
- mutex_lock(&dig_port->tc_lock);
-
- if (intel_tc_port_needs_reset(dig_port))
- intel_tc_port_reset_mode(dig_port);
-
+ intel_tc_port_lock(dig_port);
is_connected = tc_port_live_status_mask(dig_port) &
BIT(dig_port->tc_mode);
-
- mutex_unlock(&dig_port->tc_lock);
+ intel_tc_port_unlock(dig_port);
return is_connected;
}
-void intel_tc_port_lock(struct intel_digital_port *dig_port)
+static void __intel_tc_port_lock(struct intel_digital_port *dig_port,
+ int required_lanes)
{
+ struct drm_i915_private *dev_priv = to_i915(dig_port->base.base.dev);
+ intel_wakeref_t wakeref;
+
+ wakeref = intel_display_power_get(dev_priv, POWER_DOMAIN_DISPLAY_CORE);
+
mutex_lock(&dig_port->tc_lock);
- /* TODO: reset the TypeC port mode if needed */
+
+ if (!dig_port->tc_link_refcount &&
+ intel_tc_port_needs_reset(dig_port))
+ intel_tc_port_reset_mode(dig_port, required_lanes);
+
+ WARN_ON(dig_port->tc_lock_wakeref);
+ dig_port->tc_lock_wakeref = wakeref;
+}
+
+void intel_tc_port_lock(struct intel_digital_port *dig_port)
+{
+ __intel_tc_port_lock(dig_port, 1);
}
void intel_tc_port_unlock(struct intel_digital_port *dig_port)
{
+ struct drm_i915_private *dev_priv = to_i915(dig_port->base.base.dev);
+ intel_wakeref_t wakeref = fetch_and_zero(&dig_port->tc_lock_wakeref);
+
+ mutex_unlock(&dig_port->tc_lock);
+
+ intel_display_power_put_async(dev_priv, POWER_DOMAIN_DISPLAY_CORE,
+ wakeref);
+}
+
+void intel_tc_port_get_link(struct intel_digital_port *dig_port,
+ int required_lanes)
+{
+ __intel_tc_port_lock(dig_port, required_lanes);
+ dig_port->tc_link_refcount++;
+ intel_tc_port_unlock(dig_port);
+}
+
+void intel_tc_port_put_link(struct intel_digital_port *dig_port)
+{
+ mutex_lock(&dig_port->tc_lock);
+ dig_port->tc_link_refcount--;
mutex_unlock(&dig_port->tc_lock);
}
@@ -415,4 +460,5 @@ void intel_tc_port_init(struct intel_digital_port *dig_port, bool is_legacy)
mutex_init(&dig_port->tc_lock);
dig_port->tc_legacy_port = is_legacy;
+ dig_port->tc_link_refcount = 0;
}