/** * DOC: DPIO * * VLV, CHV and BXT have slightly peculiar display PHYs for driving DP/HDMI * ports. DPIO is the name given to such a display PHY. These PHYs * don't follow the standard programming model using direct MMIO * registers, and instead their registers must be accessed through IOSF * sideband. VLV has one such PHY for driving ports B and C, and CHV * adds another PHY for driving port D. Each PHY responds to specific * IOSF-SB port. * * Each display PHY is made up of one or two channels. Each channel * houses a common lane part which contains the PLL and other common * logic. CH0 common lane also contains the IOSF-SB logic for the * Common Register Interface (CRI) ie. the DPIO registers. CRI clock * must be running when any DPIO registers are accessed. * * In addition to having their own registers, the PHYs are also * controlled through some dedicated signals from the display * controller. These include PLL reference clock enable, PLL enable, * and CRI clock selection, for example. * * Eeach channel also has two splines (also called data lanes), and * each spline is made up of one Physical Access Coding Sub-Layer * (PCS) block and two TX lanes. So each channel has two PCS blocks * and four TX lanes. The TX lanes are used as DP lanes or TMDS * data/clock pairs depending on the output type. * * Additionally the PHY also contains an AUX lane with AUX blocks * for each channel. This is used for DP AUX communication, but * this fact isn't really relevant for the driver since AUX is * controlled from the display controller side. No DPIO registers * need to be accessed during AUX communication, * * Generally on VLV/CHV the common lane corresponds to the pipe and * the spline (PCS/TX) corresponds to the port. * * For dual channel PHY (VLV/CHV): * * pipe A == CMN/PLL/REF CH0 * * pipe B == CMN/PLL/REF CH1 * * port B == PCS/TX CH0 * * port C == PCS/TX CH1 * * This is especially important when we cross the streams * ie. drive port B with pipe B, or port C with pipe A. * * For single channel PHY (CHV): * * pipe C == CMN/PLL/REF CH0 * * port D == PCS/TX CH0 * * On BXT the entire PHY channel corresponds to the port. That means * the PLL is also now associated with the port rather than the pipe, * and so the clock needs to be routed to the appropriate transcoder. * Port A PLL is directly connected to transcoder EDP and port B/C * PLLs can be routed to any transcoder A/B/C. * * Note: DDI0 is digital port B, DD1 is digital port C, and DDI2 is * digital port D (CHV) or port A (BXT). :: * * * Dual channel PHY (VLV/CHV/BXT) * --------------------------------- * | CH0 | CH1 | * | CMN/PLL/REF | CMN/PLL/REF | * |---------------|---------------| Display PHY * | PCS01 | PCS23 | PCS01 | PCS23 | * |-------|-------|-------|-------| * |TX0|TX1|TX2|TX3|TX0|TX1|TX2|TX3| * --------------------------------- * | DDI0 | DDI1 | DP/HDMI ports * --------------------------------- * * Single channel PHY (CHV/BXT) * ----------------- * | CH0 | * | CMN/PLL/REF | * |---------------| Display PHY * | PCS01 | PCS23 | * |-------|-------| * |TX0|TX1|TX2|TX3| * ----------------- * | DDI2 | DP/HDMI port * -----------------
*/
/** * struct bxt_dpio_phy_info - Hold info for a broxton DDI phy
*/ struct bxt_dpio_phy_info { /** * @dual_channel: true if this phy has a second channel.
*/ bool dual_channel;
/** * @rcomp_phy: If -1, indicates this phy has its own rcomp resistor. * Otherwise the GRC value will be copied from the phy indicated by * this field.
*/ enum dpio_phy rcomp_phy;
/** * @reset_delay: delay in us to wait before setting the common reset * bit in BXT_PHY_CTL_FAMILY, which effectively enables the phy.
*/ int reset_delay;
/** * @pwron_mask: Mask with the appropriate bit set that would cause the * punit to power this phy if written to BXT_P_CR_GT_DISP_PWRON.
*/
u32 pwron_mask;
/** * @channel: struct containing per channel information.
*/ struct { /** * @channel.port: which port maps to this channel.
*/ enum port port;
} channel[2];
};
if (phy_info->dual_channel &&
port == phy_info->channel[DPIO_CH1].port) {
*phy = i;
*ch = DPIO_CH1; return;
}
}
drm_WARN(display->drm, 1, "PHY not found for PORT %c",
port_name(port));
*phy = DPIO_PHY0;
*ch = DPIO_CH0;
}
/* * Like intel_de_rmw() but reads from a single per-lane register and * writes to the group register to write the same value to all the lanes.
*/ static u32 bxt_dpio_phy_rmw_grp(struct intel_display *display,
i915_reg_t reg_single,
i915_reg_t reg_group,
u32 clear, u32 set)
{
u32 old, val;
old = intel_de_read(display, reg_single);
val = (old & ~clear) | set;
intel_de_write(display, reg_group, val);
/* * 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.
*/
bxt_dpio_phy_rmw_grp(display, BXT_PORT_PCS_DW10_LN01(phy, ch),
BXT_PORT_PCS_DW10_GRP(phy, ch),
TX2_SWING_CALC_INIT | TX1_SWING_CALC_INIT, 0);
for (lane = 0; lane < crtc_state->lane_count; lane++) { int level = intel_ddi_level(encoder, crtc_state, lane);
if (bxt_dpio_phy_is_enabled(display, phy)) { /* Still read out the GRC value for state verification */ if (phy_info->rcomp_phy != -1)
display->state.bxt_phy_grc = bxt_get_grc(display, phy);
/* * The PHY registers start out inaccessible and respond to reads with * all 1s. Eventually they become accessible as they power up, then * the reserved bit will give the default 0. Poll on the reserved bit * becoming 0 to find when the PHY is accessible. * The flag should get set in 100us according to the HW team, but * use 1ms due to occasional timeouts observed with that.
*/ if (intel_de_wait_fw(display, BXT_PORT_CL1CM_DW0(phy),
PHY_RESERVED | PHY_POWER_GOOD, PHY_POWER_GOOD, 1, NULL))
drm_err(display->drm, "timeout during PHY%d power on\n",
phy);
/* * PHY0 isn't connected to an RCOMP resistor so copy over * the corresponding calibrated value from PHY1, and disable * the automatic calibration on PHY0.
*/
val = bxt_get_grc(display, phy_info->rcomp_phy);
display->state.bxt_phy_grc = val;
/* * We need to copy the GRC calibration value from rcomp_phy, * so make sure it's powered up.
*/ if (!was_enabled)
_bxt_dpio_phy_init(display, rcomp_phy);
_bxt_dpio_phy_init(display, phy);
if (!was_enabled)
bxt_dpio_phy_uninit(display, rcomp_phy);
}
for (lane = 0; lane < 4; lane++) { /* * Note that on CHV this flag is called UPAR, but has * the same function.
*/
intel_de_rmw(display, BXT_PORT_TX_DW14_LN(phy, ch, lane),
LATENCY_OPTIM,
lane_lat_optim_mask & BIT(lane) ? LATENCY_OPTIM : 0);
}
}
u8
bxt_dpio_phy_get_lane_lat_optim_mask(struct intel_encoder *encoder)
{ struct intel_display *display = to_intel_display(encoder); enum port port = encoder->port; enum dpio_phy phy; enum dpio_channel ch; int lane;
u8 mask;
/* Clear calc init */
val = vlv_dpio_read(display->drm, phy, VLV_PCS01_DW10(ch));
val &= ~(DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3);
val &= ~(DPIO_PCS_TX1DEEMP_MASK | DPIO_PCS_TX2DEEMP_MASK);
val |= DPIO_PCS_TX1DEEMP_9P5 | DPIO_PCS_TX2DEEMP_9P5;
vlv_dpio_write(display->drm, phy, VLV_PCS01_DW10(ch), val);
if (crtc_state->lane_count > 2) {
val = vlv_dpio_read(display->drm, phy, VLV_PCS23_DW10(ch));
val &= ~(DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3);
val &= ~(DPIO_PCS_TX1DEEMP_MASK | DPIO_PCS_TX2DEEMP_MASK);
val |= DPIO_PCS_TX1DEEMP_9P5 | DPIO_PCS_TX2DEEMP_9P5;
vlv_dpio_write(display->drm, phy, VLV_PCS23_DW10(ch), val);
}
val = vlv_dpio_read(display->drm, phy, VLV_PCS01_DW9(ch));
val &= ~(DPIO_PCS_TX1MARGIN_MASK | DPIO_PCS_TX2MARGIN_MASK);
val |= DPIO_PCS_TX1MARGIN_000 | DPIO_PCS_TX2MARGIN_000;
vlv_dpio_write(display->drm, phy, VLV_PCS01_DW9(ch), val);
if (crtc_state->lane_count > 2) {
val = vlv_dpio_read(display->drm, phy, VLV_PCS23_DW9(ch));
val &= ~(DPIO_PCS_TX1MARGIN_MASK | DPIO_PCS_TX2MARGIN_MASK);
val |= DPIO_PCS_TX1MARGIN_000 | DPIO_PCS_TX2MARGIN_000;
vlv_dpio_write(display->drm, phy, VLV_PCS23_DW9(ch), val);
}
/* Program swing deemph */ for (i = 0; i < crtc_state->lane_count; i++) {
val = vlv_dpio_read(display->drm, phy, CHV_TX_DW4(ch, i));
val &= ~DPIO_SWING_DEEMPH9P5_MASK;
val |= DPIO_SWING_DEEMPH9P5(deemph_reg_value);
vlv_dpio_write(display->drm, phy, CHV_TX_DW4(ch, i), val);
}
/* Program swing margin */ for (i = 0; i < crtc_state->lane_count; i++) {
val = vlv_dpio_read(display->drm, phy, CHV_TX_DW2(ch, i));
val &= ~DPIO_SWING_MARGIN000_MASK;
val |= DPIO_SWING_MARGIN000(margin_reg_value);
/* * Supposedly this value shouldn't matter when unique transition * scale is disabled, but in fact it does matter. Let's just * always program the same value and hope it's OK.
*/
val &= ~DPIO_UNIQ_TRANS_SCALE_MASK;
val |= DPIO_UNIQ_TRANS_SCALE(0x9a);
/* * The document said it needs to set bit 27 for ch0 and bit 26 * for ch1. Might be a typo in the doc. * For now, for this unique transition scale selection, set bit * 27 for ch0 and ch1.
*/ for (i = 0; i < crtc_state->lane_count; i++) {
val = vlv_dpio_read(display->drm, phy, CHV_TX_DW3(ch, i)); if (uniq_trans_scale)
val |= DPIO_TX_UNIQ_TRANS_SCALE_EN; else
val &= ~DPIO_TX_UNIQ_TRANS_SCALE_EN;
vlv_dpio_write(display->drm, phy, CHV_TX_DW3(ch, i), val);
}
/* Start swing calculation */
val = vlv_dpio_read(display->drm, phy, VLV_PCS01_DW10(ch));
val |= DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3;
vlv_dpio_write(display->drm, phy, VLV_PCS01_DW10(ch), val);
if (crtc_state->lane_count > 2) {
val = vlv_dpio_read(display->drm, phy, VLV_PCS23_DW10(ch));
val |= DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3;
vlv_dpio_write(display->drm, phy, VLV_PCS23_DW10(ch), val);
}
val = vlv_dpio_read(display->drm, phy, VLV_PCS01_DW0(ch)); if (reset)
val &= ~(DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET); else
val |= DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET;
vlv_dpio_write(display->drm, phy, VLV_PCS01_DW0(ch), val);
if (crtc_state->lane_count > 2) {
val = vlv_dpio_read(display->drm, phy, VLV_PCS23_DW0(ch)); if (reset)
val &= ~(DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET); else
val |= DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET;
vlv_dpio_write(display->drm, phy, VLV_PCS23_DW0(ch), val);
}
val = vlv_dpio_read(display->drm, phy, VLV_PCS01_DW1(ch));
val |= CHV_PCS_REQ_SOFTRESET_EN; if (reset)
val &= ~DPIO_PCS_CLK_SOFT_RESET; else
val |= DPIO_PCS_CLK_SOFT_RESET;
vlv_dpio_write(display->drm, phy, VLV_PCS01_DW1(ch), val);
if (crtc_state->lane_count > 2) {
val = vlv_dpio_read(display->drm, phy, VLV_PCS23_DW1(ch));
val |= CHV_PCS_REQ_SOFTRESET_EN; if (reset)
val &= ~DPIO_PCS_CLK_SOFT_RESET; else
val |= DPIO_PCS_CLK_SOFT_RESET;
vlv_dpio_write(display->drm, phy, VLV_PCS23_DW1(ch), val);
}
}
/* * Must trick the second common lane into life. * Otherwise we can't even access the PLL.
*/ if (ch == DPIO_CH0 && pipe == PIPE_B)
dig_port->release_cl2_override =
!chv_phy_powergate_ch(display, DPIO_PHY0, DPIO_CH1, true);
/* Assert data lane reset */
__chv_data_lane_soft_reset(encoder, crtc_state, true);
/* program left/right clock distribution */ if (pipe != PIPE_B) {
val = vlv_dpio_read(display->drm, phy, CHV_CMN_DW5_CH0);
val &= ~(CHV_BUFLEFTENA1_MASK | CHV_BUFRIGHTENA1_MASK); if (ch == DPIO_CH0)
val |= CHV_BUFLEFTENA1_FORCE; if (ch == DPIO_CH1)
val |= CHV_BUFRIGHTENA1_FORCE;
vlv_dpio_write(display->drm, phy, CHV_CMN_DW5_CH0, val);
} else {
val = vlv_dpio_read(display->drm, phy, CHV_CMN_DW1_CH1);
val &= ~(CHV_BUFLEFTENA2_MASK | CHV_BUFRIGHTENA2_MASK); if (ch == DPIO_CH0)
val |= CHV_BUFLEFTENA2_FORCE; if (ch == DPIO_CH1)
val |= CHV_BUFRIGHTENA2_FORCE;
vlv_dpio_write(display->drm, phy, CHV_CMN_DW1_CH1, val);
}
/* program clock channel usage */
val = vlv_dpio_read(display->drm, phy, VLV_PCS01_DW8(ch));
val |= DPIO_PCS_USEDCLKCHANNEL_OVRRIDE; if (pipe == PIPE_B)
val |= DPIO_PCS_USEDCLKCHANNEL; else
val &= ~DPIO_PCS_USEDCLKCHANNEL;
vlv_dpio_write(display->drm, phy, VLV_PCS01_DW8(ch), val);
if (crtc_state->lane_count > 2) {
val = vlv_dpio_read(display->drm, phy, VLV_PCS23_DW8(ch));
val |= DPIO_PCS_USEDCLKCHANNEL_OVRRIDE; if (pipe == PIPE_B)
val |= DPIO_PCS_USEDCLKCHANNEL; else
val &= ~DPIO_PCS_USEDCLKCHANNEL;
vlv_dpio_write(display->drm, phy, VLV_PCS23_DW8(ch), val);
}
/* * This a a bit weird since generally CL * matches the pipe, but here we need to * pick the CL based on the port.
*/
val = vlv_dpio_read(display->drm, phy, CHV_CMN_DW19(ch)); if (pipe == PIPE_B)
val |= CHV_CMN_USEDCLKCHANNEL; else
val &= ~CHV_CMN_USEDCLKCHANNEL;
vlv_dpio_write(display->drm, phy, CHV_CMN_DW19(ch), val);
/* allow hardware to manage TX FIFO reset source */
val = vlv_dpio_read(display->drm, phy, VLV_PCS01_DW11(ch));
val &= ~DPIO_LANEDESKEW_STRAP_OVRD;
vlv_dpio_write(display->drm, phy, VLV_PCS01_DW11(ch), val);
if (crtc_state->lane_count > 2) {
val = vlv_dpio_read(display->drm, phy, VLV_PCS23_DW11(ch));
val &= ~DPIO_LANEDESKEW_STRAP_OVRD;
vlv_dpio_write(display->drm, phy, VLV_PCS23_DW11(ch), val);
}
/* Program Tx lane latency optimal setting*/ for (i = 0; i < crtc_state->lane_count; i++) { /* Set the upar bit */ if (crtc_state->lane_count == 1)
data = 0; else
data = (i == 1) ? 0 : DPIO_UPAR;
vlv_dpio_write(display->drm, phy, CHV_TX_DW14(ch, i), data);
}
/* disable left/right clock distribution */ if (pipe != PIPE_B) {
val = vlv_dpio_read(display->drm, phy, CHV_CMN_DW5_CH0);
val &= ~(CHV_BUFLEFTENA1_MASK | CHV_BUFRIGHTENA1_MASK);
vlv_dpio_write(display->drm, phy, CHV_CMN_DW5_CH0, val);
} else {
val = vlv_dpio_read(display->drm, phy, CHV_CMN_DW1_CH1);
val &= ~(CHV_BUFLEFTENA2_MASK | CHV_BUFRIGHTENA2_MASK);
vlv_dpio_write(display->drm, phy, CHV_CMN_DW1_CH1, val);
}
vlv_dpio_put(display->drm);
/* * Leave the power down bit cleared for at least one * lane so that chv_powergate_phy_ch() will power * on something when the channel is otherwise unused. * When the port is off and the override is removed * the lanes power down anyway, so otherwise it doesn't * really matter what the state of power down bits is * after this.
*/
chv_phy_powergate_lanes(encoder, false, 0x0);
}
/* Enable clock channels for this port */
val = DPIO_PCS_USEDCLKCHANNEL_OVRRIDE; if (pipe == PIPE_B)
val |= DPIO_PCS_USEDCLKCHANNEL;
val |= 0xc4;
vlv_dpio_write(display->drm, phy, VLV_PCS_DW8_GRP(ch), val);
/* Program lane clock */
vlv_dpio_write(display->drm, phy, VLV_PCS_DW14_GRP(ch), 0x00760018);
vlv_dpio_write(display->drm, phy, VLV_PCS_DW23_GRP(ch), 0x00400888);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.