From 46bbf334f635469d824a2c42340a326121e158e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Duval?= Date: Sun, 1 May 2022 20:34:43 +0200 Subject: [PATCH] intel_extreme: parse VBT device configs, use this to skip unused ports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * BDB version from 111 * for DDI from Gen9 * for HDMI and DisplayPort from Gen6 * use the first port to create the mode list * also probe DDI Port A * the aux channel helps to select the correct dp aux registers. Change-Id: I80549a6ec0477bed768cc5f388959b606d50c1b7 Reviewed-on: https://review.haiku-os.org/c/haiku/+/5286 Tested-by: Commit checker robot Reviewed-by: Jérôme Duval --- .../graphics/intel_extreme/intel_extreme.h | 102 ++++++++++++++- .../accelerants/intel_extreme/Ports.cpp | 122 +++++++++++++++++- src/add-ons/accelerants/intel_extreme/Ports.h | 3 + .../accelerants/intel_extreme/accelerant.cpp | 2 +- .../accelerants/intel_extreme/mode.cpp | 4 +- .../drivers/graphics/intel_extreme/bios.cpp | 40 +++++- .../graphics/intel_extreme/intel_extreme.cpp | 5 +- .../intel_extreme/intel_extreme_private.h | 2 +- 8 files changed, 270 insertions(+), 10 deletions(-) diff --git a/headers/private/graphics/intel_extreme/intel_extreme.h b/headers/private/graphics/intel_extreme/intel_extreme.h index 34bbc8d95e..5ebd38b3d1 100644 --- a/headers/private/graphics/intel_extreme/intel_extreme.h +++ b/headers/private/graphics/intel_extreme/intel_extreme.h @@ -285,7 +285,104 @@ struct ring_buffer { uint8* base; }; -struct overlay_registers; + +struct child_device_config { + uint16 handle; + uint16 device_type; + uint8 device_id[10]; + uint16 addin_offset; + uint8 dvo_port; + uint8 i2c_pin; + uint8 slave_addr; + uint8 ddc_pin; + uint16 edid_ptr; + uint8 dvo_cfg; + + struct { + bool efp_routed:1; + bool lane_reversal:1; + bool lspcon:1; + bool iboost:1; + bool hpd_invert:1; + bool use_vbt_vswing:1; + uint8 reserved:2; + bool hdmi_support:1; + bool dp_support:1; + bool tmds_support:1; + uint8 reserved2:5; + uint8 aux_channel; + uint8 dongle_detect; + } __attribute__((packed)); + + uint8 caps; + uint8 dvo_wiring; + uint8 dvo2_wiring; + uint16 extended_type; + uint8 dvo_function; + + bool dp_usb_type_c:1; + bool tbt:1; + uint8 reserved3:2; + uint8 dp_port_trace_length:4; + uint8 dp_gpio_index; + uint8 dp_gpio_pin_num; + uint8 dp_iboost_level:4; + uint8 hdmi_iboost_level:4; + uint8 dp_max_link_rate:3; + uint8 dp_max_link_rate_reserved:5; +} __attribute__((packed)); + + +enum dvo_port { + DVO_PORT_HDMIA, + DVO_PORT_HDMIB, + DVO_PORT_HDMIC, + DVO_PORT_HDMID, + DVO_PORT_LVDS, + DVO_PORT_TV, + DVO_PORT_CRT, + DVO_PORT_DPB, + DVO_PORT_DPC, + DVO_PORT_DPD, + DVO_PORT_DPA, + DVO_PORT_DPE, + DVO_PORT_HDMIE, + DVO_PORT_DPF, + DVO_PORT_HDMIF, + DVO_PORT_DPG, + DVO_PORT_HDMIG, + DVO_PORT_DPH, + DVO_PORT_HDMIH, + DVO_PORT_DPI, + DVO_PORT_HDMII, +}; + + +enum dp_aux_channel { + DP_AUX_A = 0x40, + DP_AUX_B = 0x10, + DP_AUX_C = 0x20, + DP_AUX_D = 0x30, + DP_AUX_E = 0x50, + DP_AUX_F = 0x60, + DP_AUX_G = 0x70, + DP_AUX_H = 0x80, + DP_AUX_I = 0x90 +}; + + +enum aux_channel { + AUX_CH_A, + AUX_CH_B, + AUX_CH_C, + AUX_CH_D, + AUX_CH_E, + AUX_CH_F, + AUX_CH_G, + AUX_CH_H, + AUX_CH_I, +}; + struct intel_shared_info { area_id mode_list_area; // area containing display mode list @@ -345,6 +442,9 @@ struct intel_shared_info { edid1_info vesa_edid_info; bool has_vesa_edid_info; + + uint32 device_config_count; + child_device_config device_configs[10]; }; enum pipe_index { diff --git a/src/add-ons/accelerants/intel_extreme/Ports.cpp b/src/add-ons/accelerants/intel_extreme/Ports.cpp index 04aaa612a8..2971e44fe7 100644 --- a/src/add-ons/accelerants/intel_extreme/Ports.cpp +++ b/src/add-ons/accelerants/intel_extreme/Ports.cpp @@ -400,6 +400,63 @@ Port::_SetI2CSignals(void* cookie, int clock, int data) } +bool +Port::_IsPortInVBT(uint32* foundIndex) +{ + // check VBT mapping + bool found = false; + const uint32 deviceConfigCount = gInfo->shared_info->device_config_count; + for (uint32 i = 0; i < deviceConfigCount; i++) { + child_device_config& config = gInfo->shared_info->device_configs[i]; + if (config.dvo_port > DVO_PORT_HDMII) { + ERROR("%s: DVO port unknown\n", __func__); + continue; + } + dvo_port port = (dvo_port)config.dvo_port; + switch (PortIndex()) { + case INTEL_PORT_A: + found = port == DVO_PORT_HDMIA || port == DVO_PORT_DPA; + break; + case INTEL_PORT_B: + found = port == DVO_PORT_HDMIB || port == DVO_PORT_DPB; + break; + case INTEL_PORT_C: + found = port == DVO_PORT_HDMIC || port == DVO_PORT_DPC; + break; + case INTEL_PORT_D: + found = port == DVO_PORT_HDMID || port == DVO_PORT_DPD; + break; + case INTEL_PORT_E: + found = port == DVO_PORT_HDMIE || port == DVO_PORT_DPE || port == DVO_PORT_CRT; + break; + case INTEL_PORT_F: + found = port == DVO_PORT_HDMIF || port == DVO_PORT_DPF; + break; + default: + ERROR("%s: DDI port unknown\n", __func__); + break; + } + if (found) { + if (foundIndex != NULL) + *foundIndex = i; + break; + } + } + return found; +} + + +bool +Port::_IsDisplayPortInVBT() +{ + uint32 foundIndex = 0; + if (!_IsPortInVBT(&foundIndex)) + return false; + child_device_config& config = gInfo->shared_info->device_configs[foundIndex]; + return config.aux_channel > 0; +} + + // #pragma mark - Analog Port @@ -956,6 +1013,16 @@ HDMIPort::IsConnected() if (portRegister == 0) return false; + const uint32 deviceConfigCount = gInfo->shared_info->device_config_count; + if (gInfo->shared_info->device_type.Generation() >= 6 && deviceConfigCount > 0) { + // check VBT mapping + if (!_IsPortInVBT()) { + TRACE("%s: %s: port not found in VBT\n", __func__, PortName()); + return false; + } else + TRACE("%s: %s: port found in VBT\n", __func__, PortName()); + } + //Notes: //- DISPLAY_MONITOR_PORT_DETECTED does only tell you *some* sort of digital display is // connected to the port *if* you have the AUX channel stuff under power. It does not @@ -1128,6 +1195,16 @@ DisplayPort::IsConnected() if (portRegister == 0) return false; + const uint32 deviceConfigCount = gInfo->shared_info->device_config_count; + if (gInfo->shared_info->device_type.Generation() >= 6 && deviceConfigCount > 0) { + // check VBT mapping + if (!_IsPortInVBT()) { + TRACE("%s: %s: port not found in VBT\n", __func__, PortName()); + return false; + } else + TRACE("%s: %s: port found in VBT\n", __func__, PortName()); + } + //Notes: //- DISPLAY_MONITOR_PORT_DETECTED does only tell you *some* sort of digital display is // connected to the port *if* you have the AUX channel stuff under power. It does not @@ -1181,6 +1258,13 @@ status_t DigitalDisplayInterface::SetupI2c(i2c_bus *bus) { CALLED(); + + const uint32 deviceConfigCount = gInfo->shared_info->device_config_count; + if (gInfo->shared_info->device_type.Generation() >= 9 && deviceConfigCount > 0) { + if (!_IsDisplayPortInVBT()) + return Port::SetupI2c(bus); + } + ddc2_init_timing(bus); bus->cookie = this; bus->send_receive = &_DpAuxSendReceiveHook; @@ -1428,9 +1512,9 @@ DigitalDisplayInterface::_DpAuxTransfer(uint8* transmitBuffer, uint8 transmitSiz addr_t channelData[5]; if (gInfo->shared_info->device_type.Generation() >= 9) { // assume AUX channel 0 - channelControl = DP_AUX_CH_CTL(0); + channelControl = DP_AUX_CH_CTL(_DpAuxChannel()); for (int i = 0; i < 5; i++) - channelData[i] = DP_AUX_CH_DATA(0, i); + channelData[i] = DP_AUX_CH_DATA(_DpAuxChannel(), i); } else { ERROR("DigitalDisplayInterface::_DpAuxTransfer() unknown register config\n"); return B_BUSY; @@ -1521,6 +1605,30 @@ done: } +aux_channel +DigitalDisplayInterface::_DpAuxChannel() +{ + uint32 foundIndex = 0; + if (!_IsPortInVBT(&foundIndex)) + return AUX_CH_A; + child_device_config& config = gInfo->shared_info->device_configs[foundIndex]; + switch (config.aux_channel) { + case DP_AUX_B: + return AUX_CH_B; + case DP_AUX_C: + return AUX_CH_C; + case DP_AUX_D: + return AUX_CH_D; + case DP_AUX_E: + return AUX_CH_E; + case DP_AUX_F: + return AUX_CH_F; + default: + return AUX_CH_A; + } +} + + addr_t DisplayPort::_PortRegister() { @@ -2066,6 +2174,16 @@ DigitalDisplayInterface::IsConnected() } } + const uint32 deviceConfigCount = gInfo->shared_info->device_config_count; + if (gInfo->shared_info->device_type.Generation() >= 9 && deviceConfigCount > 0) { + // check VBT mapping + if (!_IsPortInVBT()) { + TRACE("%s: %s: port not found in VBT\n", __func__, PortName()); + return false; + } else + TRACE("%s: %s: port found in VBT\n", __func__, PortName()); + } + TRACE("%s: %s Maximum Lanes: %" B_PRId8 "\n", __func__, PortName(), fMaxLanes); diff --git a/src/add-ons/accelerants/intel_extreme/Ports.h b/src/add-ons/accelerants/intel_extreme/Ports.h index c06598a186..f23de95384 100644 --- a/src/add-ons/accelerants/intel_extreme/Ports.h +++ b/src/add-ons/accelerants/intel_extreme/Ports.h @@ -75,6 +75,8 @@ static status_t _GetI2CSignals(void* cookie, int* _clock, int* _data); static status_t _SetI2CSignals(void* cookie, int clock, int data); + bool _IsPortInVBT(uint32* foundIndex = NULL); + bool _IsDisplayPortInVBT(); display_mode fCurrentMode; Pipe* fPipe; @@ -239,6 +241,7 @@ static status_t _DpAuxSendReceiveHook(const struct i2c_bus *bus, uint32 slave_address, const uint8 *writeBuffer, size_t writeLength, uint8 *readBuffer, size_t readLength); + aux_channel _DpAuxChannel(); }; diff --git a/src/add-ons/accelerants/intel_extreme/accelerant.cpp b/src/add-ons/accelerants/intel_extreme/accelerant.cpp index db02a8a2ec..d47bba9ea8 100644 --- a/src/add-ons/accelerants/intel_extreme/accelerant.cpp +++ b/src/add-ons/accelerants/intel_extreme/accelerant.cpp @@ -301,7 +301,7 @@ probe_ports() // Digital Display Interface (for DP, HDMI, DVI and eDP) if (gInfo->shared_info->device_type.HasDDI()) { - for (int i = INTEL_PORT_B; i <= INTEL_PORT_F; i++) { + for (int i = INTEL_PORT_A; i <= INTEL_PORT_F; i++) { TRACE("Probing DDI %d\n", i); Port* ddiPort diff --git a/src/add-ons/accelerants/intel_extreme/mode.cpp b/src/add-ons/accelerants/intel_extreme/mode.cpp index 4ee2bf103a..0777fd7b42 100644 --- a/src/add-ons/accelerants/intel_extreme/mode.cpp +++ b/src/add-ons/accelerants/intel_extreme/mode.cpp @@ -201,8 +201,10 @@ create_mode_list(void) continue; status_t status = gInfo->ports[i]->GetEDID(&gInfo->edid_info); - if (status == B_OK) + if (status == B_OK) { gInfo->has_edid = true; + break; + } } // use EDID found at boot time if there since we don't have any ourselves if (!gInfo->has_edid && gInfo->shared_info->has_vesa_edid_info) { diff --git a/src/add-ons/kernel/drivers/graphics/intel_extreme/bios.cpp b/src/add-ons/kernel/drivers/graphics/intel_extreme/bios.cpp index 24de7395e5..ad8dd01dad 100644 --- a/src/add-ons/kernel/drivers/graphics/intel_extreme/bios.cpp +++ b/src/add-ons/kernel/drivers/graphics/intel_extreme/bios.cpp @@ -44,6 +44,7 @@ struct bdb_header { enum bdb_block_id { + BDB_GENERAL_DEFINITIONS = 2, BDB_LVDS_OPTIONS = 40, BDB_LVDS_LFP_DATA_PTRS = 41, BDB_LVDS_BACKLIGHT = 43, @@ -51,6 +52,17 @@ enum bdb_block_id { }; +struct bdb_general_definitions { + uint8 id; + uint16 size; + uint8 crt_ddc_gmbus_pin; + uint8 dpms_bits; + uint8 boot_display[2]; + uint8 child_device_size; + uint8 devices[]; +} __attribute__((packed)); + + // FIXME the struct definition for the bdb_header is not complete, so we rely // on direct access with hardcoded offsets to get the timings out of it. #define _PIXEL_CLOCK(x) (x[0] + (x[1] << 8)) * 10000 @@ -422,8 +434,11 @@ sanitize_panel_timing(display_timing& timing) bool -get_lvds_mode_from_bios(display_timing* panelTiming, uint16* minBrightness) +parse_vbt_from_bios(struct intel_shared_info* info) { + display_timing* panelTiming = &info->panel_timing; + uint16* minBrightness = &info->min_brightness; + int vbtOffset = 0; if (!get_bios(&vbtOffset)) return false; @@ -450,6 +465,29 @@ get_lvds_mode_from_bios(display_timing* panelTiming, uint16* minBrightness) int id = vbios.memory[start]; blockSize = vbios.ReadWord(start + 1) + 3; switch (id) { + case BDB_GENERAL_DEFINITIONS: + { + info->device_config_count = 0; + struct bdb_general_definitions* defs; + if (bdb->version < 111) + break; + defs = (struct bdb_general_definitions*)(vbios.memory + start); + uint8 childDeviceSize = defs->child_device_size; + uint32 device_config_count = (blockSize - sizeof(*defs)) / childDeviceSize; + for (uint32 i = 0; i < device_config_count; i++) { + child_device_config* config = + (child_device_config*)(&defs->devices[i * childDeviceSize]); + if (config->device_type == 0) + continue; + memcpy(&info->device_configs[info->device_config_count], config, + min_c(sizeof(child_device_config), childDeviceSize)); + TRACE((DEVICE_NAME ": found child device type: 0x%x\n", config->device_type)); + info->device_config_count++; + if (info->device_config_count > 10) + break; + } + break; + } case BDB_LVDS_OPTIONS: { struct lvds_bdb1 *lvds1; diff --git a/src/add-ons/kernel/drivers/graphics/intel_extreme/intel_extreme.cpp b/src/add-ons/kernel/drivers/graphics/intel_extreme/intel_extreme.cpp index 797dcd9487..1043e6f8f7 100644 --- a/src/add-ons/kernel/drivers/graphics/intel_extreme/intel_extreme.cpp +++ b/src/add-ons/kernel/drivers/graphics/intel_extreme/intel_extreme.cpp @@ -679,9 +679,8 @@ intel_extreme_init(intel_info &info) info.shared_info->dpms_mode = B_DPMS_ON; info.shared_info->min_brightness = 2; - // Pull VBIOS panel mode for later use - info.shared_info->got_vbt = get_lvds_mode_from_bios( - &info.shared_info->panel_timing, &info.shared_info->min_brightness); + // Pull VBIOS info for later use + info.shared_info->got_vbt = parse_vbt_from_bios(info.shared_info); /* at least 855gm can't drive more than one head at time */ if (info.device_type.InFamily(INTEL_FAMILY_8xx)) diff --git a/src/add-ons/kernel/drivers/graphics/intel_extreme/intel_extreme_private.h b/src/add-ons/kernel/drivers/graphics/intel_extreme/intel_extreme_private.h index bb58cae97d..1c85eaab9b 100644 --- a/src/add-ons/kernel/drivers/graphics/intel_extreme/intel_extreme_private.h +++ b/src/add-ons/kernel/drivers/graphics/intel_extreme/intel_extreme_private.h @@ -72,7 +72,7 @@ find_reg(const intel_info& info, uint32 target) } -extern bool get_lvds_mode_from_bios(display_timing *timing, uint16 *minBrightness); +extern bool parse_vbt_from_bios(struct intel_shared_info* info); extern status_t intel_free_memory(intel_info& info, addr_t offset); extern status_t intel_allocate_memory(intel_info& info, size_t size, size_t alignment, uint32 flags, addr_t* _offset,