status_t detect_displays() { // reset known displays for (uint32 id = 0; id < MAX_DISPLAY; id++) { gDisplay[id]->attached = false; gDisplay[id]->powered = false; gDisplay[id]->foundRanges = false; } uint32 displayIndex = 0; for (uint32 id = 0; id < ATOM_MAX_SUPPORTED_DEVICE; id++) { if (gConnector[id]->valid == false) continue; if (displayIndex >= MAX_DISPLAY) continue; if (gConnector[id]->type == VIDEO_CONNECTOR_9DIN) { TRACE("%s: Skipping 9DIN connector (not yet supported)\n", __func__); continue; } // TODO: As DP aux transactions don't work yet, just use LVDS as a hack #if 0 if (gConnector[id]->encoderExternal.isDPBridge == true) { // If this is a DisplayPort Bridge, setup ddc on bus // TRAVIS (LVDS) or NUTMEG (VGA) TRACE("%s: is bridge, performing bridge DDC setup\n", __func__); encoder_external_setup(id, 23860, EXTERNAL_ENCODER_ACTION_V3_DDC_SETUP); gDisplay[displayIndex]->attached = true; } else if (gConnector[id]->type == VIDEO_CONNECTOR_LVDS) { #endif if (gConnector[id]->type == VIDEO_CONNECTOR_LVDS) { // If plain (non-DP) laptop LVDS, read mode info from AtomBIOS //TRACE("%s: non-DP laptop LVDS detected\n", __func__); gDisplay[displayIndex]->attached = connector_read_mode_lvds(id, &gDisplay[displayIndex]->preferredMode); } // If no display found yet, try more standard detection methods if (gDisplay[displayIndex]->attached == false) { TRACE("%s: bit-banging ddc for EDID on connector %" B_PRIu32 "\n", __func__, id); // Lets try bit-banging edid from connector gDisplay[displayIndex]->attached = connector_read_edid(id, &gDisplay[displayIndex]->edidData); // Since DVI-I shows up as two connectors, and there is only one // edid channel, we have to make *sure* the edid data received is // valid for te connector. // Found EDID data? if (gDisplay[displayIndex]->attached) { TRACE("%s: found EDID data on connector %" B_PRIu32 "\n", __func__, id); bool analogEncoder = gConnector[id]->encoder.type == VIDEO_ENCODER_TVDAC || gConnector[id]->encoder.type == VIDEO_ENCODER_DAC; edid1_info* edid = &gDisplay[displayIndex]->edidData; if (!edid->display.input_type && analogEncoder) { // If non-digital EDID + the encoder is analog... TRACE("%s: connector %" B_PRIu32 " has non-digital EDID " "and a analog encoder.\n", __func__, id); gDisplay[displayIndex]->attached = encoder_analog_load_detect(id); } else if (edid->display.input_type && !analogEncoder) { // If EDID is digital, we make an assumption here. TRACE("%s: connector %" B_PRIu32 " has digital EDID " "and is not a analog encoder.\n", __func__, id); } else { // This generally means the monitor is of poor design // Since we *know* there is no load on the analog encoder // we assume that it is a digital display. TRACE("%s: Warning: monitor on connector %" B_PRIu32 " has " "false digital EDID flag and unloaded analog encoder!\n", __func__, id); } } } if (gDisplay[displayIndex]->attached != true) { // Nothing interesting here, move along continue; } // We found a valid / attached display gDisplay[displayIndex]->connectorIndex = id; // Populate physical connector index from gConnector init_registers(gDisplay[displayIndex]->regs, displayIndex); if (gDisplay[displayIndex]->preferredMode.virtual_width > 0) { // Found a single preferred mode gDisplay[displayIndex]->foundRanges = false; } else { // Use edid data and pull ranges if (detect_crt_ranges(displayIndex) == B_OK) gDisplay[displayIndex]->foundRanges = true; } displayIndex++; } // fallback if no attached monitors were found if (displayIndex == 0) { // This is a hack, however as we don't support HPD just yet, // it tries to prevent a "no displays" situation. ERROR("%s: ERROR: 0 attached monitors were found on display connectors." " Injecting first connector as a last resort.\n", __func__); for (uint32 id = 0; id < ATOM_MAX_SUPPORTED_DEVICE; id++) { // skip TV DAC connectors as likely fallback isn't for TV if (gConnector[id]->encoder.type == VIDEO_ENCODER_TVDAC) continue; gDisplay[0]->attached = true; gDisplay[0]->connectorIndex = id; init_registers(gDisplay[0]->regs, 0); if (detect_crt_ranges(0) == B_OK) gDisplay[0]->foundRanges = true; break; } } // Initial boot state is the first two crtc's powered if (gDisplay[0]->attached == true) gDisplay[0]->powered = true; if (gDisplay[1]->attached == true) gDisplay[1]->powered = true; return B_OK; } void debug_displays() { TRACE("Currently detected monitors===============\n"); for (uint32 id = 0; id < MAX_DISPLAY; id++) { ERROR("Display #%" B_PRIu32 " attached = %s\n", id, gDisplay[id]->attached ? "true" : "false"); uint32 connectorIndex = gDisplay[id]->connectorIndex; if (gDisplay[id]->attached) { uint32 connectorType = gConnector[connectorIndex]->type; uint32 encoderType = gConnector[connectorIndex]->encoder.type; ERROR(" + connector ID: %" B_PRIu32 "\n", connectorIndex); ERROR(" + connector type: %s\n", get_connector_name(connectorType)); ERROR(" + encoder type: %s\n", get_encoder_name(encoderType)); ERROR(" + limits: Vert Min/Max: %" B_PRIu32 "/%" B_PRIu32"\n", gDisplay[id]->vfreqMin, gDisplay[id]->vfreqMax); ERROR(" + limits: Horz Min/Max: %" B_PRIu32 "/%" B_PRIu32"\n", gDisplay[id]->hfreqMin, gDisplay[id]->hfreqMax); } } TRACE("==========================================\n"); }
status_t detect_displays() { // reset known displays for (uint32 id = 0; id < MAX_DISPLAY; id++) { gDisplay[id]->attached = false; gDisplay[id]->powered = false; gDisplay[id]->foundRanges = false; } uint32 displayIndex = 0; for (uint32 id = 0; id < ATOM_MAX_SUPPORTED_DEVICE; id++) { if (gConnector[id]->valid == false) continue; if (displayIndex >= MAX_DISPLAY) continue; if (gConnector[id]->type == VIDEO_CONNECTOR_9DIN) { TRACE("%s: connector(%" B_PRIu32 "): Skipping 9DIN connector " "(not yet supported)\n", __func__, id); continue; } if (gConnector[id]->type == VIDEO_CONNECTOR_DP) { TRACE("%s: connector(%" B_PRIu32 "): Checking DP.\n", __func__, id); edid1_info* edid = &gDisplay[displayIndex]->edidData; gDisplay[displayIndex]->attached = ddc2_dp_read_edid1(id, edid); if (gDisplay[displayIndex]->attached) { TRACE("%s: connector(%" B_PRIu32 "): Found DisplayPort EDID!\n", __func__); } } // TODO: Handle external DP brides - ?? #if 0 if (gConnector[id]->encoderExternal.isDPBridge == true) { // If this is a DisplayPort Bridge, setup ddc on bus // TRAVIS (LVDS) or NUTMEG (VGA) TRACE("%s: is bridge, performing bridge DDC setup\n", __func__); encoder_external_setup(id, 23860, EXTERNAL_ENCODER_ACTION_V3_DDC_SETUP); gDisplay[displayIndex]->attached = true; // TODO: DDC Router switching for DisplayPort (and others?) } #endif if (gConnector[id]->type == VIDEO_CONNECTOR_LVDS) { display_mode preferredMode; bool lvdsInfoFound = connector_read_mode_lvds(id, &preferredMode); TRACE("%s: connector(%" B_PRIu32 "): bit-banging LVDS for EDID.\n", __func__, id); gDisplay[displayIndex]->attached = connector_read_edid(id, &gDisplay[displayIndex]->edidData); if (!gDisplay[displayIndex]->attached && lvdsInfoFound) { // If we didn't find ddc edid data, fallback to lvdsInfo // We have to call connector_read_mode_lvds first to // collect SS data for the lvds connector TRACE("%s: connector(%" B_PRIu32 "): using AtomBIOS LVDS_Info " "preferred mode\n", __func__, id); gDisplay[displayIndex]->attached = true; memcpy(&gDisplay[displayIndex]->preferredMode, &preferredMode, sizeof(display_mode)); } } // If no display found yet, try more standard detection methods if (gDisplay[displayIndex]->attached == false) { TRACE("%s: connector(%" B_PRIu32 "): bit-banging ddc for EDID.\n", __func__, id); // Bit-bang edid from connector gDisplay[displayIndex]->attached = connector_read_edid(id, &gDisplay[displayIndex]->edidData); // Found EDID data? if (gDisplay[displayIndex]->attached) { TRACE("%s: connector(%" B_PRIu32 "): found EDID data.\n", __func__, id); if (gConnector[id]->type == VIDEO_CONNECTOR_DVII || gConnector[id]->type == VIDEO_CONNECTOR_HDMIB) { // These connectors can share gpio pins for data // communication between digital and analog encoders // (DVI-I is most common) edid1_info* edid = &gDisplay[displayIndex]->edidData; bool analogEncoder = gConnector[id]->encoder.type == VIDEO_ENCODER_TVDAC || gConnector[id]->encoder.type == VIDEO_ENCODER_DAC; bool digitalEncoder = gConnector[id]->encoder.type == VIDEO_ENCODER_TMDS; bool digitalEdid = edid->display.input_type ? true : false; if (digitalEdid && analogEncoder) { // Digital EDID + analog encoder? Lets try a load test gDisplay[displayIndex]->attached = encoder_analog_load_detect(id); } else if (!digitalEdid && digitalEncoder) { // non-digital EDID + digital encoder? Nope. gDisplay[displayIndex]->attached = false; } // Else... everything aligns as it should and attached = 1 } } } if (gDisplay[displayIndex]->attached != true) { // Nothing interesting here, move along continue; } // We found a valid / attached display gDisplay[displayIndex]->connectorIndex = id; // Populate physical connector index from gConnector init_registers(gDisplay[displayIndex]->regs, displayIndex); if (gDisplay[displayIndex]->preferredMode.virtual_width > 0) { // Found a single preferred mode gDisplay[displayIndex]->foundRanges = false; } else { // Use edid data and pull ranges if (detect_crt_ranges(displayIndex) == B_OK) gDisplay[displayIndex]->foundRanges = true; } displayIndex++; } // fallback if no attached monitors were found if (displayIndex == 0) { // This is a hack, however as we don't support HPD just yet, // it tries to prevent a "no displays" situation. ERROR("%s: ERROR: 0 attached monitors were found on display connectors." " Injecting first connector as a last resort.\n", __func__); for (uint32 id = 0; id < ATOM_MAX_SUPPORTED_DEVICE; id++) { // skip TV DAC connectors as likely fallback isn't for TV if (gConnector[id]->encoder.type == VIDEO_ENCODER_TVDAC) continue; gDisplay[0]->attached = true; gDisplay[0]->connectorIndex = id; init_registers(gDisplay[0]->regs, 0); if (detect_crt_ranges(0) == B_OK) gDisplay[0]->foundRanges = true; break; } } // Initial boot state is the first two crtc's powered if (gDisplay[0]->attached == true) gDisplay[0]->powered = true; if (gDisplay[1]->attached == true) gDisplay[1]->powered = true; return B_OK; }
status_t detect_displays() { // reset known displays for (uint32 id = 0; id < MAX_DISPLAY; id++) { gDisplay[id]->attached = false; gDisplay[id]->powered = false; gDisplay[id]->foundRanges = false; } uint32 displayIndex = 0; for (uint32 id = 0; id < ATOM_MAX_SUPPORTED_DEVICE; id++) { if (gConnector[id]->valid == false) continue; if (displayIndex >= MAX_DISPLAY) continue; if (gConnector[id]->type == VIDEO_CONNECTOR_9DIN) { TRACE("%s: connector(%" B_PRIu32 "): Skipping 9DIN connector " "(not yet supported)\n", __func__, id); continue; } if (gConnector[id]->type == VIDEO_CONNECTOR_DP) { TRACE("%s: connector(%" B_PRIu32 "): Checking DP.\n", __func__, id); edid1_info* edid = &gDisplay[displayIndex]->edidData; gDisplay[displayIndex]->attached = ddc2_dp_read_edid1(id, edid); if (gDisplay[displayIndex]->attached) { TRACE("%s: connector(%" B_PRIu32 "): Found DisplayPort EDID!\n", __func__); } } // TODO: Handle external DP brides - ?? #if 0 if (gConnector[id]->encoderExternal.isDPBridge == true) { // If this is a DisplayPort Bridge, setup ddc on bus // TRAVIS (LVDS) or NUTMEG (VGA) TRACE("%s: is bridge, performing bridge DDC setup\n", __func__); encoder_external_setup(id, 23860, EXTERNAL_ENCODER_ACTION_V3_DDC_SETUP); gDisplay[displayIndex]->attached = true; // TODO: DDC Router switching for DisplayPort (and others?) } #endif if (gConnector[id]->type == VIDEO_CONNECTOR_LVDS) { // If plain (non-DP) laptop LVDS, read mode info from AtomBIOS //TRACE("%s: non-DP laptop LVDS detected\n", __func__); gDisplay[displayIndex]->attached = connector_read_mode_lvds(id, &gDisplay[displayIndex]->preferredMode); if (gDisplay[displayIndex]->attached) { TRACE("%s: connector(%" B_PRIu32 "): found LVDS preferred " "mode\n", __func__, id); } } // If no display found yet, try more standard detection methods if (gDisplay[displayIndex]->attached == false) { TRACE("%s: connector(%" B_PRIu32 "): bit-banging ddc for EDID.\n", __func__, id); // Lets try bit-banging edid from connector gDisplay[displayIndex]->attached = connector_read_edid(id, &gDisplay[displayIndex]->edidData); // Since DVI-I shows up as two connectors, and there is only one // edid channel, we have to make *sure* the edid data received is // valid for the connector. // Found EDID data? if (gDisplay[displayIndex]->attached) { TRACE("%s: connector(%" B_PRIu32 "): found EDID data.\n", __func__, id); bool analogEncoder = gConnector[id]->encoder.type == VIDEO_ENCODER_TVDAC || gConnector[id]->encoder.type == VIDEO_ENCODER_DAC; edid1_info* edid = &gDisplay[displayIndex]->edidData; if (!edid->display.input_type && analogEncoder) { // If non-digital EDID + the encoder is analog... TRACE("%s: connector(%" B_PRIu32 "): has non-digital EDID " "and a analog encoder.\n", __func__, id); gDisplay[displayIndex]->attached = encoder_analog_load_detect(id); remove_dup_displays(displayIndex, id); } else if (edid->display.input_type && !analogEncoder) { // If EDID is digital, we make an assumption here. TRACE("%s: connector(%" B_PRIu32 "): has digital EDID " "and is not a analog encoder.\n", __func__, id); } else { // This generally means the monitor is of poor design // Since we *know* there is no load on the analog encoder // we assume that it is a digital display. // This can also occur when a display has both DVI and VGA // inputs and the graphics board has a DVI-I connector // (reported as both digital and analog connectors) and the // analog connection is the one in use. In that case, we // get here when checking the digital connector and want // to disable that display in favor of the analog one. TRACE("%s: connector(%" B_PRIu32 "): Warning: monitor has " "false digital EDID flag + unloaded analog encoder!\n", __func__, id); gDisplay[displayIndex]->attached = false; } } } if (gDisplay[displayIndex]->attached != true) { // Nothing interesting here, move along continue; } // We found a valid / attached display gDisplay[displayIndex]->connectorIndex = id; // Populate physical connector index from gConnector init_registers(gDisplay[displayIndex]->regs, displayIndex); if (gDisplay[displayIndex]->preferredMode.virtual_width > 0) { // Found a single preferred mode gDisplay[displayIndex]->foundRanges = false; } else { // Use edid data and pull ranges if (detect_crt_ranges(displayIndex) == B_OK) gDisplay[displayIndex]->foundRanges = true; } displayIndex++; } // fallback if no attached monitors were found if (displayIndex == 0) { // This is a hack, however as we don't support HPD just yet, // it tries to prevent a "no displays" situation. ERROR("%s: ERROR: 0 attached monitors were found on display connectors." " Injecting first connector as a last resort.\n", __func__); for (uint32 id = 0; id < ATOM_MAX_SUPPORTED_DEVICE; id++) { // skip TV DAC connectors as likely fallback isn't for TV if (gConnector[id]->encoder.type == VIDEO_ENCODER_TVDAC) continue; gDisplay[0]->attached = true; gDisplay[0]->connectorIndex = id; init_registers(gDisplay[0]->regs, 0); if (detect_crt_ranges(0) == B_OK) gDisplay[0]->foundRanges = true; break; } } // Initial boot state is the first two crtc's powered if (gDisplay[0]->attached == true) gDisplay[0]->powered = true; if (gDisplay[1]->attached == true) gDisplay[1]->powered = true; return B_OK; }