Skip to content

Commit

Permalink
drm/i915: Keep the TypeC port mode fixed when the port is active
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Imre Deak committed Jul 1, 2019
1 parent eea72c4 commit 24a7bfe
Show file tree
Hide file tree
Showing 7 changed files with 247 additions and 30 deletions.
41 changes: 37 additions & 4 deletions drivers/gpu/drm/i915/display/intel_ddi.c
Original file line number Diff line number Diff line change
Expand Up @@ -3623,17 +3623,44 @@ static void intel_ddi_set_fia_lane_count(struct intel_encoder *encoder,
I915_WRITE(PORT_TX_DFLEXDPMLE1, val);
}

static void
intel_ddi_update_prepare(struct intel_atomic_state *state,
struct intel_encoder *encoder,
struct intel_crtc *crtc)
{
struct intel_crtc_state *crtc_state =
crtc ? intel_atomic_get_new_crtc_state(state, crtc) : NULL;
int required_lanes = crtc_state ? crtc_state->lane_count : 1;

WARN_ON(crtc && crtc->active);

intel_tc_port_get_link(enc_to_dig_port(&encoder->base), required_lanes);
if (crtc_state && crtc_state->base.active)
intel_update_active_dpll(state, crtc, encoder);
}

static void
intel_ddi_update_complete(struct intel_atomic_state *state,
struct intel_encoder *encoder,
struct intel_crtc *crtc)
{
intel_tc_port_put_link(enc_to_dig_port(&encoder->base));
}

static void
intel_ddi_pre_pll_enable(struct intel_encoder *encoder,
const struct intel_crtc_state *crtc_state,
const struct drm_connector_state *conn_state)
{
struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
struct intel_digital_port *dig_port = enc_to_dig_port(&encoder->base);
bool is_tc_port = intel_port_is_tc(dev_priv, encoder->port);
enum port port = encoder->port;

if (intel_crtc_has_dp_encoder(crtc_state) ||
intel_port_is_tc(dev_priv, encoder->port))
if (is_tc_port)
intel_tc_port_get_link(dig_port, crtc_state->lane_count);

if (intel_crtc_has_dp_encoder(crtc_state) || is_tc_port)
intel_display_power_get(dev_priv,
intel_ddi_main_link_aux_domain(dig_port));

Expand All @@ -3658,11 +3685,14 @@ intel_ddi_post_pll_disable(struct intel_encoder *encoder,
{
struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
struct intel_digital_port *dig_port = enc_to_dig_port(&encoder->base);
bool is_tc_port = intel_port_is_tc(dev_priv, encoder->port);

if (intel_crtc_has_dp_encoder(crtc_state) ||
intel_port_is_tc(dev_priv, encoder->port))
if (intel_crtc_has_dp_encoder(crtc_state) || is_tc_port)
intel_display_power_put_unchecked(dev_priv,
intel_ddi_main_link_aux_domain(dig_port));

if (is_tc_port)
intel_tc_port_put_link(dig_port);
}

static void intel_ddi_prepare_link_retrain(struct intel_dp *intel_dp)
Expand Down Expand Up @@ -4256,6 +4286,9 @@ void intel_ddi_init(struct drm_i915_private *dev_priv, enum port port)
!port_info->supports_tbt;

intel_tc_port_init(intel_dig_port, is_legacy);

intel_encoder->update_prepare = intel_ddi_update_prepare;
intel_encoder->update_complete = intel_ddi_update_complete;
}

switch (port) {
Expand Down
100 changes: 99 additions & 1 deletion drivers/gpu/drm/i915/display/intel_display.c
Original file line number Diff line number Diff line change
Expand Up @@ -6037,6 +6037,98 @@ static void intel_crtc_disable_planes(struct intel_atomic_state *state,
intel_frontbuffer_flip(dev_priv, fb_bits);
}

/*
* intel_connector_primary_encoder - get the primary encoder for a connector
* @connector: connector for which to return the encoder
*
* Returns the primary encoder for a connector. There is a 1:1 mapping from
* all connectors to their encoder, except for DP-MST connectors which have
* both a virtual and a primary encoder. These DP-MST primary encoders can be
* pointed to by as many DP-MST connectors as there are pipes.
*/
static struct intel_encoder *
intel_connector_primary_encoder(struct intel_connector *connector)
{
struct intel_encoder *encoder;

if (connector->mst_port)
return &dp_to_dig_port(connector->mst_port)->base;

encoder = intel_attached_encoder(&connector->base);
WARN_ON(!encoder);

return encoder;
}

static bool
intel_connector_needs_modeset(struct intel_atomic_state *state,
const struct drm_connector_state *old_conn_state,
const struct drm_connector_state *new_conn_state)
{
struct intel_crtc *old_crtc = old_conn_state->crtc ?
to_intel_crtc(old_conn_state->crtc) : NULL;
struct intel_crtc *new_crtc = new_conn_state->crtc ?
to_intel_crtc(new_conn_state->crtc) : NULL;

return new_crtc != old_crtc ||
(new_crtc &&
needs_modeset(intel_atomic_get_new_crtc_state(state, new_crtc)));
}

static void intel_encoders_update_prepare(struct intel_atomic_state *state)
{
struct drm_connector_state *old_conn_state;
struct drm_connector_state *new_conn_state;
struct drm_connector *conn;
int i;

for_each_oldnew_connector_in_state(&state->base, conn,
old_conn_state, new_conn_state, i) {
struct intel_encoder *encoder;
struct intel_crtc *crtc;

if (!intel_connector_needs_modeset(state,
old_conn_state,
new_conn_state))
continue;

encoder = intel_connector_primary_encoder(to_intel_connector(conn));
if (!encoder->update_prepare)
continue;

crtc = new_conn_state->crtc ?
to_intel_crtc(new_conn_state->crtc) : NULL;
encoder->update_prepare(state, encoder, crtc);
}
}

static void intel_encoders_update_complete(struct intel_atomic_state *state)
{
struct drm_connector_state *old_conn_state;
struct drm_connector_state *new_conn_state;
struct drm_connector *conn;
int i;

for_each_oldnew_connector_in_state(&state->base, conn,
old_conn_state, new_conn_state, i) {
struct intel_encoder *encoder;
struct intel_crtc *crtc;

if (!intel_connector_needs_modeset(state,
old_conn_state,
new_conn_state))
continue;

encoder = intel_connector_primary_encoder(to_intel_connector(conn));
if (!encoder->update_complete)
continue;

crtc = new_conn_state->crtc ?
to_intel_crtc(new_conn_state->crtc) : NULL;
encoder->update_complete(state, encoder, crtc);
}
}

static void intel_encoders_pre_pll_enable(struct intel_crtc *crtc,
struct intel_crtc_state *crtc_state,
struct intel_atomic_state *state)
Expand Down Expand Up @@ -13859,14 +13951,20 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state)
}
}

if (state->modeset)
intel_encoders_update_prepare(state);

/* Now enable the clocks, plane, pipe, and connectors that we set up. */
dev_priv->display.update_crtcs(state);

if (state->modeset)
if (state->modeset) {
intel_encoders_update_complete(state);

intel_set_cdclk_post_plane_update(dev_priv,
&state->cdclk.actual,
&dev_priv->cdclk.actual,
state->cdclk.pipe);
}

/* FIXME: We should call drm_atomic_helper_commit_hw_done() here
* already, but still need the state for the delayed optimization. To
Expand Down
28 changes: 27 additions & 1 deletion drivers/gpu/drm/i915/display/intel_dpll_mgr.c
Original file line number Diff line number Diff line change
Expand Up @@ -1939,7 +1939,9 @@ struct intel_dpll_mgr {
struct intel_encoder *encoder);
void (*put_dplls)(struct intel_atomic_state *state,
struct intel_crtc *crtc);

void (*update_active_dpll)(struct intel_atomic_state *state,
struct intel_crtc *crtc,
struct intel_encoder *encoder);
void (*dump_hw_state)(struct drm_i915_private *dev_priv,
const struct intel_dpll_hw_state *hw_state);
};
Expand Down Expand Up @@ -3402,6 +3404,7 @@ static const struct intel_dpll_mgr icl_pll_mgr = {
.dpll_info = icl_plls,
.get_dplls = icl_get_dplls,
.put_dplls = icl_put_dplls,
.update_active_dpll = icl_update_active_dpll,
.dump_hw_state = icl_dump_hw_state,
};

Expand Down Expand Up @@ -3526,6 +3529,29 @@ void intel_release_shared_dplls(struct intel_atomic_state *state,
dpll_mgr->put_dplls(state, crtc);
}

/**
* intel_update_active_dpll - update the active DPLL for a CRTC/encoder
* @state: atomic state
* @crtc: the CRTC for which to update the active DPLL
* @encoder: encoder determining the type of port DPLL
*
* Update the active DPLL for the given @crtc/@encoder in @crtc's atomic state,
* from the port DPLLs reserved previously by intel_reserve_shared_dplls(). The
* DPLL selected will be based on the current mode of the encoder's port.
*/
void intel_update_active_dpll(struct intel_atomic_state *state,
struct intel_crtc *crtc,
struct intel_encoder *encoder)
{
struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
const struct intel_dpll_mgr *dpll_mgr = dev_priv->dpll_mgr;

if (WARN_ON(!dpll_mgr))
return;

dpll_mgr->update_active_dpll(state, crtc, encoder);
}

/**
* intel_shared_dpll_dump_hw_state - write hw_state to dmesg
* @dev_priv: i915 drm device
Expand Down
3 changes: 3 additions & 0 deletions drivers/gpu/drm/i915/display/intel_dpll_mgr.h
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,9 @@ void intel_release_shared_dplls(struct intel_atomic_state *state,
struct intel_crtc *crtc);
void icl_set_active_port_dpll(struct intel_crtc_state *crtc_state,
enum icl_port_dpll_id port_dpll_id);
void intel_update_active_dpll(struct intel_atomic_state *state,
struct intel_crtc *crtc,
struct intel_encoder *encoder);
void intel_prepare_shared_dpll(const struct intel_crtc_state *crtc_state);
void intel_enable_shared_dpll(const struct intel_crtc_state *crtc_state);
void intel_disable_shared_dpll(const struct intel_crtc_state *crtc_state);
Expand Down
Loading

0 comments on commit 24a7bfe

Please sign in to comment.