diff --git a/src/add-ons/accelerants/radeon_hd/accelerant.h b/src/add-ons/accelerants/radeon_hd/accelerant.h index 4f5379a792..29651fa962 100644 --- a/src/add-ons/accelerants/radeon_hd/accelerant.h +++ b/src/add-ons/accelerants/radeon_hd/accelerant.h @@ -172,6 +172,7 @@ typedef struct { uint32 hfreqMin; edid1_info edidData; display_mode preferredMode; + display_mode currentMode; } display_info; diff --git a/src/add-ons/accelerants/radeon_hd/display.cpp b/src/add-ons/accelerants/radeon_hd/display.cpp index 7b212ee090..3400f083f4 100644 --- a/src/add-ons/accelerants/radeon_hd/display.cpp +++ b/src/add-ons/accelerants/radeon_hd/display.cpp @@ -3,7 +3,8 @@ * Distributed under the terms of the MIT License. * * Authors: - * Alexander von Gluck, kallisti5@unixzen.com + * Alexander von Gluck, kallisti5@unixzen.com + * Bill Randle, billr@neocat.org */ /* @@ -240,6 +241,29 @@ detect_crt_ranges(uint32 crtid) } +static void +remove_dup_displays(uint32 displayIndex, uint32 id) +{ + /* hack for both digital and analog interfaces active */ + if ((displayIndex > 0) && gDisplay[displayIndex]->attached) { + if (gConnector[id-1]->encoder.type == VIDEO_ENCODER_TMDS) { + int gpioID1 = gConnector[id-1]->gpioID; + int gpioID2 = gConnector[id]->gpioID; + edid1_info* edid = &gDisplay[displayIndex-1]->edidData; + + if ((gGPIOInfo[gpioID1]->hwPin == gGPIOInfo[gpioID2]->hwPin) && + edid->display.input_type) + // give preference to digital display when both are present + // and other display indicates it is digital + TRACE("%s: skipping connector %" B_PRIu32 + ": giving preference to digital " + "connector %d\n", __func__, id, id-1); + gDisplay[displayIndex]->attached = 0; + } + } +} + + status_t detect_displays() { @@ -264,19 +288,18 @@ detect_displays() } if (gConnector[id]->type == VIDEO_CONNECTOR_DP) { - edid1_info* edid = &gDisplay[displayIndex]->edidData; - TRACE("%s: connector(%" B_PRIu32 "): Checking DP.\n", __func__, id); - status_t dpHasEDID = ddc2_dp_read_edid1(id, edid); + + edid1_info* edid = &gDisplay[displayIndex]->edidData; gDisplay[displayIndex]->attached - = (dpHasEDID == B_OK ? true : false); + = ddc2_dp_read_edid1(id, edid); + if (gDisplay[displayIndex]->attached) { TRACE("%s: connector(%" B_PRIu32 "): Found DisplayPort EDID!\n", __func__); } } - - // TODO: As DP aux transactions don't work yet, just use LVDS as a hack + // TODO: Handle external DP brides - ?? #if 0 if (gConnector[id]->encoderExternal.isDPBridge == true) { // If this is a DisplayPort Bridge, setup ddc on bus @@ -287,11 +310,9 @@ detect_displays() gDisplay[displayIndex]->attached = true; // TODO: DDC Router switching for DisplayPort (and others?) - } else if (gConnector[id]->type == VIDEO_CONNECTOR_LVDS) { + } #endif - - if (gDisplay[displayIndex]->attached == false - && gConnector[id]->type == VIDEO_CONNECTOR_LVDS) { + 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, @@ -313,7 +334,7 @@ detect_displays() // 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. + // valid for the connector. // Found EDID data? if (gDisplay[displayIndex]->attached) { @@ -331,6 +352,7 @@ detect_displays() "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 " @@ -339,9 +361,16 @@ detect_displays() // 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; } } } @@ -558,7 +587,6 @@ display_crtc_scale(uint8 crtcID, display_mode* mode) void display_crtc_dpms(uint8 crtcID, int mode) { - radeon_shared_info &info = *gInfo->shared_info; switch (mode) { @@ -566,8 +594,8 @@ display_crtc_dpms(uint8 crtcID, int mode) TRACE("%s: crtc %" B_PRIu8 " dpms powerup\n", __func__, crtcID); if (gDisplay[crtcID]->attached == false) return; - gDisplay[crtcID]->powered = true; display_crtc_power(crtcID, ATOM_ENABLE); + gDisplay[crtcID]->powered = true; if (info.dceMajor >= 3) display_crtc_memreq(crtcID, ATOM_ENABLE); display_crtc_blank(crtcID, ATOM_BLANKING_OFF); @@ -588,12 +616,121 @@ display_crtc_dpms(uint8 crtcID, int mode) } +void +display_dce45_crtc_load_lut(uint8 crtcID) +{ + radeon_shared_info &info = *gInfo->shared_info; + register_info* regs = gDisplay[crtcID]->regs; + + TRACE("%s: crtcID %" B_PRIu8 "\n", __func__, crtcID); + + uint16* r = info.color_data; + uint16* g = r + 256; + uint16* b = r + 512; + + if (info.dceMajor >= 5) { + Write32(OUT, NI_INPUT_CSC_CONTROL + regs->crtcOffset, + (NI_INPUT_CSC_GRPH_MODE(NI_INPUT_CSC_BYPASS) | + NI_INPUT_CSC_OVL_MODE(NI_INPUT_CSC_BYPASS))); + Write32(OUT, NI_PRESCALE_GRPH_CONTROL + regs->crtcOffset, + NI_GRPH_PRESCALE_BYPASS); + Write32(OUT, NI_PRESCALE_OVL_CONTROL + regs->crtcOffset, + NI_OVL_PRESCALE_BYPASS); + Write32(OUT, NI_INPUT_GAMMA_CONTROL + regs->crtcOffset, + (NI_GRPH_INPUT_GAMMA_MODE(NI_INPUT_GAMMA_USE_LUT) | + NI_OVL_INPUT_GAMMA_MODE(NI_INPUT_GAMMA_USE_LUT))); + } + + Write32(OUT, EVERGREEN_DC_LUT_CONTROL + regs->crtcOffset, 0); + + Write32(OUT, EVERGREEN_DC_LUT_BLACK_OFFSET_BLUE + regs->crtcOffset, 0); + Write32(OUT, EVERGREEN_DC_LUT_BLACK_OFFSET_GREEN + regs->crtcOffset, 0); + Write32(OUT, EVERGREEN_DC_LUT_BLACK_OFFSET_RED + regs->crtcOffset, 0); + + Write32(OUT, EVERGREEN_DC_LUT_WHITE_OFFSET_BLUE + regs->crtcOffset, 0xffff); + Write32(OUT, EVERGREEN_DC_LUT_WHITE_OFFSET_GREEN + regs->crtcOffset, 0xffff); + Write32(OUT, EVERGREEN_DC_LUT_WHITE_OFFSET_RED + regs->crtcOffset, 0xffff); + + Write32(OUT, EVERGREEN_DC_LUT_RW_MODE, 0); + Write32(OUT, EVERGREEN_DC_LUT_WRITE_EN_MASK, 0x00000007); + + Write32(OUT, EVERGREEN_DC_LUT_RW_INDEX, 0); + for (int i = 0; i < 256; i++) { + Write32(OUT, EVERGREEN_DC_LUT_30_COLOR + regs->crtcOffset, + (r[i] << 20) | + (g[i] << 10) | + (b[i] << 0)); + } + + if (info.dceMajor >= 5) { + Write32(OUT, NI_DEGAMMA_CONTROL + regs->crtcOffset, + (NI_GRPH_DEGAMMA_MODE(NI_DEGAMMA_BYPASS) | + NI_OVL_DEGAMMA_MODE(NI_DEGAMMA_BYPASS) | + NI_ICON_DEGAMMA_MODE(NI_DEGAMMA_BYPASS) | + NI_CURSOR_DEGAMMA_MODE(NI_DEGAMMA_BYPASS))); + Write32(OUT, NI_GAMUT_REMAP_CONTROL + regs->crtcOffset, + (NI_GRPH_GAMUT_REMAP_MODE(NI_GAMUT_REMAP_BYPASS) | + NI_OVL_GAMUT_REMAP_MODE(NI_GAMUT_REMAP_BYPASS))); + Write32(OUT, NI_REGAMMA_CONTROL + regs->crtcOffset, + (NI_GRPH_REGAMMA_MODE(NI_REGAMMA_BYPASS) | + NI_OVL_REGAMMA_MODE(NI_REGAMMA_BYPASS))); + Write32(OUT, NI_OUTPUT_CSC_CONTROL + regs->crtcOffset, + (NI_OUTPUT_CSC_GRPH_MODE(NI_OUTPUT_CSC_BYPASS) | + NI_OUTPUT_CSC_OVL_MODE(NI_OUTPUT_CSC_BYPASS))); + /* XXX match this to the depth of the crtc fmt block, move to modeset? */ + Write32(OUT, 0x6940 + regs->crtcOffset, 0); + } +} + + +void +display_avivo_crtc_load_lut(uint8 crtcID) +{ + radeon_shared_info &info = *gInfo->shared_info; + register_info* regs = gDisplay[crtcID]->regs; + + TRACE("%s: crtcID %" B_PRIu8 "\n", __func__, crtcID); + + uint16* r = info.color_data; + uint16* g = r + 256; + uint16* b = r + 512; + + Write32(OUT, AVIVO_DC_LUTA_CONTROL + regs->crtcOffset, 0); + + Write32(OUT, AVIVO_DC_LUTA_BLACK_OFFSET_BLUE + regs->crtcOffset, 0); + Write32(OUT, AVIVO_DC_LUTA_BLACK_OFFSET_GREEN + regs->crtcOffset, 0); + Write32(OUT, AVIVO_DC_LUTA_BLACK_OFFSET_RED + regs->crtcOffset, 0); + + Write32(OUT, AVIVO_DC_LUTA_WHITE_OFFSET_BLUE + regs->crtcOffset, 0xffff); + Write32(OUT, AVIVO_DC_LUTA_WHITE_OFFSET_GREEN + regs->crtcOffset, 0xffff); + Write32(OUT, AVIVO_DC_LUTA_WHITE_OFFSET_RED + regs->crtcOffset, 0xffff); + + Write32(OUT, AVIVO_DC_LUT_RW_SELECT, crtcID); + Write32(OUT, AVIVO_DC_LUT_RW_MODE, 0); + Write32(OUT, AVIVO_DC_LUT_WRITE_EN_MASK, 0x0000003f); + + Write32(OUT, AVIVO_DC_LUT_RW_INDEX, 0); + for (int i = 0; i < 256; i++) { + Write32(OUT, AVIVO_DC_LUT_30_COLOR, + (r[i] << 20) | + (g[i] << 10) | + (b[i] << 0)); + } + + Write32(OUT, AVIVO_D1GRPH_LUT_SEL + regs->crtcOffset, crtcID); +} + + void display_crtc_fb_set(uint8 crtcID, display_mode* mode) { radeon_shared_info &info = *gInfo->shared_info; register_info* regs = gDisplay[crtcID]->regs; + uint16* r = info.color_data; + uint16* g = r + 256; + uint16* b = r + 512; + uint32 fbSwap; if (info.dceMajor >= 4) fbSwap = EVERGREEN_GRPH_ENDIAN_SWAP(EVERGREEN_GRPH_ENDIAN_NONE); @@ -616,6 +753,7 @@ display_crtc_fb_set(uint8 crtcID, display_mode* mode) fbFormat = AVIVO_D1GRPH_CONTROL_DEPTH_8BPP | AVIVO_D1GRPH_CONTROL_8BPP_INDEXED; } + // TODO: copy system color map into shared info break; case B_RGB15_LITTLE: bytesPerPixel = 2; @@ -646,6 +784,17 @@ display_crtc_fb_set(uint8 crtcID, display_mode* mode) fbSwap = R600_D1GRPH_SWAP_ENDIAN_16BIT; #endif } + + { + // default gamma table + uint16 gamma = 0; + for (int i = 0; i < 256; i++) { + r[i] = gamma; + g[i] = gamma; + b[i] = gamma; + gamma += 4; + } + } break; case B_RGB24_LITTLE: case B_RGB32_LITTLE: @@ -666,6 +815,17 @@ display_crtc_fb_set(uint8 crtcID, display_mode* mode) fbSwap = R600_D1GRPH_SWAP_ENDIAN_32BIT; #endif } + + { + // default gamma table + uint16 gamma = 0; + for (int i = 0; i < 256; i++) { + r[i] = gamma; + g[i] = gamma; + b[i] = gamma; + gamma += 4; + } + } break; } @@ -685,7 +845,7 @@ display_crtc_fb_set(uint8 crtcID, display_mode* mode) (fbAddress >> 32) & 0xf); } - TRACE("%s: Set SurfaceAddress: 0x%" B_PRIX32 "\n", + TRACE("%s: Set SurfaceAddress: 0x%" B_PRIX64 "\n", __func__, (fbAddress & 0xFFFFFFFF)); Write32(OUT, regs->grphPrimarySurfaceAddr, (fbAddress & 0xFFFFFFFF)); @@ -700,16 +860,17 @@ display_crtc_fb_set(uint8 crtcID, display_mode* mode) uint32 widthAligned = mode->virtual_width; uint32 pitchMask = 0; + // assume micro-linear/macro-linear mode (i.e., not tiled) switch (bytesPerPixel) { case 1: - pitchMask = 255; + pitchMask = 63; break; case 2: - pitchMask = 127; + pitchMask = 31; break; case 3: case 4: - pitchMask = 63; + pitchMask = 31; break; } widthAligned += pitchMask; @@ -718,7 +879,7 @@ display_crtc_fb_set(uint8 crtcID, display_mode* mode) TRACE("%s: fb: %" B_PRIu32 "x%" B_PRIu32 " (%" B_PRIu32 " bpp)\n", __func__, mode->virtual_width, mode->virtual_height, bitsPerPixel); TRACE("%s: fb pitch: %" B_PRIu32 " \n", __func__, - widthAligned * bytesPerPixel / 4); + widthAligned); TRACE("%s: fb width aligned: %" B_PRIu32 "\n", __func__, widthAligned); @@ -728,7 +889,7 @@ display_crtc_fb_set(uint8 crtcID, display_mode* mode) Write32(CRT, regs->grphYStart, 0); Write32(CRT, regs->grphXEnd, mode->virtual_width); Write32(CRT, regs->grphYEnd, mode->virtual_height); - Write32(CRT, regs->grphPitch, widthAligned * bytesPerPixel / 4); + Write32(CRT, regs->grphPitch, widthAligned); Write32(CRT, regs->grphEnable, 1); // Enable Frame buffer @@ -751,7 +912,7 @@ display_crtc_fb_set(uint8 crtcID, display_mode* mode) Write32(OUT, EVERGREEN_MASTER_UPDATE_MODE + regs->crtcOffset, 0); // Pageflip to happen anywhere in vblank - + display_dce45_crtc_load_lut(crtcID); } else { uint32 tmp = Read32(OUT, AVIVO_D1GRPH_FLIP_CONTROL + regs->crtcOffset); tmp &= ~AVIVO_D1GRPH_SURFACE_UPDATE_H_RETRACE_EN; @@ -759,6 +920,7 @@ display_crtc_fb_set(uint8 crtcID, display_mode* mode) Write32(OUT, AVIVO_D1MODE_MASTER_UPDATE_MODE + regs->crtcOffset, 0); // Pageflip to happen anywhere in vblank + display_avivo_crtc_load_lut(crtcID); } // update shared info @@ -816,8 +978,8 @@ display_crtc_set_dtd(uint8 crtcID, display_mode* mode) { display_timing& displayTiming = mode->timing; - TRACE("%s called to do %dx%d\n", - __func__, displayTiming.h_display, displayTiming.v_display); + TRACE("%s called to do %dx%d\n", __func__, + displayTiming.h_display, displayTiming.v_display); SET_CRTC_USING_DTD_TIMING_PARAMETERS args; int index = GetIndexIntoMasterTable(COMMAND, SetCRTC_UsingDTDTiming); @@ -825,6 +987,7 @@ display_crtc_set_dtd(uint8 crtcID, display_mode* mode) memset(&args, 0, sizeof(args)); + // Note: the code below assumes H & V borders are both zero uint16 blankStart = MIN(displayTiming.h_sync_start, displayTiming.h_display); uint16 blankEnd diff --git a/src/add-ons/accelerants/radeon_hd/display.h b/src/add-ons/accelerants/radeon_hd/display.h index 93c25e1b32..5676bbdb58 100644 --- a/src/add-ons/accelerants/radeon_hd/display.h +++ b/src/add-ons/accelerants/radeon_hd/display.h @@ -31,6 +31,8 @@ void display_crtc_set(uint8 crtcID, display_mode* mode); void display_crtc_set_dtd(uint8 crtcID, display_mode* mode); void display_crtc_power(uint8 crtcID, int command); void display_crtc_memreq(uint8 crtcID, int command); +void display_avivo_crtc_load_lut(uint8 crtcID); +void display_dce45_crtc_load_lut(uint8 crtcID); #endif /* RADEON_HD_DISPLAY_H */ diff --git a/src/add-ons/accelerants/radeon_hd/displayport.cpp b/src/add-ons/accelerants/radeon_hd/displayport.cpp index 3acd66d267..a2421a54b9 100644 --- a/src/add-ons/accelerants/radeon_hd/displayport.cpp +++ b/src/add-ons/accelerants/radeon_hd/displayport.cpp @@ -15,6 +15,7 @@ #include "accelerant_protos.h" #include "connector.h" #include "mode.h" +#include "edid.h" #undef TRACE @@ -341,8 +342,8 @@ dp_get_lane_count(uint32 connectorIndex, display_mode* mode) size_t pixelChunk; size_t pixelsPerChunk; - status_t result = dp_get_pixel_size_for((color_space)mode->space, &pixelChunk, - NULL, &pixelsPerChunk); + status_t result = dp_get_pixel_size_for((color_space)mode->space, + &pixelChunk, NULL, &pixelsPerChunk); if (result != B_OK) { TRACE("%s: Invalid color space!\n", __func__); @@ -355,7 +356,8 @@ dp_get_lane_count(uint32 connectorIndex, display_mode* mode) uint32 dpMaxLaneCount = dp_get_lane_count_max(dpInfo); uint32 lane; - for (lane = 1; lane < dpMaxLaneCount; lane <<= 1) { + // don't go below 2 lanes or display is jittery + for (lane = 2; lane < dpMaxLaneCount; lane <<= 1) { uint32 maxPixelClock = dp_get_pixel_clock_max(dpMaxLinkRate, lane, bitsPerPixel); if (mode->timing.pixel_clock <= maxPixelClock) @@ -380,8 +382,8 @@ dp_get_link_rate(uint32 connectorIndex, display_mode* mode) size_t pixelChunk; size_t pixelsPerChunk; - status_t result = dp_get_pixel_size_for((color_space)mode->space, &pixelChunk, - NULL, &pixelsPerChunk); + status_t result = dp_get_pixel_size_for((color_space)mode->space, + &pixelChunk, NULL, &pixelsPerChunk); if (result != B_OK) { TRACE("%s: Invalid color space!\n", __func__); @@ -430,6 +432,8 @@ dp_setup_connectors() continue; } + TRACE("%s: found dp connector on index %" B_PRIu32 "\n", + __func__, index); uint32 gpioID = gConnector[index]->gpioID; uint32 auxPin = gGPIOInfo[gpioID]->hwPin; @@ -655,6 +659,7 @@ dp_link_train_cr(uint32 connectorIndex) dp_set_tp(connectorIndex, DP_TRAIN_PATTERN_1); memset(dp->trainingSet, 0, 4); dp_update_vs_emph(connectorIndex); + snooze(400); while (1) { if (dp->trainingReadInterval == 0) @@ -763,11 +768,13 @@ dp_link_train_ce(uint32 connectorIndex) status_t -dp_link_train(uint32 connectorIndex, display_mode* mode) +dp_link_train(uint8 crtcID) { TRACE("%s\n", __func__); + uint32 connectorIndex = gDisplay[crtcID]->connectorIndex; dp_info* dp = &gConnector[connectorIndex]->dpInfo; + display_mode* mode = &gDisplay[crtcID]->currentMode; if (dp->valid != true) { ERROR("%s: started on invalid DisplayPort connector #%" B_PRIu32 "\n", @@ -830,7 +837,10 @@ dp_link_train(uint32 connectorIndex, display_mode* mode) encoder_dig_setup(connectorIndex, mode->timing.pixel_clock, ATOM_ENCODER_CMD_SETUP_PANEL_MODE); - if (dp->config[0] >= DP_DPCD_REV_11) + // TODO: Doesn't this overwrite important dpcd info? + sandbox = dp->laneCount; + if ((dp->config[0] >= DP_DPCD_REV_11) + && (dp->config[2] & DP_ENHANCED_FRAME_CAP_EN)) sandbox |= DP_ENHANCED_FRAME_EN; dpcd_reg_write(hwPin, DP_LANE_COUNT, sandbox); @@ -872,7 +882,7 @@ dp_link_train(uint32 connectorIndex, display_mode* mode) } -status_t +bool ddc2_dp_read_edid1(uint32 connectorIndex, edid1_info* edid) { TRACE("%s\n", __func__); @@ -880,15 +890,16 @@ ddc2_dp_read_edid1(uint32 connectorIndex, edid1_info* edid) dp_info* dpInfo = &gConnector[connectorIndex]->dpInfo; if (!dpInfo->valid) - return B_ERROR; - - edid1_raw raw; - uint8* rdata = (uint8_t*)&raw; - uint8 sdata = 0; + return false; // The following sequence is from a trace of the Linux kernel // radeon code; not sure if the initial writes to address 0 are // requried. + + // TODO: This surely cane be cleaned up + edid1_raw raw; + uint8* rdata = (uint8*)&raw; + uint8 sdata = 0; dp_aux_set_i2c_byte(dpInfo->auxPin, 0x00, &sdata, true, false); dp_aux_set_i2c_byte(dpInfo->auxPin, 0x00, &sdata, false, true); @@ -902,25 +913,25 @@ ddc2_dp_read_edid1(uint32 connectorIndex, edid1_info* edid) dp_aux_get_i2c_byte(dpInfo->auxPin, 0x50, rdata, true, false); for (uint32 i = 0; i < sizeof(raw); i++) { - status_t result = dp_aux_get_i2c_byte(dpInfo->auxPin, 0x50, rdata++, - false, false); - if (result) { - ERROR("%s: error reading EDID data at index %" B_PRIu32 "\n", - __func__, i); + status_t ret = dp_aux_get_i2c_byte(dpInfo->auxPin, 0x50, + rdata++, false, false); + if (ret) { + TRACE("%s: error reading EDID data at index %d, ret = %d\n", + __func__, i, ret); dp_aux_get_i2c_byte(dpInfo->auxPin, 0x50, &sdata, false, true); - return B_ERROR; + return false; } } dp_aux_get_i2c_byte(dpInfo->auxPin, 0x50, &sdata, false, true); if (raw.version.version != 1 || raw.version.revision > 4) { ERROR("%s: EDID version or revision out of range\n", __func__); - return B_ERROR; + return false; } edid_decode(edid, &raw); - return B_OK; + return true; } diff --git a/src/add-ons/accelerants/radeon_hd/displayport.h b/src/add-ons/accelerants/radeon_hd/displayport.h index ae484d081a..46c97fac2f 100644 --- a/src/add-ons/accelerants/radeon_hd/displayport.h +++ b/src/add-ons/accelerants/radeon_hd/displayport.h @@ -40,7 +40,7 @@ uint32 dp_get_lane_count(uint32 connectorIndex, display_mode* mode); void dp_setup_connectors(); -status_t dp_link_train(uint32 connectorIndex, display_mode* mode); +status_t dp_link_train(uint8 crtcID); status_t dp_link_train_cr(uint32 connectorIndex); status_t dp_link_train_ce(uint32 connectorIndex); @@ -48,7 +48,8 @@ void debug_dp_info(); status_t dp_get_pixel_size_for(color_space space, size_t *pixelChunk, size_t *rowAlignment, size_t *pixelsPerChunk); -status_t ddc2_dp_read_edid1(uint32 connectorIndex, edid1_info *edid); + +bool ddc2_dp_read_edid1(uint32 connectorIndex, edid1_info *edid); -#endif /* RADEON_HD_DISPLAYPORT_H */ \ No newline at end of file +#endif /* RADEON_HD_DISPLAYPORT_H */ diff --git a/src/add-ons/accelerants/radeon_hd/encoder.cpp b/src/add-ons/accelerants/radeon_hd/encoder.cpp index 314bb4ef61..4064d2cb01 100644 --- a/src/add-ons/accelerants/radeon_hd/encoder.cpp +++ b/src/add-ons/accelerants/radeon_hd/encoder.cpp @@ -294,7 +294,7 @@ encoder_mode_set(uint8 crtcID) uint16 encoderFlags = gConnector[connectorIndex]->encoder.flags; pll_info* pll = &gConnector[connectorIndex]->encoder.pll; - // Adjusted pixel clock (*NOT* original mode pixel clock) + // TODO: Should this be the adjusted pll or the original? uint32 pixelClock = pll->pixelClock; switch (gConnector[connectorIndex]->encoder.objectID) { @@ -324,6 +324,9 @@ encoder_mode_set(uint8 crtcID) case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: + // already handled by virue of setting DPMS_OFF before + // getting here +#if 0 if ((info.chipsetFlags & CHIP_APU) != 0 || info.dceMajor >= 5) { // Setup DIG encoder @@ -356,6 +359,7 @@ encoder_mode_set(uint8 crtcID) transmitter_dig_setup(connectorIndex, pixelClock, 0, 0, ATOM_TRANSMITTER_ACTION_ENABLE); } +#endif break; case ENCODER_OBJECT_ID_INTERNAL_DDI: case ENCODER_OBJECT_ID_INTERNAL_DVO1: @@ -1828,8 +1832,17 @@ encoder_dpms_set_dig(uint8 crtcID, int mode) transmitter_dig_setup(connectorIndex, pll->pixelClock, 0, 0, ATOM_ENCODER_CMD_SETUP); } + encoder_dig_setup(connectorIndex, pll->pixelClock, ATOM_ENABLE); + transmitter_dig_setup(connectorIndex, pll->pixelClock, 0, 0, + ATOM_TRANSMITTER_ACTION_SETUP); transmitter_dig_setup(connectorIndex, pll->pixelClock, 0, 0, ATOM_TRANSMITTER_ACTION_ENABLE); + /* some early dce3.2 boards have a bug in their transmitter + control table */ + if (info.chipsetID != RADEON_RV710 + && info.chipsetID != RADEON_RV730) + transmitter_dig_setup(connectorIndex, pll->pixelClock, 0, 0, + ATOM_TRANSMITTER_ACTION_ENABLE_OUTPUT); } else { transmitter_dig_setup(connectorIndex, pll->pixelClock, 0, 0, ATOM_TRANSMITTER_ACTION_ENABLE_OUTPUT); @@ -1845,12 +1858,15 @@ encoder_dpms_set_dig(uint8 crtcID, int mode) encoder_dig_setup(connectorIndex, pll->pixelClock, ATOM_ENCODER_CMD_DP_VIDEO_OFF); } - // TODO: dp link train here - //radeon_dp_link_train(encoder, connector); + // dp link train + dp_link_train(crtcID); if (info.dceMajor >= 4) { encoder_dig_setup(connectorIndex, pll->pixelClock, ATOM_ENCODER_CMD_DP_VIDEO_ON); } + // not sure what AtomBIOS table/command sets this + // register, but it's required to get the video output + Write32(OUT, AVIVO_DP_VID_STREAM_CNTL, 0x201); } if ((encoderFlags & ATOM_DEVICE_LCD_SUPPORT) != 0) { transmitter_dig_setup(connectorIndex, pll->pixelClock, @@ -1866,6 +1882,9 @@ encoder_dpms_set_dig(uint8 crtcID, int mode) } else { transmitter_dig_setup(connectorIndex, pll->pixelClock, 0, 0, ATOM_TRANSMITTER_ACTION_DISABLE_OUTPUT); + transmitter_dig_setup(connectorIndex, pll->pixelClock, 0, 0, + ATOM_TRANSMITTER_ACTION_DISABLE); + encoder_dig_setup(connectorIndex, pll->pixelClock, ATOM_DISABLE); } if (connector_is_dp(connectorIndex)) { if (info.dceMajor >= 4) { diff --git a/src/add-ons/accelerants/radeon_hd/hooks.cpp b/src/add-ons/accelerants/radeon_hd/hooks.cpp index d57bb73517..238c9624e8 100644 --- a/src/add-ons/accelerants/radeon_hd/hooks.cpp +++ b/src/add-ons/accelerants/radeon_hd/hooks.cpp @@ -39,7 +39,7 @@ get_accelerant_hook(uint32 feature, void* data) case B_DPMS_MODE: return (void*)radeon_dpms_mode; case B_SET_DPMS_MODE: - return (void*)radeon_dpms_set; + return (void*)radeon_dpms_set_hook; /* mode configuration */ case B_ACCELERANT_MODE_COUNT: diff --git a/src/add-ons/accelerants/radeon_hd/mode.cpp b/src/add-ons/accelerants/radeon_hd/mode.cpp index d272965cc3..815d52f42f 100644 --- a/src/add-ons/accelerants/radeon_hd/mode.cpp +++ b/src/add-ons/accelerants/radeon_hd/mode.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2006-2011, Haiku, Inc. All Rights Reserved. + * Copyright 2006-2013, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. * * Support for i915 chipset and up based on the X driver, @@ -69,6 +69,7 @@ uint32 radeon_accelerant_mode_count(void) { TRACE("%s\n", __func__); + // TODO: multi-monitor? we need crtcid here return gInfo->shared_info->mode_count; } @@ -78,6 +79,7 @@ status_t radeon_get_mode_list(display_mode* modeList) { TRACE("%s\n", __func__); + // TODO: multi-monitor? we need crtcid here memcpy(modeList, gInfo->mode_list, gInfo->shared_info->mode_count * sizeof(display_mode)); return B_OK; @@ -88,8 +90,8 @@ status_t radeon_get_preferred_mode(display_mode* preferredMode) { TRACE("%s\n", __func__); + // TODO: multi-monitor? we need crtcid here - // TODO: Argh! Which display? :) uint8_t crtc = 0; if (gDisplay[crtc]->preferredMode.virtual_width > 0 @@ -119,7 +121,7 @@ radeon_get_edid_info(void* info, size_t size, uint32* edid_version) memcpy(info, &gInfo->shared_info->edid_info, sizeof(struct edid1_info)); // VESA //memcpy(info, &gDisplay[0]->edidData, sizeof(struct edid1_info)); - // BitBanged display 0 + // Display 0 *edid_version = EDID_VERSION_1; @@ -145,27 +147,46 @@ radeon_dpms_mode(void) void -radeon_dpms_set(int mode) +radeon_dpms_set(uint8 id, int mode) { - for (uint8 id = 0; id < MAX_DISPLAY; id++) { + if (mode == B_DPMS_ON) { + display_crtc_dpms(id, mode); + encoder_dpms_set(id, mode); + } else { encoder_dpms_set(id, mode); display_crtc_dpms(id, mode); } - gInfo->dpms_mode = mode; } +void +radeon_dpms_set_hook(int mode) +{ + // TODO: multi-monitor? for now we use VESA edid + + // As the accelerant hook doesn't pass crtc id + for (uint8 id = 0; id < MAX_DISPLAY; id++) { + radeon_dpms_set(id, mode); + } +} + + status_t radeon_set_display_mode(display_mode* mode) { - radeon_shared_info &info = *gInfo->shared_info; + // TODO: multi-monitor? For now we set the mode on + // all displays (this is very incorrect). This also + // causes a lot of problems on DisplayPort devices // Set mode on each display for (uint8 id = 0; id < MAX_DISPLAY; id++) { if (gDisplay[id]->attached == false) continue; + // Copy this display mode into the "current mode" for the display + memcpy(&gDisplay[id]->currentMode, mode, sizeof(display_mode)); + uint32 connectorIndex = gDisplay[id]->connectorIndex; // Determine DP lanes if DP @@ -177,9 +198,8 @@ radeon_set_display_mode(display_mode* mode) // *** crtc and encoder prep encoder_output_lock(true); - encoder_dpms_set(id, B_DPMS_OFF); display_crtc_lock(id, ATOM_ENABLE); - display_crtc_dpms(id, B_DPMS_OFF); + radeon_dpms_set(id, B_DPMS_OFF); // *** Set up encoder -> crtc routing encoder_assign_crtc(id); @@ -201,31 +221,15 @@ radeon_set_display_mode(display_mode* mode) // *** encoder mode set encoder_mode_set(id); - // *** CRT controler commit - display_crtc_dpms(id, B_DPMS_ON); + // *** encoder and CRT controller commit + radeon_dpms_set(id, B_DPMS_ON); display_crtc_lock(id, ATOM_DISABLE); - - // *** encoder commit - - // handle DisplayPort link training - if (connector_is_dp(connectorIndex)) { - if (info.dceMajor >= 4) - encoder_dig_setup(connectorIndex, - ATOM_ENCODER_CMD_DP_VIDEO_OFF, 0); - - dp_link_train(connectorIndex, mode); - - if (info.dceMajor >= 4) - encoder_dig_setup(connectorIndex, - ATOM_ENCODER_CMD_DP_VIDEO_ON, 0); - } - - encoder_dpms_set(id, B_DPMS_ON); encoder_output_lock(false); } + #ifdef TRACE_MODE // for debugging - // debug_dp_info(); + debug_dp_info(); TRACE("D1CRTC_STATUS Value: 0x%X\n", Read32(CRT, AVIVO_D1CRTC_STATUS)); @@ -247,6 +251,7 @@ radeon_set_display_mode(display_mode* mode) Read32(CRT, AVIVO_D1CRTC_BLANK_CONTROL)); TRACE("D2CRTC_BLANK_CONTROL Value: 0x%X\n", Read32(CRT, AVIVO_D1CRTC_BLANK_CONTROL)); + #endif return B_OK; } @@ -258,6 +263,7 @@ radeon_get_display_mode(display_mode* _currentMode) TRACE("%s\n", __func__); *_currentMode = gInfo->shared_info->current_mode; + //*_currentMode = gDisplay[X]->currentMode; return B_OK; } diff --git a/src/add-ons/accelerants/radeon_hd/mode.h b/src/add-ons/accelerants/radeon_hd/mode.h index 59a5ce1147..db98aecb90 100644 --- a/src/add-ons/accelerants/radeon_hd/mode.h +++ b/src/add-ons/accelerants/radeon_hd/mode.h @@ -32,7 +32,8 @@ bool is_mode_supported(display_mode* mode); status_t is_mode_sane(display_mode* mode); uint32 radeon_dpms_capabilities(void); uint32 radeon_dpms_mode(void); -void radeon_dpms_set(int mode); +void radeon_dpms_set(uint8 id, int mode); +void radeon_dpms_set_hook(int mode); uint32 get_mode_bpp(display_mode* mode); diff --git a/src/add-ons/accelerants/radeon_hd/pll.cpp b/src/add-ons/accelerants/radeon_hd/pll.cpp index 9ee2695eeb..4a614d47d6 100644 --- a/src/add-ons/accelerants/radeon_hd/pll.cpp +++ b/src/add-ons/accelerants/radeon_hd/pll.cpp @@ -301,7 +301,7 @@ pll_compute_post_divider(pll_info* pll) if ((pll->flags & PLL_IS_LCD) != 0) vco = pll->lcdPllOutMin; else - vco = pll->pllOutMin; + vco = pll->pllOutMax; } else { if ((pll->flags & PLL_IS_LCD) != 0) vco = pll->lcdPllOutMax; @@ -311,8 +311,8 @@ pll_compute_post_divider(pll_info* pll) TRACE("%s: vco = %" B_PRIu32 "\n", __func__, vco); - uint32 postDivider = vco / pll->pixelClock; - uint32 tmp = vco % pll->pixelClock; + uint32 postDivider = vco / pll->adjustedClock; + uint32 tmp = vco % pll->adjustedClock; if ((pll->flags & PLL_PREFER_MINM_OVER_MAXP) != 0) { if (tmp) @@ -337,7 +337,7 @@ pll_compute(pll_info* pll) { pll_compute_post_divider(pll); - uint32 targetClock = pll->pixelClock; + uint32 targetClock = pll->adjustedClock; pll->feedbackDiv = 0; pll->feedbackDivFrac = 0; @@ -364,7 +364,7 @@ pll_compute(pll_info* pll) pll->feedbackDiv = pll->minFeedbackDiv; pll->feedbackDivFrac - = (100 * pll->feedbackDivFrac) / pll->referenceFreq; + = (1000 * pll->feedbackDivFrac) / pll->referenceFreq; if (pll->feedbackDivFrac >= 5) { pll->feedbackDivFrac -= 5; @@ -436,12 +436,12 @@ pll_compute(pll_info* pll) TRACE("%s: pixel clock: %" B_PRIu32 " gives:" " feedbackDivider = %" B_PRIu32 ".%" B_PRIu32 "; referenceDivider = %" B_PRIu32 "; postDivider = %" B_PRIu32 "\n", - __func__, pll->pixelClock, pll->feedbackDiv, pll->feedbackDivFrac, + __func__, pll->adjustedClock, pll->feedbackDiv, pll->feedbackDivFrac, pll->referenceDiv, pll->postDiv); - if (pll->pixelClock != calculatedClock) { + if (pll->adjustedClock != calculatedClock) { TRACE("%s: pixel clock %" B_PRIu32 " was changed to %" B_PRIu32 "\n", - __func__, pll->pixelClock, calculatedClock); + __func__, pll->adjustedClock, calculatedClock); pll->pixelClock = calculatedClock; } @@ -515,6 +515,7 @@ pll_adjust(pll_info* pll, display_mode* mode, uint8 crtcID) uint32 encoderFlags = connector->encoder.flags; uint32 externalEncoderID = 0; + pll->adjustedClock = pll->pixelClock; if (connector->encoderExternal.isDPBridge) externalEncoderID = connector->encoderExternal.objectID; @@ -557,9 +558,9 @@ pll_adjust(pll_info* pll, display_mode* mode, uint8 crtcID) atom_execute_table(gAtomContext, index, (uint32*)&args); // get returned adjusted clock - pll->pixelClock + pll->adjustedClock = B_LENDIAN_TO_HOST_INT16(args.v1.usPixelClock); - pll->pixelClock *= 10; + pll->adjustedClock *= 10; break; case 3: args.v3.sInput.usPixelClock @@ -607,10 +608,10 @@ pll_adjust(pll_info* pll, display_mode* mode, uint8 crtcID) atom_execute_table(gAtomContext, index, (uint32*)&args); // get returned adjusted clock - pll->pixelClock + pll->adjustedClock = B_LENDIAN_TO_HOST_INT32( args.v3.sOutput.ulDispPllFreq); - pll->pixelClock *= 10; + pll->adjustedClock *= 10; // convert to kHz for storage if (args.v3.sOutput.ucRefDiv) { @@ -638,7 +639,7 @@ pll_adjust(pll_info* pll, display_mode* mode, uint8 crtcID) } TRACE("%s: was: %" B_PRIu32 ", now: %" B_PRIu32 "\n", __func__, - pixelClock, pll->pixelClock); + pixelClock, pll->adjustedClock); return B_OK; } @@ -649,6 +650,8 @@ pll_set(display_mode* mode, uint8 crtcID) { uint32 connectorIndex = gDisplay[crtcID]->connectorIndex; pll_info* pll = &gConnector[connectorIndex]->encoder.pll; + uint32 dp_clock = gConnector[connectorIndex]->dpInfo.linkRate / 10; + bool ssEnabled = false; pll->pixelClock = mode->timing.pixel_clock; @@ -668,23 +671,28 @@ pll_set(display_mode* mode, uint8 crtcID) if (info.dceMajor >= 4) pll_asic_ss_probe(pll, ASIC_INTERNAL_SS_ON_DP); else { - // TODO: DP Clock == 1.62Ghz? - pll_ppll_ss_probe(pll, ATOM_DP_SS_ID1); + if (dp_clock == 16200) { + ssEnabled = pll_ppll_ss_probe(pll, ATOM_DP_SS_ID2); + if (!ssEnabled) + // id2 failed, try id1 + ssEnabled = pll_ppll_ss_probe(pll, ATOM_DP_SS_ID1); + } else + ssEnabled = pll_ppll_ss_probe(pll, ATOM_DP_SS_ID1); } break; case ATOM_ENCODER_MODE_LVDS: if (info.dceMajor >= 4) - pll_asic_ss_probe(pll, gInfo->lvdsSpreadSpectrumID); + ssEnabled = pll_asic_ss_probe(pll, gInfo->lvdsSpreadSpectrumID); else - pll_ppll_ss_probe(pll, gInfo->lvdsSpreadSpectrumID); + ssEnabled = pll_ppll_ss_probe(pll, gInfo->lvdsSpreadSpectrumID); break; case ATOM_ENCODER_MODE_DVI: if (info.dceMajor >= 4) - pll_asic_ss_probe(pll, ASIC_INTERNAL_SS_ON_TMDS); + ssEnabled = pll_asic_ss_probe(pll, ASIC_INTERNAL_SS_ON_TMDS); break; case ATOM_ENCODER_MODE_HDMI: if (info.dceMajor >= 4) - pll_asic_ss_probe(pll, ASIC_INTERNAL_SS_ON_HDMI); + ssEnabled = pll_asic_ss_probe(pll, ASIC_INTERNAL_SS_ON_HDMI); break; } @@ -836,7 +844,8 @@ pll_set(display_mode* mode, uint8 crtcID) status_t result = atom_execute_table(gAtomContext, index, (uint32*)&args); - display_crtc_ss(pll, ATOM_ENABLE); + if (ssEnabled) + display_crtc_ss(pll, ATOM_ENABLE); return result; } diff --git a/src/add-ons/accelerants/radeon_hd/pll.h b/src/add-ons/accelerants/radeon_hd/pll.h index 3a5cbba7e0..1a29381082 100644 --- a/src/add-ons/accelerants/radeon_hd/pll.h +++ b/src/add-ons/accelerants/radeon_hd/pll.h @@ -93,6 +93,9 @@ struct pll_info { /* asic spread spectrum */ uint16 ssRate; uint16 ssAmount; + + /* pixel clock to be used in pll calculations (kHz) */ + uint32 adjustedClock; };