We support two kinds of analog connections: 1. VGA, which only supports analog signals: For VGA, we need to create a link encoder that only works with the DAC without perturbing any digital transmitter functionality. This is achieved by the new dce110_analog_link_encoder_construct.
2. DVI-I, which allows both digital and analog signals: The DC code base only allows 1 encoder per connector, and the preferred engine type is still going to be digital. So, for DVI-I to work, we need to make sure the pre-existing link encoder can also work with analog signals. Signed-off-by: Timur Kristóf <timur.kris...@gmail.com> --- .../drm/amd/display/dc/dce/dce_link_encoder.c | 100 ++++++++++++++++++ .../drm/amd/display/dc/dce/dce_link_encoder.h | 21 ++-- .../dc/resource/dce100/dce100_resource.c | 13 ++- .../dc/resource/dce60/dce60_resource.c | 16 ++- .../dc/resource/dce80/dce80_resource.c | 13 ++- 5 files changed, 152 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.c b/drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.c index 4a9d07c31bc5..0d5069773f57 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.c @@ -804,6 +804,24 @@ bool dce110_link_encoder_validate_dp_output( return true; } +static bool dce110_link_encoder_validate_rgb_output( + const struct dce110_link_encoder *enc110, + const struct dc_crtc_timing *crtc_timing) +{ + uint32_t max_pixel_clock_khz = 400000; + + if (enc110->base.ctx->dc_bios->fw_info_valid && + enc110->base.ctx->dc_bios->fw_info.max_pixel_clock) { + max_pixel_clock_khz = + enc110->base.ctx->dc_bios->fw_info.max_pixel_clock; + } + + if (crtc_timing->pix_clk_100hz > max_pixel_clock_khz * 10) + return false; + + return true; +} + void dce110_link_encoder_construct( struct dce110_link_encoder *enc110, const struct encoder_init_data *init_data, @@ -824,6 +842,7 @@ void dce110_link_encoder_construct( enc110->base.connector = init_data->connector; enc110->base.preferred_engine = ENGINE_ID_UNKNOWN; + enc110->base.analog_engine = init_data->analog_engine; enc110->base.features = *enc_features; @@ -847,6 +866,11 @@ void dce110_link_encoder_construct( SIGNAL_TYPE_EDP | SIGNAL_TYPE_HDMI_TYPE_A; + if ((enc110->base.connector.id == CONNECTOR_ID_DUAL_LINK_DVII || + enc110->base.connector.id == CONNECTOR_ID_SINGLE_LINK_DVII) && + enc110->base.analog_engine != ENGINE_ID_UNKNOWN) + enc110->base.output_signals |= SIGNAL_TYPE_RGB; + /* For DCE 8.0 and 8.1, by design, UNIPHY is hardwired to DIG_BE. * SW always assign DIG_FE 1:1 mapped to DIG_FE for non-MST UNIPHY. * SW assign DIG_FE to non-MST UNIPHY first and MST last. So prefer @@ -939,6 +963,10 @@ bool dce110_link_encoder_validate_output_with_stream( is_valid = dce110_link_encoder_validate_dp_output( enc110, &stream->timing); break; + case SIGNAL_TYPE_RGB: + is_valid = dce110_link_encoder_validate_rgb_output( + enc110, &stream->timing); + break; case SIGNAL_TYPE_EDP: case SIGNAL_TYPE_LVDS: is_valid = stream->timing.pixel_encoding == PIXEL_ENCODING_RGB; @@ -1034,6 +1062,8 @@ void dce110_link_encoder_setup( /* DP MST */ REG_UPDATE(DIG_BE_CNTL, DIG_MODE, 5); break; + case SIGNAL_TYPE_RGB: + break; default: ASSERT_CRITICAL(false); /* invalid mode ! */ @@ -1282,6 +1312,23 @@ void dce110_link_encoder_disable_output( struct bp_transmitter_control cntl = { 0 }; enum bp_result result; + switch (enc->analog_engine) { + case ENGINE_ID_DACA: + REG_UPDATE(DAC_ENABLE, DAC_ENABLE, 0); + break; + case ENGINE_ID_DACB: + /* DACB doesn't seem to be present on DCE6+, + * although there are references to it in the register file. + */ + DC_LOG_ERROR("%s DACB is unsupported\n", __func__); + break; + default: + break; + } + + if (enc->preferred_engine == enc->analog_engine) + return; + if (!dce110_is_dig_enabled(enc)) { /* OF_SKIP_POWER_DOWN_INACTIVE_ENCODER */ return; @@ -1726,6 +1773,7 @@ void dce60_link_encoder_construct( enc110->base.connector = init_data->connector; enc110->base.preferred_engine = ENGINE_ID_UNKNOWN; + enc110->base.analog_engine = init_data->analog_engine; enc110->base.features = *enc_features; @@ -1749,6 +1797,11 @@ void dce60_link_encoder_construct( SIGNAL_TYPE_EDP | SIGNAL_TYPE_HDMI_TYPE_A; + if ((enc110->base.connector.id == CONNECTOR_ID_DUAL_LINK_DVII || + enc110->base.connector.id == CONNECTOR_ID_SINGLE_LINK_DVII) && + enc110->base.analog_engine != ENGINE_ID_UNKNOWN) + enc110->base.output_signals |= SIGNAL_TYPE_RGB; + /* For DCE 8.0 and 8.1, by design, UNIPHY is hardwired to DIG_BE. * SW always assign DIG_FE 1:1 mapped to DIG_FE for non-MST UNIPHY. * SW assign DIG_FE to non-MST UNIPHY first and MST last. So prefer @@ -1814,3 +1867,50 @@ void dce60_link_encoder_construct( } } #endif + +static void dce110_analog_link_encoder_hw_init( + struct link_encoder *enc) +{ +} + +static void dce110_analog_link_encoder_setup( + struct link_encoder *enc, + enum signal_type signal) +{ +} + +static void dce110_analog_link_encoder_get_max_link_cap( + struct link_encoder *enc, + struct dc_link_settings *link_settings) +{ + memset(link_settings, 0, sizeof(struct dc_link_settings)); +} + +static const struct link_encoder_funcs dce110_an_lnk_enc_funcs = { + .validate_output_with_stream = + dce110_link_encoder_validate_output_with_stream, + .hw_init = dce110_analog_link_encoder_hw_init, + .setup = dce110_analog_link_encoder_setup, + .disable_output = dce110_link_encoder_disable_output, + .destroy = dce110_link_encoder_destroy, + .get_max_link_cap = dce110_analog_link_encoder_get_max_link_cap, +}; + +void dce110_analog_link_encoder_construct( + struct dce110_link_encoder *enc110, + const struct encoder_init_data *init_data, + const struct dce110_link_enc_registers *link_regs) +{ + enc110->base.funcs = &dce110_an_lnk_enc_funcs; + enc110->base.ctx = init_data->ctx; + enc110->base.id = init_data->encoder; + + enc110->base.hpd_source = init_data->hpd_source; + enc110->base.connector = init_data->connector; + enc110->base.preferred_engine = init_data->analog_engine; + enc110->base.analog_engine = init_data->analog_engine; + enc110->base.transmitter = init_data->transmitter; + enc110->base.output_signals = SIGNAL_TYPE_RGB; + + enc110->link_regs = link_regs; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.h b/drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.h index 261c70e01e33..1eda1a1a539c 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.h +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.h @@ -101,18 +101,21 @@ SRI(DP_SEC_CNTL, DP, id), \ SRI(DP_VID_STREAM_CNTL, DP, id), \ SRI(DP_DPHY_FAST_TRAINING, DP, id), \ - SRI(DP_SEC_CNTL1, DP, id) + SRI(DP_SEC_CNTL1, DP, id), \ + SR(DAC_ENABLE) #endif #define LE_DCE80_REG_LIST(id)\ SRI(DP_DPHY_INTERNAL_CTRL, DP, id), \ - LE_COMMON_REG_LIST_BASE(id) + LE_COMMON_REG_LIST_BASE(id), \ + SR(DAC_ENABLE) #define LE_DCE100_REG_LIST(id)\ LE_COMMON_REG_LIST_BASE(id), \ SRI(DP_DPHY_BS_SR_SWAP_CNTL, DP, id), \ SRI(DP_DPHY_INTERNAL_CTRL, DP, id), \ - SR(DCI_MEM_PWR_STATUS) + SR(DCI_MEM_PWR_STATUS), \ + SR(DAC_ENABLE) #define LE_DCE110_REG_LIST(id)\ LE_COMMON_REG_LIST_BASE(id), \ @@ -181,6 +184,9 @@ struct dce110_link_enc_registers { uint32_t DP_DPHY_BS_SR_SWAP_CNTL; uint32_t DP_DPHY_HBR2_PATTERN_CONTROL; uint32_t DP_SEC_CNTL1; + + /* DAC registers */ + uint32_t DAC_ENABLE; }; struct dce110_link_encoder { @@ -199,6 +205,11 @@ void dce110_link_encoder_construct( const struct dce110_link_enc_aux_registers *aux_regs, const struct dce110_link_enc_hpd_registers *hpd_regs); +void dce110_analog_link_encoder_construct( + struct dce110_link_encoder *enc110, + const struct encoder_init_data *init_data, + const struct dce110_link_enc_registers *link_regs); + #if defined(CONFIG_DRM_AMD_DC_SI) void dce60_link_encoder_construct( struct dce110_link_encoder *enc110, @@ -215,10 +226,6 @@ bool dce110_link_encoder_validate_dvi_output( enum signal_type signal, const struct dc_crtc_timing *crtc_timing); -bool dce110_link_encoder_validate_rgb_output( - const struct dce110_link_encoder *enc110, - const struct dc_crtc_timing *crtc_timing); - bool dce110_link_encoder_validate_dp_output( const struct dce110_link_encoder *enc110, const struct dc_crtc_timing *crtc_timing); diff --git a/drivers/gpu/drm/amd/display/dc/resource/dce100/dce100_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dce100/dce100_resource.c index 9e70e920eb69..6a4c1b47f80d 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dce100/dce100_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dce100/dce100_resource.c @@ -224,6 +224,7 @@ static const struct dce110_link_enc_registers link_enc_regs[] = { link_regs(4), link_regs(5), link_regs(6), + { .DAC_ENABLE = mmDAC_ENABLE }, }; #define stream_enc_regs(id)\ @@ -629,7 +630,17 @@ static struct link_encoder *dce100_link_encoder_create( kzalloc(sizeof(struct dce110_link_encoder), GFP_KERNEL); int link_regs_id; - if (!enc110 || enc_init_data->hpd_source >= ARRAY_SIZE(link_enc_hpd_regs)) + if (!enc110) + return NULL; + + if (enc_init_data->connector.id == CONNECTOR_ID_VGA) { + dce110_analog_link_encoder_construct(enc110, + enc_init_data, + &link_enc_regs[ENGINE_ID_DACA]); + return &enc110->base; + } + + if (enc_init_data->hpd_source >= ARRAY_SIZE(link_enc_hpd_regs)) return NULL; link_regs_id = diff --git a/drivers/gpu/drm/amd/display/dc/resource/dce60/dce60_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dce60/dce60_resource.c index 29ccfbddb492..98775e5ef0bf 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dce60/dce60_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dce60/dce60_resource.c @@ -239,7 +239,9 @@ static const struct dce110_link_enc_registers link_enc_regs[] = { link_regs(2), link_regs(3), link_regs(4), - link_regs(5) + link_regs(5), + {0}, + { .DAC_ENABLE = mmDAC_ENABLE }, }; #define stream_enc_regs(id)\ @@ -725,7 +727,17 @@ static struct link_encoder *dce60_link_encoder_create( kzalloc(sizeof(struct dce110_link_encoder), GFP_KERNEL); int link_regs_id; - if (!enc110 || enc_init_data->hpd_source >= ARRAY_SIZE(link_enc_hpd_regs)) + if (!enc110) + return NULL; + + if (enc_init_data->connector.id == CONNECTOR_ID_VGA) { + dce110_analog_link_encoder_construct(enc110, + enc_init_data, + &link_enc_regs[ENGINE_ID_DACA]); + return &enc110->base; + } + + if (enc_init_data->hpd_source >= ARRAY_SIZE(link_enc_hpd_regs)) return NULL; link_regs_id = diff --git a/drivers/gpu/drm/amd/display/dc/resource/dce80/dce80_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dce80/dce80_resource.c index f90e51696bda..7fde2b8719c7 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dce80/dce80_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dce80/dce80_resource.c @@ -240,6 +240,7 @@ static const struct dce110_link_enc_registers link_enc_regs[] = { link_regs(4), link_regs(5), link_regs(6), + { .DAC_ENABLE = mmDAC_ENABLE }, }; #define stream_enc_regs(id)\ @@ -731,7 +732,17 @@ static struct link_encoder *dce80_link_encoder_create( kzalloc(sizeof(struct dce110_link_encoder), GFP_KERNEL); int link_regs_id; - if (!enc110 || enc_init_data->hpd_source >= ARRAY_SIZE(link_enc_hpd_regs)) + if (!enc110) + return NULL; + + if (enc_init_data->connector.id == CONNECTOR_ID_VGA) { + dce110_analog_link_encoder_construct(enc110, + enc_init_data, + &link_enc_regs[ENGINE_ID_DACA]); + return &enc110->base; + } + + if (enc_init_data->hpd_source >= ARRAY_SIZE(link_enc_hpd_regs)) return NULL; link_regs_id = -- 2.50.1