Skip to content

Commit e45d60a

Browse files
Timur Kristófalexdeucher
authored andcommitted
drm/amd/display: Add analog link detection (v2)
Analog displays typically have a DDC connection which can be used by the GPU to read EDID. This commit adds the capability to probe analog displays using DDC, reading the EDID header and deciding whether the analog link is connected based on the data that was read. Note that VGA has no HPD (hotplug detection), so we need to to do analog link detection for VGA before checking HPD. In case of DVI-I, while the connector supports HPD, not all analog cables connect the HPD pins, so we can't rely on HPD either. For reference, see the legacy display code: amdgpu_connector_vga_detect amdgpu_display_ddc_probe DAC load detection will be implemented in a separate commit. v2: Fix crash / black screen on newer GPUs during link detection. Ignore HPD pin for analog connectors. Signed-off-by: Timur Kristóf <timur.kristof@gmail.com> Reviewed-by: Harry Wentland <harry.wentland@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
1 parent 3be26d8 commit e45d60a

4 files changed

Lines changed: 101 additions & 7 deletions

File tree

drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_dio.c

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,9 @@ void setup_dio_stream_encoder(struct pipe_ctx *pipe_ctx)
5858
return;
5959
}
6060

61-
link_enc->funcs->connect_dig_be_to_fe(link_enc,
62-
pipe_ctx->stream_res.stream_enc->id, true);
61+
if (!dc_is_rgb_signal(pipe_ctx->stream->signal))
62+
link_enc->funcs->connect_dig_be_to_fe(link_enc,
63+
pipe_ctx->stream_res.stream_enc->id, true);
6364
if (dc_is_dp_signal(pipe_ctx->stream->signal))
6465
pipe_ctx->stream->ctx->dc->link_srv->dp_trace_source_sequence(pipe_ctx->stream->link,
6566
DPCD_SOURCE_SEQ_AFTER_CONNECT_DIG_FE_BE);
@@ -98,10 +99,13 @@ void reset_dio_stream_encoder(struct pipe_ctx *pipe_ctx)
9899
if (stream_enc->funcs->enable_stream)
99100
stream_enc->funcs->enable_stream(stream_enc,
100101
pipe_ctx->stream->signal, false);
101-
link_enc->funcs->connect_dig_be_to_fe(
102-
link_enc,
103-
pipe_ctx->stream_res.stream_enc->id,
104-
false);
102+
103+
if (!dc_is_rgb_signal(pipe_ctx->stream->signal))
104+
link_enc->funcs->connect_dig_be_to_fe(
105+
link_enc,
106+
pipe_ctx->stream_res.stream_enc->id,
107+
false);
108+
105109
if (dc_is_dp_signal(pipe_ctx->stream->signal))
106110
pipe_ctx->stream->ctx->dc->link_srv->dp_trace_source_sequence(
107111
pipe_ctx->stream->link,

drivers/gpu/drm/amd/display/dc/link/link_detection.c

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -862,6 +862,48 @@ static void verify_link_capability(struct dc_link *link, struct dc_sink *sink,
862862
verify_link_capability_non_destructive(link);
863863
}
864864

865+
/**
866+
* link_detect_evaluate_edid_header() - Evaluate if an EDID header is acceptable.
867+
*
868+
* Evaluates an 8-byte EDID header to check if it's good enough
869+
* for the purpose of determining whether a display is connected
870+
* without reading the full EDID.
871+
*/
872+
static bool link_detect_evaluate_edid_header(uint8_t edid_header[8])
873+
{
874+
int edid_header_score = 0;
875+
int i;
876+
877+
for (i = 0; i < 8; ++i)
878+
edid_header_score += edid_header[i] == ((i == 0 || i == 7) ? 0x00 : 0xff);
879+
880+
return edid_header_score >= 6;
881+
}
882+
883+
/**
884+
* link_detect_ddc_probe() - Probe the DDC to see if a display is connected.
885+
*
886+
* Detect whether a display is connected to DDC without reading full EDID.
887+
* Reads only the EDID header (the first 8 bytes of EDID) from DDC and
888+
* evaluates whether that matches.
889+
*/
890+
static bool link_detect_ddc_probe(struct dc_link *link)
891+
{
892+
if (!link->ddc)
893+
return false;
894+
895+
uint8_t edid_header[8] = {0};
896+
bool ddc_probed = i2c_read(link->ddc, 0x50, edid_header, sizeof(edid_header));
897+
898+
if (!ddc_probed)
899+
return false;
900+
901+
if (!link_detect_evaluate_edid_header(edid_header))
902+
return false;
903+
904+
return true;
905+
}
906+
865907
/*
866908
* detect_link_and_local_sink() - Detect if a sink is attached to a given link
867909
*
@@ -946,6 +988,12 @@ static bool detect_link_and_local_sink(struct dc_link *link,
946988
break;
947989
}
948990

991+
case SIGNAL_TYPE_RGB: {
992+
sink_caps.transaction_type = DDC_TRANSACTION_TYPE_I2C;
993+
sink_caps.signal = SIGNAL_TYPE_RGB;
994+
break;
995+
}
996+
949997
case SIGNAL_TYPE_LVDS: {
950998
sink_caps.transaction_type = DDC_TRANSACTION_TYPE_I2C;
951999
sink_caps.signal = SIGNAL_TYPE_LVDS;
@@ -1139,9 +1187,17 @@ static bool detect_link_and_local_sink(struct dc_link *link,
11391187
sink = prev_sink;
11401188
prev_sink = NULL;
11411189
}
1142-
query_hdcp_capability(sink->sink_signal, link);
1190+
1191+
if (!sink->edid_caps.analog)
1192+
query_hdcp_capability(sink->sink_signal, link);
11431193
}
11441194

1195+
/* DVI-I connector connected to analog display. */
1196+
if ((link->link_id.id == CONNECTOR_ID_DUAL_LINK_DVII ||
1197+
link->link_id.id == CONNECTOR_ID_SINGLE_LINK_DVII) &&
1198+
sink->edid_caps.analog)
1199+
sink->sink_signal = SIGNAL_TYPE_RGB;
1200+
11451201
/* HDMI-DVI Dongle */
11461202
if (sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A &&
11471203
!sink->edid_caps.edid_hdmi)
@@ -1238,6 +1294,23 @@ static bool detect_link_and_local_sink(struct dc_link *link,
12381294
return true;
12391295
}
12401296

1297+
/**
1298+
* link_detect_analog() - Determines if an analog sink is connected.
1299+
*/
1300+
static bool link_detect_analog(struct dc_link *link, enum dc_connection_type *type)
1301+
{
1302+
/* Don't care about connectors that don't support an analog signal. */
1303+
ASSERT(dc_connector_supports_analog(link->link_id.id));
1304+
1305+
if (link_detect_ddc_probe(link)) {
1306+
*type = dc_connection_single;
1307+
return true;
1308+
}
1309+
1310+
*type = dc_connection_none;
1311+
return true;
1312+
}
1313+
12411314
/*
12421315
* link_detect_connection_type() - Determine if there is a sink connected
12431316
*
@@ -1254,6 +1327,17 @@ bool link_detect_connection_type(struct dc_link *link, enum dc_connection_type *
12541327
return true;
12551328
}
12561329

1330+
/* Ignore the HPD pin (if any) for analog connectors.
1331+
* Instead rely on DDC.
1332+
*
1333+
* - VGA connectors don't have any HPD at all.
1334+
* - Some DVI-A cables don't connect the HPD pin.
1335+
* - Some DVI-A cables pull up the HPD pin.
1336+
* (So it's high even when no display is connected.)
1337+
*/
1338+
if (dc_connector_supports_analog(link->link_id.id))
1339+
return link_detect_analog(link, type);
1340+
12571341
if (link->connector_signal == SIGNAL_TYPE_EDP) {
12581342
/*in case it is not on*/
12591343
if (!link->dc->config.edp_no_power_sequencing)

drivers/gpu/drm/amd/display/dc/link/link_dpms.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2258,6 +2258,9 @@ static enum dc_status enable_link(
22582258
enable_link_lvds(pipe_ctx);
22592259
status = DC_OK;
22602260
break;
2261+
case SIGNAL_TYPE_RGB:
2262+
status = DC_OK;
2263+
break;
22612264
case SIGNAL_TYPE_VIRTUAL:
22622265
status = enable_link_virtual(pipe_ctx);
22632266
break;

drivers/gpu/drm/amd/display/dc/link/link_factory.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,9 @@ static bool construct_phy(struct dc_link *link,
583583
case CONNECTOR_ID_DUAL_LINK_DVII:
584584
link->connector_signal = SIGNAL_TYPE_DVI_DUAL_LINK;
585585
break;
586+
case CONNECTOR_ID_VGA:
587+
link->connector_signal = SIGNAL_TYPE_RGB;
588+
break;
586589
case CONNECTOR_ID_DISPLAY_PORT:
587590
case CONNECTOR_ID_MXM:
588591
case CONNECTOR_ID_USBC:

0 commit comments

Comments
 (0)