/* * Copyright 2000 ATI Technologies Inc., Markham, Ontario, and * VA Linux Systems Inc., Fremont, California. * * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation on the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice (including the * next paragraph) shall be included in all copies or substantial * portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NON-INFRINGEMENT. IN NO EVENT SHALL ATI, VA LINUX SYSTEMS AND/OR * THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include /* X and server generic header files */ #include "xf86.h" #include "xf86_OSproc.h" #include "vgaHW.h" #include "xf86Modes.h" /* Driver data structures */ #include "radeon.h" #include "radeon_reg.h" #include "radeon_macros.h" #include "radeon_probe.h" #include "radeon_version.h" #include "radeon_tv.h" #include "radeon_atombios.h" const char *MonTypeName[10] = { "AUTO", "NONE", "CRT", "LVDS", "TMDS", "CTV", "STV", "CV", "HDMI", "DP" }; const RADEONMonitorType MonTypeID[10] = { MT_UNKNOWN, /* this is just a dummy value for AUTO DETECTION */ MT_NONE, /* NONE -> NONE */ MT_CRT, /* CRT -> CRT */ MT_LCD, /* Laptop LCDs are driven via LVDS port */ MT_DFP, /* DFPs are driven via TMDS */ MT_CTV, /* CTV -> CTV */ MT_STV, /* STV -> STV */ MT_CV, MT_HDMI, MT_DP }; const char *TMDSTypeName[4] = { "None", "Internal", "External", "LVTMA", }; const char *DACTypeName[4] = { "None", "Primary", "TVDAC/ExtDAC", "ExtDac" }; const char *ConnectorTypeName[17] = { "None", "VGA", "DVI-I", "DVI-D", "DVI-A", "STV", "CTV", "LVDS", "Digital", "SCART", "HDMI-A", "HDMI-B", "Unsupported", "Unsupported", "DIN", "DisplayPort", "Unsupported" }; const char *OutputType[11] = { "None", "VGA", "DVI", "DVI", "DVI", "LVDS", "S-video", "Composite", "Component", "HDMI", "DisplayPort", }; static const RADEONTMDSPll default_tmds_pll[CHIP_FAMILY_LAST][4] = { {{0, 0}, {0, 0}, {0, 0}, {0, 0}}, /*CHIP_FAMILY_UNKNOW*/ {{0, 0}, {0, 0}, {0, 0}, {0, 0}}, /*CHIP_FAMILY_LEGACY*/ {{12000, 0xa1b}, {0xffffffff, 0xa3f}, {0, 0}, {0, 0}}, /*CHIP_FAMILY_RADEON*/ {{12000, 0xa1b}, {0xffffffff, 0xa3f}, {0, 0}, {0, 0}}, /*CHIP_FAMILY_RV100*/ {{0, 0}, {0, 0}, {0, 0}, {0, 0}}, /*CHIP_FAMILY_RS100*/ {{15000, 0xa1b}, {0xffffffff, 0xa3f}, {0, 0}, {0, 0}}, /*CHIP_FAMILY_RV200*/ {{12000, 0xa1b}, {0xffffffff, 0xa3f}, {0, 0}, {0, 0}}, /*CHIP_FAMILY_RS200*/ {{15000, 0xa1b}, {0xffffffff, 0xa3f}, {0, 0}, {0, 0}}, /*CHIP_FAMILY_R200*/ {{15500, 0x81b}, {0xffffffff, 0x83f}, {0, 0}, {0, 0}}, /*CHIP_FAMILY_RV250*/ {{0, 0}, {0, 0}, {0, 0}, {0, 0}}, /*CHIP_FAMILY_RS300*/ {{13000, 0x400f4}, {15000, 0x400f7}, {0xffffffff, 0x40111}, {0, 0}}, /*CHIP_FAMILY_RV280*/ {{0xffffffff, 0xb01cb}, {0, 0}, {0, 0}, {0, 0}}, /*CHIP_FAMILY_R300*/ {{0xffffffff, 0xb01cb}, {0, 0}, {0, 0}, {0, 0}}, /*CHIP_FAMILY_R350*/ {{15000, 0xb0155}, {0xffffffff, 0xb01cb}, {0, 0}, {0, 0}}, /*CHIP_FAMILY_RV350*/ {{15000, 0xb0155}, {0xffffffff, 0xb01cb}, {0, 0}, {0, 0}}, /*CHIP_FAMILY_RV380*/ {{0xffffffff, 0xb01cb}, {0, 0}, {0, 0}, {0, 0}}, /*CHIP_FAMILY_R420*/ {{0xffffffff, 0xb01cb}, {0, 0}, {0, 0}, {0, 0}}, /*CHIP_FAMILY_RV410*/ /* FIXME: just values from r420 used... */ {{15000, 0xb0155}, {0xffffffff, 0xb01cb}, {0, 0}, {0, 0}}, /*CHIP_FAMILY_RS400*/ /* FIXME: just values from rv380 used... */ }; static const CARD32 default_tvdac_adj [CHIP_FAMILY_LAST] = { 0x00000000, /* unknown */ 0x00000000, /* legacy */ 0x00000000, /* r100 */ 0x00280000, /* rv100 */ 0x00000000, /* rs100 */ 0x00880000, /* rv200 */ 0x00000000, /* rs200 */ 0x00000000, /* r200 */ 0x00770000, /* rv250 */ 0x00290000, /* rs300 */ 0x00560000, /* rv280 */ 0x00780000, /* r300 */ 0x00770000, /* r350 */ 0x00780000, /* rv350 */ 0x00780000, /* rv380 */ 0x01080000, /* r420 */ 0x01080000, /* rv410 */ /* FIXME: just values from r420 used... */ 0x00780000, /* rs400 */ /* FIXME: just values from rv380 used... */ }; static RADEONMonitorType RADEONPortCheckNonDDC(ScrnInfoPtr pScrn, xf86OutputPtr output); static void RADEONUpdatePanelSize(xf86OutputPtr output); static void RADEONGetTMDSInfoFromTable(xf86OutputPtr output); #define AVIVO_I2C_DISABLE 0 #define AVIVO_I2C_ENABLE 1 static Bool AVIVOI2CDoLock(ScrnInfoPtr pScrn, int lock_state, int gpio); extern void atombios_output_mode_set(xf86OutputPtr output, DisplayModePtr mode, DisplayModePtr adjusted_mode); extern void legacy_output_mode_set(xf86OutputPtr output, DisplayModePtr mode, DisplayModePtr adjusted_mode); extern void atombios_output_dpms(xf86OutputPtr output, int mode); extern void legacy_output_dpms(xf86OutputPtr output, int mode); extern RADEONMonitorType atombios_dac_detect(ScrnInfoPtr pScrn, xf86OutputPtr output); extern RADEONMonitorType legacy_dac_detect(ScrnInfoPtr pScrn, xf86OutputPtr output); extern int atombios_external_tmds_setup(xf86OutputPtr output, DisplayModePtr mode); extern I2CDevPtr RADEONDVODeviceInit(I2CBusPtr b, I2CSlaveAddr addr); void RADEONPrintPortMap(ScrnInfoPtr pScrn) { xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn); RADEONOutputPrivatePtr radeon_output; xf86OutputPtr output; int o; for (o = 0; o < xf86_config->num_output; o++) { output = xf86_config->output[o]; radeon_output = output->driver_private; xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Port%d:\n Monitor -- %s\n Connector -- %s\n DAC Type -- %s\n TMDS Type -- %s\n DDC Type -- 0x%x\n", o, MonTypeName[radeon_output->MonType+1], ConnectorTypeName[radeon_output->ConnectorType], DACTypeName[radeon_output->DACType], TMDSTypeName[radeon_output->TMDSType], (unsigned int)radeon_output->ddc_line); } } static RADEONMonitorType avivo_display_ddc_connected(ScrnInfoPtr pScrn, xf86OutputPtr output) { RADEONInfoPtr info = RADEONPTR(pScrn); RADEONMonitorType MonType = MT_NONE; xf86MonPtr MonInfo = NULL; RADEONOutputPrivatePtr radeon_output = output->driver_private; if (radeon_output->pI2CBus) { AVIVOI2CDoLock(pScrn, AVIVO_I2C_ENABLE, radeon_output->ddc_line); MonInfo = xf86OutputGetEDID(output, radeon_output->pI2CBus); AVIVOI2CDoLock(pScrn, AVIVO_I2C_DISABLE, radeon_output->ddc_line); } if (MonInfo) { if (!xf86ReturnOptValBool(info->Options, OPTION_IGNORE_EDID, FALSE)) xf86OutputSetEDID(output, MonInfo); if (radeon_output->type == OUTPUT_LVDS) MonType = MT_LCD; else if (radeon_output->type == OUTPUT_DVI_D) MonType = MT_DFP; else if (radeon_output->type == OUTPUT_HDMI) MonType = MT_DFP; else if (radeon_output->type == OUTPUT_DVI_I && (MonInfo->rawData[0x14] & 0x80)) /* if it's digital and DVI */ MonType = MT_DFP; else MonType = MT_CRT; } else MonType = MT_NONE; xf86DrvMsg(pScrn->scrnIndex, X_INFO, "DDC Type: 0x%x, Detected Monitor Type: %d\n", (unsigned int)radeon_output->ddc_line, MonType); return MonType; } static RADEONMonitorType RADEONDisplayDDCConnected(ScrnInfoPtr pScrn, xf86OutputPtr output) { RADEONInfoPtr info = RADEONPTR(pScrn); unsigned char *RADEONMMIO = info->MMIO; CARD32 DDCReg; RADEONMonitorType MonType = MT_NONE; xf86MonPtr MonInfo = NULL; RADEONOutputPrivatePtr radeon_output = output->driver_private; int i, j; DDCReg = radeon_output->ddc_line; /* Read and output monitor info using DDC2 over I2C bus */ if (radeon_output->pI2CBus && info->ddc2 && (DDCReg != RADEON_LCD_GPIO_MASK) && (DDCReg != RADEON_MDGPIO_EN_REG)) { OUTREG(DDCReg, INREG(DDCReg) & (CARD32)~(RADEON_GPIO_A_0 | RADEON_GPIO_A_1)); /* For some old monitors (like Compaq Presario FP500), we need * following process to initialize/stop DDC */ OUTREG(DDCReg, INREG(DDCReg) & ~(RADEON_GPIO_EN_1)); for (j = 0; j < 3; j++) { OUTREG(DDCReg, INREG(DDCReg) & ~(RADEON_GPIO_EN_0)); usleep(13000); OUTREG(DDCReg, INREG(DDCReg) & ~(RADEON_GPIO_EN_1)); for (i = 0; i < 10; i++) { usleep(15000); if (INREG(DDCReg) & RADEON_GPIO_Y_1) break; } if (i == 10) continue; usleep(15000); OUTREG(DDCReg, INREG(DDCReg) | RADEON_GPIO_EN_0); usleep(15000); OUTREG(DDCReg, INREG(DDCReg) | RADEON_GPIO_EN_1); usleep(15000); OUTREG(DDCReg, INREG(DDCReg) & ~(RADEON_GPIO_EN_0)); usleep(15000); MonInfo = xf86DoEDID_DDC2(pScrn->scrnIndex, radeon_output->pI2CBus); OUTREG(DDCReg, INREG(DDCReg) | RADEON_GPIO_EN_1); OUTREG(DDCReg, INREG(DDCReg) | RADEON_GPIO_EN_0); usleep(15000); OUTREG(DDCReg, INREG(DDCReg) & ~(RADEON_GPIO_EN_1)); for (i = 0; i < 5; i++) { usleep(15000); if (INREG(DDCReg) & RADEON_GPIO_Y_1) break; } usleep(15000); OUTREG(DDCReg, INREG(DDCReg) & ~(RADEON_GPIO_EN_0)); usleep(15000); OUTREG(DDCReg, INREG(DDCReg) | RADEON_GPIO_EN_1); OUTREG(DDCReg, INREG(DDCReg) | RADEON_GPIO_EN_0); usleep(15000); if (MonInfo) break; } } else if (radeon_output->pI2CBus && info->ddc2 && ((DDCReg == RADEON_LCD_GPIO_MASK) || (DDCReg == RADEON_MDGPIO_EN_REG))) { MonInfo = xf86DoEDID_DDC2(pScrn->scrnIndex, radeon_output->pI2CBus); } else { xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "DDC2/I2C is not properly initialized\n"); MonType = MT_NONE; } OUTREG(DDCReg, INREG(DDCReg) & ~(RADEON_GPIO_EN_0 | RADEON_GPIO_EN_1)); if (MonInfo) { if (!xf86ReturnOptValBool(info->Options, OPTION_IGNORE_EDID, FALSE)) xf86OutputSetEDID(output, MonInfo); if (radeon_output->type == OUTPUT_LVDS) MonType = MT_LCD; else if (radeon_output->type == OUTPUT_DVI_D) MonType = MT_DFP; else if (radeon_output->type == OUTPUT_HDMI) MonType = MT_DFP; else if (radeon_output->type == OUTPUT_DVI_I && (MonInfo->rawData[0x14] & 0x80)) /* if it's digital and DVI */ MonType = MT_DFP; else MonType = MT_CRT; } else MonType = MT_NONE; xf86DrvMsg(pScrn->scrnIndex, X_INFO, "DDC Type: 0x%x, Detected Monitor Type: %d\n", (unsigned int)radeon_output->ddc_line, MonType); return MonType; } /* Primary Head (DVI or Laptop Int. panel)*/ /* A ddc capable display connected on DVI port */ /* Secondary Head (mostly VGA, can be DVI on some OEM boards)*/ void RADEONConnectorFindMonitor(ScrnInfoPtr pScrn, xf86OutputPtr output) { RADEONInfoPtr info = RADEONPTR(pScrn); RADEONOutputPrivatePtr radeon_output = output->driver_private; if (radeon_output->MonType == MT_UNKNOWN) { if (IS_AVIVO_VARIANT) { radeon_output->MonType = avivo_display_ddc_connected(pScrn, output); if (!radeon_output->MonType) { if (radeon_output->type == OUTPUT_LVDS) radeon_output->MonType = MT_LCD; else radeon_output->MonType = atombios_dac_detect(pScrn, output); } } else { radeon_output->MonType = RADEONDisplayDDCConnected(pScrn, output); if (!radeon_output->MonType) { if (radeon_output->type == OUTPUT_LVDS || OUTPUT_IS_DVI) radeon_output->MonType = RADEONPortCheckNonDDC(pScrn, output); if (!radeon_output->MonType) { if (info->IsAtomBios) radeon_output->MonType = atombios_dac_detect(pScrn, output); else radeon_output->MonType = legacy_dac_detect(pScrn, output); } } } } /* update panel info for RMX */ if (radeon_output->MonType == MT_LCD || radeon_output->MonType == MT_DFP) RADEONUpdatePanelSize(output); if (output->MonInfo) { xf86DrvMsg(pScrn->scrnIndex, X_INFO, "EDID data from the display on connector: %s ----------------------\n", ConnectorTypeName[radeon_output->ConnectorType]); xf86PrintEDID( output->MonInfo ); } } #ifndef __powerpc__ static RADEONMonitorType RADEONDetectLidStatus(ScrnInfoPtr pScrn) { RADEONInfoPtr info = RADEONPTR(pScrn); RADEONMonitorType MonType = MT_NONE; #ifdef __linux__ char lidline[50]; /* 50 should be sufficient for our purposes */ FILE *f = fopen ("/proc/acpi/button/lid/LID/state", "r"); if (f != NULL) { while (fgets(lidline, sizeof lidline, f)) { if (!strncmp(lidline, "state:", strlen ("state:"))) { if (strstr(lidline, "open")) { fclose(f); ErrorF("proc lid open\n"); return MT_LCD; } else if (strstr(lidline, "closed")) { fclose(f); ErrorF("proc lid closed\n"); return MT_NONE; } } } fclose(f); } #endif if (!info->IsAtomBios) { unsigned char *RADEONMMIO = info->MMIO; /* see if the lid is closed -- only works at boot */ if (INREG(RADEON_BIOS_6_SCRATCH) & 0x10) MonType = MT_NONE; else MonType = MT_LCD; } else MonType = MT_LCD; return MonType; } #endif /* __powerpc__ */ static RADEONMonitorType RADEONPortCheckNonDDC(ScrnInfoPtr pScrn, xf86OutputPtr output) { RADEONInfoPtr info = RADEONPTR(output->scrn); RADEONOutputPrivatePtr radeon_output = output->driver_private; RADEONMonitorType MonType = MT_NONE; if (radeon_output->type == OUTPUT_LVDS) { if (xf86ReturnOptValBool(info->Options, OPTION_IGNORE_LID_STATUS, TRUE)) MonType = MT_LCD; else #if defined(__powerpc__) MonType = MT_LCD; #else MonType = RADEONDetectLidStatus(pScrn); #endif } /*else if (radeon_output->type == OUTPUT_DVI) { if (radeon_output->TMDSType == TMDS_INT) { if (INREG(RADEON_FP_GEN_CNTL) & RADEON_FP_DETECT_SENSE) MonType = MT_DFP; } else if (radeon_output->TMDSType == TMDS_EXT) { if (INREG(RADEON_FP2_GEN_CNTL) & RADEON_FP2_DETECT_SENSE) MonType = MT_DFP; } }*/ xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Detected non-DDC Monitor Type: %d\n", MonType); return MonType; } static void radeon_dpms(xf86OutputPtr output, int mode) { RADEONInfoPtr info = RADEONPTR(output->scrn); if (IS_AVIVO_VARIANT) { atombios_output_dpms(output, mode); } else { legacy_output_dpms(output, mode); } } static void radeon_save(xf86OutputPtr output) { } static void radeon_restore(xf86OutputPtr restore) { } static int radeon_mode_valid(xf86OutputPtr output, DisplayModePtr pMode) { RADEONOutputPrivatePtr radeon_output = output->driver_private; ScrnInfoPtr pScrn = output->scrn; RADEONInfoPtr info = RADEONPTR(pScrn); RADEONEntPtr pRADEONEnt = RADEONEntPriv(pScrn); /* * RN50 has effective maximum mode bandwidth of about 300MiB/s. * XXX should really do this for all chips by properly computing * memory bandwidth and an overhead factor. */ if (info->ChipFamily == CHIP_FAMILY_RV100 && !pRADEONEnt->HasCRTC2) { if (xf86ModeBandwidth(pMode, pScrn->bitsPerPixel) > 300) return MODE_BANDWIDTH; } if (OUTPUT_IS_TV) { /* FIXME: Update when more modes are added */ if (IS_AVIVO_VARIANT) { int max_v; /* tv-scaler can scale horizontal width * but frame ends must match tv_pll * for now cap v size */ if (radeon_output->tvStd == TV_STD_NTSC || radeon_output->tvStd == TV_STD_NTSC_J || radeon_output->tvStd == TV_STD_PAL_M) max_v = 480; else max_v = 600; if (pMode->VDisplay == max_v) return MODE_OK; else return MODE_CLOCK_RANGE; } else { if (pMode->HDisplay == 800 && pMode->VDisplay == 600) return MODE_OK; else return MODE_CLOCK_RANGE; } } if (radeon_output->type == OUTPUT_LVDS) { if (radeon_output->rmx_type == RMX_OFF) { if (pMode->HDisplay != radeon_output->PanelXRes || pMode->VDisplay != radeon_output->PanelYRes) return MODE_PANEL; } if (pMode->HDisplay > radeon_output->PanelXRes || pMode->VDisplay > radeon_output->PanelYRes) return MODE_PANEL; } return MODE_OK; } static Bool radeon_mode_fixup(xf86OutputPtr output, DisplayModePtr mode, DisplayModePtr adjusted_mode) { RADEONInfoPtr info = RADEONPTR(output->scrn); RADEONOutputPrivatePtr radeon_output = output->driver_private; radeon_output->Flags &= ~RADEON_USE_RMX; /* decide if we are using RMX */ if ((radeon_output->MonType == MT_LCD || radeon_output->MonType == MT_DFP) && radeon_output->rmx_type != RMX_OFF) { xf86CrtcPtr crtc = output->crtc; RADEONCrtcPrivatePtr radeon_crtc = crtc->driver_private; if (IS_AVIVO_VARIANT || radeon_crtc->crtc_id == 0) { if (mode->HDisplay < radeon_output->PanelXRes || mode->VDisplay < radeon_output->PanelYRes) radeon_output->Flags |= RADEON_USE_RMX; } } /* update timing for LVDS and DFP if RMX is active */ if (radeon_output->Flags & RADEON_USE_RMX) { /* set to the panel's native mode */ adjusted_mode->HTotal = radeon_output->PanelXRes + radeon_output->HBlank; adjusted_mode->HSyncStart = radeon_output->PanelXRes + radeon_output->HOverPlus; adjusted_mode->HSyncEnd = adjusted_mode->HSyncStart + radeon_output->HSyncWidth; adjusted_mode->VTotal = radeon_output->PanelYRes + radeon_output->VBlank; adjusted_mode->VSyncStart = radeon_output->PanelYRes + radeon_output->VOverPlus; adjusted_mode->VSyncEnd = adjusted_mode->VSyncStart + radeon_output->VSyncWidth; /* update crtc values */ xf86SetModeCrtc(adjusted_mode, INTERLACE_HALVE_V); /* adjust crtc values */ adjusted_mode->CrtcHTotal = adjusted_mode->CrtcHDisplay + radeon_output->HBlank; adjusted_mode->CrtcHSyncStart = adjusted_mode->CrtcHDisplay + radeon_output->HOverPlus; adjusted_mode->CrtcHSyncEnd = adjusted_mode->CrtcHSyncStart + radeon_output->HSyncWidth; adjusted_mode->CrtcVTotal = adjusted_mode->CrtcVDisplay + radeon_output->VBlank; adjusted_mode->CrtcVSyncStart = adjusted_mode->CrtcVDisplay + radeon_output->VOverPlus; adjusted_mode->CrtcVSyncEnd = adjusted_mode->CrtcVSyncStart + radeon_output->VSyncWidth; adjusted_mode->Clock = radeon_output->DotClock; adjusted_mode->Flags = radeon_output->Flags; } return TRUE; } static void radeon_mode_prepare(xf86OutputPtr output) { } static void radeon_mode_set(xf86OutputPtr output, DisplayModePtr mode, DisplayModePtr adjusted_mode) { RADEONInfoPtr info = RADEONPTR(output->scrn); if (IS_AVIVO_VARIANT) atombios_output_mode_set(output, mode, adjusted_mode); else legacy_output_mode_set(output, mode, adjusted_mode); } static void radeon_mode_commit(xf86OutputPtr output) { radeon_dpms(output, DPMSModeOn); } static xf86OutputStatus radeon_detect(xf86OutputPtr output) { ScrnInfoPtr pScrn = output->scrn; RADEONInfoPtr info = RADEONPTR(pScrn); RADEONOutputPrivatePtr radeon_output = output->driver_private; Bool connected = TRUE; radeon_output->MonType = MT_UNKNOWN; RADEONConnectorFindMonitor(pScrn, output); /* nothing connected, light up some defaults so the server comes up */ if (radeon_output->MonType == MT_NONE && info->first_load_no_devices) { if (info->IsMobility) { if (radeon_output->type == OUTPUT_LVDS) { radeon_output->MonType = MT_LCD; info->first_load_no_devices = FALSE; xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Using LVDS default\n"); } } else { if (radeon_output->type == OUTPUT_VGA || radeon_output->type == OUTPUT_DVI_I) { radeon_output->MonType = MT_CRT; info->first_load_no_devices = FALSE; xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Using VGA default\n"); } else if (radeon_output->type == OUTPUT_DVI_D) { radeon_output->MonType = MT_DFP; info->first_load_no_devices = FALSE; xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Using DVI default\n"); } } } /* set montype so users can force outputs on even if detection fails */ if (radeon_output->MonType == MT_NONE) { connected = FALSE; if (radeon_output->type == OUTPUT_LVDS) radeon_output->MonType = MT_LCD; else if (radeon_output->type == OUTPUT_VGA) radeon_output->MonType = MT_CRT; else if (radeon_output->type == OUTPUT_STV) radeon_output->MonType = MT_STV; else if (radeon_output->type == OUTPUT_CTV) radeon_output->MonType = MT_CTV; else if (radeon_output->type == OUTPUT_CV) radeon_output->MonType = MT_CV; else if (radeon_output->type == OUTPUT_DVI_D) radeon_output->MonType = MT_DFP; else if (radeon_output->type == OUTPUT_HDMI) radeon_output->MonType = MT_DFP; else if (radeon_output->type == OUTPUT_DVI_A) radeon_output->MonType = MT_CRT; else if (radeon_output->type == OUTPUT_DVI_I) { if (radeon_output->DVIType == DVI_ANALOG) radeon_output->MonType = MT_CRT; else if (radeon_output->DVIType == DVI_DIGITAL) radeon_output->MonType = MT_DFP; } } if (radeon_output->MonType == MT_UNKNOWN) { output->subpixel_order = SubPixelUnknown; return XF86OutputStatusUnknown; } else { switch(radeon_output->MonType) { case MT_LCD: case MT_DFP: output->subpixel_order = SubPixelHorizontalRGB; break; default: output->subpixel_order = SubPixelNone; break; } #if 0 if (!connected) { /* default to unknown for flaky chips/connectors * so we can get something on the screen */ if ((radeon_output->type == OUTPUT_VGA || radeon_output->type == OUTPUT_DVI_I) && (radeon_output->DACType == DAC_TVDAC) && (info->ChipFamily == CHIP_FAMILY_RS400)) { radeon_output->MonType = MT_CRT; return XF86OutputStatusUnknown; } else if ((info->ChipFamily == CHIP_FAMILY_RS400) && radeon_output->type == OUTPUT_DVI_D) { radeon_output->MonType = MT_DFP; /* MT_LCD ??? */ return XF86OutputStatusUnknown; } } #endif if (connected) return XF86OutputStatusConnected; else return XF86OutputStatusDisconnected; } } static DisplayModePtr radeon_get_modes(xf86OutputPtr output) { DisplayModePtr modes; modes = RADEONProbeOutputModes(output); return modes; } static void radeon_destroy (xf86OutputPtr output) { if (output->driver_private) xfree(output->driver_private); } static void radeon_set_backlight_level(xf86OutputPtr output, int level) { #if 0 ScrnInfoPtr pScrn = output->scrn; RADEONInfoPtr info = RADEONPTR(pScrn); unsigned char * RADEONMMIO = info->MMIO; CARD32 lvds_gen_cntl; lvds_gen_cntl = INREG(RADEON_LVDS_GEN_CNTL); lvds_gen_cntl |= RADEON_LVDS_BL_MOD_EN; lvds_gen_cntl &= ~RADEON_LVDS_BL_MOD_LEVEL_MASK; lvds_gen_cntl |= (level << RADEON_LVDS_BL_MOD_LEVEL_SHIFT) & RADEON_LVDS_BL_MOD_LEVEL_MASK; //usleep (radeon_output->PanelPwrDly * 1000); OUTREG(RADEON_LVDS_GEN_CNTL, lvds_gen_cntl); lvds_gen_cntl &= ~RADEON_LVDS_BL_MOD_EN; //usleep (radeon_output->PanelPwrDly * 1000); OUTREG(RADEON_LVDS_GEN_CNTL, lvds_gen_cntl); #endif } static Atom backlight_atom; static Atom tmds_pll_atom; static Atom rmx_atom; static Atom monitor_type_atom; static Atom load_detection_atom; static Atom tv_hsize_atom; static Atom tv_hpos_atom; static Atom tv_vpos_atom; static Atom tv_std_atom; #define RADEON_MAX_BACKLIGHT_LEVEL 255 static void radeon_create_resources(xf86OutputPtr output) { ScrnInfoPtr pScrn = output->scrn; RADEONInfoPtr info = RADEONPTR(pScrn); RADEONOutputPrivatePtr radeon_output = output->driver_private; INT32 range[2]; int data, err; const char *s; /* backlight control */ if (radeon_output->type == OUTPUT_LVDS) { backlight_atom = MAKE_ATOM("backlight"); range[0] = 0; range[1] = RADEON_MAX_BACKLIGHT_LEVEL; err = RRConfigureOutputProperty(output->randr_output, backlight_atom, FALSE, TRUE, FALSE, 2, range); if (err != 0) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "RRConfigureOutputProperty error, %d\n", err); } /* Set the current value of the backlight property */ //data = (info->SavedReg->lvds_gen_cntl & RADEON_LVDS_BL_MOD_LEVEL_MASK) >> RADEON_LVDS_BL_MOD_LEVEL_SHIFT; data = RADEON_MAX_BACKLIGHT_LEVEL; err = RRChangeOutputProperty(output->randr_output, backlight_atom, XA_INTEGER, 32, PropModeReplace, 1, &data, FALSE, TRUE); if (err != 0) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "RRChangeOutputProperty error, %d\n", err); } } if (radeon_output->DACType == DAC_PRIMARY || radeon_output->DACType == DAC_TVDAC) { load_detection_atom = MAKE_ATOM("load_detection"); range[0] = 0; /* off */ range[1] = 1; /* on */ err = RRConfigureOutputProperty(output->randr_output, load_detection_atom, FALSE, TRUE, FALSE, 2, range); if (err != 0) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "RRConfigureOutputProperty error, %d\n", err); } if (radeon_output->load_detection) data = 1; /* user forces on tv dac load detection */ else data = 0; /* shared tvdac between vga/dvi/tv */ err = RRChangeOutputProperty(output->randr_output, load_detection_atom, XA_INTEGER, 32, PropModeReplace, 1, &data, FALSE, TRUE); if (err != 0) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "RRChangeOutputProperty error, %d\n", err); } } if (OUTPUT_IS_DVI && radeon_output->TMDSType == TMDS_INT) { tmds_pll_atom = MAKE_ATOM("tmds_pll"); err = RRConfigureOutputProperty(output->randr_output, tmds_pll_atom, FALSE, FALSE, FALSE, 0, NULL); if (err != 0) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "RRConfigureOutputProperty error, %d\n", err); } /* Set the current value of the property */ #if defined(__powerpc__) s = "driver"; #else s = "bios"; #endif if (xf86ReturnOptValBool(info->Options, OPTION_DEFAULT_TMDS_PLL, FALSE)) { s = "driver"; } err = RRChangeOutputProperty(output->randr_output, tmds_pll_atom, XA_STRING, 8, PropModeReplace, strlen(s), (pointer)s, FALSE, FALSE); if (err != 0) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "RRChangeOutputProperty error, %d\n", err); } } /* RMX control - fullscreen, centered, keep ratio, off */ /* actually more of a crtc property as only crtc1 has rmx */ if (radeon_output->type == OUTPUT_LVDS || OUTPUT_IS_DVI) { rmx_atom = MAKE_ATOM("scaler"); err = RRConfigureOutputProperty(output->randr_output, rmx_atom, FALSE, FALSE, FALSE, 0, NULL); if (err != 0) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "RRConfigureOutputProperty error, %d\n", err); } /* Set the current value of the property */ if (radeon_output->type == OUTPUT_LVDS) s = "full"; else s = "off"; err = RRChangeOutputProperty(output->randr_output, rmx_atom, XA_STRING, 8, PropModeReplace, strlen(s), (pointer)s, FALSE, FALSE); if (err != 0) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "RRChangeOutputProperty error, %d\n", err); } } /* force auto/analog/digital for DVI-I ports */ if (radeon_output->type == OUTPUT_DVI_I) { monitor_type_atom = MAKE_ATOM("dvi_monitor_type"); err = RRConfigureOutputProperty(output->randr_output, monitor_type_atom, FALSE, FALSE, FALSE, 0, NULL); if (err != 0) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "RRConfigureOutputProperty error, %d\n", err); } /* Set the current value of the backlight property */ s = "auto"; err = RRChangeOutputProperty(output->randr_output, monitor_type_atom, XA_STRING, 8, PropModeReplace, strlen(s), (pointer)s, FALSE, FALSE); if (err != 0) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "RRChangeOutputProperty error, %d\n", err); } } if (OUTPUT_IS_TV) { if (!IS_AVIVO_VARIANT) { tv_hsize_atom = MAKE_ATOM("tv_horizontal_size"); range[0] = -MAX_H_SIZE; range[1] = MAX_H_SIZE; err = RRConfigureOutputProperty(output->randr_output, tv_hsize_atom, FALSE, TRUE, FALSE, 2, range); if (err != 0) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "RRConfigureOutputProperty error, %d\n", err); } data = 0; err = RRChangeOutputProperty(output->randr_output, tv_hsize_atom, XA_INTEGER, 32, PropModeReplace, 1, &data, FALSE, TRUE); if (err != 0) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "RRChangeOutputProperty error, %d\n", err); } tv_hpos_atom = MAKE_ATOM("tv_horizontal_position"); range[0] = -MAX_H_POSITION; range[1] = MAX_H_POSITION; err = RRConfigureOutputProperty(output->randr_output, tv_hpos_atom, FALSE, TRUE, FALSE, 2, range); if (err != 0) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "RRConfigureOutputProperty error, %d\n", err); } data = 0; err = RRChangeOutputProperty(output->randr_output, tv_hpos_atom, XA_INTEGER, 32, PropModeReplace, 1, &data, FALSE, TRUE); if (err != 0) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "RRChangeOutputProperty error, %d\n", err); } tv_vpos_atom = MAKE_ATOM("tv_vertical_position"); range[0] = -MAX_V_POSITION; range[1] = MAX_V_POSITION; err = RRConfigureOutputProperty(output->randr_output, tv_vpos_atom, FALSE, TRUE, FALSE, 2, range); if (err != 0) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "RRConfigureOutputProperty error, %d\n", err); } data = 0; err = RRChangeOutputProperty(output->randr_output, tv_vpos_atom, XA_INTEGER, 32, PropModeReplace, 1, &data, FALSE, TRUE); if (err != 0) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "RRChangeOutputProperty error, %d\n", err); } } tv_std_atom = MAKE_ATOM("tv_standard"); err = RRConfigureOutputProperty(output->randr_output, tv_std_atom, FALSE, FALSE, FALSE, 0, NULL); if (err != 0) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "RRConfigureOutputProperty error, %d\n", err); } /* Set the current value of the property */ switch (radeon_output->tvStd) { case TV_STD_PAL: s = "pal"; break; case TV_STD_PAL_M: s = "pal-m"; break; case TV_STD_PAL_60: s = "pal-60"; break; case TV_STD_NTSC_J: s = "ntsc-j"; break; case TV_STD_SCART_PAL: s = "scart-pal"; break; case TV_STD_NTSC: default: s = "ntsc"; break; } err = RRChangeOutputProperty(output->randr_output, tv_std_atom, XA_STRING, 8, PropModeReplace, strlen(s), (pointer)s, FALSE, FALSE); if (err != 0) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "RRChangeOutputProperty error, %d\n", err); } } } static Bool radeon_set_property(xf86OutputPtr output, Atom property, RRPropertyValuePtr value) { RADEONInfoPtr info = RADEONPTR(output->scrn); RADEONOutputPrivatePtr radeon_output = output->driver_private; INT32 val; if (property == backlight_atom) { if (value->type != XA_INTEGER || value->format != 32 || value->size != 1) { return FALSE; } val = *(INT32 *)value->data; if (val < 0 || val > RADEON_MAX_BACKLIGHT_LEVEL) return FALSE; #if defined(__powerpc__) val = RADEON_MAX_BACKLIGHT_LEVEL - val; #endif radeon_set_backlight_level(output, val); } else if (property == load_detection_atom) { if (value->type != XA_INTEGER || value->format != 32 || value->size != 1) { return FALSE; } val = *(INT32 *)value->data; if (val < 0 || val > 1) return FALSE; radeon_output->load_detection = val; } else if (property == rmx_atom) { const char *s; if (value->type != XA_STRING || value->format != 8) return FALSE; s = (char*)value->data; if (value->size == strlen("full") && !strncmp("full", s, strlen("full"))) { radeon_output->rmx_type = RMX_FULL; return TRUE; } else if (value->size == strlen("aspect") && !strncmp("aspect", s, strlen("aspect"))) { radeon_output->rmx_type = RMX_ASPECT; return TRUE; } else if (value->size == strlen("center") && !strncmp("center", s, strlen("center"))) { radeon_output->rmx_type = RMX_CENTER; return TRUE; } else if (value->size == strlen("off") && !strncmp("off", s, strlen("off"))) { radeon_output->rmx_type = RMX_OFF; return TRUE; } return FALSE; } else if (property == tmds_pll_atom) { const char *s; if (value->type != XA_STRING || value->format != 8) return FALSE; s = (char*)value->data; if (value->size == strlen("bios") && !strncmp("bios", s, strlen("bios"))) { if (!RADEONGetTMDSInfoFromBIOS(output)) RADEONGetTMDSInfoFromTable(output); return TRUE; } else if (value->size == strlen("driver") && !strncmp("driver", s, strlen("driver"))) { RADEONGetTMDSInfoFromTable(output); return TRUE; } return FALSE; } else if (property == monitor_type_atom) { const char *s; if (value->type != XA_STRING || value->format != 8) return FALSE; s = (char*)value->data; if (value->size == strlen("auto") && !strncmp("auto", s, strlen("auto"))) { radeon_output->DVIType = DVI_AUTO; return TRUE; } else if (value->size == strlen("analog") && !strncmp("analog", s, strlen("analog"))) { radeon_output->DVIType = DVI_ANALOG; return TRUE; } else if (value->size == strlen("digital") && !strncmp("digital", s, strlen("digital"))) { radeon_output->DVIType = DVI_DIGITAL; return TRUE; } return FALSE; } else if (property == tv_hsize_atom) { if (value->type != XA_INTEGER || value->format != 32 || value->size != 1) { return FALSE; } val = *(INT32 *)value->data; if (val < -MAX_H_SIZE || val > MAX_H_SIZE) return FALSE; radeon_output->hSize = val; if (radeon_output->tv_on && !IS_AVIVO_VARIANT) RADEONUpdateHVPosition(output, &output->crtc->mode); return TRUE; } else if (property == tv_hpos_atom) { if (value->type != XA_INTEGER || value->format != 32 || value->size != 1) { return FALSE; } val = *(INT32 *)value->data; if (val < -MAX_H_POSITION || val > MAX_H_POSITION) return FALSE; radeon_output->hPos = val; if (radeon_output->tv_on && !IS_AVIVO_VARIANT) RADEONUpdateHVPosition(output, &output->crtc->mode); return TRUE; } else if (property == tv_vpos_atom) { if (value->type != XA_INTEGER || value->format != 32 || value->size != 1) { return FALSE; } val = *(INT32 *)value->data; if (val < -MAX_H_POSITION || val > MAX_H_POSITION) return FALSE; radeon_output->vPos = val; if (radeon_output->tv_on && !IS_AVIVO_VARIANT) RADEONUpdateHVPosition(output, &output->crtc->mode); return TRUE; } else if (property == tv_std_atom) { const char *s; if (value->type != XA_STRING || value->format != 8) return FALSE; s = (char*)value->data; if (value->size == strlen("ntsc") && !strncmp("ntsc", s, strlen("ntsc"))) { radeon_output->tvStd = TV_STD_NTSC; return TRUE; } else if (value->size == strlen("pal") && !strncmp("pal", s, strlen("pal"))) { radeon_output->tvStd = TV_STD_PAL; return TRUE; } else if (value->size == strlen("pal-m") && !strncmp("pal-m", s, strlen("pal-m"))) { radeon_output->tvStd = TV_STD_PAL_M; return TRUE; } else if (value->size == strlen("pal-60") && !strncmp("pal-60", s, strlen("pal-60"))) { radeon_output->tvStd = TV_STD_PAL_60; return TRUE; } else if (value->size == strlen("ntsc-j") && !strncmp("ntsc-j", s, strlen("ntsc-j"))) { radeon_output->tvStd = TV_STD_NTSC_J; return TRUE; } else if (value->size == strlen("scart-pal") && !strncmp("scart-pal", s, strlen("scart-pal"))) { radeon_output->tvStd = TV_STD_SCART_PAL; return TRUE; } else if (value->size == strlen("pal-cn") && !strncmp("pal-cn", s, strlen("pal-cn"))) { radeon_output->tvStd = TV_STD_PAL_CN; return TRUE; } else if (value->size == strlen("secam") && !strncmp("secam", s, strlen("secam"))) { radeon_output->tvStd = TV_STD_SECAM; return TRUE; } return FALSE; } return TRUE; } static const xf86OutputFuncsRec radeon_output_funcs = { .create_resources = radeon_create_resources, .dpms = radeon_dpms, .save = radeon_save, .restore = radeon_restore, .mode_valid = radeon_mode_valid, .mode_fixup = radeon_mode_fixup, .prepare = radeon_mode_prepare, .mode_set = radeon_mode_set, .commit = radeon_mode_commit, .detect = radeon_detect, .get_modes = radeon_get_modes, .set_property = radeon_set_property, .destroy = radeon_destroy }; void RADEONSetOutputType(ScrnInfoPtr pScrn, RADEONOutputPrivatePtr radeon_output) { RADEONOutputType output = OUTPUT_NONE; switch(radeon_output->ConnectorType) { case CONNECTOR_VGA: output = OUTPUT_VGA; break; case CONNECTOR_DVI_I: output = OUTPUT_DVI_I; break; case CONNECTOR_DVI_D: output = OUTPUT_DVI_D; break; case CONNECTOR_DVI_A: output = OUTPUT_DVI_A; break; case CONNECTOR_DIN: if (radeon_output->devices & ATOM_DEVICE_CV_SUPPORT) output = OUTPUT_CV; else if (radeon_output->devices & ATOM_DEVICE_TV1_SUPPORT) output = OUTPUT_STV; break; case CONNECTOR_STV: output = OUTPUT_STV; break; case CONNECTOR_CTV: output = OUTPUT_CTV; break; case CONNECTOR_LVDS: output = OUTPUT_LVDS; break; case CONNECTOR_HDMI_TYPE_A: case CONNECTOR_HDMI_TYPE_B: output = OUTPUT_HDMI; break; case CONNECTOR_DIGITAL: case CONNECTOR_NONE: case CONNECTOR_UNSUPPORTED: default: output = OUTPUT_NONE; break; } radeon_output->type = output; } #if 0 static Bool AVIVOI2CReset(ScrnInfoPtr pScrn) { RADEONInfoPtr info = RADEONPTR(pScrn); unsigned char *RADEONMMIO = info->MMIO; OUTREG(AVIVO_I2C_STOP, 1); INREG(AVIVO_I2C_STOP); OUTREG(AVIVO_I2C_STOP, 0x0); return TRUE; } #endif static Bool AVIVOI2CDoLock(ScrnInfoPtr pScrn, int lock_state, int gpio_reg) { RADEONInfoPtr info = RADEONPTR(pScrn); unsigned char *RADEONMMIO = info->MMIO; CARD32 temp; temp = INREG(gpio_reg); if (gpio_reg == AVIVO_GPIO_0) { if (lock_state == AVIVO_I2C_ENABLE) temp |= (1 << 19) | (1 << 18); else temp &= ~((1 << 19) | (1 << 18)); } else { if (lock_state == AVIVO_I2C_ENABLE) temp |= (1 << 0) | (1 << 8); else temp &= ~((1 << 0) | (1 << 8)); } OUTREG(gpio_reg, temp); temp = INREG(gpio_reg); return TRUE; } void avivo_i2c_gpio_get_bits(I2CBusPtr b, int *Clock, int *data) { ScrnInfoPtr screen_info = xf86Screens[b->scrnIndex]; RADEONInfoPtr info = RADEONPTR(screen_info); unsigned char *RADEONMMIO = info->MMIO; unsigned long val; /* Get the result */ if (b->DriverPrivate.uval == AVIVO_GPIO_0) { val = INREG(b->DriverPrivate.uval + 0xc); *Clock = (val & (1<<19)) != 0; *data = (val & (1<<18)) != 0; } else { val = INREG(b->DriverPrivate.uval + 0xc); *Clock = (val & (1<<0)) != 0; *data = (val & (1<<8)) != 0; } } static void avivo_i2c_gpio_put_bits(I2CBusPtr b, int Clock, int data) { ScrnInfoPtr screen_info = xf86Screens[b->scrnIndex]; RADEONInfoPtr info = RADEONPTR(screen_info); unsigned char *RADEONMMIO = info->MMIO; unsigned long val; val = 0; if (b->DriverPrivate.uval == AVIVO_GPIO_0) { val |= (Clock ? 0:(1<<19)); val |= (data ? 0:(1<<18)); } else { val |= (Clock ? 0:(1<<0)); val |= (data ? 0:(1<<8)); } OUTREG(b->DriverPrivate.uval + 0x8, val); /* read back to improve reliability on some cards. */ val = INREG(b->DriverPrivate.uval + 0x8); } static Bool avivo_i2c_init(ScrnInfoPtr pScrn, I2CBusPtr *bus_ptr, int i2c_reg, char *name) { I2CBusPtr pI2CBus; pI2CBus = xf86CreateI2CBusRec(); if (!pI2CBus) return FALSE; pI2CBus->BusName = name; pI2CBus->scrnIndex = pScrn->scrnIndex; pI2CBus->I2CPutBits = avivo_i2c_gpio_put_bits; pI2CBus->I2CGetBits = avivo_i2c_gpio_get_bits; pI2CBus->AcknTimeout = 5; pI2CBus->DriverPrivate.uval = i2c_reg; if (!xf86I2CBusInit(pI2CBus)) return FALSE; *bus_ptr = pI2CBus; return TRUE; } static void RADEONI2CGetBits(I2CBusPtr b, int *Clock, int *data) { ScrnInfoPtr pScrn = xf86Screens[b->scrnIndex]; RADEONInfoPtr info = RADEONPTR(pScrn); unsigned long val; unsigned char *RADEONMMIO = info->MMIO; /* Get the result */ if (b->DriverPrivate.uval == RADEON_LCD_GPIO_MASK) { val = INREG(b->DriverPrivate.uval+4); *Clock = (val & (1<<13)) != 0; *data = (val & (1<<12)) != 0; } else if (b->DriverPrivate.uval == RADEON_MDGPIO_EN_REG) { val = INREG(b->DriverPrivate.uval+4); *Clock = (val & (1<<19)) != 0; *data = (val & (1<<18)) != 0; } else { val = INREG(b->DriverPrivate.uval); *Clock = (val & RADEON_GPIO_Y_1) != 0; *data = (val & RADEON_GPIO_Y_0) != 0; } } static void RADEONI2CPutBits(I2CBusPtr b, int Clock, int data) { ScrnInfoPtr pScrn = xf86Screens[b->scrnIndex]; RADEONInfoPtr info = RADEONPTR(pScrn); unsigned long val; unsigned char *RADEONMMIO = info->MMIO; if (b->DriverPrivate.uval == RADEON_LCD_GPIO_MASK) { val = INREG(b->DriverPrivate.uval) & (CARD32)~((1<<12) | (1<<13)); val |= (Clock ? 0:(1<<13)); val |= (data ? 0:(1<<12)); OUTREG(b->DriverPrivate.uval, val); } else if (b->DriverPrivate.uval == RADEON_MDGPIO_EN_REG) { val = INREG(b->DriverPrivate.uval) & (CARD32)~((1<<18) | (1<<19)); val |= (Clock ? 0:(1<<19)); val |= (data ? 0:(1<<18)); OUTREG(b->DriverPrivate.uval, val); } else { val = INREG(b->DriverPrivate.uval) & (CARD32)~(RADEON_GPIO_EN_0 | RADEON_GPIO_EN_1); val |= (Clock ? 0:RADEON_GPIO_EN_1); val |= (data ? 0:RADEON_GPIO_EN_0); OUTREG(b->DriverPrivate.uval, val); } /* read back to improve reliability on some cards. */ val = INREG(b->DriverPrivate.uval); } static Bool RADEONI2CInit(ScrnInfoPtr pScrn, I2CBusPtr *bus_ptr, int i2c_reg, char *name) { I2CBusPtr pI2CBus; pI2CBus = xf86CreateI2CBusRec(); if (!pI2CBus) return FALSE; pI2CBus->BusName = name; pI2CBus->scrnIndex = pScrn->scrnIndex; pI2CBus->I2CPutBits = RADEONI2CPutBits; pI2CBus->I2CGetBits = RADEONI2CGetBits; pI2CBus->AcknTimeout = 5; pI2CBus->DriverPrivate.uval = i2c_reg; if (!xf86I2CBusInit(pI2CBus)) return FALSE; *bus_ptr = pI2CBus; return TRUE; } static void RADEONGetPanelInfoFromReg (xf86OutputPtr output) { ScrnInfoPtr pScrn = output->scrn; RADEONInfoPtr info = RADEONPTR(pScrn); RADEONOutputPrivatePtr radeon_output = output->driver_private; unsigned char *RADEONMMIO = info->MMIO; CARD32 fp_vert_stretch = INREG(RADEON_FP_VERT_STRETCH); CARD32 fp_horz_stretch = INREG(RADEON_FP_HORZ_STRETCH); radeon_output->PanelPwrDly = 200; if (fp_vert_stretch & RADEON_VERT_STRETCH_ENABLE) { radeon_output->PanelYRes = ((fp_vert_stretch & RADEON_VERT_PANEL_SIZE) >> RADEON_VERT_PANEL_SHIFT) + 1; } else { radeon_output->PanelYRes = (INREG(RADEON_CRTC_V_TOTAL_DISP)>>16) + 1; } if (fp_horz_stretch & RADEON_HORZ_STRETCH_ENABLE) { radeon_output->PanelXRes = (((fp_horz_stretch & RADEON_HORZ_PANEL_SIZE) >> RADEON_HORZ_PANEL_SHIFT) + 1) * 8; } else { radeon_output->PanelXRes = ((INREG(RADEON_CRTC_H_TOTAL_DISP)>>16) + 1) * 8; } if ((radeon_output->PanelXRes < 640) || (radeon_output->PanelYRes < 480)) { radeon_output->PanelXRes = 640; radeon_output->PanelYRes = 480; } // move this to crtc function if (xf86ReturnOptValBool(info->Options, OPTION_LVDS_PROBE_PLL, TRUE)) { CARD32 ppll_div_sel, ppll_val; ppll_div_sel = INREG8(RADEON_CLOCK_CNTL_INDEX + 1) & 0x3; RADEONPllErrataAfterIndex(info); ppll_val = INPLL(pScrn, RADEON_PPLL_DIV_0 + ppll_div_sel); if ((ppll_val & 0x000707ff) == 0x1bb) goto noprobe; info->FeedbackDivider = ppll_val & 0x7ff; info->PostDivider = (ppll_val >> 16) & 0x7; info->RefDivider = info->pll.reference_div; info->UseBiosDividers = TRUE; xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Existing panel PLL dividers will be used.\n"); } noprobe: xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Panel size %dx%d is derived, this may not be correct.\n" "If not, use PanelSize option to overwrite this setting\n", radeon_output->PanelXRes, radeon_output->PanelYRes); } /* BIOS may not have right panel size, we search through all supported * DDC modes looking for the maximum panel size. */ static void RADEONUpdatePanelSize(xf86OutputPtr output) { ScrnInfoPtr pScrn = output->scrn; RADEONInfoPtr info = RADEONPTR(pScrn); RADEONOutputPrivatePtr radeon_output = output->driver_private; int j; /* XXX: fixme */ //xf86MonPtr ddc = pScrn->monitor->DDC; xf86MonPtr ddc = output->MonInfo; DisplayModePtr p; // crtc should handle? if ((info->UseBiosDividers && radeon_output->DotClock != 0) || (ddc == NULL)) return; /* Go thru detailed timing table first */ for (j = 0; j < 4; j++) { if (ddc->det_mon[j].type == 0) { struct detailed_timings *d_timings = &ddc->det_mon[j].section.d_timings; int match = 0; /* If we didn't get a panel clock or guessed one, try to match the * mode with the panel size. We do that because we _need_ a panel * clock, or ValidateFPModes will fail, even when UseBiosDividers * is set. */ if (radeon_output->DotClock == 0 && radeon_output->PanelXRes == d_timings->h_active && radeon_output->PanelYRes == d_timings->v_active) match = 1; /* If we don't have a BIOS provided panel data with fixed dividers, * check for a larger panel size */ if (radeon_output->PanelXRes < d_timings->h_active && radeon_output->PanelYRes < d_timings->v_active && !info->UseBiosDividers) match = 1; if (match) { radeon_output->PanelXRes = d_timings->h_active; radeon_output->PanelYRes = d_timings->v_active; radeon_output->DotClock = d_timings->clock / 1000; radeon_output->HOverPlus = d_timings->h_sync_off; radeon_output->HSyncWidth = d_timings->h_sync_width; radeon_output->HBlank = d_timings->h_blanking; radeon_output->VOverPlus = d_timings->v_sync_off; radeon_output->VSyncWidth = d_timings->v_sync_width; radeon_output->VBlank = d_timings->v_blanking; radeon_output->Flags = (d_timings->interlaced ? V_INTERLACE : 0); switch (d_timings->misc) { case 0: radeon_output->Flags |= V_NHSYNC | V_NVSYNC; break; case 1: radeon_output->Flags |= V_PHSYNC | V_NVSYNC; break; case 2: radeon_output->Flags |= V_NHSYNC | V_PVSYNC; break; case 3: radeon_output->Flags |= V_PHSYNC | V_PVSYNC; break; } xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Panel infos found from DDC detailed: %dx%d\n", radeon_output->PanelXRes, radeon_output->PanelYRes); } } } if (info->UseBiosDividers && radeon_output->DotClock != 0) return; /* Search thru standard VESA modes from EDID */ for (j = 0; j < 8; j++) { if ((radeon_output->PanelXRes < ddc->timings2[j].hsize) && (radeon_output->PanelYRes < ddc->timings2[j].vsize)) { for (p = pScrn->monitor->Modes; p; p = p->next) { if ((ddc->timings2[j].hsize == p->HDisplay) && (ddc->timings2[j].vsize == p->VDisplay)) { float refresh = (float)p->Clock * 1000.0 / p->HTotal / p->VTotal; if (abs((float)ddc->timings2[j].refresh - refresh) < 1.0) { /* Is this good enough? */ radeon_output->PanelXRes = ddc->timings2[j].hsize; radeon_output->PanelYRes = ddc->timings2[j].vsize; radeon_output->HBlank = p->HTotal - p->HDisplay; radeon_output->HOverPlus = p->HSyncStart - p->HDisplay; radeon_output->HSyncWidth = p->HSyncEnd - p->HSyncStart; radeon_output->VBlank = p->VTotal - p->VDisplay; radeon_output->VOverPlus = p->VSyncStart - p->VDisplay; radeon_output->VSyncWidth = p->VSyncEnd - p->VSyncStart; radeon_output->DotClock = p->Clock; radeon_output->Flags = p->Flags; xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Panel infos found from DDC VESA/EDID: %dx%d\n", radeon_output->PanelXRes, radeon_output->PanelYRes); } } } } } } static Bool RADEONGetLVDSInfo (xf86OutputPtr output) { ScrnInfoPtr pScrn = output->scrn; RADEONInfoPtr info = RADEONPTR(pScrn); RADEONOutputPrivatePtr radeon_output = output->driver_private; char* s; if (!RADEONGetLVDSInfoFromBIOS(output)) RADEONGetPanelInfoFromReg(output); if ((s = xf86GetOptValString(info->Options, OPTION_PANEL_SIZE))) { radeon_output->PanelPwrDly = 200; if (sscanf (s, "%dx%d", &radeon_output->PanelXRes, &radeon_output->PanelYRes) != 2) { xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Invalid PanelSize option: %s\n", s); RADEONGetPanelInfoFromReg(output); } } /* The panel size we collected from BIOS may not be the * maximum size supported by the panel. If not, we update * it now. These will be used if no matching mode can be * found from EDID data. */ RADEONUpdatePanelSize(output); if (radeon_output->DotClock == 0) { DisplayModePtr tmp_mode = NULL; xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "No valid timing info from BIOS.\n"); /* No timing information for the native mode, use whatever specified in the Modeline. If no Modeline specified, we'll just pick the VESA mode at 60Hz refresh rate which is likely to be the best for a flat panel. */ tmp_mode = pScrn->monitor->Modes; while(tmp_mode) { if ((tmp_mode->HDisplay == radeon_output->PanelXRes) && (tmp_mode->VDisplay == radeon_output->PanelYRes)) { float refresh = (float)tmp_mode->Clock * 1000.0 / tmp_mode->HTotal / tmp_mode->VTotal; if ((abs(60.0 - refresh) < 1.0) || (tmp_mode->type == 0)) { radeon_output->HBlank = tmp_mode->HTotal - tmp_mode->HDisplay; radeon_output->HOverPlus = tmp_mode->HSyncStart - tmp_mode->HDisplay; radeon_output->HSyncWidth = tmp_mode->HSyncEnd - tmp_mode->HSyncStart; radeon_output->VBlank = tmp_mode->VTotal - tmp_mode->VDisplay; radeon_output->VOverPlus = tmp_mode->VSyncStart - tmp_mode->VDisplay; radeon_output->VSyncWidth = tmp_mode->VSyncEnd - tmp_mode->VSyncStart; radeon_output->DotClock = tmp_mode->Clock; radeon_output->Flags = 0; break; } } tmp_mode = tmp_mode->next; if (tmp_mode == pScrn->monitor->Modes) break; } if ((radeon_output->DotClock == 0) && !output->MonInfo) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Panel size is not correctly detected.\n" "Please try to use PanelSize option for correct settings.\n"); return FALSE; } } return TRUE; } static void RADEONGetTMDSInfoFromTable(xf86OutputPtr output) { ScrnInfoPtr pScrn = output->scrn; RADEONInfoPtr info = RADEONPTR(pScrn); RADEONOutputPrivatePtr radeon_output = output->driver_private; int i; for (i=0; i<4; i++) { radeon_output->tmds_pll[i].value = default_tmds_pll[info->ChipFamily][i].value; radeon_output->tmds_pll[i].freq = default_tmds_pll[info->ChipFamily][i].freq; } } static void RADEONGetTMDSInfo(xf86OutputPtr output) { RADEONOutputPrivatePtr radeon_output = output->driver_private; int i; for (i=0; i<4; i++) { radeon_output->tmds_pll[i].value = 0; radeon_output->tmds_pll[i].freq = 0; } if (RADEONGetTMDSInfoFromBIOS(output)) return; RADEONGetTMDSInfoFromTable(output); } static void RADEONGetTVInfo(xf86OutputPtr output) { ScrnInfoPtr pScrn = output->scrn; RADEONInfoPtr info = RADEONPTR(pScrn); RADEONOutputPrivatePtr radeon_output = output->driver_private; char *optstr; radeon_output->hPos = 0; radeon_output->vPos = 0; radeon_output->hSize = 0; if (!RADEONGetTVInfoFromBIOS(output)) { /* set some reasonable defaults */ radeon_output->default_tvStd = TV_STD_NTSC; radeon_output->tvStd = TV_STD_NTSC; radeon_output->TVRefClk = 27.000000000; radeon_output->SupportedTVStds = TV_STD_NTSC | TV_STD_PAL; } optstr = (char *)xf86GetOptValString(info->Options, OPTION_TVSTD); if (optstr) { if (!strncmp("ntsc", optstr, strlen("ntsc"))) radeon_output->tvStd = TV_STD_NTSC; else if (!strncmp("pal", optstr, strlen("pal"))) radeon_output->tvStd = TV_STD_PAL; else if (!strncmp("pal-m", optstr, strlen("pal-m"))) radeon_output->tvStd = TV_STD_PAL_M; else if (!strncmp("pal-60", optstr, strlen("pal-60"))) radeon_output->tvStd = TV_STD_PAL_60; else if (!strncmp("ntsc-j", optstr, strlen("ntsc-j"))) radeon_output->tvStd = TV_STD_NTSC_J; else if (!strncmp("scart-pal", optstr, strlen("scart-pal"))) radeon_output->tvStd = TV_STD_SCART_PAL; else { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Invalid TV Standard: %s\n", optstr); } } } static void RADEONGetTVDacAdjInfo(xf86OutputPtr output) { ScrnInfoPtr pScrn = output->scrn; RADEONInfoPtr info = RADEONPTR(pScrn); RADEONOutputPrivatePtr radeon_output = output->driver_private; /* Todo: get this setting from BIOS */ radeon_output->tv_dac_adj = default_tvdac_adj[info->ChipFamily]; if (info->IsMobility) { /* some mobility chips may different */ if (info->ChipFamily == CHIP_FAMILY_RV250) radeon_output->tv_dac_adj = 0x00880000; } } void RADEONInitConnector(xf86OutputPtr output) { ScrnInfoPtr pScrn = output->scrn; RADEONInfoPtr info = RADEONPTR(pScrn); RADEONOutputPrivatePtr radeon_output = output->driver_private; char stmp[16]; char *name; sprintf(stmp, "DDC_0x%x", (unsigned int)radeon_output->ddc_line); name = xnfalloc(strlen(stmp) + 1); strcpy(name, stmp); if (IS_AVIVO_VARIANT) { if (radeon_output->ddc_line) avivo_i2c_init(pScrn, &radeon_output->pI2CBus, radeon_output->ddc_line, name); } else { if (radeon_output->ddc_line) RADEONI2CInit(pScrn, &radeon_output->pI2CBus, radeon_output->ddc_line, name); } if (radeon_output->DACType == DAC_PRIMARY) radeon_output->load_detection = 1; /* primary dac, only drives vga */ /*else if (radeon_output->DACType == DAC_TVDAC && info->tvdac_use_count < 2) radeon_output->load_detection = 1;*/ /* only one output with tvdac */ else if ((radeon_output->DACType == DAC_TVDAC) && (xf86ReturnOptValBool(info->Options, OPTION_TVDAC_LOAD_DETECT, FALSE))) radeon_output->load_detection = 1; /* shared tvdac between vga/dvi/tv */ else radeon_output->load_detection = 0; /* shared tvdac between vga/dvi/tv */ if (radeon_output->type == OUTPUT_LVDS) { radeon_output->rmx_type = RMX_FULL; RADEONGetLVDSInfo(output); } if (OUTPUT_IS_DVI) { I2CBusPtr pDVOBus; radeon_output->rmx_type = RMX_OFF; if ((!IS_AVIVO_VARIANT) && radeon_output->TMDSType == TMDS_EXT) { #if defined(__powerpc__) radeon_output->dvo_i2c_reg = RADEON_GPIO_MONID; radeon_output->dvo_i2c_slave_addr = 0x70; #else if (!RADEONGetExtTMDSInfoFromBIOS(output)) { radeon_output->dvo_i2c_reg = RADEON_GPIO_CRT2_DDC; radeon_output->dvo_i2c_slave_addr = 0x70; } #endif if (RADEONI2CInit(pScrn, &pDVOBus, radeon_output->dvo_i2c_reg, "DVO")) { radeon_output->DVOChip = RADEONDVODeviceInit(pDVOBus, radeon_output->dvo_i2c_slave_addr); if (!radeon_output->DVOChip) xfree(pDVOBus); } } else RADEONGetTMDSInfo(output); } if (OUTPUT_IS_TV) { RADEONGetTVInfo(output); RADEONGetTVDacAdjInfo(output); } if (radeon_output->DACType == DAC_TVDAC) { radeon_output->tv_on = FALSE; RADEONGetTVDacAdjInfo(output); } } #if defined(__powerpc__) static Bool RADEONSetupAppleConnectors(ScrnInfoPtr pScrn) { RADEONInfoPtr info = RADEONPTR(pScrn); switch (info->MacModel) { case RADEON_MAC_IBOOK: info->BiosConnector[0].ddc_line = RADEON_GPIO_DVI_DDC; info->BiosConnector[0].DACType = DAC_NONE; info->BiosConnector[0].TMDSType = TMDS_NONE; info->BiosConnector[0].ConnectorType = CONNECTOR_LVDS; info->BiosConnector[0].valid = TRUE; info->BiosConnector[1].ddc_line = RADEON_GPIO_VGA_DDC; info->BiosConnector[1].DACType = DAC_TVDAC; info->BiosConnector[1].TMDSType = TMDS_NONE; info->BiosConnector[1].ConnectorType = CONNECTOR_VGA; info->BiosConnector[1].valid = TRUE; info->BiosConnector[2].ConnectorType = CONNECTOR_STV; info->BiosConnector[2].DACType = DAC_TVDAC; info->BiosConnector[2].TMDSType = TMDS_NONE; info->BiosConnector[2].ddc_line = 0; info->BiosConnector[2].valid = TRUE; return TRUE; case RADEON_MAC_POWERBOOK_EXTERNAL: info->BiosConnector[0].ddc_line = RADEON_GPIO_DVI_DDC; info->BiosConnector[0].DACType = DAC_NONE; info->BiosConnector[0].TMDSType = TMDS_NONE; info->BiosConnector[0].ConnectorType = CONNECTOR_LVDS; info->BiosConnector[0].valid = TRUE; info->BiosConnector[1].ddc_line = RADEON_GPIO_VGA_DDC; info->BiosConnector[1].DACType = DAC_PRIMARY; info->BiosConnector[1].TMDSType = TMDS_EXT; info->BiosConnector[1].ConnectorType = CONNECTOR_DVI_I; info->BiosConnector[1].valid = TRUE; info->BiosConnector[2].ConnectorType = CONNECTOR_STV; info->BiosConnector[2].DACType = DAC_TVDAC; info->BiosConnector[2].TMDSType = TMDS_NONE; info->BiosConnector[2].ddc_line = 0; info->BiosConnector[2].valid = TRUE; return TRUE; case RADEON_MAC_POWERBOOK_INTERNAL: info->BiosConnector[0].ddc_line = RADEON_GPIO_DVI_DDC; info->BiosConnector[0].DACType = DAC_NONE; info->BiosConnector[0].TMDSType = TMDS_NONE; info->BiosConnector[0].ConnectorType = CONNECTOR_LVDS; info->BiosConnector[0].valid = TRUE; info->BiosConnector[1].ddc_line = RADEON_GPIO_VGA_DDC; info->BiosConnector[1].DACType = DAC_PRIMARY; info->BiosConnector[1].TMDSType = TMDS_INT; info->BiosConnector[1].ConnectorType = CONNECTOR_DVI_I; info->BiosConnector[1].valid = TRUE; info->BiosConnector[2].ConnectorType = CONNECTOR_STV; info->BiosConnector[2].DACType = DAC_TVDAC; info->BiosConnector[2].TMDSType = TMDS_NONE; info->BiosConnector[2].ddc_line = 0; info->BiosConnector[2].valid = TRUE; return TRUE; case RADEON_MAC_POWERBOOK_VGA: info->BiosConnector[0].ddc_line = RADEON_GPIO_DVI_DDC; info->BiosConnector[0].DACType = DAC_NONE; info->BiosConnector[0].TMDSType = TMDS_NONE; info->BiosConnector[0].ConnectorType = CONNECTOR_VGA; info->BiosConnector[0].valid = TRUE; info->BiosConnector[1].ddc_line = RADEON_GPIO_VGA_DDC; info->BiosConnector[1].DACType = DAC_PRIMARY; info->BiosConnector[1].TMDSType = TMDS_INT; info->BiosConnector[1].ConnectorType = CONNECTOR_DVI_I; info->BiosConnector[1].valid = TRUE; info->BiosConnector[2].ConnectorType = CONNECTOR_STV; info->BiosConnector[2].DACType = DAC_TVDAC; info->BiosConnector[2].TMDSType = TMDS_NONE; info->BiosConnector[2].ddc_line = 0; info->BiosConnector[2].valid = TRUE; return TRUE; case RADEON_MAC_MINI_EXTERNAL: info->BiosConnector[0].ddc_line = RADEON_GPIO_CRT2_DDC; info->BiosConnector[0].DACType = DAC_TVDAC; info->BiosConnector[0].TMDSType = TMDS_EXT; info->BiosConnector[0].ConnectorType = CONNECTOR_DVI_I; info->BiosConnector[0].valid = TRUE; info->BiosConnector[1].ConnectorType = CONNECTOR_STV; info->BiosConnector[1].DACType = DAC_TVDAC; info->BiosConnector[1].TMDSType = TMDS_NONE; info->BiosConnector[1].ddc_line = 0; info->BiosConnector[1].valid = TRUE; return TRUE; case RADEON_MAC_MINI_INTERNAL: info->BiosConnector[0].ddc_line = RADEON_GPIO_CRT2_DDC; info->BiosConnector[0].DACType = DAC_TVDAC; info->BiosConnector[0].TMDSType = TMDS_INT; info->BiosConnector[0].ConnectorType = CONNECTOR_DVI_I; info->BiosConnector[0].valid = TRUE; info->BiosConnector[1].ConnectorType = CONNECTOR_STV; info->BiosConnector[1].DACType = DAC_TVDAC; info->BiosConnector[1].TMDSType = TMDS_NONE; info->BiosConnector[1].ddc_line = 0; info->BiosConnector[1].valid = TRUE; return TRUE; case RADEON_MAC_IMAC_G5_ISIGHT: info->BiosConnector[0].ddc_line = RADEON_GPIO_MONID; info->BiosConnector[0].DACType = DAC_NONE; info->BiosConnector[0].TMDSType = TMDS_INT; info->BiosConnector[0].ConnectorType = CONNECTOR_DVI_D; info->BiosConnector[0].valid = TRUE; info->BiosConnector[1].ddc_line = RADEON_GPIO_DVI_DDC; info->BiosConnector[1].DACType = DAC_TVDAC; info->BiosConnector[1].TMDSType = TMDS_NONE; info->BiosConnector[1].ConnectorType = CONNECTOR_VGA; info->BiosConnector[1].valid = TRUE; info->BiosConnector[2].ConnectorType = CONNECTOR_STV; info->BiosConnector[2].DACType = DAC_TVDAC; info->BiosConnector[2].TMDSType = TMDS_NONE; info->BiosConnector[2].ddc_line = 0; info->BiosConnector[2].valid = TRUE; return TRUE; default: return FALSE; } return FALSE; } #endif static void RADEONSetupGenericConnectors(ScrnInfoPtr pScrn) { RADEONInfoPtr info = RADEONPTR(pScrn); RADEONEntPtr pRADEONEnt = RADEONEntPriv(pScrn); if (!pRADEONEnt->HasCRTC2) { info->BiosConnector[0].ddc_line = RADEON_GPIO_VGA_DDC; info->BiosConnector[0].DACType = DAC_PRIMARY; info->BiosConnector[0].TMDSType = TMDS_NONE; info->BiosConnector[0].ConnectorType = CONNECTOR_VGA; info->BiosConnector[0].valid = TRUE; return; } if (IS_AVIVO_VARIANT) { if (info->IsMobility) { info->BiosConnector[0].ddc_line = 0x7e60; info->BiosConnector[0].DACType = DAC_NONE; info->BiosConnector[0].TMDSType = TMDS_NONE; info->BiosConnector[0].ConnectorType = CONNECTOR_LVDS; info->BiosConnector[0].devices = ATOM_DEVICE_LCD1_SUPPORT; info->BiosConnector[0].valid = TRUE; info->BiosConnector[1].ddc_line = 0x7e40; info->BiosConnector[1].DACType = DAC_PRIMARY; info->BiosConnector[1].TMDSType = TMDS_NONE; info->BiosConnector[1].ConnectorType = CONNECTOR_VGA; info->BiosConnector[1].devices = ATOM_DEVICE_CRT1_SUPPORT; info->BiosConnector[1].valid = TRUE; } else { info->BiosConnector[0].ddc_line = 0x7e50; info->BiosConnector[0].DACType = DAC_TVDAC; info->BiosConnector[0].TMDSType = TMDS_INT; info->BiosConnector[0].ConnectorType = CONNECTOR_DVI_I; info->BiosConnector[0].devices = ATOM_DEVICE_CRT2_SUPPORT | ATOM_DEVICE_DFP1_SUPPORT; info->BiosConnector[0].valid = TRUE; info->BiosConnector[1].ddc_line = 0x7e40; info->BiosConnector[1].DACType = DAC_PRIMARY; info->BiosConnector[1].TMDSType = TMDS_NONE; info->BiosConnector[1].ConnectorType = CONNECTOR_VGA; info->BiosConnector[1].devices = ATOM_DEVICE_CRT1_SUPPORT; info->BiosConnector[1].valid = TRUE; } info->BiosConnector[2].ConnectorType = CONNECTOR_STV; info->BiosConnector[2].DACType = DAC_TVDAC; info->BiosConnector[2].TMDSType = TMDS_NONE; info->BiosConnector[2].ddc_line = 0; info->BiosConnector[2].devices = ATOM_DEVICE_TV1_SUPPORT; info->BiosConnector[2].valid = TRUE; } else { if (info->IsMobility) { /* Below is the most common setting, but may not be true */ if (info->IsIGP) { info->BiosConnector[0].ddc_line = RADEON_LCD_GPIO_MASK; info->BiosConnector[0].DACType = DAC_NONE; info->BiosConnector[0].TMDSType = TMDS_NONE; info->BiosConnector[0].ConnectorType = CONNECTOR_LVDS; info->BiosConnector[0].valid = TRUE; /* IGP only has TVDAC */ if (info->ChipFamily == CHIP_FAMILY_RS400) info->BiosConnector[1].ddc_line = RADEON_GPIO_CRT2_DDC; else info->BiosConnector[1].ddc_line = RADEON_GPIO_VGA_DDC; info->BiosConnector[1].DACType = DAC_TVDAC; info->BiosConnector[1].TMDSType = TMDS_NONE; info->BiosConnector[1].ConnectorType = CONNECTOR_VGA; info->BiosConnector[1].valid = TRUE; } else { #if defined(__powerpc__) info->BiosConnector[0].ddc_line = RADEON_GPIO_DVI_DDC; #else info->BiosConnector[0].ddc_line = RADEON_LCD_GPIO_MASK; #endif info->BiosConnector[0].DACType = DAC_NONE; info->BiosConnector[0].TMDSType = TMDS_NONE; info->BiosConnector[0].ConnectorType = CONNECTOR_LVDS; info->BiosConnector[0].valid = TRUE; info->BiosConnector[1].ddc_line = RADEON_GPIO_VGA_DDC; info->BiosConnector[1].DACType = DAC_PRIMARY; info->BiosConnector[1].TMDSType = TMDS_NONE; info->BiosConnector[1].ConnectorType = CONNECTOR_VGA; info->BiosConnector[1].valid = TRUE; } } else { /* Below is the most common setting, but may not be true */ if (info->IsIGP) { if (info->ChipFamily == CHIP_FAMILY_RS400) info->BiosConnector[0].ddc_line = RADEON_GPIO_CRT2_DDC; else info->BiosConnector[0].ddc_line = RADEON_GPIO_VGA_DDC; info->BiosConnector[0].DACType = DAC_TVDAC; info->BiosConnector[0].TMDSType = TMDS_NONE; info->BiosConnector[0].ConnectorType = CONNECTOR_VGA; info->BiosConnector[0].valid = TRUE; /* not sure what a good default DDCType for DVI on * IGP desktop chips is */ info->BiosConnector[1].ddc_line = RADEON_GPIO_MONID; /* DDC_DVI? */ info->BiosConnector[1].DACType = DAC_NONE; info->BiosConnector[1].TMDSType = TMDS_EXT; info->BiosConnector[1].ConnectorType = CONNECTOR_DVI_D; info->BiosConnector[1].valid = TRUE; } else { info->BiosConnector[0].ddc_line = RADEON_GPIO_DVI_DDC; info->BiosConnector[0].DACType = DAC_TVDAC; info->BiosConnector[0].TMDSType = TMDS_INT; info->BiosConnector[0].ConnectorType = CONNECTOR_DVI_I; info->BiosConnector[0].valid = TRUE; #if defined(__powerpc__) info->BiosConnector[1].ddc_line = RADEON_GPIO_VGA_DDC; info->BiosConnector[1].DACType = DAC_PRIMARY; info->BiosConnector[1].TMDSType = TMDS_EXT; info->BiosConnector[1].ConnectorType = CONNECTOR_DVI_I; info->BiosConnector[1].valid = TRUE; #else info->BiosConnector[1].ddc_line = RADEON_GPIO_VGA_DDC; info->BiosConnector[1].DACType = DAC_PRIMARY; info->BiosConnector[1].TMDSType = TMDS_EXT; info->BiosConnector[1].ConnectorType = CONNECTOR_VGA; info->BiosConnector[1].valid = TRUE; #endif } } if (info->InternalTVOut) { info->BiosConnector[2].ConnectorType = CONNECTOR_STV; info->BiosConnector[2].DACType = DAC_TVDAC; info->BiosConnector[2].TMDSType = TMDS_NONE; info->BiosConnector[2].ddc_line = 0; info->BiosConnector[2].valid = TRUE; } /* Some cards have the DDC lines swapped and we have no way to * detect it yet (Mac cards) */ if (xf86ReturnOptValBool(info->Options, OPTION_REVERSE_DDC, FALSE)) { info->BiosConnector[0].ddc_line = RADEON_GPIO_VGA_DDC; info->BiosConnector[1].ddc_line = RADEON_GPIO_DVI_DDC; } } } #if defined(__powerpc__) /* * Returns RADEONMacModel or 0 based on lines 'detected as' and 'machine' * in /proc/cpuinfo (on Linux) */ static RADEONMacModel RADEONDetectMacModel(ScrnInfoPtr pScrn) { RADEONInfoPtr info = RADEONPTR(pScrn); RADEONMacModel ret = 0; #ifdef __linux__ char cpuline[50]; /* 50 should be sufficient for our purposes */ FILE *f = fopen ("/proc/cpuinfo", "r"); /* Some macs (minis and powerbooks) use internal tmds, others use external tmds * and not just for dual-link TMDS, it shows up with single-link as well. * Unforunately, there doesn't seem to be any good way to figure it out. */ /* * PowerBook5,[1-5]: external tmds, single-link * PowerBook5,[789]: external tmds, dual-link * PowerBook5,6: external tmds, single-link or dual-link * need to add another option to specify the external tmds chip * or find out what's used and add it. */ if (f != NULL) { while (fgets(cpuline, sizeof cpuline, f)) { if (!strncmp(cpuline, "machine", strlen ("machine"))) { if (strstr(cpuline, "PowerBook5,1") || strstr(cpuline, "PowerBook5,2") || strstr(cpuline, "PowerBook5,3") || strstr(cpuline, "PowerBook5,4") || strstr(cpuline, "PowerBook5,5")) { ret = RADEON_MAC_POWERBOOK_EXTERNAL; /* single link */ info->ext_tmds_chip = RADEON_SIL_164; /* works on 5,2 */ break; } if (strstr(cpuline, "PowerBook5,6")) { ret = RADEON_MAC_POWERBOOK_EXTERNAL; /* dual or single link */ break; } if (strstr(cpuline, "PowerBook5,7") || strstr(cpuline, "PowerBook5,8") || strstr(cpuline, "PowerBook5,9")) { ret = RADEON_MAC_POWERBOOK_EXTERNAL; /* dual link */ info->ext_tmds_chip = RADEON_SIL_1178; /* guess */ break; } if (strstr(cpuline, "PowerBook3,3")) { ret = RADEON_MAC_POWERBOOK_VGA; /* vga rather than dvi */ break; } if (strstr(cpuline, "PowerMac10,1")) { ret = RADEON_MAC_MINI_INTERNAL; /* internal tmds */ break; } if (strstr(cpuline, "PowerMac10,2")) { ret = RADEON_MAC_MINI_EXTERNAL; /* external tmds */ break; } } else if (!strncmp(cpuline, "detected as", strlen("detected as"))) { if (strstr(cpuline, "iBook")) { ret = RADEON_MAC_IBOOK; break; } else if (strstr(cpuline, "PowerBook")) { ret = RADEON_MAC_POWERBOOK_INTERNAL; /* internal tmds */ break; } else if (strstr(cpuline, "iMac G5 (iSight)")) { ret = RADEON_MAC_IMAC_G5_ISIGHT; break; } /* No known PowerMac model detected */ break; } } fclose (f); } else xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Cannot detect PowerMac model because /proc/cpuinfo not " "readable.\n"); #endif /* __linux */ if (ret) { xf86DrvMsg(pScrn->scrnIndex, X_DEFAULT, "Detected %s.\n", ret == RADEON_MAC_POWERBOOK_EXTERNAL ? "PowerBook with external DVI" : ret == RADEON_MAC_POWERBOOK_INTERNAL ? "PowerBook with integrated DVI" : ret == RADEON_MAC_POWERBOOK_VGA ? "PowerBook with VGA" : ret == RADEON_MAC_IBOOK ? "iBook" : ret == RADEON_MAC_MINI_EXTERNAL ? "Mac Mini with external DVI" : ret == RADEON_MAC_MINI_INTERNAL ? "Mac Mini with integrated DVI" : "iMac G5 iSight"); xf86DrvMsg(pScrn->scrnIndex, X_INFO, "If this is not correct, try Option \"MacModel\" and " "consider reporting to the\n"); xf86DrvMsg(pScrn->scrnIndex, X_INFO, "xorg-driver-ati@lists.x.org mailing list" #ifdef __linux__ " with the contents of /proc/cpuinfo" #endif ".\n"); } return ret; } #endif /* __powerpc__ */ /* * initialise the static data sos we don't have to re-do at randr change */ Bool RADEONSetupConnectors(ScrnInfoPtr pScrn) { RADEONInfoPtr info = RADEONPTR(pScrn); RADEONEntPtr pRADEONEnt = RADEONEntPriv(pScrn); xf86OutputPtr output; char *optstr; int i = 0; int num_vga = 0; int num_dvi = 0; int num_hdmi = 0; /* We first get the information about all connectors from BIOS. * This is how the card is phyiscally wired up. * The information should be correct even on a OEM card. */ for (i = 0; i < RADEON_MAX_BIOS_CONNECTOR; i++) { info->BiosConnector[i].valid = FALSE; info->BiosConnector[i].ddc_line = 0; info->BiosConnector[i].DACType = DAC_NONE; info->BiosConnector[i].TMDSType = TMDS_NONE; info->BiosConnector[i].ConnectorType = CONNECTOR_NONE; } #if defined(__powerpc__) info->MacModel = 0; optstr = (char *)xf86GetOptValString(info->Options, OPTION_MAC_MODEL); if (optstr) { if (!strncmp("ibook", optstr, strlen("ibook"))) info->MacModel = RADEON_MAC_IBOOK; else if (!strncmp("powerbook-duallink", optstr, strlen("powerbook-duallink"))) /* alias */ info->MacModel = RADEON_MAC_POWERBOOK_EXTERNAL; else if (!strncmp("powerbook-external", optstr, strlen("powerbook-external"))) info->MacModel = RADEON_MAC_POWERBOOK_EXTERNAL; else if (!strncmp("powerbook-internal", optstr, strlen("powerbook-internal"))) info->MacModel = RADEON_MAC_POWERBOOK_INTERNAL; else if (!strncmp("powerbook-vga", optstr, strlen("powerbook-vga"))) info->MacModel = RADEON_MAC_POWERBOOK_VGA; else if (!strncmp("powerbook", optstr, strlen("powerbook"))) /* alias */ info->MacModel = RADEON_MAC_POWERBOOK_INTERNAL; else if (!strncmp("mini-internal", optstr, strlen("mini-internal"))) info->MacModel = RADEON_MAC_MINI_INTERNAL; else if (!strncmp("mini-external", optstr, strlen("mini-external"))) info->MacModel = RADEON_MAC_MINI_EXTERNAL; else if (!strncmp("mini", optstr, strlen("mini"))) /* alias */ info->MacModel = RADEON_MAC_MINI_EXTERNAL; else if (!strncmp("imac-g5-isight", optstr, strlen("imac-g5-isight"))) info->MacModel = RADEON_MAC_IMAC_G5_ISIGHT; else { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Invalid Mac Model: %s\n", optstr); } } if (!info->MacModel) { info->MacModel = RADEONDetectMacModel(pScrn); } if (info->MacModel){ if (!RADEONSetupAppleConnectors(pScrn)) RADEONSetupGenericConnectors(pScrn); } else #endif if (xf86ReturnOptValBool(info->Options, OPTION_DEFAULT_CONNECTOR_TABLE, FALSE)) { RADEONSetupGenericConnectors(pScrn); } else { if (!RADEONGetConnectorInfoFromBIOS(pScrn)) RADEONSetupGenericConnectors(pScrn); } if (!pRADEONEnt->HasCRTC2) { for (i = 0; i < RADEON_MAX_BIOS_CONNECTOR; i++) { if (info->BiosConnector[i].ConnectorType == CONNECTOR_VGA) info->BiosConnector[i].DACType = DAC_PRIMARY; } } /* parse connector table option */ optstr = (char *)xf86GetOptValString(info->Options, OPTION_CONNECTORTABLE); if (optstr) { unsigned int ddc_line[2]; for (i = 2; i < RADEON_MAX_BIOS_CONNECTOR; i++) { info->BiosConnector[i].valid = FALSE; } info->BiosConnector[0].valid = TRUE; info->BiosConnector[1].valid = TRUE; if (sscanf(optstr, "%u,%u,%u,%u,%u,%u,%u,%u", &ddc_line[0], &info->BiosConnector[0].DACType, &info->BiosConnector[0].TMDSType, &info->BiosConnector[0].ConnectorType, &ddc_line[1], &info->BiosConnector[1].DACType, &info->BiosConnector[1].TMDSType, &info->BiosConnector[1].ConnectorType) != 8) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Invalid ConnectorTable option: %s\n", optstr); return FALSE; } info->BiosConnector[0].ddc_line = ddc_line[0]; info->BiosConnector[1].ddc_line = ddc_line[1]; } info->tvdac_use_count = 0; for (i = 0; i < RADEON_MAX_BIOS_CONNECTOR; i++) { if (info->BiosConnector[i].valid) { if (info->BiosConnector[i].DACType == DAC_TVDAC) info->tvdac_use_count++; if ((info->BiosConnector[i].ConnectorType == CONNECTOR_DVI_D) || (info->BiosConnector[i].ConnectorType == CONNECTOR_DVI_I) || (info->BiosConnector[i].ConnectorType == CONNECTOR_DVI_A)) { num_dvi++; } else if (info->BiosConnector[i].ConnectorType == CONNECTOR_VGA) { num_vga++; } else if ((info->BiosConnector[i].ConnectorType == CONNECTOR_HDMI_TYPE_A) || (info->BiosConnector[i].ConnectorType == CONNECTOR_HDMI_TYPE_B)) { num_hdmi++; } } } /* clear the enable masks */ info->output_crt1 = 0; info->output_crt2 = 0; info->output_dfp1 = 0; info->output_dfp2 = 0; info->output_lcd1 = 0; info->output_tv1 = 0; for (i = 0 ; i < RADEON_MAX_BIOS_CONNECTOR; i++) { if (info->BiosConnector[i].valid) { if (info->BiosConnector[i].ConnectorType == CONNECTOR_NONE) continue; RADEONOutputPrivatePtr radeon_output = xnfcalloc(sizeof(RADEONOutputPrivateRec), 1); if (!radeon_output) { return FALSE; } radeon_output->MonType = MT_UNKNOWN; radeon_output->ConnectorType = info->BiosConnector[i].ConnectorType; radeon_output->ddc_line = info->BiosConnector[i].ddc_line; radeon_output->devices = info->BiosConnector[i].devices; radeon_output->output_id = info->BiosConnector[i].output_id; if (radeon_output->ConnectorType == CONNECTOR_DVI_D) radeon_output->DACType = DAC_NONE; else radeon_output->DACType = info->BiosConnector[i].DACType; if (radeon_output->ConnectorType == CONNECTOR_VGA) radeon_output->TMDSType = TMDS_NONE; else radeon_output->TMDSType = info->BiosConnector[i].TMDSType; RADEONSetOutputType(pScrn, radeon_output); if ((info->BiosConnector[i].ConnectorType == CONNECTOR_DVI_D) || (info->BiosConnector[i].ConnectorType == CONNECTOR_DVI_I) || (info->BiosConnector[i].ConnectorType == CONNECTOR_DVI_A)) { if (num_dvi > 1) { output = xf86OutputCreate(pScrn, &radeon_output_funcs, "DVI-1"); num_dvi--; } else { output = xf86OutputCreate(pScrn, &radeon_output_funcs, "DVI-0"); } } else if (info->BiosConnector[i].ConnectorType == CONNECTOR_VGA) { if (num_vga > 1) { output = xf86OutputCreate(pScrn, &radeon_output_funcs, "VGA-1"); num_vga--; } else { output = xf86OutputCreate(pScrn, &radeon_output_funcs, "VGA-0"); } } else if ((info->BiosConnector[i].ConnectorType == CONNECTOR_HDMI_TYPE_A) || (info->BiosConnector[i].ConnectorType == CONNECTOR_HDMI_TYPE_B)) { if (num_hdmi > 1) { output = xf86OutputCreate(pScrn, &radeon_output_funcs, "HDMI-1"); num_hdmi--; } else { output = xf86OutputCreate(pScrn, &radeon_output_funcs, "HDMI-0"); } } else output = xf86OutputCreate(pScrn, &radeon_output_funcs, OutputType[radeon_output->type]); if (!output) { return FALSE; } output->driver_private = radeon_output; output->possible_crtcs = 1; /* crtc2 can drive LVDS, it just doesn't have RMX */ if (radeon_output->type != OUTPUT_LVDS) output->possible_crtcs |= 2; /* we can clone the DACs, and probably TV-out, but I'm not sure it's worth the trouble */ output->possible_clones = 0; RADEONInitConnector(output); } } return TRUE; }