summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorTimur Kristóf <timur.kristof@gmail.com>2026-01-26 22:08:33 +0100
committerAlex Deucher <alexander.deucher@amd.com>2026-02-23 14:28:32 -0500
commit9f556ac2e3c2a175685186355bc0f815b12929f2 (patch)
treee70f9315b3f80565b8fd310232882026070d9895 /drivers
parent88b2cbd2bc76d7c95003a3f027d9c2ffc14eb9c4 (diff)
drm/amd/display: Add DCE HWSS support for external DP bridge encoders
Some GPUs use external DP bridge encoders NUTMEG and TRAVIS to implement analog and/or LVDS connections. Typically found in CIK APU based laptops or on FM2 motherboards that have analog connectors. These were necessary at the time because Kaveri didn't have a built-in DAC nor LVDS support. These devices sadly don't work transparently and need to be controlled by the driver. Implement the necessary control for the NUTMEG and TRAVIS encoders in the DCE HWSS. For reference, see the legacy non-DC amdgpu display code: amdgpu_atombios_encoder_setup_external_encoder() amdgpu_atombios_encoder_setup_dig() amdgpu_atombios_encoder_setup_ext_encoder_ddc() - Prepare DDC before using it: Call the EXTERNAL_ENCODER_CONTROL_DDC_SETUP action so that the encoder knows to set up DDC over the AUX channel. - When a stream is enabled or disabled: Call the EXTERNAL_ENCODER_CONTROL_ENABLE/DISABLE actions. - Before enabling the DP link: Call the EXTERNAL_ENCODER_CONTROL_SETUP action. This commit just hooks up the HWSS support. Detecting the external DP bridge encoders will be done in a subsequent commit. Signed-off-by: Timur Kristóf <timur.kristof@gmail.com> Reviewed-by: Alex Hung <alex.hung@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc.h4
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c53
2 files changed, 57 insertions, 0 deletions
diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h
index 71f2812acf59..eb9de0e48c63 100644
--- a/drivers/gpu/drm/amd/display/dc/dc.h
+++ b/drivers/gpu/drm/amd/display/dc/dc.h
@@ -1682,6 +1682,10 @@ struct dc_scratch_space {
struct panel_cntl *panel_cntl;
struct link_encoder *link_enc;
struct graphics_object_id link_id;
+
+ /* External encoder eg. NUTMEG or TRAVIS used on CIK APUs. */
+ struct graphics_object_id ext_enc_id;
+
/* Endpoint type distinguishes display endpoints which do not have entries
* in the BIOS connector table from those that do. Helps when tracking link
* encoder to display endpoint assignments.
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c
index cbc8c2f1b2ab..3313acf57553 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c
@@ -660,6 +660,48 @@ void dce110_update_info_frame(struct pipe_ctx *pipe_ctx)
}
}
+static void
+dce110_external_encoder_control(enum bp_external_encoder_control_action action,
+ struct dc_link *link,
+ struct dc_crtc_timing *timing)
+{
+ struct dc *dc = link->ctx->dc;
+ struct dc_bios *bios = link->ctx->dc_bios;
+ const struct dc_link_settings *link_settings = &link->cur_link_settings;
+ enum bp_result bp_result = BP_RESULT_OK;
+ struct bp_external_encoder_control ext_cntl = {
+ .action = action,
+ .connector_obj_id = link->link_enc->connector,
+ .encoder_id = link->ext_enc_id,
+ .lanes_number = link_settings->lane_count,
+ .link_rate = link_settings->link_rate,
+
+ /* Use signal type of the real link encoder, ie. DP */
+ .signal = link->connector_signal,
+
+ /* We don't know the timing yet when executing the SETUP action,
+ * so use a reasonably high default value. It seems that ENABLE
+ * can change the actual pixel clock but doesn't work with higher
+ * pixel clocks than what SETUP was called with.
+ */
+ .pixel_clock = timing ? timing->pix_clk_100hz / 10 : 300000,
+ .color_depth = timing ? timing->display_color_depth : COLOR_DEPTH_888,
+ };
+ DC_LOGGER_INIT();
+
+ bp_result = bios->funcs->external_encoder_control(bios, &ext_cntl);
+
+ if (bp_result != BP_RESULT_OK)
+ DC_LOG_ERROR("Failed to execute external encoder action: 0x%x\n", action);
+}
+
+static void
+dce110_prepare_ddc(struct dc_link *link)
+{
+ if (link->ext_enc_id.id)
+ dce110_external_encoder_control(EXTERNAL_ENCODER_CONTROL_DDC_SETUP, link, NULL);
+}
+
static bool
dce110_dac_load_detect(struct dc_link *link)
{
@@ -701,6 +743,8 @@ void dce110_enable_stream(struct pipe_ctx *pipe_ctx)
tg->funcs->set_early_control(tg, early_control);
+ if (link->ext_enc_id.id)
+ dce110_external_encoder_control(EXTERNAL_ENCODER_CONTROL_ENABLE, link, timing);
}
static enum bp_result link_transmitter_control(
@@ -1194,6 +1238,9 @@ void dce110_disable_stream(struct pipe_ctx *pipe_ctx)
dccg->funcs->disable_symclk_se(dccg, stream_enc->stream_enc_inst,
link_enc->transmitter - TRANSMITTER_UNIPHY_A);
}
+
+ if (link->ext_enc_id.id)
+ dce110_external_encoder_control(EXTERNAL_ENCODER_CONTROL_DISABLE, link, NULL);
}
void dce110_unblank_stream(struct pipe_ctx *pipe_ctx,
@@ -3328,6 +3375,11 @@ void dce110_enable_dp_link_output(
}
}
+ if (link->ext_enc_id.id) {
+ dce110_external_encoder_control(EXTERNAL_ENCODER_CONTROL_INIT, link, NULL);
+ dce110_external_encoder_control(EXTERNAL_ENCODER_CONTROL_SETUP, link, NULL);
+ }
+
if (dc->link_srv->dp_get_encoding_format(link_settings) == DP_8b_10b_ENCODING) {
if (dc->clk_mgr->funcs->notify_link_rate_change)
dc->clk_mgr->funcs->notify_link_rate_change(dc->clk_mgr, link);
@@ -3421,6 +3473,7 @@ static const struct hw_sequencer_funcs dce110_funcs = {
.enable_analog_link_output = dce110_enable_analog_link_output,
.disable_link_output = dce110_disable_link_output,
.dac_load_detect = dce110_dac_load_detect,
+ .prepare_ddc = dce110_prepare_ddc,
};
static const struct hwseq_private_funcs dce110_private_funcs = {