Skip to content

Commit cf54ca8

Browse files
committed
drm/i915/cnl: Implement voltage swing sequence.
This is an important part of the DDI initalization as well as for changing the voltage during DisplayPort link training. This new sequence for Cannonlake is more like Broxton style but still with different registers, different table and different steps. v2: Do not write to DW4_GRP to avoid overwrite individual loadgen. Fix PORT_CL_DW5 SUS Clock Config set. v3: As previous platforms use only eDP table if low voltage was requested. v4: fix Werror:maybe uninitialized (Paulo) v5: Rebase on top of dw2_swing_sel changes on previous patches. v6: Using flexible SCALING_MODE_SEL(x). Cc: Manasi Navare <[email protected]> Cc: Ville Syrjälä <[email protected]> Signed-off-by: Rodrigo Vivi <[email protected]> Reviewed-by: Manasi Navare <[email protected]> Link: http://patchwork.freedesktop.org/patch/msgid/[email protected]
1 parent 83fb7ab commit cf54ca8

File tree

3 files changed

+177
-2
lines changed

3 files changed

+177
-2
lines changed

drivers/gpu/drm/i915/i915_reg.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1668,6 +1668,7 @@ enum skl_disp_power_wells {
16681668

16691669
#define CNL_PORT_CL1CM_DW5 _MMIO(0x162014)
16701670
#define CL_POWER_DOWN_ENABLE (1 << 4)
1671+
#define SUS_CLOCK_CONFIG (3 << 0)
16711672

16721673
#define _PORT_CL1CM_DW9_A 0x162024
16731674
#define _PORT_CL1CM_DW9_BC 0x6C024

drivers/gpu/drm/i915/intel_ddi.c

Lines changed: 175 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1720,6 +1720,173 @@ u8 intel_ddi_dp_voltage_max(struct intel_encoder *encoder)
17201720
DP_TRAIN_VOLTAGE_SWING_MASK;
17211721
}
17221722

1723+
static const struct cnl_ddi_buf_trans *
1724+
cnl_get_buf_trans_hdmi(struct drm_i915_private *dev_priv,
1725+
u32 voltage, int *n_entries)
1726+
{
1727+
if (voltage == VOLTAGE_INFO_0_85V) {
1728+
*n_entries = ARRAY_SIZE(cnl_ddi_translations_hdmi_0_85V);
1729+
return cnl_ddi_translations_hdmi_0_85V;
1730+
} else if (voltage == VOLTAGE_INFO_0_95V) {
1731+
*n_entries = ARRAY_SIZE(cnl_ddi_translations_hdmi_0_95V);
1732+
return cnl_ddi_translations_hdmi_0_95V;
1733+
} else if (voltage == VOLTAGE_INFO_1_05V) {
1734+
*n_entries = ARRAY_SIZE(cnl_ddi_translations_hdmi_1_05V);
1735+
return cnl_ddi_translations_hdmi_1_05V;
1736+
}
1737+
return NULL;
1738+
}
1739+
1740+
static const struct cnl_ddi_buf_trans *
1741+
cnl_get_buf_trans_dp(struct drm_i915_private *dev_priv,
1742+
u32 voltage, int *n_entries)
1743+
{
1744+
if (voltage == VOLTAGE_INFO_0_85V) {
1745+
*n_entries = ARRAY_SIZE(cnl_ddi_translations_dp_0_85V);
1746+
return cnl_ddi_translations_dp_0_85V;
1747+
} else if (voltage == VOLTAGE_INFO_0_95V) {
1748+
*n_entries = ARRAY_SIZE(cnl_ddi_translations_dp_0_95V);
1749+
return cnl_ddi_translations_dp_0_95V;
1750+
} else if (voltage == VOLTAGE_INFO_1_05V) {
1751+
*n_entries = ARRAY_SIZE(cnl_ddi_translations_dp_1_05V);
1752+
return cnl_ddi_translations_dp_1_05V;
1753+
}
1754+
return NULL;
1755+
}
1756+
1757+
static const struct cnl_ddi_buf_trans *
1758+
cnl_get_buf_trans_edp(struct drm_i915_private *dev_priv,
1759+
u32 voltage, int *n_entries)
1760+
{
1761+
if (dev_priv->vbt.edp.low_vswing) {
1762+
if (voltage == VOLTAGE_INFO_0_85V) {
1763+
*n_entries = ARRAY_SIZE(cnl_ddi_translations_edp_0_85V);
1764+
return cnl_ddi_translations_dp_0_85V;
1765+
} else if (voltage == VOLTAGE_INFO_0_95V) {
1766+
*n_entries = ARRAY_SIZE(cnl_ddi_translations_edp_0_95V);
1767+
return cnl_ddi_translations_edp_0_95V;
1768+
} else if (voltage == VOLTAGE_INFO_1_05V) {
1769+
*n_entries = ARRAY_SIZE(cnl_ddi_translations_edp_1_05V);
1770+
return cnl_ddi_translations_edp_1_05V;
1771+
}
1772+
return NULL;
1773+
} else {
1774+
return cnl_get_buf_trans_dp(dev_priv, voltage, n_entries);
1775+
}
1776+
}
1777+
1778+
static void cnl_ddi_vswing_program(struct drm_i915_private *dev_priv,
1779+
u32 level, enum port port, int type)
1780+
{
1781+
const struct cnl_ddi_buf_trans *ddi_translations = NULL;
1782+
u32 n_entries, val, voltage;
1783+
int ln;
1784+
1785+
/*
1786+
* Values for each port type are listed in
1787+
* voltage swing programming tables.
1788+
* Vccio voltage found in PORT_COMP_DW3.
1789+
*/
1790+
voltage = I915_READ(CNL_PORT_COMP_DW3) & VOLTAGE_INFO_MASK;
1791+
1792+
if (type == INTEL_OUTPUT_HDMI) {
1793+
ddi_translations = cnl_get_buf_trans_hdmi(dev_priv,
1794+
voltage, &n_entries);
1795+
} else if (type == INTEL_OUTPUT_DP) {
1796+
ddi_translations = cnl_get_buf_trans_dp(dev_priv,
1797+
voltage, &n_entries);
1798+
} else if (type == INTEL_OUTPUT_EDP) {
1799+
ddi_translations = cnl_get_buf_trans_edp(dev_priv,
1800+
voltage, &n_entries);
1801+
}
1802+
1803+
if (ddi_translations == NULL) {
1804+
MISSING_CASE(voltage);
1805+
return;
1806+
}
1807+
1808+
if (level >= n_entries) {
1809+
DRM_DEBUG_KMS("DDI translation not found for level %d. Using %d instead.", level, n_entries - 1);
1810+
level = n_entries - 1;
1811+
}
1812+
1813+
/* Set PORT_TX_DW5 Scaling Mode Sel to 010b. */
1814+
val = I915_READ(CNL_PORT_TX_DW5_LN0(port));
1815+
val |= SCALING_MODE_SEL(2);
1816+
I915_WRITE(CNL_PORT_TX_DW5_GRP(port), val);
1817+
1818+
/* Program PORT_TX_DW2 */
1819+
val = I915_READ(CNL_PORT_TX_DW2_LN0(port));
1820+
val |= SWING_SEL_UPPER(ddi_translations[level].dw2_swing_sel);
1821+
val |= SWING_SEL_LOWER(ddi_translations[level].dw2_swing_sel);
1822+
/* Rcomp scalar is fixed as 0x98 for every table entry */
1823+
val |= RCOMP_SCALAR(0x98);
1824+
I915_WRITE(CNL_PORT_TX_DW2_GRP(port), val);
1825+
1826+
/* Program PORT_TX_DW4 */
1827+
/* We cannot write to GRP. It would overrite individual loadgen */
1828+
for (ln = 0; ln < 4; ln++) {
1829+
val = I915_READ(CNL_PORT_TX_DW4_LN(port, ln));
1830+
val |= POST_CURSOR_1(ddi_translations[level].dw4_post_cursor_1);
1831+
val |= POST_CURSOR_2(ddi_translations[level].dw4_post_cursor_2);
1832+
val |= CURSOR_COEFF(ddi_translations[level].dw4_cursor_coeff);
1833+
I915_WRITE(CNL_PORT_TX_DW4_LN(port, ln), val);
1834+
}
1835+
1836+
/* Program PORT_TX_DW5 */
1837+
/* All DW5 values are fixed for every table entry */
1838+
val = I915_READ(CNL_PORT_TX_DW5_LN0(port));
1839+
val |= RTERM_SELECT(6);
1840+
val |= TAP3_DISABLE;
1841+
I915_WRITE(CNL_PORT_TX_DW5_GRP(port), val);
1842+
1843+
/* Program PORT_TX_DW7 */
1844+
val = I915_READ(CNL_PORT_TX_DW7_LN0(port));
1845+
val |= N_SCALAR(ddi_translations[level].dw7_n_scalar);
1846+
I915_WRITE(CNL_PORT_TX_DW7_GRP(port), val);
1847+
}
1848+
1849+
static void cnl_ddi_vswing_sequence(struct drm_i915_private *dev_priv,
1850+
u32 level, enum port port, int type)
1851+
{
1852+
u32 val;
1853+
1854+
/*
1855+
* 1. If port type is eDP or DP,
1856+
* set PORT_PCS_DW1 cmnkeeper_enable to 1b,
1857+
* else clear to 0b.
1858+
*/
1859+
val = I915_READ(CNL_PORT_PCS_DW1_LN0(port));
1860+
if (type == INTEL_OUTPUT_EDP || type == INTEL_OUTPUT_DP)
1861+
val |= COMMON_KEEPER_EN;
1862+
else
1863+
val &= ~COMMON_KEEPER_EN;
1864+
I915_WRITE(CNL_PORT_PCS_DW1_GRP(port), val);
1865+
1866+
/* 2. Program loadgen select */
1867+
/*
1868+
* FIXME: Program PORT_TX_DW4_LN depending on Bit rate and used lanes
1869+
*/
1870+
1871+
/* 3. Set PORT_CL_DW5 SUS Clock Config to 11b */
1872+
val = I915_READ(CNL_PORT_CL1CM_DW5);
1873+
val |= SUS_CLOCK_CONFIG;
1874+
I915_WRITE(CNL_PORT_CL1CM_DW5, val);
1875+
1876+
/* 4. Clear training enable to change swing values */
1877+
val = I915_READ(CNL_PORT_TX_DW5_LN0(port));
1878+
val &= ~TX_TRAINING_EN;
1879+
I915_WRITE(CNL_PORT_TX_DW5_GRP(port), val);
1880+
1881+
/* 5. Program swing and de-emphasis */
1882+
cnl_ddi_vswing_program(dev_priv, level, port, type);
1883+
1884+
/* 6. Set training enable to trigger update */
1885+
val = I915_READ(CNL_PORT_TX_DW5_LN0(port));
1886+
val |= TX_TRAINING_EN;
1887+
I915_WRITE(CNL_PORT_TX_DW5_GRP(port), val);
1888+
}
1889+
17231890
static uint32_t translate_signal_level(int signal_levels)
17241891
{
17251892
int i;
@@ -1752,7 +1919,11 @@ uint32_t ddi_signal_levels(struct intel_dp *intel_dp)
17521919
skl_ddi_set_iboost(encoder, level);
17531920
else if (IS_GEN9_LP(dev_priv))
17541921
bxt_ddi_vswing_sequence(dev_priv, level, port, encoder->type);
1755-
1922+
else if (IS_CANNONLAKE(dev_priv)) {
1923+
cnl_ddi_vswing_sequence(dev_priv, level, port, encoder->type);
1924+
/* DDI_BUF_CTL bits 27:24 are reserved on CNL */
1925+
return 0;
1926+
}
17561927
return DDI_BUF_TRANS_SELECT(level);
17571928
}
17581929

@@ -1850,6 +2021,9 @@ static void intel_ddi_pre_enable_hdmi(struct intel_encoder *encoder,
18502021
else if (IS_GEN9_LP(dev_priv))
18512022
bxt_ddi_vswing_sequence(dev_priv, level, port,
18522023
INTEL_OUTPUT_HDMI);
2024+
else if (IS_CANNONLAKE(dev_priv))
2025+
cnl_ddi_vswing_sequence(dev_priv, level, port,
2026+
INTEL_OUTPUT_HDMI);
18532027

18542028
intel_hdmi->set_infoframes(drm_encoder,
18552029
has_hdmi_sink,

drivers/gpu/drm/i915/intel_dp.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3492,7 +3492,7 @@ intel_dp_set_signal_levels(struct intel_dp *intel_dp)
34923492
if (HAS_DDI(dev_priv)) {
34933493
signal_levels = ddi_signal_levels(intel_dp);
34943494

3495-
if (IS_GEN9_LP(dev_priv))
3495+
if (IS_GEN9_LP(dev_priv) || IS_CANNONLAKE(dev_priv))
34963496
signal_levels = 0;
34973497
else
34983498
mask = DDI_BUF_EMP_MASK;

0 commit comments

Comments
 (0)