drm_WARN(display->drm, !(val & VIDEO_DIP_ENABLE), "Writing DIP with CTL reg disabled\n");
val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */
val |= g4x_infoframe_index(type);
val &= ~g4x_infoframe_enable(type);
intel_de_write(display, VIDEO_DIP_CTL, val);
for (i = 0; i < len; i += 4) {
intel_de_write(display, VIDEO_DIP_DATA, *data);
data++;
} /* Write every possible data byte to force correct ECC calculation. */ for (; i < VIDEO_DIP_DATA_SIZE; i += 4)
intel_de_write(display, VIDEO_DIP_DATA, 0);
val |= g4x_infoframe_enable(type);
val &= ~VIDEO_DIP_FREQ_MASK;
val |= VIDEO_DIP_FREQ_VSYNC;
drm_WARN(display->drm, !(val & VIDEO_DIP_ENABLE), "Writing DIP with CTL reg disabled\n");
val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */
val |= g4x_infoframe_index(type);
val &= ~g4x_infoframe_enable(type);
intel_de_write(display, reg, val);
for (i = 0; i < len; i += 4) {
intel_de_write(display, TVIDEO_DIP_DATA(crtc->pipe),
*data);
data++;
} /* Write every possible data byte to force correct ECC calculation. */ for (; i < VIDEO_DIP_DATA_SIZE; i += 4)
intel_de_write(display, TVIDEO_DIP_DATA(crtc->pipe), 0);
val |= g4x_infoframe_enable(type);
val &= ~VIDEO_DIP_FREQ_MASK;
val |= VIDEO_DIP_FREQ_VSYNC;
drm_WARN(display->drm, !(val & VIDEO_DIP_ENABLE), "Writing DIP with CTL reg disabled\n");
val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */
val |= g4x_infoframe_index(type);
/* The DIP control register spec says that we need to update the AVI
* infoframe without clearing its enable bit */ if (type != HDMI_INFOFRAME_TYPE_AVI)
val &= ~g4x_infoframe_enable(type);
intel_de_write(display, reg, val);
for (i = 0; i < len; i += 4) {
intel_de_write(display, TVIDEO_DIP_DATA(crtc->pipe),
*data);
data++;
} /* Write every possible data byte to force correct ECC calculation. */ for (; i < VIDEO_DIP_DATA_SIZE; i += 4)
intel_de_write(display, TVIDEO_DIP_DATA(crtc->pipe), 0);
val |= g4x_infoframe_enable(type);
val &= ~VIDEO_DIP_FREQ_MASK;
val |= VIDEO_DIP_FREQ_VSYNC;
drm_WARN(display->drm, !(val & VIDEO_DIP_ENABLE), "Writing DIP with CTL reg disabled\n");
val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */
val |= g4x_infoframe_index(type);
val &= ~g4x_infoframe_enable(type);
intel_de_write(display, reg, val);
for (i = 0; i < len; i += 4) {
intel_de_write(display,
VLV_TVIDEO_DIP_DATA(crtc->pipe), *data);
data++;
} /* Write every possible data byte to force correct ECC calculation. */ for (; i < VIDEO_DIP_DATA_SIZE; i += 4)
intel_de_write(display,
VLV_TVIDEO_DIP_DATA(crtc->pipe), 0);
val |= g4x_infoframe_enable(type);
val &= ~VIDEO_DIP_FREQ_MASK;
val |= VIDEO_DIP_FREQ_VSYNC;
val &= ~hsw_infoframe_enable(type);
intel_de_write(display, ctl_reg, val);
for (i = 0; i < len; i += 4) {
intel_de_write(display,
hsw_dip_data_reg(display, cpu_transcoder, type, i >> 2),
*data);
data++;
} /* Write every possible data byte to force correct ECC calculation. */ for (; i < data_size; i += 4)
intel_de_write(display,
hsw_dip_data_reg(display, cpu_transcoder, type, i >> 2),
0);
/* Wa_14013475917 */ if (!(IS_DISPLAY_VER(display, 13, 14) && crtc_state->has_psr &&
!crtc_state->has_panel_replay && type == DP_SDP_VSC))
val |= hsw_infoframe_enable(type);
if (type == DP_SDP_VSC)
val |= VSC_DIP_HW_DATA_SW_HEA;
val = dig_port->infoframes_enabled(encoder, crtc_state);
/* map from hardware bits to dip idx */ for (i = 0; i < ARRAY_SIZE(infoframe_type_to_idx); i++) { unsignedint type = infoframe_type_to_idx[i];
if (HAS_DDI(display)) { if (val & hsw_infoframe_enable(type))
ret |= BIT(i);
} else { if (val & g4x_infoframe_enable(type))
ret |= BIT(i);
}
}
return ret;
}
/* * The data we write to the DIP data buffer registers is 1 byte bigger than the * HDMI infoframe size because of an ECC/reserved byte at position 3 (starting * at 0). It's also a byte used by DisplayPort so the same DIP registers can be * used for both technologies. * * DW0: Reserved/ECC/DP | HB2 | HB1 | HB0 * DW1: DB3 | DB2 | DB1 | DB0 * DW2: DB7 | DB6 | DB5 | DB4 * DW3: ... * * (HB is Header Byte, DB is Data Byte) * * The hdmi pack() functions don't know about that hardware specific hole so we * trick them by giving an offset into the buffer and moving back the header * bytes by one.
*/ staticvoid intel_write_infoframe(struct intel_encoder *encoder, conststruct intel_crtc_state *crtc_state, enum hdmi_infoframe_type type, constunion hdmi_infoframe *frame)
{ struct intel_digital_port *dig_port = enc_to_dig_port(encoder);
u8 buffer[VIDEO_DIP_DATA_SIZE];
ssize_t len;
if ((crtc_state->infoframes.enable &
intel_hdmi_infoframe_enable(type)) == 0) return;
if (drm_WARN_ON(encoder->base.dev, frame->any.type != type)) return;
/* see comment above for the reason for this offset */
len = hdmi_infoframe_pack_only(frame, buffer + 1, sizeof(buffer) - 1); if (drm_WARN_ON(encoder->base.dev, len < 0)) return;
/* Insert the 'hole' (see big comment above) at position 3 */
memmove(&buffer[0], &buffer[1], 3);
buffer[3] = 0;
len++;
/* Fill the 'hole' (see big comment above) at position 3 */
memmove(&buffer[1], &buffer[0], 3);
/* see comment above for the reason for this offset */
ret = hdmi_infoframe_unpack(frame, buffer + 1, sizeof(buffer) - 1); if (ret) {
drm_dbg_kms(encoder->base.dev, "Failed to unpack infoframe type 0x%02x\n", type); return;
}
if (frame->any.type != type)
drm_dbg_kms(encoder->base.dev, "Found the wrong infoframe type 0x%x (expected 0x%02x)\n",
frame->any.type, type);
}
if (display->platform.dgfx)
ret = hdmi_spd_infoframe_init(frame, "Intel", "Discrete gfx"); else
ret = hdmi_spd_infoframe_init(frame, "Intel", "Integrated gfx");
if (drm_WARN_ON(encoder->base.dev, ret)) returnfalse;
frame->sdi = HDMI_SPD_SDI_PC;
ret = hdmi_spd_infoframe_check(frame); if (drm_WARN_ON(encoder->base.dev, ret)) returnfalse;
ret = drm_hdmi_vendor_infoframe_from_display_mode(frame,
conn_state->connector,
&crtc_state->hw.adjusted_mode); if (drm_WARN_ON(encoder->base.dev, ret)) returnfalse;
ret = hdmi_vendor_infoframe_check(frame); if (drm_WARN_ON(encoder->base.dev, ret)) returnfalse;
ret = drm_hdmi_infoframe_set_hdr_metadata(frame, conn_state); if (ret < 0) {
drm_dbg_kms(display->drm, "couldn't set HDR metadata in infoframe\n"); returnfalse;
}
ret = hdmi_drm_infoframe_check(frame); if (drm_WARN_ON(display->drm, ret)) returnfalse;
/* If the registers were not initialized yet, they might be zeroes, * which means we're selecting the AVI DIP and we're setting its * frequency to once. This seems to really confuse the HW and make * things stop working (the register spec says the AVI always needs to * be sent every VSync). So here we avoid writing to the register more * than we need and also explicitly select the AVI DIP and explicitly * set its frequency to every VSync. Avoiding to write it twice seems to * be enough to solve the problem, but being defensive shouldn't hurt us
* either. */
val |= VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC;
if (!enable) { if (!(val & VIDEO_DIP_ENABLE)) return; if (port != (val & VIDEO_DIP_PORT_MASK)) {
drm_dbg_kms(display->drm, "video DIP still enabled on port %c\n",
(val & VIDEO_DIP_PORT_MASK) >> 29); return;
}
val &= ~(VIDEO_DIP_ENABLE | VIDEO_DIP_ENABLE_AVI |
VIDEO_DIP_ENABLE_VENDOR | VIDEO_DIP_ENABLE_SPD);
intel_de_write(display, reg, val);
intel_de_posting_read(display, reg); return;
}
if (port != (val & VIDEO_DIP_PORT_MASK)) { if (val & VIDEO_DIP_ENABLE) {
drm_dbg_kms(display->drm, "video DIP already enabled on port %c\n",
(val & VIDEO_DIP_PORT_MASK) >> 29); return;
}
val &= ~VIDEO_DIP_PORT_MASK;
val |= port;
}
val |= VIDEO_DIP_ENABLE;
val &= ~(VIDEO_DIP_ENABLE_AVI |
VIDEO_DIP_ENABLE_VENDOR | VIDEO_DIP_ENABLE_SPD);
/* * Determine if default_phase=1 can be indicated in the GCP infoframe. * * From HDMI specification 1.4a: * - The first pixel of each Video Data Period shall always have a pixel packing phase of 0 * - The first pixel following each Video Data Period shall have a pixel packing phase of 0 * - The PP bits shall be constant for all GCPs and will be equal to the last packing phase * - The first pixel following every transition of HSYNC or VSYNC shall have a pixel packing * phase of 0
*/ staticbool gcp_default_phase_possible(int pipe_bpp, conststruct drm_display_mode *mode)
{ unsignedint pixels_per_group;
switch (pipe_bpp) { case 30: /* 4 pixels in 5 clocks */
pixels_per_group = 4; break; case 36: /* 2 pixels in 3 clocks */
pixels_per_group = 2; break; case 48: /* 1 pixel in 2 clocks */
pixels_per_group = 1; break; default: /* phase information not relevant for 8bpc */ returnfalse;
}
/* See the big comment in g4x_set_infoframes() */
val |= VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC;
if (!enable) { if (!(val & VIDEO_DIP_ENABLE)) return;
val &= ~(VIDEO_DIP_ENABLE | VIDEO_DIP_ENABLE_AVI |
VIDEO_DIP_ENABLE_VENDOR | VIDEO_DIP_ENABLE_GAMUT |
VIDEO_DIP_ENABLE_SPD | VIDEO_DIP_ENABLE_GCP);
intel_de_write(display, reg, val);
intel_de_posting_read(display, reg); return;
}
if (port != (val & VIDEO_DIP_PORT_MASK)) {
drm_WARN(display->drm, val & VIDEO_DIP_ENABLE, "DIP already enabled on port %c\n",
(val & VIDEO_DIP_PORT_MASK) >> 29);
val &= ~VIDEO_DIP_PORT_MASK;
val |= port;
}
val |= VIDEO_DIP_ENABLE;
val &= ~(VIDEO_DIP_ENABLE_AVI |
VIDEO_DIP_ENABLE_VENDOR | VIDEO_DIP_ENABLE_GAMUT |
VIDEO_DIP_ENABLE_SPD | VIDEO_DIP_ENABLE_GCP);
if (intel_hdmi_set_gcp_infoframe(encoder, crtc_state, conn_state))
val |= VIDEO_DIP_ENABLE_GCP;
/* See the big comment in g4x_set_infoframes() */
val |= VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC;
if (!enable) { if (!(val & VIDEO_DIP_ENABLE)) return;
val &= ~(VIDEO_DIP_ENABLE | VIDEO_DIP_ENABLE_AVI |
VIDEO_DIP_ENABLE_VENDOR | VIDEO_DIP_ENABLE_GAMUT |
VIDEO_DIP_ENABLE_SPD | VIDEO_DIP_ENABLE_GCP);
intel_de_write(display, reg, val);
intel_de_posting_read(display, reg); return;
}
/* Set both together, unset both together: see the spec. */
val |= VIDEO_DIP_ENABLE | VIDEO_DIP_ENABLE_AVI;
val &= ~(VIDEO_DIP_ENABLE_VENDOR | VIDEO_DIP_ENABLE_GAMUT |
VIDEO_DIP_ENABLE_SPD | VIDEO_DIP_ENABLE_GCP);
if (intel_hdmi_set_gcp_infoframe(encoder, crtc_state, conn_state))
val |= VIDEO_DIP_ENABLE_GCP;
/* See the big comment in g4x_set_infoframes() */
val |= VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC;
if (!enable) { if (!(val & VIDEO_DIP_ENABLE)) return;
val &= ~(VIDEO_DIP_ENABLE | VIDEO_DIP_ENABLE_AVI |
VIDEO_DIP_ENABLE_VENDOR | VIDEO_DIP_ENABLE_GAMUT |
VIDEO_DIP_ENABLE_SPD | VIDEO_DIP_ENABLE_GCP);
intel_de_write(display, reg, val);
intel_de_posting_read(display, reg); return;
}
if (port != (val & VIDEO_DIP_PORT_MASK)) {
drm_WARN(display->drm, val & VIDEO_DIP_ENABLE, "DIP already enabled on port %c\n",
(val & VIDEO_DIP_PORT_MASK) >> 29);
val &= ~VIDEO_DIP_PORT_MASK;
val |= port;
}
val |= VIDEO_DIP_ENABLE;
val &= ~(VIDEO_DIP_ENABLE_AVI |
VIDEO_DIP_ENABLE_VENDOR | VIDEO_DIP_ENABLE_GAMUT |
VIDEO_DIP_ENABLE_SPD | VIDEO_DIP_ENABLE_GCP);
if (intel_hdmi_set_gcp_infoframe(encoder, crtc_state, conn_state))
val |= VIDEO_DIP_ENABLE_GCP;
ret = intel_hdmi_hdcp_write(dig_port, DRM_HDCP_DDC_AN, an,
DRM_HDCP_AN_LEN); if (ret) {
drm_dbg_kms(display->drm, "Write An over DDC failed (%d)\n",
ret); return ret;
}
ret = intel_gmbus_output_aksv(ddc); if (ret < 0) {
drm_dbg_kms(display->drm, "Failed to output aksv (%d)\n", ret); return ret;
} return 0;
}
int ret;
ret = intel_hdmi_hdcp_read(dig_port, DRM_HDCP_DDC_RI_PRIME,
ri_prime, DRM_HDCP_RI_LEN); if (ret)
drm_dbg_kms(display->drm, "Read Ri' over DDC failed (%d)\n",
ret); return ret;
}
static int intel_hdmi_hdcp_read_ksv_ready(struct intel_digital_port *dig_port, bool *ksv_ready)
{ struct intel_display *display = to_intel_display(dig_port); int ret;
u8 val;
ret = intel_hdmi_hdcp_read(dig_port, DRM_HDCP_DDC_BCAPS, &val, 1); if (ret) {
drm_dbg_kms(display->drm, "Read bcaps over DDC failed (%d)\n",
ret); return ret;
}
*ksv_ready = val & DRM_HDCP_DDC_BCAPS_KSV_FIFO_READY; return 0;
}
static int intel_hdmi_hdcp_read_ksv_fifo(struct intel_digital_port *dig_port, int num_downstream, u8 *ksv_fifo)
{ struct intel_display *display = to_intel_display(dig_port); int ret;
ret = intel_hdmi_hdcp_read(dig_port, DRM_HDCP_DDC_KSV_FIFO,
ksv_fifo, num_downstream * DRM_HDCP_KSV_LEN); if (ret) {
drm_dbg_kms(display->drm, "Read ksv fifo over DDC failed (%d)\n", ret); return ret;
} return 0;
}
static int intel_hdmi_hdcp_read_v_prime_part(struct intel_digital_port *dig_port, int i, u32 *part)
{ struct intel_display *display = to_intel_display(dig_port); int ret;
if (i >= DRM_HDCP_V_PRIME_NUM_PARTS) return -EINVAL;
ret = intel_hdmi_hdcp_read(dig_port, DRM_HDCP_DDC_V_PRIME(i),
part, DRM_HDCP_V_PRIME_PART_LEN); if (ret)
drm_dbg_kms(display->drm, "Read V'[%d] over DDC failed (%d)\n",
i, ret); return ret;
}
/* * WA: To fix incorrect positioning of the window of * opportunity and enc_en signalling in KABYLAKE.
*/ if (display->platform.kabylake && enable) return kbl_repositioning_enc_en_signal(connector,
cpu_transcoder);
return 0;
}
static bool intel_hdmi_hdcp_check_link_once(struct intel_digital_port *dig_port, struct intel_connector *connector)
{ struct intel_display *display = to_intel_display(dig_port); enum port port = dig_port->base.port; enum transcoder cpu_transcoder = connector->hdcp.cpu_transcoder; int ret; union {
u32 reg;
u8 shim[DRM_HDCP_RI_LEN];
} ri;
ret = intel_hdmi_hdcp_read_ri_prime(dig_port, ri.shim); if (ret) returnfalse;
ret = intel_hdmi_hdcp2_wait_for_msg(dig_port, msg_id,
hdcp->is_paired); if (ret < 0) return ret;
/* * Available msg size should be equal to or lesser than the * available buffer.
*/ if (ret > size) {
drm_dbg_kms(display->drm, "msg_sz(%zd) is more than exp size(%zu)\n",
ret, size); return -EINVAL;
}
offset = HDCP_2_2_HDMI_REG_RD_MSG_OFFSET;
ret = intel_hdmi_hdcp_read(dig_port, offset, buf, ret); if (ret)
drm_dbg_kms(display->drm, "Failed to read msg_id: %d(%zd)\n",
msg_id, ret);
return ret;
}
static int intel_hdmi_hdcp2_check_link(struct intel_digital_port *dig_port, struct intel_connector *connector)
{
u8 rx_status[HDCP_2_2_HDMI_RXSTATUS_LEN]; int ret;
ret = intel_hdmi_hdcp2_read_rx_status(dig_port, rx_status); if (ret) return ret;
/* * Re-auth request and Link Integrity Failures are represented by * same bit. i.e reauth_req.
*/ if (HDCP_2_2_HDMI_RXSTATUS_REAUTH_REQ(rx_status[1]))
ret = HDCP_REAUTH_REQUEST; elseif (HDCP_2_2_HDMI_RXSTATUS_READY(rx_status[1]))
ret = HDCP_TOPOLOGY_CHANGE;
return ret;
}
static int intel_hdmi_hdcp2_get_capability(struct intel_connector *connector, bool *capable)
{ struct intel_digital_port *dig_port = intel_attached_dig_port(connector);
u8 hdcp2_version; int ret;
*capable = false;
ret = intel_hdmi_hdcp_read(dig_port, HDCP_2_2_HDMI_REG_VER_OFFSET,
&hdcp2_version, sizeof(hdcp2_version)); if (!ret && hdcp2_version & HDCP_2_2_HDMI_SUPPORT_MASK)
*capable = true;
/* * Try all color depths since valid port clock range * can have holes. Any mode that can be used with at * least one color depth is accepted.
*/ for (bpc = 12; bpc >= 8; bpc -= 2) { int tmds_clock = intel_hdmi_tmds_clock(clock, bpc, sink_format);
if (!intel_hdmi_source_bpc_possible(display, bpc)) continue;
if (!intel_hdmi_sink_bpc_possible(&connector->base, bpc, has_hdmi_sink,
sink_format)) continue;
status = hdmi_port_clock_valid(hdmi, tmds_clock, true, has_hdmi_sink); if (status == MODE_OK) return MODE_OK;
}
/* can never happen */
drm_WARN_ON(display->drm, status == MODE_OK);
status = intel_cpu_transcoder_mode_valid(display, mode); if (status != MODE_OK) return status;
if ((mode->flags & DRM_MODE_FLAG_3D_MASK) == DRM_MODE_FLAG_3D_FRAME_PACKING)
clock *= 2;
if (clock > max_dotclk) return MODE_CLOCK_HIGH;
if (mode->flags & DRM_MODE_FLAG_DBLCLK) { if (!has_hdmi_sink) return MODE_CLOCK_LOW;
clock *= 2;
}
/* * HDMI2.1 requires higher resolution modes like 8k60, 4K120 to be * enumerated only if FRL is supported. Current platforms do not support * FRL so prune the higher resolution modes that require doctclock more * than 600MHz.
*/ if (clock > 600000) return MODE_CLOCK_HIGH;
staticint intel_hdmi_compute_bpc(struct intel_encoder *encoder, struct intel_crtc_state *crtc_state, int clock, bool respect_downstream_limits)
{ struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); int bpc;
/* * pipe_bpp could already be below 8bpc due to FDI * bandwidth constraints. HDMI minimum is 8bpc however.
*/
bpc = max(crtc_state->pipe_bpp / 3, 8);
/* * We will never exceed downstream TMDS clock limits while * attempting deep color. If the user insists on forcing an * out of spec mode they will have to be satisfied with 8bpc.
*/ if (!respect_downstream_limits)
bpc = 8;
for (; bpc >= 8; bpc -= 2) { int tmds_clock = intel_hdmi_tmds_clock(clock, bpc,
crtc_state->sink_format);
/* * pipe_bpp could already be below 8bpc due to * FDI bandwidth constraints. We shouldn't bump it * back up to the HDMI minimum 8bpc in that case.
*/
crtc_state->pipe_bpp = min(crtc_state->pipe_bpp, bpc * 3);
/* * Our YCbCr output is always limited range. * crtc_state->limited_color_range only applies to RGB, * and it must never be set for YCbCr or we risk setting * some conflicting bits in TRANSCONF which will mess up * the colors on the monitor.
*/ if (crtc_state->output_format != INTEL_OUTPUT_FORMAT_RGB) returnfalse;
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.