OSDN Git Service

drm/amd/display: linux enable oled panel support dc part
authorHersen Wu <hersenxs.wu@amd.com>
Tue, 14 Jan 2020 20:39:07 +0000 (15:39 -0500)
committerAlex Deucher <alexander.deucher@amd.com>
Thu, 6 Feb 2020 20:04:38 +0000 (15:04 -0500)
[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.wu@amd.com>
Reviewed-by: Harry Wentland <Harry.Wentland@amd.com>
Reviewed-by: Hersen Wu <hersenxs.wu@amd.com>
Acked-by: Bhawanpreet Lakha <Bhawanpreet.Lakha@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
drivers/gpu/drm/amd/display/dc/core/dc_link.c
drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c
drivers/gpu/drm/amd/display/dc/dc.h
drivers/gpu/drm/amd/display/dc/dc_dp_types.h
drivers/gpu/drm/amd/display/dc/dc_link.h
drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c
drivers/gpu/drm/amd/display/dc/inc/dc_link_dp.h

index fe50747..bed5705 100644 (file)
@@ -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;
 }
 
index 07b9aa1..c5b45d1 100644 (file)
 
 #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;
+}
index e52a469..6e2e5e2 100644 (file)
@@ -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"
 
 /*******************************************************************************
index dfe4472..f762f35 100644 (file)
@@ -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 {
index d256031..5f341e9 100644 (file)
@@ -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);
index 56d4ec7..28b681b 100644 (file)
@@ -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)
index 8b1f0ce..e94e5fb 100644 (file)
@@ -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);