Skip to content

Commit

Permalink
drm/i915: Add support for starting FRL training for HDMI2.1 via PCON
Browse files Browse the repository at this point in the history
This patch adds functions to start FRL training for an HDMI2.1 sink,
connected via a PCON as a DP branch device.
This patch also adds a new structure for storing frl training related
data, when FRL training is completed.

v2: As suggested by Uma Shankar:
-renamed couple of variables for better clarity
-tweaked the macros used for correct semantics for true/false
-fixed other styling issues.

v3: Completed the TODO for condition for going to FRL mode.
Modified the condition to determine the required FRL b/w
based only on the Pcon and Sink's max FRL values.
Moved the frl structure initialization to intel_dp_init_connector().

v4: Fixed typo in initialization of frl structure.

v5: Always use FRL if its possible, instead of enabling only for
higher modes as done in v3.

Signed-off-by: Ankit Nautiyal <ankit.k.nautiyal@intel.com>
Reviewed-by: Uma Shankar <uma.shankar@intel.com> (v2)
[Jani: Fixed checkpatch BRACES, CONSTANT_COMPARISON.]
Signed-off-by: Jani Nikula <jani.nikula@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201218103723.30844-10-ankit.k.nautiyal@intel.com
  • Loading branch information
Ankit Nautiyal authored and Jani Nikula committed Dec 22, 2020
1 parent 2f78347 commit ced42f2
Show file tree
Hide file tree
Showing 3 changed files with 160 additions and 0 deletions.
7 changes: 7 additions & 0 deletions drivers/gpu/drm/i915/display/intel_display_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -1321,6 +1321,11 @@ struct intel_dp_compliance {
u8 test_lane_count;
};

struct intel_dp_pcon_frl {
bool is_trained;
int trained_rate_gbps;
};

struct intel_dp {
i915_reg_t output_reg;
u32 DP;
Expand Down Expand Up @@ -1442,6 +1447,8 @@ struct intel_dp {

bool hobl_failed;
bool hobl_active;

struct intel_dp_pcon_frl frl;
};

enum lspcon_vendor {
Expand Down
151 changes: 151 additions & 0 deletions drivers/gpu/drm/i915/display/intel_dp.c
Original file line number Diff line number Diff line change
Expand Up @@ -3879,6 +3879,8 @@ static void intel_disable_dp(struct intel_atomic_state *state,
intel_edp_backlight_off(old_conn_state);
intel_dp_set_power(intel_dp, DP_SET_POWER_D3);
intel_edp_panel_off(intel_dp);
intel_dp->frl.is_trained = false;
intel_dp->frl.trained_rate_gbps = 0;
}

static void g4x_disable_dp(struct intel_atomic_state *state,
Expand Down Expand Up @@ -3974,6 +3976,152 @@ cpt_set_link_train(struct intel_dp *intel_dp,
intel_de_posting_read(dev_priv, intel_dp->output_reg);
}

static int intel_dp_pcon_get_frl_mask(u8 frl_bw_mask)
{
int bw_gbps[] = {9, 18, 24, 32, 40, 48};
int i;

for (i = ARRAY_SIZE(bw_gbps) - 1; i >= 0; i--) {
if (frl_bw_mask & (1 << i))
return bw_gbps[i];
}
return 0;
}

static int intel_dp_pcon_set_frl_mask(int max_frl)
{
switch (max_frl) {
case 48:
return DP_PCON_FRL_BW_MASK_48GBPS;
case 40:
return DP_PCON_FRL_BW_MASK_40GBPS;
case 32:
return DP_PCON_FRL_BW_MASK_32GBPS;
case 24:
return DP_PCON_FRL_BW_MASK_24GBPS;
case 18:
return DP_PCON_FRL_BW_MASK_18GBPS;
case 9:
return DP_PCON_FRL_BW_MASK_9GBPS;
}

return 0;
}

static int intel_dp_hdmi_sink_max_frl(struct intel_dp *intel_dp)
{
struct intel_connector *intel_connector = intel_dp->attached_connector;
struct drm_connector *connector = &intel_connector->base;

return (connector->display_info.hdmi.max_frl_rate_per_lane *
connector->display_info.hdmi.max_lanes);
}

static int intel_dp_pcon_start_frl_training(struct intel_dp *intel_dp)
{
#define PCON_EXTENDED_TRAIN_MODE (1 > 0)
#define PCON_CONCURRENT_MODE (1 > 0)
#define PCON_SEQUENTIAL_MODE !PCON_CONCURRENT_MODE
#define PCON_NORMAL_TRAIN_MODE !PCON_EXTENDED_TRAIN_MODE
#define TIMEOUT_FRL_READY_MS 500
#define TIMEOUT_HDMI_LINK_ACTIVE_MS 1000

struct drm_i915_private *i915 = dp_to_i915(intel_dp);
int max_frl_bw, max_pcon_frl_bw, max_edid_frl_bw, ret;
u8 max_frl_bw_mask = 0, frl_trained_mask;
bool is_active;

ret = drm_dp_pcon_reset_frl_config(&intel_dp->aux);
if (ret < 0)
return ret;

max_pcon_frl_bw = intel_dp->dfp.pcon_max_frl_bw;
drm_dbg(&i915->drm, "PCON max rate = %d Gbps\n", max_pcon_frl_bw);

max_edid_frl_bw = intel_dp_hdmi_sink_max_frl(intel_dp);
drm_dbg(&i915->drm, "Sink max rate from EDID = %d Gbps\n", max_edid_frl_bw);

max_frl_bw = min(max_edid_frl_bw, max_pcon_frl_bw);

if (max_frl_bw <= 0)
return -EINVAL;

ret = drm_dp_pcon_frl_prepare(&intel_dp->aux, false);
if (ret < 0)
return ret;
/* Wait for PCON to be FRL Ready */
wait_for(is_active = drm_dp_pcon_is_frl_ready(&intel_dp->aux) == true, TIMEOUT_FRL_READY_MS);

if (!is_active)
return -ETIMEDOUT;

max_frl_bw_mask = intel_dp_pcon_set_frl_mask(max_frl_bw);
ret = drm_dp_pcon_frl_configure_1(&intel_dp->aux, max_frl_bw, PCON_SEQUENTIAL_MODE);
if (ret < 0)
return ret;
ret = drm_dp_pcon_frl_configure_2(&intel_dp->aux, max_frl_bw_mask, PCON_NORMAL_TRAIN_MODE);
if (ret < 0)
return ret;
ret = drm_dp_pcon_frl_enable(&intel_dp->aux);
if (ret < 0)
return ret;
/*
* Wait for FRL to be completed
* Check if the HDMI Link is up and active.
*/
wait_for(is_active = drm_dp_pcon_hdmi_link_active(&intel_dp->aux) == true, TIMEOUT_HDMI_LINK_ACTIVE_MS);

if (!is_active)
return -ETIMEDOUT;

/* Verify HDMI Link configuration shows FRL Mode */
if (drm_dp_pcon_hdmi_link_mode(&intel_dp->aux, &frl_trained_mask) !=
DP_PCON_HDMI_MODE_FRL) {
drm_dbg(&i915->drm, "HDMI couldn't be trained in FRL Mode\n");
return -EINVAL;
}
drm_dbg(&i915->drm, "MAX_FRL_MASK = %u, FRL_TRAINED_MASK = %u\n", max_frl_bw_mask, frl_trained_mask);

intel_dp->frl.trained_rate_gbps = intel_dp_pcon_get_frl_mask(frl_trained_mask);
intel_dp->frl.is_trained = true;
drm_dbg(&i915->drm, "FRL trained with : %d Gbps\n", intel_dp->frl.trained_rate_gbps);

return 0;
}

static bool intel_dp_is_hdmi_2_1_sink(struct intel_dp *intel_dp)
{
if (drm_dp_is_branch(intel_dp->dpcd) &&
intel_dp->has_hdmi_sink &&
intel_dp_hdmi_sink_max_frl(intel_dp) > 0)
return true;

return false;
}

void intel_dp_check_frl_training(struct intel_dp *intel_dp)
{
struct drm_i915_private *dev_priv = dp_to_i915(intel_dp);

/* Always go for FRL training if supported */
if (!intel_dp_is_hdmi_2_1_sink(intel_dp) ||
intel_dp->frl.is_trained)
return;

if (intel_dp_pcon_start_frl_training(intel_dp) < 0) {
int ret, mode;

drm_dbg(&dev_priv->drm, "Couldnt set FRL mode, continuing with TMDS mode\n");
ret = drm_dp_pcon_reset_frl_config(&intel_dp->aux);
mode = drm_dp_pcon_hdmi_link_mode(&intel_dp->aux, NULL);

if (ret < 0 || mode != DP_PCON_HDMI_MODE_TMDS)
drm_dbg(&dev_priv->drm, "Issue with PCON, cannot set TMDS mode\n");
} else {
drm_dbg(&dev_priv->drm, "FRL training Completed\n");
}
}

static void
g4x_set_link_train(struct intel_dp *intel_dp,
const struct intel_crtc_state *crtc_state,
Expand Down Expand Up @@ -8172,6 +8320,9 @@ intel_dp_init_connector(struct intel_digital_port *dig_port,
(temp & ~0xf) | 0xd);
}

intel_dp->frl.is_trained = false;
intel_dp->frl.trained_rate_gbps = 0;

return true;

fail:
Expand Down
2 changes: 2 additions & 0 deletions drivers/gpu/drm/i915/display/intel_dp.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,4 +144,6 @@ bool intel_dp_initial_fastset_check(struct intel_encoder *encoder,
void intel_dp_sync_state(struct intel_encoder *encoder,
const struct intel_crtc_state *crtc_state);

void intel_dp_check_frl_training(struct intel_dp *intel_dp);

#endif /* __INTEL_DP_H__ */

0 comments on commit ced42f2

Please sign in to comment.