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 <[email protected]> --- drivers/gpu/drm/amd/display/dc/dc.h | 4 ++ .../amd/display/dc/hwss/dce110/dce110_hwseq.c | 54 +++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h index ff15e5990ec0..11c0fa317f88 100644 --- a/drivers/gpu/drm/amd/display/dc/dc.h +++ b/drivers/gpu/drm/amd/display/dc/dc.h @@ -1681,6 +1681,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 4e820020252b..3f5c833ce18a 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 void dce110_dac_encoder_control(struct pipe_ctx *pipe_ctx, bool enable) { @@ -717,6 +759,9 @@ void dce110_enable_stream(struct pipe_ctx *pipe_ctx) if (dc_is_rgb_signal(pipe_ctx->stream->signal)) dce110_dac_encoder_control(pipe_ctx, true); + + if (link->ext_enc_id.id) + dce110_external_encoder_control(EXTERNAL_ENCODER_CONTROL_ENABLE, link, timing); } static enum bp_result link_transmitter_control( @@ -1213,6 +1258,9 @@ void dce110_disable_stream(struct pipe_ctx *pipe_ctx) if (dc_is_rgb_signal(pipe_ctx->stream->signal)) dce110_dac_encoder_control(pipe_ctx, false); + + 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, @@ -3362,6 +3410,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); @@ -3454,6 +3507,7 @@ static const struct hw_sequencer_funcs dce110_funcs = { .enable_dp_link_output = dce110_enable_dp_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 = { -- 2.52.0
