intel_extreme: parse VBT device configs, use this to skip unused ports

* 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 <no-reply+buildbot@haiku-os.org>
Reviewed-by: Jérôme Duval <jerome.duval@gmail.com>
This commit is contained in:
Jérôme Duval 2022-05-01 20:34:43 +02:00
parent 2aac05508f
commit 46bbf334f6
8 changed files with 270 additions and 10 deletions

View File

@ -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 {

View File

@ -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);

View File

@ -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();
};

View File

@ -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

View File

@ -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) {

View File

@ -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;

View File

@ -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))

View File

@ -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,