diff options
Diffstat (limited to 'drivers/gpu/drm/amd/display/dc/dcn31/dcn31_dio_link_encoder.c')
-rw-r--r-- | drivers/gpu/drm/amd/display/dc/dcn31/dcn31_dio_link_encoder.c | 202 |
1 files changed, 194 insertions, 8 deletions
diff --git a/drivers/gpu/drm/amd/display/dc/dcn31/dcn31_dio_link_encoder.c b/drivers/gpu/drm/amd/display/dc/dcn31/dcn31_dio_link_encoder.c index 90127c1f9e35..8f8eee475144 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn31/dcn31_dio_link_encoder.c +++ b/drivers/gpu/drm/amd/display/dc/dcn31/dcn31_dio_link_encoder.c @@ -37,6 +37,7 @@ #include "link_enc_cfg.h" #include "dc_dmub_srv.h" +#include "dal_asic_id.h" #define CTX \ enc10->base.ctx @@ -62,6 +63,10 @@ #define AUX_REG_WRITE(reg_name, val) \ dm_write_reg(CTX, AUX_REG(reg_name), val) +#ifndef MIN +#define MIN(X, Y) ((X) < (Y) ? (X) : (Y)) +#endif + void dcn31_link_encoder_set_dio_phy_mux( struct link_encoder *enc, enum encoder_type_select sel, @@ -215,8 +220,8 @@ static const struct link_encoder_funcs dcn31_link_enc_funcs = { .fec_is_active = enc2_fec_is_active, .get_dig_frontend = dcn10_get_dig_frontend, .get_dig_mode = dcn10_get_dig_mode, - .is_in_alt_mode = dcn20_link_encoder_is_in_alt_mode, - .get_max_link_cap = dcn20_link_encoder_get_max_link_cap, + .is_in_alt_mode = dcn31_link_encoder_is_in_alt_mode, + .get_max_link_cap = dcn31_link_encoder_get_max_link_cap, .set_dio_phy_mux = dcn31_link_encoder_set_dio_phy_mux, }; @@ -320,6 +325,10 @@ void dcn31_link_encoder_construct( enc10->base.features.flags.bits.IS_HBR3_CAPABLE = bp_cap_info.DP_HBR3_EN; enc10->base.features.flags.bits.HDMI_6GB_EN = bp_cap_info.HDMI_6GB_EN; + enc10->base.features.flags.bits.IS_DP2_CAPABLE = bp_cap_info.IS_DP2_CAPABLE; + enc10->base.features.flags.bits.IS_UHBR10_CAPABLE = bp_cap_info.DP_UHBR10_EN; + enc10->base.features.flags.bits.IS_UHBR13_5_CAPABLE = bp_cap_info.DP_UHBR13_5_EN; + enc10->base.features.flags.bits.IS_UHBR20_CAPABLE = bp_cap_info.DP_UHBR20_EN; enc10->base.features.flags.bits.DP_IS_USB_C = bp_cap_info.DP_IS_USB_C; } else { @@ -357,19 +366,79 @@ void dcn31_link_encoder_construct_minimal( SIGNAL_TYPE_EDP; } +/* DPIA equivalent of link_transmitter_control. */ +static bool link_dpia_control(struct dc_context *dc_ctx, + struct dmub_cmd_dig_dpia_control_data *dpia_control) +{ + union dmub_rb_cmd cmd; + struct dc_dmub_srv *dmub = dc_ctx->dmub_srv; + + memset(&cmd, 0, sizeof(cmd)); + + cmd.dig1_dpia_control.header.type = DMUB_CMD__DPIA; + cmd.dig1_dpia_control.header.sub_type = + DMUB_CMD__DPIA_DIG1_DPIA_CONTROL; + cmd.dig1_dpia_control.header.payload_bytes = + sizeof(cmd.dig1_dpia_control) - + sizeof(cmd.dig1_dpia_control.header); + + cmd.dig1_dpia_control.dpia_control = *dpia_control; + + dc_dmub_srv_cmd_queue(dmub, &cmd); + dc_dmub_srv_cmd_execute(dmub); + dc_dmub_srv_wait_idle(dmub); + + return true; +} + +static void link_encoder_disable(struct dcn10_link_encoder *enc10) +{ + /* reset training complete */ + REG_UPDATE(DP_LINK_CNTL, DP_LINK_TRAINING_COMPLETE, 0); +} + void dcn31_link_encoder_enable_dp_output( struct link_encoder *enc, const struct dc_link_settings *link_settings, enum clock_source_id clock_source) { + struct dcn10_link_encoder *enc10 = TO_DCN10_LINK_ENC(enc); + /* Enable transmitter and encoder. */ - if (!link_enc_cfg_is_transmitter_mappable(enc->ctx->dc->current_state, enc)) { + if (!link_enc_cfg_is_transmitter_mappable(enc->ctx->dc, enc)) { dcn20_link_encoder_enable_dp_output(enc, link_settings, clock_source); } else { - /** @todo Handle transmitter with programmable mapping to link encoder. */ + struct dmub_cmd_dig_dpia_control_data dpia_control = { 0 }; + struct dc_link *link; + + link = link_enc_cfg_get_link_using_link_enc(enc->ctx->dc, enc->preferred_engine); + + enc1_configure_encoder(enc10, link_settings); + + dpia_control.action = (uint8_t)TRANSMITTER_CONTROL_ENABLE; + dpia_control.enc_id = enc->preferred_engine; + dpia_control.mode_laneset.digmode = 0; /* 0 for SST; 5 for MST */ + dpia_control.lanenum = (uint8_t)link_settings->lane_count; + dpia_control.symclk_10khz = link_settings->link_rate * + LINK_RATE_REF_FREQ_IN_KHZ / 10; + /* DIG_BE_CNTL.DIG_HPD_SELECT set to 5 (hpdsel - 1) to indicate HPD pin + * unused by DPIA. + */ + dpia_control.hpdsel = 6; + + if (link) { + dpia_control.dpia_id = link->ddc_hw_inst; + dpia_control.fec_rdy = link->fec_state == dc_link_fec_ready ? 1 : 0; + } else { + DC_LOG_ERROR("%s: Failed to execute DPIA enable DMUB command.\n", __func__); + BREAK_TO_DEBUGGER(); + return; + } + + link_dpia_control(enc->ctx, &dpia_control); } } @@ -378,14 +447,43 @@ void dcn31_link_encoder_enable_dp_mst_output( const struct dc_link_settings *link_settings, enum clock_source_id clock_source) { + struct dcn10_link_encoder *enc10 = TO_DCN10_LINK_ENC(enc); + /* Enable transmitter and encoder. */ - if (!link_enc_cfg_is_transmitter_mappable(enc->ctx->dc->current_state, enc)) { + if (!link_enc_cfg_is_transmitter_mappable(enc->ctx->dc, enc)) { dcn10_link_encoder_enable_dp_mst_output(enc, link_settings, clock_source); } else { - /** @todo Handle transmitter with programmable mapping to link encoder. */ + struct dmub_cmd_dig_dpia_control_data dpia_control = { 0 }; + struct dc_link *link; + + link = link_enc_cfg_get_link_using_link_enc(enc->ctx->dc, enc->preferred_engine); + + enc1_configure_encoder(enc10, link_settings); + + dpia_control.action = (uint8_t)TRANSMITTER_CONTROL_ENABLE; + dpia_control.enc_id = enc->preferred_engine; + dpia_control.mode_laneset.digmode = 5; /* 0 for SST; 5 for MST */ + dpia_control.lanenum = (uint8_t)link_settings->lane_count; + dpia_control.symclk_10khz = link_settings->link_rate * + LINK_RATE_REF_FREQ_IN_KHZ / 10; + /* DIG_BE_CNTL.DIG_HPD_SELECT set to 5 (hpdsel - 1) to indicate HPD pin + * unused by DPIA. + */ + dpia_control.hpdsel = 6; + + if (link) { + dpia_control.dpia_id = link->ddc_hw_inst; + dpia_control.fec_rdy = link->fec_state == dc_link_fec_ready ? 1 : 0; + } else { + DC_LOG_ERROR("%s: Failed to execute DPIA enable DMUB command.\n", __func__); + BREAK_TO_DEBUGGER(); + return; + } + + link_dpia_control(enc->ctx, &dpia_control); } } @@ -393,14 +491,102 @@ void dcn31_link_encoder_disable_output( struct link_encoder *enc, enum signal_type signal) { + struct dcn10_link_encoder *enc10 = TO_DCN10_LINK_ENC(enc); + /* Disable transmitter and encoder. */ - if (!link_enc_cfg_is_transmitter_mappable(enc->ctx->dc->current_state, enc)) { + if (!link_enc_cfg_is_transmitter_mappable(enc->ctx->dc, enc)) { dcn10_link_encoder_disable_output(enc, signal); } else { - /** @todo Handle transmitter with programmable mapping to link encoder. */ + struct dmub_cmd_dig_dpia_control_data dpia_control = { 0 }; + struct dc_link *link; + + if (!dcn10_is_dig_enabled(enc)) + return; + + link = link_enc_cfg_get_link_using_link_enc(enc->ctx->dc, enc->preferred_engine); + + dpia_control.action = (uint8_t)TRANSMITTER_CONTROL_DISABLE; + dpia_control.enc_id = enc->preferred_engine; + if (signal == SIGNAL_TYPE_DISPLAY_PORT) { + dpia_control.mode_laneset.digmode = 0; /* 0 for SST; 5 for MST */ + } else if (signal == SIGNAL_TYPE_DISPLAY_PORT_MST) { + dpia_control.mode_laneset.digmode = 5; /* 0 for SST; 5 for MST */ + } else { + DC_LOG_ERROR("%s: USB4 DPIA only supports DisplayPort.\n", __func__); + BREAK_TO_DEBUGGER(); + } + + if (link) { + dpia_control.dpia_id = link->ddc_hw_inst; + } else { + DC_LOG_ERROR("%s: Failed to execute DPIA enable DMUB command.\n", __func__); + BREAK_TO_DEBUGGER(); + return; + } + + link_dpia_control(enc->ctx, &dpia_control); + + link_encoder_disable(enc10); } } +bool dcn31_link_encoder_is_in_alt_mode(struct link_encoder *enc) +{ + struct dcn10_link_encoder *enc10 = TO_DCN10_LINK_ENC(enc); + uint32_t dp_alt_mode_disable; + bool is_usb_c_alt_mode = false; + + if (enc->features.flags.bits.DP_IS_USB_C) { + if (enc->ctx->asic_id.hw_internal_rev != YELLOW_CARP_B0) { + // [Note] no need to check hw_internal_rev once phy mux selection is ready + REG_GET(RDPCSTX_PHY_CNTL6, RDPCS_PHY_DPALT_DISABLE, &dp_alt_mode_disable); + } else { + /* + * B0 phys use a new set of registers to check whether alt mode is disabled. + * if value == 1 alt mode is disabled, otherwise it is enabled. + */ + if ((enc10->base.transmitter == TRANSMITTER_UNIPHY_A) + || (enc10->base.transmitter == TRANSMITTER_UNIPHY_B) + || (enc10->base.transmitter == TRANSMITTER_UNIPHY_E)) { + REG_GET(RDPCSTX_PHY_CNTL6, RDPCS_PHY_DPALT_DISABLE, &dp_alt_mode_disable); + } else { + // [Note] need to change TRANSMITTER_UNIPHY_C/D to F/G once phy mux selection is ready + REG_GET(RDPCSPIPE_PHY_CNTL6, RDPCS_PHY_DPALT_DISABLE, &dp_alt_mode_disable); + } + } + + is_usb_c_alt_mode = (dp_alt_mode_disable == 0); + } + + return is_usb_c_alt_mode; +} + +void dcn31_link_encoder_get_max_link_cap(struct link_encoder *enc, + struct dc_link_settings *link_settings) +{ + struct dcn10_link_encoder *enc10 = TO_DCN10_LINK_ENC(enc); + uint32_t is_in_usb_c_dp4_mode = 0; + + dcn10_link_encoder_get_max_link_cap(enc, link_settings); + + /* in usb c dp2 mode, max lane count is 2 */ + if (enc->funcs->is_in_alt_mode && enc->funcs->is_in_alt_mode(enc)) { + if (enc->ctx->asic_id.hw_internal_rev != YELLOW_CARP_B0) { + // [Note] no need to check hw_internal_rev once phy mux selection is ready + REG_GET(RDPCSTX_PHY_CNTL6, RDPCS_PHY_DPALT_DP4, &is_in_usb_c_dp4_mode); + } else { + if ((enc10->base.transmitter == TRANSMITTER_UNIPHY_A) + || (enc10->base.transmitter == TRANSMITTER_UNIPHY_B) + || (enc10->base.transmitter == TRANSMITTER_UNIPHY_E)) { + REG_GET(RDPCSTX_PHY_CNTL6, RDPCS_PHY_DPALT_DP4, &is_in_usb_c_dp4_mode); + } else { + REG_GET(RDPCSPIPE_PHY_CNTL6, RDPCS_PHY_DPALT_DP4, &is_in_usb_c_dp4_mode); + } + } + if (!is_in_usb_c_dp4_mode) + link_settings->lane_count = MIN(LANE_COUNT_TWO, link_settings->lane_count); + } +} |