From: Hersen Wu <hersenxs...@amd.com> [Why] old panel has been enabled for window driver but not linux.
[How] enable oled panel support for linux. this patch is dc part. Signed-off-by: Hersen Wu <hersenxs...@amd.com> Reviewed-by: Harry Wentland <harry.wentl...@amd.com> Reviewed-by: Hersen Wu <hersenxs...@amd.com> Acked-by: Bhawanpreet Lakha <bhawanpreet.la...@amd.com> --- drivers/gpu/drm/amd/display/dc/core/dc_link.c | 32 +++- .../gpu/drm/amd/display/dc/core/dc_link_dp.c | 168 ++++++++++++++++++ drivers/gpu/drm/amd/display/dc/dc.h | 14 ++ drivers/gpu/drm/amd/display/dc/dc_dp_types.h | 34 ++++ drivers/gpu/drm/amd/display/dc/dc_link.h | 16 ++ .../display/dc/dce110/dce110_hw_sequencer.c | 14 ++ .../gpu/drm/amd/display/dc/inc/dc_link_dp.h | 2 + 7 files changed, 279 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link.c b/drivers/gpu/drm/amd/display/dc/core/dc_link.c index fe50747692ec..bed57051f04d 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_link.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_link.c @@ -599,6 +599,9 @@ static bool detect_dp( if (sink_caps->transaction_type == DDC_TRANSACTION_TYPE_I2C_OVER_AUX) { sink_caps->signal = SIGNAL_TYPE_DISPLAY_PORT; + + dpcd_set_source_specific_data(link); + if (!detect_dp_sink_caps(link)) return false; @@ -769,8 +772,16 @@ static bool dc_link_detect_helper(struct dc_link *link, if ((link->connector_signal == SIGNAL_TYPE_LVDS || link->connector_signal == SIGNAL_TYPE_EDP) && - link->local_sink) + link->local_sink) { + + // need to re-write OUI and brightness in resume case + if (link->connector_signal == SIGNAL_TYPE_EDP) { + dpcd_set_source_specific_data(link); + dc_link_set_default_brightness_aux(link); //TODO: use cached + } + return true; + } if (false == dc_link_detect_sink(link, &new_connection_type)) { BREAK_TO_DEBUGGER(); @@ -818,6 +829,10 @@ static bool dc_link_detect_helper(struct dc_link *link, } case SIGNAL_TYPE_EDP: { + read_current_link_settings_on_detect(link); + + dpcd_set_source_specific_data(link); + detect_edp_sink_caps(link); read_current_link_settings_on_detect(link); sink_caps.transaction_type = DDC_TRANSACTION_TYPE_I2C_OVER_AUX; @@ -1492,6 +1507,7 @@ static enum dc_status enable_link_dp( bool fec_enable; int i; bool apply_seamless_boot_optimization = false; + uint32_t bl_oled_enable_delay = 50; // in ms // check for seamless boot for (i = 0; i < state->stream_count; i++) { @@ -1515,6 +1531,9 @@ static enum dc_status enable_link_dp( if (state->clk_mgr && !apply_seamless_boot_optimization) state->clk_mgr->funcs->update_clocks(state->clk_mgr, state, false); + // during mode switch we do DP_SET_POWER off then on, and OUI is lost + dpcd_set_source_specific_data(link); + skip_video_pattern = true; if (link_settings.link_rate == LINK_RATE_LOW) @@ -1538,6 +1557,17 @@ static enum dc_status enable_link_dp( fec_enable = true; dp_set_fec_enable(link, fec_enable); + + // during mode set we do DP_SET_POWER off then on, aux writes are lost + if (link->dpcd_sink_ext_caps.bits.oled == 1 || + link->dpcd_sink_ext_caps.bits.sdr_aux_backlight_control == 1 || + link->dpcd_sink_ext_caps.bits.hdr_aux_backlight_control == 1) { + dc_link_set_default_brightness_aux(link); // TODO: use cached if known + if (link->dpcd_sink_ext_caps.bits.oled == 1) + msleep(bl_oled_enable_delay); + dc_link_backlight_enable_aux(link, true); + } + return status; } diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c b/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c index 07b9aa1d01af..c5b45d17e8cd 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c @@ -20,6 +20,14 @@ #define DP_REPEATER_CONFIGURATION_AND_STATUS_SIZE 0x50 +#define DP_SOURCE_TABLE_REVISION 0x310 +#define DP_SOURCE_PAYLOAD_SIZE 0x311 +#define DP_SOURCE_SINK_CAP 0x317 +#define DP_SOURCE_BACKLIGHT_LEVEL 0x320 +#define DP_SOURCE_BACKLIGHT_CURRENT_PEAK 0x326 +#define DP_SOURCE_BACKLIGHT_CONTROL 0x32E +#define DP_SOURCE_BACKLIGHT_ENABLE 0x32F + /* maximum pre emphasis level allowed for each voltage swing level*/ static const enum dc_pre_emphasis voltage_swing_to_pre_emphasis[] = { PRE_EMPHASIS_LEVEL3, @@ -3166,6 +3174,23 @@ static void dp_wa_power_up_0010FA(struct dc_link *link, uint8_t *dpcd_data, link->wa_flags.dp_keep_receiver_powered = false; } +/* Read additional sink caps defined in source specific DPCD area + * This function currently only reads from SinkCapability address (DP_SOURCE_SINK_CAP) + */ +static bool dpcd_read_sink_ext_caps(struct dc_link *link) +{ + uint8_t dpcd_data; + + if (!link) + return false; + + if (core_link_read_dpcd(link, DP_SOURCE_SINK_CAP, &dpcd_data, 1) != DC_OK) + return false; + + link->dpcd_sink_ext_caps.raw = dpcd_data; + return true; +} + static bool retrieve_link_cap(struct dc_link *link) { /* DP_ADAPTER_CAP - DP_DPCD_REV + 1 == 16 and also DP_DSC_BITS_PER_PIXEL_INC - DP_DSC_SUPPORT + 1 == 16, @@ -3438,6 +3463,9 @@ static bool retrieve_link_cap(struct dc_link *link) sizeof(link->dpcd_caps.dsc_caps.dsc_ext_caps.raw)); } + if (!dpcd_read_sink_ext_caps(link)) + link->dpcd_sink_ext_caps.raw = 0; + /* Connectivity log: detection */ CONN_DATA_DETECT(link, dpcd_data, sizeof(dpcd_data), "Rx Caps: "); @@ -3590,6 +3618,8 @@ void detect_edp_sink_caps(struct dc_link *link) } } link->verified_link_cap = link->reported_link_cap; + + dc_link_set_default_brightness_aux(link); } void dc_link_dp_enable_hpd(const struct dc_link *link) @@ -4147,3 +4177,141 @@ void dp_set_fec_enable(struct dc_link *link, bool enable) } } +void dpcd_set_source_specific_data(struct dc_link *link) +{ + struct dpcd_amd_signature amd_signature; + const uint32_t post_oui_delay = 30; // 30ms + + amd_signature.AMD_IEEE_TxSignature_byte1 = 0x0; + amd_signature.AMD_IEEE_TxSignature_byte2 = 0x0; + amd_signature.AMD_IEEE_TxSignature_byte3 = 0x1A; + amd_signature.device_id_byte1 = + (uint8_t)(link->ctx->asic_id.chip_id); + amd_signature.device_id_byte2 = + (uint8_t)(link->ctx->asic_id.chip_id >> 8); + memset(&amd_signature.zero, 0, 4); + amd_signature.dce_version = + (uint8_t)(link->ctx->dce_version); + amd_signature.dal_version_byte1 = 0x0; // needed? where to get? + amd_signature.dal_version_byte2 = 0x0; // needed? where to get? + + core_link_write_dpcd(link, DP_SOURCE_OUI, + (uint8_t *)(&amd_signature), + sizeof(amd_signature)); + + // Sink may need to configure internals based on vendor, so allow some + // time before proceeding with possibly vendor specific transactions + msleep(post_oui_delay); +} + +bool dc_link_set_backlight_level_nits(struct dc_link *link, + bool isHDR, + uint32_t backlight_millinits, + uint32_t transition_time_in_ms) +{ + struct dpcd_source_backlight_set dpcd_backlight_set; + uint8_t backlight_control = isHDR ? 1 : 0; + + if (!link || (link->connector_signal != SIGNAL_TYPE_EDP && + link->connector_signal != SIGNAL_TYPE_DISPLAY_PORT)) + return false; + + // OLEDs have no PWM, they can only use AUX + if (link->dpcd_sink_ext_caps.bits.oled == 1) + backlight_control = 1; + + *(uint32_t *)&dpcd_backlight_set.backlight_level_millinits = backlight_millinits; + *(uint16_t *)&dpcd_backlight_set.backlight_transition_time_ms = (uint16_t)transition_time_in_ms; + + + if (core_link_write_dpcd(link, DP_SOURCE_BACKLIGHT_LEVEL, + (uint8_t *)(&dpcd_backlight_set), + sizeof(dpcd_backlight_set)) != DC_OK) + return false; + + if (core_link_write_dpcd(link, DP_SOURCE_BACKLIGHT_CONTROL, + &backlight_control, 1) != DC_OK) + return false; + + return true; +} + +bool dc_link_get_backlight_level_nits(struct dc_link *link, + uint32_t *backlight_millinits_avg, + uint32_t *backlight_millinits_peak) +{ + union dpcd_source_backlight_get dpcd_backlight_get; + + memset(&dpcd_backlight_get, 0, sizeof(union dpcd_source_backlight_get)); + + if (!link || (link->connector_signal != SIGNAL_TYPE_EDP && + link->connector_signal != SIGNAL_TYPE_DISPLAY_PORT)) + return false; + + if (!core_link_read_dpcd(link, DP_SOURCE_BACKLIGHT_CURRENT_PEAK, + dpcd_backlight_get.raw, + sizeof(union dpcd_source_backlight_get))) + return false; + + *backlight_millinits_avg = + dpcd_backlight_get.bytes.backlight_millinits_avg; + *backlight_millinits_peak = + dpcd_backlight_get.bytes.backlight_millinits_peak; + + /* On non-supported panels dpcd_read usually succeeds with 0 returned */ + if (*backlight_millinits_avg == 0 || + *backlight_millinits_avg > *backlight_millinits_peak) + return false; + + return true; +} + +bool dc_link_backlight_enable_aux(struct dc_link *link, bool enable) +{ + uint8_t backlight_enable = enable ? 1 : 0; + + if (!link || (link->connector_signal != SIGNAL_TYPE_EDP && + link->connector_signal != SIGNAL_TYPE_DISPLAY_PORT)) + return false; + + if (core_link_write_dpcd(link, DP_SOURCE_BACKLIGHT_ENABLE, + &backlight_enable, 1) != DC_OK) + return false; + + return true; +} + +// we read default from 0x320 because we expect BIOS wrote it there +// regular get_backlight_nit reads from panel set at 0x326 +bool dc_link_read_default_bl_aux(struct dc_link *link, uint32_t *backlight_millinits) +{ + if (!link || (link->connector_signal != SIGNAL_TYPE_EDP && + link->connector_signal != SIGNAL_TYPE_DISPLAY_PORT)) + return false; + + if (!core_link_read_dpcd(link, DP_SOURCE_BACKLIGHT_LEVEL, + (uint8_t *) backlight_millinits, + sizeof(uint32_t))) + return false; + + return true; +} + +bool dc_link_set_default_brightness_aux(struct dc_link *link) +{ + uint32_t default_backlight; + + if (link && + (link->dpcd_sink_ext_caps.bits.hdr_aux_backlight_control == 1 || + link->dpcd_sink_ext_caps.bits.sdr_aux_backlight_control == 1)) { + if (!dc_link_read_default_bl_aux(link, &default_backlight)) + default_backlight = 150000; + // if < 5 nits or > 5000, it might be wrong readback + if (default_backlight < 5000 || default_backlight > 5000000) + default_backlight = 150000; // + + return dc_link_set_backlight_level_nits(link, true, + default_backlight, 0); + } + return false; +} diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h index e52a469b4672..6e2e5e24daaf 100644 --- a/drivers/gpu/drm/amd/display/dc/dc.h +++ b/drivers/gpu/drm/amd/display/dc/dc.h @@ -980,6 +980,20 @@ struct dpcd_caps { }; +union dpcd_sink_ext_caps { + struct { + /* 0 - Sink supports backlight adjust via PWM during SDR/HDR mode + * 1 - Sink supports backlight adjust via AUX during SDR/HDR mode. + */ + uint8_t sdr_aux_backlight_control : 1; + uint8_t hdr_aux_backlight_control : 1; + uint8_t reserved_1 : 2; + uint8_t oled : 1; + uint8_t reserved : 3; + } bits; + uint8_t raw; +}; + #include "dc_link.h" /******************************************************************************* diff --git a/drivers/gpu/drm/amd/display/dc/dc_dp_types.h b/drivers/gpu/drm/amd/display/dc/dc_dp_types.h index dfe4472c9e40..f762f35f529d 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_dp_types.h +++ b/drivers/gpu/drm/amd/display/dc/dc_dp_types.h @@ -432,6 +432,40 @@ struct dp_sink_hw_fw_revision { uint8_t ieee_fw_rev[2]; }; +struct dpcd_amd_signature { + uint8_t AMD_IEEE_TxSignature_byte1; + uint8_t AMD_IEEE_TxSignature_byte2; + uint8_t AMD_IEEE_TxSignature_byte3; + uint8_t device_id_byte1; + uint8_t device_id_byte2; + uint8_t zero[4]; + uint8_t dce_version; + uint8_t dal_version_byte1; + uint8_t dal_version_byte2; +}; + +struct dpcd_source_backlight_set { + struct { + uint8_t byte0; + uint8_t byte1; + uint8_t byte2; + uint8_t byte3; + } backlight_level_millinits; + + struct { + uint8_t byte0; + uint8_t byte1; + } backlight_transition_time_ms; +}; + +union dpcd_source_backlight_get { + struct { + uint32_t backlight_millinits_peak; /* 326h */ + uint32_t backlight_millinits_avg; /* 32Ah */ + } bytes; + uint8_t raw[8]; +}; + /*DPCD register of DP receiver capability field bits-*/ union edp_configuration_cap { struct { diff --git a/drivers/gpu/drm/amd/display/dc/dc_link.h b/drivers/gpu/drm/amd/display/dc/dc_link.h index d25603128394..5f341e960506 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_link.h +++ b/drivers/gpu/drm/amd/display/dc/dc_link.h @@ -128,6 +128,7 @@ struct dc_link { enum edp_revision edp_revision; bool psr_feature_enabled; bool psr_allow_active; + union dpcd_sink_ext_caps dpcd_sink_ext_caps; /* MST record stream using this link */ struct link_flags { @@ -178,6 +179,21 @@ bool dc_link_set_backlight_level(const struct dc_link *dc_link, uint32_t backlight_pwm_u16_16, uint32_t frame_ramp); +/* Set/get nits-based backlight level of an embedded panel (eDP, LVDS). */ +bool dc_link_set_backlight_level_nits(struct dc_link *link, + bool isHDR, + uint32_t backlight_millinits, + uint32_t transition_time_in_ms); + +bool dc_link_get_backlight_level_nits(struct dc_link *link, + uint32_t *backlight_millinits, + uint32_t *backlight_millinits_peak); + +bool dc_link_backlight_enable_aux(struct dc_link *link, bool enable); + +bool dc_link_read_default_bl_aux(struct dc_link *link, uint32_t *backlight_millinits); +bool dc_link_set_default_brightness_aux(struct dc_link *link); + int dc_link_get_backlight_level(const struct dc_link *dc_link); bool dc_link_set_abm_disable(const struct dc_link *dc_link); diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c index 56d4ec7bdad7..28b681b33f7a 100644 --- a/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c @@ -71,6 +71,8 @@ #define PANEL_POWER_UP_TIMEOUT 300 #define PANEL_POWER_DOWN_TIMEOUT 500 #define HPD_CHECK_INTERVAL 10 +#define OLED_POST_T7_DELAY 100 +#define OLED_PRE_T11_DELAY 150 #define CTX \ hws->ctx @@ -936,9 +938,21 @@ void dce110_edp_backlight_control( if (cntl.action == TRANSMITTER_CONTROL_BACKLIGHT_ON) edp_receiver_ready_T7(link); link_transmitter_control(ctx->dc_bios, &cntl); + + if (enable && link->dpcd_sink_ext_caps.bits.oled) + msleep(OLED_POST_T7_DELAY); + + if (link->dpcd_sink_ext_caps.bits.oled || + link->dpcd_sink_ext_caps.bits.hdr_aux_backlight_control == 1 || + link->dpcd_sink_ext_caps.bits.sdr_aux_backlight_control == 1) + dc_link_backlight_enable_aux(link, enable); + /*edp 1.2*/ if (cntl.action == TRANSMITTER_CONTROL_BACKLIGHT_OFF) edp_receiver_ready_T9(link); + + if (!enable && link->dpcd_sink_ext_caps.bits.oled) + msleep(OLED_PRE_T11_DELAY); } void dce110_enable_audio_stream(struct pipe_ctx *pipe_ctx) diff --git a/drivers/gpu/drm/amd/display/dc/inc/dc_link_dp.h b/drivers/gpu/drm/amd/display/dc/inc/dc_link_dp.h index 8b1f0ce6c2a7..e94e5fbf2aa2 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/dc_link_dp.h +++ b/drivers/gpu/drm/amd/display/dc/inc/dc_link_dp.h @@ -78,6 +78,8 @@ void dp_set_panel_mode(struct dc_link *link, enum dp_panel_mode panel_mode); bool dp_overwrite_extended_receiver_cap(struct dc_link *link); +void dpcd_set_source_specific_data(struct dc_link *link); + void dp_set_fec_ready(struct dc_link *link, bool ready); void dp_set_fec_enable(struct dc_link *link, bool enable); bool dp_set_dsc_enable(struct pipe_ctx *pipe_ctx, bool enable); -- 2.17.1 _______________________________________________ amd-gfx mailing list amd-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/amd-gfx