_PORT_REF_DW8_A)
/* BXT PHY PCS registers */
+#define _PORT_PCS_DW10_LN01_A 0x162428
+#define _PORT_PCS_DW10_LN01_B 0x6C428
+#define _PORT_PCS_DW10_LN01_C 0x6C828
+#define _PORT_PCS_DW10_GRP_A 0x162C28
+#define _PORT_PCS_DW10_GRP_B 0x6CC28
+#define _PORT_PCS_DW10_GRP_C 0x6CE28
+#define BXT_PORT_PCS_DW10_LN01(port) _PORT3(port, _PORT_PCS_DW10_LN01_A, \
+ _PORT_PCS_DW10_LN01_B, \
+ _PORT_PCS_DW10_LN01_C)
+#define BXT_PORT_PCS_DW10_GRP(port) _PORT3(port, _PORT_PCS_DW10_GRP_A, \
+ _PORT_PCS_DW10_GRP_B, \
+ _PORT_PCS_DW10_GRP_C)
+#define TX2_SWING_CALC_INIT (1 << 31)
+#define TX1_SWING_CALC_INIT (1 << 30)
+
#define _PORT_PCS_DW12_LN01_A 0x162430
#define _PORT_PCS_DW12_LN01_B 0x6C430
#define _PORT_PCS_DW12_LN01_C 0x6C830
#define _BXT_LANE_OFFSET(lane) (((lane) >> 1) * 0x200 + \
((lane) & 1) * 0x80)
+#define _PORT_TX_DW2_LN0_A 0x162508
+#define _PORT_TX_DW2_LN0_B 0x6C508
+#define _PORT_TX_DW2_LN0_C 0x6C908
+#define _PORT_TX_DW2_GRP_A 0x162D08
+#define _PORT_TX_DW2_GRP_B 0x6CD08
+#define _PORT_TX_DW2_GRP_C 0x6CF08
+#define BXT_PORT_TX_DW2_GRP(port) _PORT3(port, _PORT_TX_DW2_GRP_A, \
+ _PORT_TX_DW2_GRP_B, \
+ _PORT_TX_DW2_GRP_C)
+#define BXT_PORT_TX_DW2_LN0(port) _PORT3(port, _PORT_TX_DW2_LN0_A, \
+ _PORT_TX_DW2_LN0_B, \
+ _PORT_TX_DW2_LN0_C)
+#define MARGIN_000_SHIFT 16
+#define MARGIN_000 (0xFF << MARGIN_000_SHIFT)
+#define UNIQ_TRANS_SCALE_SHIFT 8
+#define UNIQ_TRANS_SCALE (0xFF << UNIQ_TRANS_SCALE_SHIFT)
+
+#define _PORT_TX_DW3_LN0_A 0x16250C
+#define _PORT_TX_DW3_LN0_B 0x6C50C
+#define _PORT_TX_DW3_LN0_C 0x6C90C
+#define _PORT_TX_DW3_GRP_A 0x162D0C
+#define _PORT_TX_DW3_GRP_B 0x6CD0C
+#define _PORT_TX_DW3_GRP_C 0x6CF0C
+#define BXT_PORT_TX_DW3_GRP(port) _PORT3(port, _PORT_TX_DW3_GRP_A, \
+ _PORT_TX_DW3_GRP_B, \
+ _PORT_TX_DW3_GRP_C)
+#define BXT_PORT_TX_DW3_LN0(port) _PORT3(port, _PORT_TX_DW3_LN0_A, \
+ _PORT_TX_DW3_LN0_B, \
+ _PORT_TX_DW3_LN0_C)
+#define UNIQE_TRANGE_EN_METHOD (1 << 27)
+
+#define _PORT_TX_DW4_LN0_A 0x162510
+#define _PORT_TX_DW4_LN0_B 0x6C510
+#define _PORT_TX_DW4_LN0_C 0x6C910
+#define _PORT_TX_DW4_GRP_A 0x162D10
+#define _PORT_TX_DW4_GRP_B 0x6CD10
+#define _PORT_TX_DW4_GRP_C 0x6CF10
+#define BXT_PORT_TX_DW4_LN0(port) _PORT3(port, _PORT_TX_DW4_LN0_A, \
+ _PORT_TX_DW4_LN0_B, \
+ _PORT_TX_DW4_LN0_C)
+#define BXT_PORT_TX_DW4_GRP(port) _PORT3(port, _PORT_TX_DW4_GRP_A, \
+ _PORT_TX_DW4_GRP_B, \
+ _PORT_TX_DW4_GRP_C)
+#define DEEMPH_SHIFT 24
+#define DE_EMPHASIS (0xFF << DEEMPH_SHIFT)
+
#define _PORT_TX_DW14_LN0_A 0x162538
#define _PORT_TX_DW14_LN0_B 0x6C538
#define _PORT_TX_DW14_LN0_C 0x6C938
{ 0x00000018, 0x000000c7 },
};
+struct bxt_ddi_buf_trans {
+ u32 margin; /* swing value */
+ u32 scale; /* scale value */
+ u32 enable; /* scale enable */
+ u32 deemphasis;
+ bool default_index; /* true if the entry represents default value */
+};
+
+/* BSpec does not define separate vswing/pre-emphasis values for eDP.
+ * Using DP values for eDP as well.
+ */
+static const struct bxt_ddi_buf_trans bxt_ddi_translations_dp[] = {
+ /* Idx NT mV diff db */
+ { 52, 0, 0, 128, true }, /* 0: 400 0 */
+ { 78, 0, 0, 85, false }, /* 1: 400 3.5 */
+ { 104, 0, 0, 64, false }, /* 2: 400 6 */
+ { 154, 0, 0, 43, false }, /* 3: 400 9.5 */
+ { 77, 0, 0, 128, false }, /* 4: 600 0 */
+ { 116, 0, 0, 85, false }, /* 5: 600 3.5 */
+ { 154, 0, 0, 64, false }, /* 6: 600 6 */
+ { 102, 0, 0, 128, false }, /* 7: 800 0 */
+ { 154, 0, 0, 85, false }, /* 8: 800 3.5 */
+ { 154, 0x9A, 1, 128, false }, /* 9: 1200 0 */
+};
+
+/* BSpec has 2 recommended values - entries 0 and 8.
+ * Using the entry with higher vswing.
+ */
+static const struct bxt_ddi_buf_trans bxt_ddi_translations_hdmi[] = {
+ /* Idx NT mV diff db */
+ { 52, 0, 0, 128, false }, /* 0: 400 0 */
+ { 52, 0, 0, 85, false }, /* 1: 400 3.5 */
+ { 52, 0, 0, 64, false }, /* 2: 400 6 */
+ { 42, 0, 0, 43, false }, /* 3: 400 9.5 */
+ { 77, 0, 0, 128, false }, /* 4: 600 0 */
+ { 77, 0, 0, 85, false }, /* 5: 600 3.5 */
+ { 77, 0, 0, 64, false }, /* 6: 600 6 */
+ { 102, 0, 0, 128, false }, /* 7: 800 0 */
+ { 102, 0, 0, 85, false }, /* 8: 800 3.5 */
+ { 154, 0x9A, 1, 128, true }, /* 9: 1200 0 */
+};
+
enum port intel_ddi_get_encoder_port(struct intel_encoder *intel_encoder)
{
struct drm_encoder *encoder = &intel_encoder->base;
const struct ddi_buf_trans *ddi_translations_hdmi;
const struct ddi_buf_trans *ddi_translations;
- if (IS_SKYLAKE(dev)) {
+ if (IS_BROXTON(dev)) {
+ if (!intel_dig_port_supports_hdmi(intel_dig_port))
+ return;
+
+ /* Vswing programming for HDMI */
+ bxt_ddi_vswing_sequence(dev, hdmi_level, port,
+ INTEL_OUTPUT_HDMI);
+ return;
+ } else if (IS_SKYLAKE(dev)) {
ddi_translations_fdi = NULL;
ddi_translations_dp = skl_ddi_translations_dp;
n_dp_entries = ARRAY_SIZE(skl_ddi_translations_dp);
TRANS_CLK_SEL_DISABLED);
}
+void bxt_ddi_vswing_sequence(struct drm_device *dev, u32 level,
+ enum port port, int type)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ const struct bxt_ddi_buf_trans *ddi_translations;
+ u32 n_entries, i;
+ uint32_t val;
+
+ if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) {
+ n_entries = ARRAY_SIZE(bxt_ddi_translations_dp);
+ ddi_translations = bxt_ddi_translations_dp;
+ } else if (type == INTEL_OUTPUT_HDMI) {
+ n_entries = ARRAY_SIZE(bxt_ddi_translations_hdmi);
+ ddi_translations = bxt_ddi_translations_hdmi;
+ } else {
+ DRM_DEBUG_KMS("Vswing programming not done for encoder %d\n",
+ type);
+ return;
+ }
+
+ /* Check if default value has to be used */
+ if (level >= n_entries ||
+ (type == INTEL_OUTPUT_HDMI && level == HDMI_LEVEL_SHIFT_UNKNOWN)) {
+ for (i = 0; i < n_entries; i++) {
+ if (ddi_translations[i].default_index) {
+ level = i;
+ break;
+ }
+ }
+ }
+
+ /*
+ * While we write to the group register to program all lanes at once we
+ * can read only lane registers and we pick lanes 0/1 for that.
+ */
+ val = I915_READ(BXT_PORT_PCS_DW10_LN01(port));
+ val &= ~(TX2_SWING_CALC_INIT | TX1_SWING_CALC_INIT);
+ I915_WRITE(BXT_PORT_PCS_DW10_GRP(port), val);
+
+ val = I915_READ(BXT_PORT_TX_DW2_LN0(port));
+ val &= ~(MARGIN_000 | UNIQ_TRANS_SCALE);
+ val |= ddi_translations[level].margin << MARGIN_000_SHIFT |
+ ddi_translations[level].scale << UNIQ_TRANS_SCALE_SHIFT;
+ I915_WRITE(BXT_PORT_TX_DW2_GRP(port), val);
+
+ val = I915_READ(BXT_PORT_TX_DW3_LN0(port));
+ val &= ~UNIQE_TRANGE_EN_METHOD;
+ if (ddi_translations[level].enable)
+ val |= UNIQE_TRANGE_EN_METHOD;
+ I915_WRITE(BXT_PORT_TX_DW3_GRP(port), val);
+
+ val = I915_READ(BXT_PORT_TX_DW4_LN0(port));
+ val &= ~DE_EMPHASIS;
+ val |= ddi_translations[level].deemphasis << DEEMPH_SHIFT;
+ I915_WRITE(BXT_PORT_TX_DW4_GRP(port), val);
+
+ val = I915_READ(BXT_PORT_PCS_DW10_LN01(port));
+ val |= TX2_SWING_CALC_INIT | TX1_SWING_CALC_INIT;
+ I915_WRITE(BXT_PORT_PCS_DW10_GRP(port), val);
+}
+
static void intel_ddi_pre_enable(struct intel_encoder *intel_encoder)
{
struct drm_encoder *encoder = &intel_encoder->base;
struct intel_crtc *crtc = to_intel_crtc(encoder->crtc);
enum port port = intel_ddi_get_encoder_port(intel_encoder);
int type = intel_encoder->type;
+ int hdmi_level;
if (type == INTEL_OUTPUT_EDP) {
struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
} else if (type == INTEL_OUTPUT_HDMI) {
struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
+ if (IS_BROXTON(dev)) {
+ hdmi_level = dev_priv->vbt.
+ ddi_port_info[port].hdmi_level_shift;
+ bxt_ddi_vswing_sequence(dev, hdmi_level, port,
+ INTEL_OUTPUT_HDMI);
+ }
intel_hdmi->set_infoframes(encoder,
crtc->config->has_hdmi_sink,
&crtc->config->base.adjusted_mode);
}
}
+static void intel_bxt_signal_levels(struct intel_dp *intel_dp)
+{
+ struct intel_digital_port *dport = dp_to_dig_port(intel_dp);
+ enum port port = dport->port;
+ struct drm_device *dev = dport->base.base.dev;
+ struct intel_encoder *encoder = &dport->base;
+ uint8_t train_set = intel_dp->train_set[0];
+ uint32_t level = 0;
+
+ int signal_levels = train_set & (DP_TRAIN_VOLTAGE_SWING_MASK |
+ DP_TRAIN_PRE_EMPHASIS_MASK);
+ switch (signal_levels) {
+ default:
+ DRM_DEBUG_KMS("Unsupported voltage swing/pre-emph level\n");
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_0 | DP_TRAIN_PRE_EMPH_LEVEL_0:
+ level = 0;
+ break;
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_0 | DP_TRAIN_PRE_EMPH_LEVEL_1:
+ level = 1;
+ break;
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_0 | DP_TRAIN_PRE_EMPH_LEVEL_2:
+ level = 2;
+ break;
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_0 | DP_TRAIN_PRE_EMPH_LEVEL_3:
+ level = 3;
+ break;
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_1 | DP_TRAIN_PRE_EMPH_LEVEL_0:
+ level = 4;
+ break;
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_1 | DP_TRAIN_PRE_EMPH_LEVEL_1:
+ level = 5;
+ break;
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_1 | DP_TRAIN_PRE_EMPH_LEVEL_2:
+ level = 6;
+ break;
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_2 | DP_TRAIN_PRE_EMPH_LEVEL_0:
+ level = 7;
+ break;
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_2 | DP_TRAIN_PRE_EMPH_LEVEL_1:
+ level = 8;
+ break;
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_3 | DP_TRAIN_PRE_EMPH_LEVEL_0:
+ level = 9;
+ break;
+ }
+
+ bxt_ddi_vswing_sequence(dev, level, port, encoder->type);
+}
+
/* Properly updates "DP" with the correct signal levels. */
static void
intel_dp_set_signal_levels(struct intel_dp *intel_dp, uint32_t *DP)
uint32_t signal_levels, mask;
uint8_t train_set = intel_dp->train_set[0];
- if (IS_HASWELL(dev) || IS_BROADWELL(dev) || INTEL_INFO(dev)->gen >= 9) {
+ if (IS_BROXTON(dev)) {
+ signal_levels = 0;
+ intel_bxt_signal_levels(intel_dp);
+ mask = 0;
+ } else if (HAS_DDI(dev)) {
signal_levels = intel_hsw_signal_levels(train_set);
mask = DDI_BUF_EMP_MASK;
} else if (IS_CHERRYVIEW(dev)) {
mask = DP_VOLTAGE_MASK | DP_PRE_EMPHASIS_MASK;
}
- DRM_DEBUG_KMS("Using signal levels %08x\n", signal_levels);
+ if (mask)
+ DRM_DEBUG_KMS("Using signal levels %08x\n", signal_levels);
+
+ DRM_DEBUG_KMS("Using vswing level %d\n",
+ train_set & DP_TRAIN_VOLTAGE_SWING_MASK);
+ DRM_DEBUG_KMS("Using pre-emphasis level %d\n",
+ (train_set & DP_TRAIN_PRE_EMPHASIS_MASK) >>
+ DP_TRAIN_PRE_EMPHASIS_SHIFT);
*DP = (*DP & ~mask) | signal_levels;
}