/* * 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 #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 *encoder_name[34] = { "NONE", "INTERNAL_LVDS", "INTERNAL_TMDS1", "INTERNAL_TMDS2", "INTERNAL_DAC1", "INTERNAL_DAC2", "INTERNAL_SDVOA", "INTERNAL_SDVOB", "SI170B", "CH7303", "CH7301", "INTERNAL_DVO1", "EXTERNAL_SDVOA", "EXTERNAL_SDVOB", "TITFP513", "INTERNAL_LVTM1", "VT1623", "HDMI_SI1930", "HDMI_INTERNAL", "INTERNAL_KLDSCP_TMDS1", "INTERNAL_KLDSCP_DVO1", "INTERNAL_KLDSCP_DAC1", "INTERNAL_KLDSCP_DAC2", "SI178", "MVPU_FPGA", "INTERNAL_DDI", "VT1625", "HDMI_SI1932", "DP_AN9801", "DP_DP501", "INTERNAL_UNIPHY", "INTERNAL_KLDSCP_LVTMA", "INTERNAL_UNIPHY1", "INTERNAL_UNIPHY2", }; const char *ConnectorTypeName[17] = { "None", "VGA", "DVI-I", "DVI-D", "DVI-A", "S-video", "Composite", "LVDS", "Digital", "SCART", "HDMI-A", "HDMI-B", "Unsupported", "Unsupported", "DIN", "DisplayPort", "Unsupported" }; extern void atombios_output_mode_set(xf86OutputPtr output, DisplayModePtr mode, DisplayModePtr adjusted_mode); extern void atombios_output_dpms(xf86OutputPtr output, int mode); extern RADEONMonitorType atombios_dac_detect(xf86OutputPtr output); extern AtomBiosResult atombios_lock_crtc(atomBiosHandlePtr atomBIOS, int crtc, int lock); static void radeon_bios_output_dpms(xf86OutputPtr output, int mode); static void radeon_bios_output_crtc(xf86OutputPtr output); static void radeon_bios_output_lock(xf86OutputPtr output, Bool lock); void RADEONPrintPortMap(ScrnInfoPtr pScrn) { RADEONInfoPtr info = RADEONPTR(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", o); ErrorF(" XRANDR name: %s\n", output->name); ErrorF(" Connector: %s\n", ConnectorTypeName[radeon_output->ConnectorType]); if (radeon_output->devices & ATOM_DEVICE_CRT1_SUPPORT) ErrorF(" CRT1: %s\n", encoder_name[info->encoders[ATOM_DEVICE_CRT1_INDEX]->encoder_id]); if (radeon_output->devices & ATOM_DEVICE_CRT2_SUPPORT) ErrorF(" CRT2: %s\n", encoder_name[info->encoders[ATOM_DEVICE_CRT2_INDEX]->encoder_id]); if (radeon_output->devices & ATOM_DEVICE_LCD1_SUPPORT) ErrorF(" LCD1: %s\n", encoder_name[info->encoders[ATOM_DEVICE_LCD1_INDEX]->encoder_id]); if (radeon_output->devices & ATOM_DEVICE_DFP1_SUPPORT) ErrorF(" DFP1: %s\n", encoder_name[info->encoders[ATOM_DEVICE_DFP1_INDEX]->encoder_id]); if (radeon_output->devices & ATOM_DEVICE_DFP2_SUPPORT) ErrorF(" DFP2: %s\n", encoder_name[info->encoders[ATOM_DEVICE_DFP2_INDEX]->encoder_id]); if (radeon_output->devices & ATOM_DEVICE_DFP3_SUPPORT) ErrorF(" DFP3: %s\n", encoder_name[info->encoders[ATOM_DEVICE_DFP3_INDEX]->encoder_id]); if (radeon_output->devices & ATOM_DEVICE_DFP4_SUPPORT) ErrorF(" DFP4: %s\n", encoder_name[info->encoders[ATOM_DEVICE_DFP4_INDEX]->encoder_id]); if (radeon_output->devices & ATOM_DEVICE_DFP5_SUPPORT) ErrorF(" DFP5: %s\n", encoder_name[info->encoders[ATOM_DEVICE_DFP5_INDEX]->encoder_id]); if (radeon_output->devices & ATOM_DEVICE_TV1_SUPPORT) ErrorF(" TV1: %s\n", encoder_name[info->encoders[ATOM_DEVICE_TV1_INDEX]->encoder_id]); if (radeon_output->devices & ATOM_DEVICE_CV_SUPPORT) ErrorF(" CV: %s\n", encoder_name[info->encoders[ATOM_DEVICE_CV_INDEX]->encoder_id]); ErrorF(" DDC reg: 0x%x\n",(unsigned int)radeon_output->ddc_i2c.mask_clk_reg); } } static void radeon_set_active_device(xf86OutputPtr output) { RADEONOutputPrivatePtr radeon_output = output->driver_private; radeon_output->active_device = 0; switch (radeon_output->MonType) { case MT_DP: case MT_DFP: if (radeon_output->devices & ATOM_DEVICE_DFP1_SUPPORT) radeon_output->active_device = ATOM_DEVICE_DFP1_SUPPORT; else if (radeon_output->devices & ATOM_DEVICE_DFP2_SUPPORT) radeon_output->active_device = ATOM_DEVICE_DFP2_SUPPORT; else if (radeon_output->devices & ATOM_DEVICE_DFP3_SUPPORT) radeon_output->active_device = ATOM_DEVICE_DFP3_SUPPORT; else if (radeon_output->devices & ATOM_DEVICE_DFP4_SUPPORT) radeon_output->active_device = ATOM_DEVICE_DFP4_SUPPORT; else if (radeon_output->devices & ATOM_DEVICE_DFP5_SUPPORT) radeon_output->active_device = ATOM_DEVICE_DFP5_SUPPORT; break; case MT_CRT: if (radeon_output->devices & ATOM_DEVICE_CRT1_SUPPORT) radeon_output->active_device = ATOM_DEVICE_CRT1_SUPPORT; else if (radeon_output->devices & ATOM_DEVICE_CRT2_SUPPORT) radeon_output->active_device = ATOM_DEVICE_CRT2_SUPPORT; break; case MT_LCD: if (radeon_output->devices & ATOM_DEVICE_LCD1_SUPPORT) radeon_output->active_device = ATOM_DEVICE_LCD1_SUPPORT; else if (radeon_output->devices & ATOM_DEVICE_LCD2_SUPPORT) radeon_output->active_device = ATOM_DEVICE_LCD2_SUPPORT; break; case MT_STV: case MT_CTV: if (radeon_output->devices & ATOM_DEVICE_TV1_SUPPORT) radeon_output->active_device = ATOM_DEVICE_TV1_SUPPORT; else if (radeon_output->devices & ATOM_DEVICE_TV2_SUPPORT) radeon_output->active_device = ATOM_DEVICE_TV2_SUPPORT; break; case MT_CV: if (radeon_output->devices & ATOM_DEVICE_CV_SUPPORT) radeon_output->active_device = ATOM_DEVICE_CV_SUPPORT; break; default: ErrorF("Unhandled monitor type %d\n", radeon_output->MonType); radeon_output->active_device = 0; } } static Bool monitor_is_digital(xf86MonPtr MonInfo) { return (MonInfo->rawData[0x14] & 0x80) != 0; } static void RADEONGetHardCodedEDIDFromFile(xf86OutputPtr output) { ScrnInfoPtr pScrn = output->scrn; RADEONInfoPtr info = RADEONPTR(pScrn); RADEONOutputPrivatePtr radeon_output = output->driver_private; char *EDIDlist = (char *)xf86GetOptValString(info->Options, OPTION_CUSTOM_EDID); radeon_output->custom_edid = FALSE; radeon_output->custom_mon = NULL; if (EDIDlist != NULL) { unsigned char* edid = xnfcalloc(128, 1); char *name = output->name; char *outputEDID = strstr(EDIDlist, name); if (outputEDID != NULL) { char *end; char *colon; char *command = NULL; int fd; outputEDID += strlen(name) + 1; end = strstr(outputEDID, ";"); if (end != NULL) *end = 0; colon = strstr(outputEDID, ":"); if (colon != NULL) { *colon = 0; command = colon + 1; } fd = open (outputEDID, O_RDONLY); if (fd >= 0) { read(fd, edid, 128); close(fd); if (edid[1] == 0xff) { radeon_output->custom_mon = xf86InterpretEDID(output->scrn->scrnIndex, edid); radeon_output->custom_edid = TRUE; xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Successfully read Custom EDID data for output %s from %s.\n", name, outputEDID); if (command != NULL) { if (!strcmp(command, "digital")) { radeon_output->custom_mon->rawData[0x14] |= 0x80; xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Forcing digital output for output %s.\n", name); } else if (!strcmp(command, "analog")) { radeon_output->custom_mon->rawData[0x14] &= ~0x80; xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Forcing analog output for output %s.\n", name); } else { xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Unknown custom EDID command: '%s'.\n", command); } } } else { xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Custom EDID data for %s read from %s was invalid.\n", name, outputEDID); } } else { xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Could not read custom EDID for output %s from file %s.\n", name, outputEDID); } } else { xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Could not find EDID file name for output %s; using auto detection.\n", name); } } } static RADEONMonitorType radeon_ddc_connected(xf86OutputPtr output) { ScrnInfoPtr pScrn = output->scrn; RADEONInfoPtr info = RADEONPTR(pScrn); RADEONMonitorType MonType = MT_NONE; xf86MonPtr MonInfo = NULL; RADEONOutputPrivatePtr radeon_output = output->driver_private; int ret; if (radeon_output->custom_edid) { MonInfo = xnfcalloc(sizeof(xf86Monitor), 1); *MonInfo = *radeon_output->custom_mon; } else if ((radeon_output->ConnectorType == CONNECTOR_DISPLAY_PORT) || (radeon_output->ConnectorType == CONNECTOR_EDP)) { ret = RADEON_DP_GetSinkType(output); if (ret == CONNECTOR_OBJECT_ID_DISPLAYPORT || ret == CONNECTOR_OBJECT_ID_eDP) { MonInfo = xf86OutputGetEDID(output, radeon_output->dp_pI2CBus); } if (MonInfo == NULL) { if (radeon_output->pI2CBus) { RADEONI2CDoLock(output, radeon_output->pI2CBus, TRUE); MonInfo = xf86OutputGetEDID(output, radeon_output->pI2CBus); RADEONI2CDoLock(output, radeon_output->pI2CBus, FALSE); } } } else if (radeon_output->pI2CBus) { if (info->get_hardcoded_edid_from_bios) MonInfo = RADEONGetHardCodedEDIDFromBIOS(output); if (MonInfo == NULL) { RADEONI2CDoLock(output, radeon_output->pI2CBus, TRUE); MonInfo = xf86OutputGetEDID(output, radeon_output->pI2CBus); RADEONI2CDoLock(output, radeon_output->pI2CBus, FALSE); } } if (MonInfo) { switch (radeon_output->ConnectorType) { case CONNECTOR_LVDS: MonType = MT_LCD; break; case CONNECTOR_DVI_D: case CONNECTOR_HDMI_TYPE_A: if (radeon_output->shared_ddc) { xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR (output->scrn); int i; if (monitor_is_digital(MonInfo)) MonType = MT_DFP; else MonType = MT_NONE; for (i = 0; i < config->num_output; i++) { if (output != config->output[i]) { RADEONOutputPrivatePtr other_radeon_output = config->output[i]->driver_private; if (radeon_output->devices & other_radeon_output->devices) { #ifndef EDID_COMPLETE_RAWDATA if (radeon_output->ConnectorType == CONNECTOR_HDMI_TYPE_A) { MonType = MT_NONE; break; } #else if (xf86MonitorIsHDMI(MonInfo)) { if (radeon_output->ConnectorType == CONNECTOR_DVI_D) { MonType = MT_NONE; break; } } else { if (radeon_output->ConnectorType == CONNECTOR_HDMI_TYPE_A) { MonType = MT_NONE; break; } } #endif } } } } else MonType = MT_DFP; break; case CONNECTOR_DISPLAY_PORT: case CONNECTOR_EDP: /* * XXX wrong. need to infer based on whether we got DDC from I2C * or AUXCH. */ ret = RADEON_DP_GetSinkType(output); if (ret == CONNECTOR_OBJECT_ID_DISPLAYPORT) { MonType = MT_DP; RADEON_DP_GetDPCD(output); } else MonType = MT_DFP; break; case CONNECTOR_HDMI_TYPE_B: case CONNECTOR_DVI_I: if (monitor_is_digital(MonInfo)) MonType = MT_DFP; else MonType = MT_CRT; break; case CONNECTOR_VGA: case CONNECTOR_DVI_A: default: if (radeon_output->shared_ddc) { if (monitor_is_digital(MonInfo)) MonType = MT_NONE; else MonType = MT_CRT; } else MonType = MT_CRT; break; } if (MonType != MT_NONE) { if (!xf86ReturnOptValBool(info->Options, OPTION_IGNORE_EDID, FALSE)) xf86OutputSetEDID(output, MonInfo); } else xfree(MonInfo); } else MonType = MT_NONE; return MonType; } #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 void radeon_dpms(xf86OutputPtr output, int mode) { RADEONInfoPtr info = RADEONPTR(output->scrn); RADEONOutputPrivatePtr radeon_output = output->driver_private; if ((mode == DPMSModeOn) && radeon_output->enabled) return; if ((mode != DPMSModeOn) && radeon_output->shared_ddc) { xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR (output->scrn); int i; for (i = 0; i < config->num_output; i++) { if (output != config->output[i]) { RADEONOutputPrivatePtr other_radeon_output = config->output[i]->driver_private; if (radeon_output->devices & other_radeon_output->devices) { if (output->status == XF86OutputStatusDisconnected) return; } } } } if (IS_AVIVO_VARIANT || info->r4xx_atom) { atombios_output_dpms(output, mode); } else { legacy_output_dpms(output, mode); } radeon_bios_output_dpms(output, mode); if (mode == DPMSModeOn) radeon_output->enabled = TRUE; else radeon_output->enabled = FALSE; } 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; radeon_native_mode_ptr native_mode = &radeon_output->native_mode; 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 (radeon_output->active_device & (ATOM_DEVICE_TV_SUPPORT)) { if (IS_AVIVO_VARIANT) return MODE_OK; else { /* FIXME: Update when more modes are added */ if (pMode->HDisplay == 800 && pMode->VDisplay == 600) return MODE_OK; else return MODE_CLOCK_RANGE; } } /* clocks over 135 MHz have heat issues with DVI on RV100 */ if ((radeon_output->MonType == MT_DFP) && (info->ChipFamily == CHIP_FAMILY_RV100) && (pMode->Clock > 135000)) return MODE_CLOCK_HIGH; /* single link DVI check */ if (pMode->Clock > 165000 && radeon_output->MonType == MT_DFP) { /* DP->DVI converter */ if (radeon_output->ConnectorType == CONNECTOR_DISPLAY_PORT) return MODE_CLOCK_HIGH; if (radeon_output->ConnectorType == CONNECTOR_EDP) return MODE_CLOCK_HIGH; /* XXX some HDMI can do better than 165MHz on a link */ if (radeon_output->ConnectorType == CONNECTOR_HDMI_TYPE_A) return MODE_CLOCK_HIGH; /* XXX some R300 and R400 can actually do this */ if (!IS_AVIVO_VARIANT) return MODE_CLOCK_HIGH; /* XXX and some AVIVO can't */ } if (radeon_output->active_device & (ATOM_DEVICE_LCD_SUPPORT)) { if (radeon_output->rmx_type == RMX_OFF) { if (pMode->HDisplay != native_mode->PanelXRes || pMode->VDisplay != native_mode->PanelYRes) return MODE_PANEL; } if (pMode->HDisplay > native_mode->PanelXRes || pMode->VDisplay > native_mode->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_native_mode_ptr native_mode = &radeon_output->native_mode; xf86CrtcPtr crtc = output->crtc; RADEONCrtcPrivatePtr radeon_crtc = crtc->driver_private; radeon_output->Flags &= ~RADEON_USE_RMX; radeon_crtc->scaler_enabled = FALSE; /* * Refresh the Crtc values without INTERLACE_HALVE_V * Should we use output->scrn->adjustFlags like xf86RandRModeConvert() does? */ xf86SetModeCrtc(adjusted_mode, 0); /* decide if we are using RMX */ if ((radeon_output->active_device & (ATOM_DEVICE_LCD_SUPPORT | ATOM_DEVICE_DFP_SUPPORT)) && radeon_output->rmx_type != RMX_OFF) { if (IS_AVIVO_VARIANT || radeon_crtc->crtc_id == 0) { if (mode->HDisplay < native_mode->PanelXRes || mode->VDisplay < native_mode->PanelYRes) { radeon_output->Flags |= RADEON_USE_RMX; radeon_crtc->scaler_enabled = TRUE; if (IS_AVIVO_VARIANT) { radeon_crtc->hsc = (float)mode->HDisplay / (float)native_mode->PanelXRes; radeon_crtc->vsc = (float)mode->VDisplay / (float)native_mode->PanelYRes; /* set to the panel's native mode */ adjusted_mode->HDisplay = native_mode->PanelXRes; adjusted_mode->VDisplay = native_mode->PanelYRes; adjusted_mode->HTotal = native_mode->PanelXRes + native_mode->HBlank; adjusted_mode->HSyncStart = native_mode->PanelXRes + native_mode->HOverPlus; adjusted_mode->HSyncEnd = adjusted_mode->HSyncStart + native_mode->HSyncWidth; adjusted_mode->VTotal = native_mode->PanelYRes + native_mode->VBlank; adjusted_mode->VSyncStart = native_mode->PanelYRes + native_mode->VOverPlus; adjusted_mode->VSyncEnd = adjusted_mode->VSyncStart + native_mode->VSyncWidth; /* update crtc values */ xf86SetModeCrtc(adjusted_mode, INTERLACE_HALVE_V); /* adjust crtc values */ adjusted_mode->CrtcHDisplay = native_mode->PanelXRes; adjusted_mode->CrtcVDisplay = native_mode->PanelYRes; adjusted_mode->CrtcHTotal = adjusted_mode->CrtcHDisplay + native_mode->HBlank; adjusted_mode->CrtcHSyncStart = adjusted_mode->CrtcHDisplay + native_mode->HOverPlus; adjusted_mode->CrtcHSyncEnd = adjusted_mode->CrtcHSyncStart + native_mode->HSyncWidth; adjusted_mode->CrtcVTotal = adjusted_mode->CrtcVDisplay + native_mode->VBlank; adjusted_mode->CrtcVSyncStart = adjusted_mode->CrtcVDisplay + native_mode->VOverPlus; adjusted_mode->CrtcVSyncEnd = adjusted_mode->CrtcVSyncStart + native_mode->VSyncWidth; } else { /* set to the panel's native mode */ adjusted_mode->HTotal = native_mode->PanelXRes + native_mode->HBlank; adjusted_mode->HSyncStart = native_mode->PanelXRes + native_mode->HOverPlus; adjusted_mode->HSyncEnd = adjusted_mode->HSyncStart + native_mode->HSyncWidth; adjusted_mode->VTotal = native_mode->PanelYRes + native_mode->VBlank; adjusted_mode->VSyncStart = native_mode->PanelYRes + native_mode->VOverPlus; adjusted_mode->VSyncEnd = adjusted_mode->VSyncStart + native_mode->VSyncWidth; adjusted_mode->Clock = native_mode->DotClock; /* update crtc values */ xf86SetModeCrtc(adjusted_mode, INTERLACE_HALVE_V); /* adjust crtc values */ adjusted_mode->CrtcHTotal = adjusted_mode->CrtcHDisplay + native_mode->HBlank; adjusted_mode->CrtcHSyncStart = adjusted_mode->CrtcHDisplay + native_mode->HOverPlus; adjusted_mode->CrtcHSyncEnd = adjusted_mode->CrtcHSyncStart + native_mode->HSyncWidth; adjusted_mode->CrtcVTotal = adjusted_mode->CrtcVDisplay + native_mode->VBlank; adjusted_mode->CrtcVSyncStart = adjusted_mode->CrtcVDisplay + native_mode->VOverPlus; adjusted_mode->CrtcVSyncEnd = adjusted_mode->CrtcVSyncStart + native_mode->VSyncWidth; } adjusted_mode->Clock = native_mode->DotClock; adjusted_mode->Flags = native_mode->Flags; } } } /* FIXME: vsc/hsc */ if (radeon_output->active_device & (ATOM_DEVICE_TV_SUPPORT | ATOM_DEVICE_CV_SUPPORT)) { radeon_crtc->scaler_enabled = TRUE; radeon_crtc->hsc = (float)mode->HDisplay / (float)640; radeon_crtc->vsc = (float)mode->VDisplay / (float)480; } if (IS_AVIVO_VARIANT) { /* hw bug */ if ((mode->Flags & V_INTERLACE) && (adjusted_mode->CrtcVSyncStart < (adjusted_mode->CrtcVDisplay + 2))) adjusted_mode->CrtcVSyncStart = adjusted_mode->CrtcVDisplay + 2; } if (IS_AVIVO_VARIANT || info->r4xx_atom) { if (radeon_output->MonType == MT_STV || radeon_output->MonType == MT_CTV) { radeon_tvout_ptr tvout = &radeon_output->tvout; ScrnInfoPtr pScrn = output->scrn; if (tvout->tvStd == TV_STD_NTSC || tvout->tvStd == TV_STD_NTSC_J || tvout->tvStd == TV_STD_PAL_M) RADEONATOMGetTVTimings(pScrn, 0, adjusted_mode); else RADEONATOMGetTVTimings(pScrn, 1, adjusted_mode); } } if (((radeon_output->ConnectorType == CONNECTOR_DISPLAY_PORT) || (radeon_output->ConnectorType == CONNECTOR_EDP)) && (radeon_output->MonType == MT_DP)) { radeon_dp_mode_fixup(output, mode, adjusted_mode); } return TRUE; } static void radeon_mode_prepare(xf86OutputPtr output) { RADEONInfoPtr info = RADEONPTR(output->scrn); xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR (output->scrn); int o; for (o = 0; o < config->num_output; o++) { xf86OutputPtr loop_output = config->output[o]; if (loop_output == output) continue; else if (loop_output->crtc) { xf86CrtcPtr other_crtc = loop_output->crtc; RADEONCrtcPrivatePtr other_radeon_crtc = other_crtc->driver_private; if (other_crtc->enabled) { if (other_radeon_crtc->initialized) { radeon_crtc_dpms(other_crtc, DPMSModeOff); if (IS_AVIVO_VARIANT || info->r4xx_atom) atombios_lock_crtc(info->atomBIOS, other_radeon_crtc->crtc_id, 1); radeon_dpms(loop_output, DPMSModeOff); } } } } radeon_bios_output_lock(output, TRUE); radeon_dpms(output, DPMSModeOff); radeon_crtc_dpms(output->crtc, DPMSModeOff); } static void radeon_mode_set(xf86OutputPtr output, DisplayModePtr mode, DisplayModePtr adjusted_mode) { RADEONInfoPtr info = RADEONPTR(output->scrn); if (IS_AVIVO_VARIANT || info->r4xx_atom) atombios_output_mode_set(output, mode, adjusted_mode); else legacy_output_mode_set(output, mode, adjusted_mode); radeon_bios_output_crtc(output); } static void radeon_mode_commit(xf86OutputPtr output) { RADEONInfoPtr info = RADEONPTR(output->scrn); xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR (output->scrn); int o; for (o = 0; o < config->num_output; o++) { xf86OutputPtr loop_output = config->output[o]; if (loop_output == output) continue; else if (loop_output->crtc) { xf86CrtcPtr other_crtc = loop_output->crtc; RADEONCrtcPrivatePtr other_radeon_crtc = other_crtc->driver_private; if (other_crtc->enabled) { if (other_radeon_crtc->initialized) { radeon_crtc_dpms(other_crtc, DPMSModeOn); if (IS_AVIVO_VARIANT || info->r4xx_atom) atombios_lock_crtc(info->atomBIOS, other_radeon_crtc->crtc_id, 0); radeon_dpms(loop_output, DPMSModeOn); } } } } radeon_dpms(output, DPMSModeOn); radeon_crtc_dpms(output->crtc, DPMSModeOn); radeon_bios_output_lock(output, FALSE); } static void radeon_bios_output_lock(xf86OutputPtr output, Bool lock) { ScrnInfoPtr pScrn = output->scrn; RADEONInfoPtr info = RADEONPTR(pScrn); unsigned char *RADEONMMIO = info->MMIO; RADEONSavePtr save = info->ModeReg; if (info->IsAtomBios) { if (lock) { save->bios_6_scratch |= ATOM_S6_CRITICAL_STATE; } else { save->bios_6_scratch &= ~ATOM_S6_CRITICAL_STATE; } } else { if (lock) { save->bios_6_scratch |= RADEON_DRIVER_CRITICAL; } else { save->bios_6_scratch &= ~RADEON_DRIVER_CRITICAL; } } if (info->ChipFamily >= CHIP_FAMILY_R600) OUTREG(R600_BIOS_6_SCRATCH, save->bios_6_scratch); else OUTREG(RADEON_BIOS_6_SCRATCH, save->bios_6_scratch); } static void radeon_bios_output_dpms(xf86OutputPtr output, int mode) { ScrnInfoPtr pScrn = output->scrn; RADEONInfoPtr info = RADEONPTR(pScrn); RADEONOutputPrivatePtr radeon_output = output->driver_private; unsigned char *RADEONMMIO = info->MMIO; RADEONSavePtr save = info->ModeReg; if (info->IsAtomBios) { if (radeon_output->active_device & ATOM_DEVICE_TV1_SUPPORT) { if (mode == DPMSModeOn) save->bios_2_scratch &= ~ATOM_S2_TV1_DPMS_STATE; else save->bios_2_scratch |= ATOM_S2_TV1_DPMS_STATE; } else if (radeon_output->active_device & ATOM_DEVICE_CV_SUPPORT) { if (mode == DPMSModeOn) save->bios_2_scratch &= ~ATOM_S2_CV_DPMS_STATE; else save->bios_2_scratch |= ATOM_S2_CV_DPMS_STATE; } else if (radeon_output->active_device & ATOM_DEVICE_CRT1_SUPPORT) { if (mode == DPMSModeOn) save->bios_2_scratch &= ~ATOM_S2_CRT1_DPMS_STATE; else save->bios_2_scratch |= ATOM_S2_CRT1_DPMS_STATE; } else if (radeon_output->active_device & ATOM_DEVICE_CRT2_SUPPORT) { if (mode == DPMSModeOn) save->bios_2_scratch &= ~ATOM_S2_CRT2_DPMS_STATE; else save->bios_2_scratch |= ATOM_S2_CRT2_DPMS_STATE; } else if (radeon_output->active_device & ATOM_DEVICE_LCD1_SUPPORT) { if (mode == DPMSModeOn) save->bios_2_scratch &= ~ATOM_S2_LCD1_DPMS_STATE; else save->bios_2_scratch |= ATOM_S2_LCD1_DPMS_STATE; } else if (radeon_output->active_device & ATOM_DEVICE_DFP1_SUPPORT) { if (mode == DPMSModeOn) save->bios_2_scratch &= ~ATOM_S2_DFP1_DPMS_STATE; else save->bios_2_scratch |= ATOM_S2_DFP1_DPMS_STATE; } else if (radeon_output->active_device & ATOM_DEVICE_DFP2_SUPPORT) { if (mode == DPMSModeOn) save->bios_2_scratch &= ~ATOM_S2_DFP2_DPMS_STATE; else save->bios_2_scratch |= ATOM_S2_DFP2_DPMS_STATE; } else if (radeon_output->active_device & ATOM_DEVICE_DFP3_SUPPORT) { if (mode == DPMSModeOn) save->bios_2_scratch &= ~ATOM_S2_DFP3_DPMS_STATE; else save->bios_2_scratch |= ATOM_S2_DFP3_DPMS_STATE; } else if (radeon_output->active_device & ATOM_DEVICE_DFP4_SUPPORT) { if (mode == DPMSModeOn) save->bios_2_scratch &= ~ATOM_S2_DFP4_DPMS_STATE; else save->bios_2_scratch |= ATOM_S2_DFP4_DPMS_STATE; } else if (radeon_output->active_device & ATOM_DEVICE_DFP5_SUPPORT) { if (mode == DPMSModeOn) save->bios_2_scratch &= ~ATOM_S2_DFP5_DPMS_STATE; else save->bios_2_scratch |= ATOM_S2_DFP5_DPMS_STATE; } if (info->ChipFamily >= CHIP_FAMILY_R600) OUTREG(R600_BIOS_2_SCRATCH, save->bios_2_scratch); else OUTREG(RADEON_BIOS_2_SCRATCH, save->bios_2_scratch); } else { if (mode == DPMSModeOn) { save->bios_6_scratch &= ~(RADEON_DPMS_MASK | RADEON_SCREEN_BLANKING); save->bios_6_scratch |= RADEON_DPMS_ON; } else { save->bios_6_scratch &= ~RADEON_DPMS_MASK; save->bios_6_scratch |= (RADEON_DPMS_OFF | RADEON_SCREEN_BLANKING); } if (radeon_output->active_device & ATOM_DEVICE_TV1_SUPPORT) { if (mode == DPMSModeOn) save->bios_6_scratch |= RADEON_TV_DPMS_ON; else save->bios_6_scratch &= ~RADEON_TV_DPMS_ON; } else if (radeon_output->active_device & ATOM_DEVICE_CRT1_SUPPORT) { if (mode == DPMSModeOn) save->bios_6_scratch |= RADEON_CRT_DPMS_ON; else save->bios_6_scratch &= ~RADEON_CRT_DPMS_ON; } else if (radeon_output->active_device & ATOM_DEVICE_CRT2_SUPPORT) { if (mode == DPMSModeOn) save->bios_6_scratch |= RADEON_CRT_DPMS_ON; else save->bios_6_scratch &= ~RADEON_CRT_DPMS_ON; } else if (radeon_output->active_device & ATOM_DEVICE_LCD1_SUPPORT) { if (mode == DPMSModeOn) save->bios_6_scratch |= RADEON_LCD_DPMS_ON; else save->bios_6_scratch &= ~RADEON_LCD_DPMS_ON; } else if (radeon_output->active_device & ATOM_DEVICE_DFP1_SUPPORT) { if (mode == DPMSModeOn) save->bios_6_scratch |= RADEON_DFP_DPMS_ON; else save->bios_6_scratch &= ~RADEON_DFP_DPMS_ON; } else if (radeon_output->active_device & ATOM_DEVICE_DFP2_SUPPORT) { if (mode == DPMSModeOn) save->bios_6_scratch |= RADEON_DFP_DPMS_ON; else save->bios_6_scratch &= ~RADEON_DFP_DPMS_ON; } OUTREG(RADEON_BIOS_6_SCRATCH, save->bios_6_scratch); } } static void radeon_bios_output_crtc(xf86OutputPtr output) { ScrnInfoPtr pScrn = output->scrn; RADEONInfoPtr info = RADEONPTR(pScrn); RADEONOutputPrivatePtr radeon_output = output->driver_private; unsigned char *RADEONMMIO = info->MMIO; RADEONSavePtr save = info->ModeReg; xf86CrtcPtr crtc = output->crtc; RADEONCrtcPrivatePtr radeon_crtc = crtc->driver_private; /* no need to update crtc routing scratch regs on DCE4 */ if (IS_DCE4_VARIANT) return; if (info->IsAtomBios) { if (radeon_output->active_device & ATOM_DEVICE_TV1_SUPPORT) { save->bios_3_scratch &= ~ATOM_S3_TV1_CRTC_ACTIVE; save->bios_3_scratch |= (radeon_crtc->crtc_id << 18); } else if (radeon_output->active_device & ATOM_DEVICE_CV_SUPPORT) { save->bios_3_scratch &= ~ATOM_S3_CV_CRTC_ACTIVE; save->bios_3_scratch |= (radeon_crtc->crtc_id << 24); } else if (radeon_output->active_device & ATOM_DEVICE_CRT1_SUPPORT) { save->bios_3_scratch &= ~ATOM_S3_CRT1_CRTC_ACTIVE; save->bios_3_scratch |= (radeon_crtc->crtc_id << 16); } else if (radeon_output->active_device & ATOM_DEVICE_CRT2_SUPPORT) { save->bios_3_scratch &= ~ATOM_S3_CRT2_CRTC_ACTIVE; save->bios_3_scratch |= (radeon_crtc->crtc_id << 20); } else if (radeon_output->active_device & ATOM_DEVICE_LCD1_SUPPORT) { save->bios_3_scratch &= ~ATOM_S3_LCD1_CRTC_ACTIVE; save->bios_3_scratch |= (radeon_crtc->crtc_id << 17); } else if (radeon_output->active_device & ATOM_DEVICE_DFP1_SUPPORT) { save->bios_3_scratch &= ~ATOM_S3_DFP1_CRTC_ACTIVE; save->bios_3_scratch |= (radeon_crtc->crtc_id << 19); } else if (radeon_output->active_device & ATOM_DEVICE_DFP2_SUPPORT) { save->bios_3_scratch &= ~ATOM_S3_DFP2_CRTC_ACTIVE; save->bios_3_scratch |= (radeon_crtc->crtc_id << 23); } else if (radeon_output->active_device & ATOM_DEVICE_DFP3_SUPPORT) { save->bios_3_scratch &= ~ATOM_S3_DFP3_CRTC_ACTIVE; save->bios_3_scratch |= (radeon_crtc->crtc_id << 25); } if (info->ChipFamily >= CHIP_FAMILY_R600) OUTREG(R600_BIOS_3_SCRATCH, save->bios_3_scratch); else OUTREG(RADEON_BIOS_3_SCRATCH, save->bios_3_scratch); } else { if (radeon_output->active_device & ATOM_DEVICE_TV1_SUPPORT) { save->bios_5_scratch &= ~RADEON_TV1_CRTC_MASK; save->bios_5_scratch |= (radeon_crtc->crtc_id << RADEON_TV1_CRTC_SHIFT); } else if (radeon_output->active_device & ATOM_DEVICE_CRT1_SUPPORT) { save->bios_5_scratch &= ~RADEON_CRT1_CRTC_MASK; save->bios_5_scratch |= (radeon_crtc->crtc_id << RADEON_CRT1_CRTC_SHIFT); } else if (radeon_output->active_device & ATOM_DEVICE_CRT2_SUPPORT) { save->bios_5_scratch &= ~RADEON_CRT2_CRTC_MASK; save->bios_5_scratch |= (radeon_crtc->crtc_id << RADEON_CRT2_CRTC_SHIFT); } else if (radeon_output->active_device & ATOM_DEVICE_LCD1_SUPPORT) { save->bios_5_scratch &= ~RADEON_LCD1_CRTC_MASK; save->bios_5_scratch |= (radeon_crtc->crtc_id << RADEON_LCD1_CRTC_SHIFT); } else if (radeon_output->active_device & ATOM_DEVICE_DFP1_SUPPORT) { save->bios_5_scratch &= ~RADEON_DFP1_CRTC_MASK; save->bios_5_scratch |= (radeon_crtc->crtc_id << RADEON_DFP1_CRTC_SHIFT); } else if (radeon_output->active_device & ATOM_DEVICE_DFP2_SUPPORT) { save->bios_5_scratch &= ~RADEON_DFP2_CRTC_MASK; save->bios_5_scratch |= (radeon_crtc->crtc_id << RADEON_DFP2_CRTC_SHIFT); } OUTREG(RADEON_BIOS_5_SCRATCH, save->bios_5_scratch); } } static void radeon_bios_output_connected(xf86OutputPtr output, Bool connected) { ScrnInfoPtr pScrn = output->scrn; RADEONInfoPtr info = RADEONPTR(pScrn); RADEONOutputPrivatePtr radeon_output = output->driver_private; unsigned char *RADEONMMIO = info->MMIO; RADEONSavePtr save = info->ModeReg; if (info->IsAtomBios) { switch (radeon_output->active_device) { case ATOM_DEVICE_TV1_SUPPORT: if (connected) save->bios_3_scratch |= ATOM_S3_TV1_ACTIVE; else { save->bios_0_scratch &= ~ATOM_S0_TV1_MASK; save->bios_3_scratch &= ~ATOM_S3_TV1_ACTIVE; } break; case ATOM_DEVICE_CV_SUPPORT: if (connected) save->bios_3_scratch |= ATOM_S3_CV_ACTIVE; else { save->bios_0_scratch &= ~ATOM_S0_CV_MASK; save->bios_3_scratch &= ~ATOM_S3_CV_ACTIVE; } break; case ATOM_DEVICE_LCD1_SUPPORT: if (connected) { save->bios_0_scratch |= ATOM_S0_LCD1; save->bios_3_scratch |= ATOM_S3_LCD1_ACTIVE; } else { save->bios_0_scratch &= ~ATOM_S0_LCD1; save->bios_3_scratch &= ~ATOM_S3_LCD1_ACTIVE; } break; case ATOM_DEVICE_CRT1_SUPPORT: if (connected) { save->bios_0_scratch |= ATOM_S0_CRT1_COLOR; save->bios_3_scratch |= ATOM_S3_CRT1_ACTIVE; } else { save->bios_0_scratch &= ~ATOM_S0_CRT1_MASK; save->bios_3_scratch &= ~ATOM_S3_CRT1_ACTIVE; } break; case ATOM_DEVICE_CRT2_SUPPORT: if (connected) { save->bios_0_scratch |= ATOM_S0_CRT2_COLOR; save->bios_3_scratch |= ATOM_S3_CRT2_ACTIVE; } else { save->bios_0_scratch &= ~ATOM_S0_CRT2_MASK; save->bios_3_scratch &= ~ATOM_S3_CRT2_ACTIVE; } break; case ATOM_DEVICE_DFP1_SUPPORT: if (connected) { save->bios_0_scratch |= ATOM_S0_DFP1; save->bios_3_scratch |= ATOM_S3_DFP1_ACTIVE; } else { save->bios_0_scratch &= ~ATOM_S0_DFP1; save->bios_3_scratch &= ~ATOM_S3_DFP1_ACTIVE; } break; case ATOM_DEVICE_DFP2_SUPPORT: if (connected) { save->bios_0_scratch |= ATOM_S0_DFP2; save->bios_3_scratch |= ATOM_S3_DFP2_ACTIVE; } else { save->bios_0_scratch &= ~ATOM_S0_DFP2; save->bios_3_scratch &= ~ATOM_S3_DFP2_ACTIVE; } break; case ATOM_DEVICE_DFP3_SUPPORT: if (connected) { save->bios_0_scratch |= ATOM_S0_DFP3; save->bios_3_scratch |= ATOM_S3_DFP3_ACTIVE; } else { save->bios_0_scratch &= ~ATOM_S0_DFP3; save->bios_3_scratch &= ~ATOM_S3_DFP3_ACTIVE; } break; case ATOM_DEVICE_DFP4_SUPPORT: if (connected) { save->bios_0_scratch |= ATOM_S0_DFP4; save->bios_3_scratch |= ATOM_S3_DFP4_ACTIVE; } else { save->bios_0_scratch &= ~ATOM_S0_DFP4; save->bios_3_scratch &= ~ATOM_S3_DFP4_ACTIVE; } break; case ATOM_DEVICE_DFP5_SUPPORT: if (connected) { save->bios_0_scratch |= ATOM_S0_DFP5; save->bios_3_scratch |= ATOM_S3_DFP5_ACTIVE; } else { save->bios_0_scratch &= ~ATOM_S0_DFP5; save->bios_3_scratch &= ~ATOM_S3_DFP5_ACTIVE; } break; } if (info->ChipFamily >= CHIP_FAMILY_R600) { OUTREG(R600_BIOS_0_SCRATCH, save->bios_0_scratch); OUTREG(R600_BIOS_3_SCRATCH, save->bios_3_scratch); } else { OUTREG(RADEON_BIOS_0_SCRATCH, save->bios_0_scratch); OUTREG(RADEON_BIOS_3_SCRATCH, save->bios_3_scratch); } } else { switch (radeon_output->active_device) { case ATOM_DEVICE_TV1_SUPPORT: if (connected) { if (radeon_output->MonType == MT_STV) save->bios_4_scratch |= RADEON_TV1_ATTACHED_SVIDEO; else if (radeon_output->MonType == MT_CTV) save->bios_4_scratch |= RADEON_TV1_ATTACHED_COMP; save->bios_5_scratch |= RADEON_TV1_ON; } else { save->bios_4_scratch &= ~RADEON_TV1_ATTACHED_MASK; save->bios_5_scratch &= ~RADEON_TV1_ON; } break; case ATOM_DEVICE_LCD1_SUPPORT: if (connected) { save->bios_4_scratch |= RADEON_LCD1_ATTACHED; save->bios_5_scratch |= RADEON_LCD1_ON; } else { save->bios_4_scratch &= ~RADEON_LCD1_ATTACHED; save->bios_5_scratch &= ~RADEON_LCD1_ON; } break; case ATOM_DEVICE_CRT1_SUPPORT: if (connected) { save->bios_4_scratch |= RADEON_CRT1_ATTACHED_COLOR; save->bios_5_scratch |= RADEON_CRT1_ON; } else { save->bios_4_scratch &= ~RADEON_CRT1_ATTACHED_MASK; save->bios_5_scratch &= ~RADEON_CRT1_ON; } break; case ATOM_DEVICE_CRT2_SUPPORT: if (connected) { save->bios_4_scratch |= RADEON_CRT2_ATTACHED_COLOR; save->bios_5_scratch |= RADEON_CRT2_ON; } else { save->bios_4_scratch &= ~RADEON_CRT2_ATTACHED_MASK; save->bios_5_scratch &= ~RADEON_CRT2_ON; } break; case ATOM_DEVICE_DFP1_SUPPORT: if (connected) { save->bios_4_scratch |= RADEON_DFP1_ATTACHED; save->bios_5_scratch |= RADEON_DFP1_ON; } else { save->bios_4_scratch &= ~RADEON_DFP1_ATTACHED; save->bios_5_scratch &= ~RADEON_DFP1_ON; } break; case ATOM_DEVICE_DFP2_SUPPORT: if (connected) { save->bios_4_scratch |= RADEON_DFP2_ATTACHED; save->bios_5_scratch |= RADEON_DFP2_ON; } else { save->bios_4_scratch &= ~RADEON_DFP2_ATTACHED; save->bios_5_scratch &= ~RADEON_DFP2_ON; } break; } OUTREG(RADEON_BIOS_4_SCRATCH, save->bios_4_scratch); OUTREG(RADEON_BIOS_5_SCRATCH, save->bios_5_scratch); } } 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; radeon_bios_output_connected(output, FALSE); radeon_output->MonType = radeon_ddc_connected(output); if (!radeon_output->MonType) { if (radeon_output->devices & (ATOM_DEVICE_LCD_SUPPORT)) { if (xf86ReturnOptValBool(info->Options, OPTION_IGNORE_LID_STATUS, TRUE)) radeon_output->MonType = MT_LCD; else #if defined(__powerpc__) radeon_output->MonType = MT_LCD; #else radeon_output->MonType = RADEONDetectLidStatus(pScrn); #endif } else { if (info->IsAtomBios) radeon_output->MonType = atombios_dac_detect(output); else radeon_output->MonType = legacy_dac_detect(output); } } // if size is zero panel probably broken or not connected if (radeon_output->devices & (ATOM_DEVICE_LCD_SUPPORT)) { radeon_encoder_ptr radeon_encoder = info->encoders[ATOM_DEVICE_LCD1_INDEX]; if (radeon_encoder) { radeon_lvds_ptr lvds = (radeon_lvds_ptr)radeon_encoder->dev_priv; if (lvds) { if ((lvds->native_mode.PanelXRes == 0) || (lvds->native_mode.PanelYRes == 0)) radeon_output->MonType = MT_NONE; } } } xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Output: %s, Detected Monitor Type: %d\n", output->name, radeon_output->MonType); if (output->MonInfo) { xf86DrvMsg(pScrn->scrnIndex, X_INFO, "EDID data from the display on output: %s ----------------------\n", output->name); xf86PrintEDID( output->MonInfo ); } /* 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->devices & (ATOM_DEVICE_LCD_SUPPORT)) { radeon_output->MonType = MT_LCD; info->first_load_no_devices = FALSE; xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Using LCD default\n"); } } else { if (radeon_output->devices & (ATOM_DEVICE_CRT_SUPPORT)) { radeon_output->MonType = MT_CRT; info->first_load_no_devices = FALSE; xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Using CRT default\n"); } else if (radeon_output->devices & (ATOM_DEVICE_DFP_SUPPORT)) { radeon_output->MonType = MT_DFP; info->first_load_no_devices = FALSE; xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Using DFP default\n"); } } } radeon_bios_output_connected(output, TRUE); /* set montype so users can force outputs on even if detection fails */ if (radeon_output->MonType == MT_NONE) { connected = FALSE; switch (radeon_output->ConnectorType) { case CONNECTOR_LVDS: radeon_output->MonType = MT_LCD; break; case CONNECTOR_DVI_D: case CONNECTOR_HDMI_TYPE_A: case CONNECTOR_HDMI_TYPE_B: radeon_output->MonType = MT_DFP; break; case CONNECTOR_VGA: case CONNECTOR_DVI_A: default: radeon_output->MonType = MT_CRT; break; case CONNECTOR_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; break; case CONNECTOR_STV: radeon_output->MonType = MT_STV; break; case CONNECTOR_CTV: radeon_output->MonType = MT_CTV; break; case CONNECTOR_DIN: radeon_output->MonType = MT_CV; break; case CONNECTOR_DISPLAY_PORT: case CONNECTOR_EDP: radeon_output->MonType = MT_DP; break; } } radeon_set_active_device(output); if (radeon_output->active_device & (ATOM_DEVICE_LCD_SUPPORT | ATOM_DEVICE_DFP_SUPPORT)) output->subpixel_order = SubPixelHorizontalRGB; else output->subpixel_order = SubPixelNone; 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; uint32_t 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 coherent_mode_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; #if 0 /* 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); } } #endif if (radeon_output->devices & (ATOM_DEVICE_CRT_SUPPORT | ATOM_DEVICE_TV_SUPPORT | ATOM_DEVICE_CV_SUPPORT)) { 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; else data = 0; 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 (IS_AVIVO_VARIANT && (radeon_output->devices & (ATOM_DEVICE_DFP_SUPPORT))) { coherent_mode_atom = MAKE_ATOM("coherent_mode"); range[0] = 0; /* off */ range[1] = 1; /* on */ err = RRConfigureOutputProperty(output->randr_output, coherent_mode_atom, FALSE, TRUE, FALSE, 2, range); if (err != 0) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "RRConfigureOutputProperty error, %d\n", err); } data = 1; /* coherent mode on by default */ err = RRChangeOutputProperty(output->randr_output, coherent_mode_atom, XA_INTEGER, 32, PropModeReplace, 1, &data, FALSE, TRUE); if (err != 0) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "RRChangeOutputProperty error, %d\n", err); } } if ((!IS_AVIVO_VARIANT) && (radeon_output->devices & (ATOM_DEVICE_DFP1_SUPPORT))) { 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->devices & (ATOM_DEVICE_DFP_SUPPORT | ATOM_DEVICE_LCD_SUPPORT)) { 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 */ switch (radeon_output->rmx_type) { case RMX_OFF: default: s = "off"; break; case RMX_FULL: s = "full"; break; case RMX_CENTER: s = "center"; break; case RMX_ASPECT: s = "aspect"; break; } 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->devices & (ATOM_DEVICE_CRT_SUPPORT)) && (radeon_output->devices & (ATOM_DEVICE_DFP_SUPPORT))){ 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 (radeon_output->devices & (ATOM_DEVICE_TV_SUPPORT)) { radeon_tvout_ptr tvout = &radeon_output->tvout; 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 (tvout->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_mode_for_property(xf86OutputPtr output) { ScrnInfoPtr pScrn = output->scrn; if (output->crtc) { xf86CrtcPtr crtc = output->crtc; if (crtc->enabled) { if (!xf86CrtcSetMode(crtc, &crtc->desiredMode, crtc->desiredRotation, crtc->desiredX, crtc->desiredY)) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Failed to set mode after propery change!\n"); return FALSE; } } } return TRUE; } 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 == coherent_mode_atom) { Bool coherent_mode = radeon_output->coherent_mode; 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->coherent_mode = val; if (!radeon_set_mode_for_property(output)) { radeon_output->coherent_mode = coherent_mode; (void)radeon_set_mode_for_property(output); return FALSE; } } else if (property == rmx_atom) { const char *s; RADEONRMXType rmx = radeon_output->rmx_type; 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; } else if (value->size == strlen("center") && !strncmp("center", s, strlen("center"))) { radeon_output->rmx_type = RMX_CENTER; } else if (value->size == strlen("aspect") && !strncmp("aspect", s, strlen("aspect"))) { if (IS_AVIVO_VARIANT) radeon_output->rmx_type = RMX_ASPECT; else return FALSE; } else if (value->size == strlen("off") && !strncmp("off", s, strlen("off"))) { radeon_output->rmx_type = RMX_OFF; } else return FALSE; if (!radeon_set_mode_for_property(output)) { radeon_output->rmx_type = rmx; (void)radeon_set_mode_for_property(output); return FALSE; } } else if (property == tmds_pll_atom) { radeon_tmds_ptr tmds = NULL; const char *s; if (info->encoders[ATOM_DEVICE_DFP1_INDEX] && info->encoders[ATOM_DEVICE_DFP1_INDEX]->dev_priv) tmds = (radeon_tmds_ptr)info->encoders[ATOM_DEVICE_DFP1_INDEX]->dev_priv; else return FALSE; 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->scrn, tmds)) RADEONGetTMDSInfoFromTable(output->scrn, tmds); } else if (value->size == strlen("driver") && !strncmp("driver", s, strlen("driver"))) RADEONGetTMDSInfoFromTable(output->scrn, tmds); else return FALSE; return radeon_set_mode_for_property(output); } 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; } else return FALSE; } else if (property == tv_hsize_atom) { radeon_tvout_ptr tvout = &radeon_output->tvout; 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; tvout->hSize = val; if (tvout->tv_on && !IS_AVIVO_VARIANT) RADEONUpdateHVPosition(output, &output->crtc->mode); } else if (property == tv_hpos_atom) { radeon_tvout_ptr tvout = &radeon_output->tvout; 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; tvout->hPos = val; if (tvout->tv_on && !IS_AVIVO_VARIANT) RADEONUpdateHVPosition(output, &output->crtc->mode); } else if (property == tv_vpos_atom) { radeon_tvout_ptr tvout = &radeon_output->tvout; 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; tvout->vPos = val; if (tvout->tv_on && !IS_AVIVO_VARIANT) RADEONUpdateHVPosition(output, &output->crtc->mode); } else if (property == tv_std_atom) { const char *s; radeon_tvout_ptr tvout = &radeon_output->tvout; TVStd std = tvout->tvStd; if (value->type != XA_STRING || value->format != 8) return FALSE; s = (char*)value->data; if (value->size == strlen("ntsc") && !strncmp("ntsc", s, strlen("ntsc"))) { tvout->tvStd = TV_STD_NTSC; } else if (value->size == strlen("pal") && !strncmp("pal", s, strlen("pal"))) { tvout->tvStd = TV_STD_PAL; } else if (value->size == strlen("pal-m") && !strncmp("pal-m", s, strlen("pal-m"))) { tvout->tvStd = TV_STD_PAL_M; } else if (value->size == strlen("pal-60") && !strncmp("pal-60", s, strlen("pal-60"))) { tvout->tvStd = TV_STD_PAL_60; } else if (value->size == strlen("ntsc-j") && !strncmp("ntsc-j", s, strlen("ntsc-j"))) { tvout->tvStd = TV_STD_NTSC_J; } else if (value->size == strlen("scart-pal") && !strncmp("scart-pal", s, strlen("scart-pal"))) { tvout->tvStd = TV_STD_SCART_PAL; } else if (value->size == strlen("pal-cn") && !strncmp("pal-cn", s, strlen("pal-cn"))) { tvout->tvStd = TV_STD_PAL_CN; } else if (value->size == strlen("secam") && !strncmp("secam", s, strlen("secam"))) { tvout->tvStd = TV_STD_SECAM; } else return FALSE; if (!radeon_set_mode_for_property(output)) { tvout->tvStd = std; (void)radeon_set_mode_for_property(output); 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 }; Bool RADEONI2CDoLock(xf86OutputPtr output, I2CBusPtr b, int lock_state) { ScrnInfoPtr pScrn = output->scrn; RADEONInfoPtr info = RADEONPTR(pScrn); RADEONI2CBusPtr pRADEONI2CBus = b->DriverPrivate.ptr; unsigned char *RADEONMMIO = info->MMIO; uint32_t temp; if (lock_state) { /* RV410 appears to have a bug where the hw i2c in reset * holds the i2c port in a bad state - switch hw i2c away before * doing DDC - do this for all r200s/r300s for safety sakes */ if ((info->ChipFamily >= CHIP_FAMILY_R200) && (!IS_AVIVO_VARIANT)) { if (pRADEONI2CBus->mask_clk_reg == RADEON_GPIO_MONID) OUTREG(RADEON_DVI_I2C_CNTL_0, (RADEON_I2C_SOFT_RST | R200_DVI_I2C_PIN_SEL(R200_SEL_DDC1))); else OUTREG(RADEON_DVI_I2C_CNTL_0, (RADEON_I2C_SOFT_RST | R200_DVI_I2C_PIN_SEL(R200_SEL_DDC3))); } temp = INREG(pRADEONI2CBus->a_clk_reg); temp &= ~(pRADEONI2CBus->a_clk_mask); OUTREG(pRADEONI2CBus->a_clk_reg, temp); temp = INREG(pRADEONI2CBus->a_data_reg); temp &= ~(pRADEONI2CBus->a_data_mask); OUTREG(pRADEONI2CBus->a_data_reg, temp); } temp = INREG(pRADEONI2CBus->mask_clk_reg); if (lock_state) temp |= (pRADEONI2CBus->mask_clk_mask); else temp &= ~(pRADEONI2CBus->mask_clk_mask); OUTREG(pRADEONI2CBus->mask_clk_reg, temp); temp = INREG(pRADEONI2CBus->mask_clk_reg); temp = INREG(pRADEONI2CBus->mask_data_reg); if (lock_state) temp |= (pRADEONI2CBus->mask_data_mask); else temp &= ~(pRADEONI2CBus->mask_data_mask); OUTREG(pRADEONI2CBus->mask_data_reg, temp); temp = INREG(pRADEONI2CBus->mask_data_reg); 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; RADEONI2CBusPtr pRADEONI2CBus = b->DriverPrivate.ptr; /* Get the result */ val = INREG(pRADEONI2CBus->get_clk_reg); *Clock = (val & pRADEONI2CBus->get_clk_mask) != 0; val = INREG(pRADEONI2CBus->get_data_reg); *data = (val & pRADEONI2CBus->get_data_mask) != 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; RADEONI2CBusPtr pRADEONI2CBus = b->DriverPrivate.ptr; val = INREG(pRADEONI2CBus->put_clk_reg) & (uint32_t)~(pRADEONI2CBus->put_clk_mask); val |= (Clock ? 0:pRADEONI2CBus->put_clk_mask); OUTREG(pRADEONI2CBus->put_clk_reg, val); /* read back to improve reliability on some cards. */ val = INREG(pRADEONI2CBus->put_clk_reg); val = INREG(pRADEONI2CBus->put_data_reg) & (uint32_t)~(pRADEONI2CBus->put_data_mask); val |= (data ? 0:pRADEONI2CBus->put_data_mask); OUTREG(pRADEONI2CBus->put_data_reg, val); /* read back to improve reliability on some cards. */ val = INREG(pRADEONI2CBus->put_data_reg); } Bool RADEONI2CInit(ScrnInfoPtr pScrn, I2CBusPtr *bus_ptr, char *name, RADEONI2CBusPtr pRADEONI2CBus) { 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.ptr = (pointer)pRADEONI2CBus; if (!xf86I2CBusInit(pI2CBus)) return FALSE; *bus_ptr = pI2CBus; return TRUE; } RADEONI2CBusRec legacy_setup_i2c_bus(int ddc_line) { RADEONI2CBusRec i2c; i2c.hw_line = 0; i2c.hw_capable = FALSE; i2c.mask_clk_mask = RADEON_GPIO_EN_1; i2c.mask_data_mask = RADEON_GPIO_EN_0; i2c.a_clk_mask = RADEON_GPIO_A_1; i2c.a_data_mask = RADEON_GPIO_A_0; i2c.put_clk_mask = RADEON_GPIO_EN_1; i2c.put_data_mask = RADEON_GPIO_EN_0; i2c.get_clk_mask = RADEON_GPIO_Y_1; i2c.get_data_mask = RADEON_GPIO_Y_0; if ((ddc_line == RADEON_LCD_GPIO_MASK) || (ddc_line == RADEON_MDGPIO_EN_REG)) { i2c.mask_clk_reg = ddc_line; i2c.mask_data_reg = ddc_line; i2c.a_clk_reg = ddc_line; i2c.a_data_reg = ddc_line; i2c.put_clk_reg = ddc_line; i2c.put_data_reg = ddc_line; i2c.get_clk_reg = ddc_line + 4; i2c.get_data_reg = ddc_line + 4; } else { i2c.mask_clk_reg = ddc_line; i2c.mask_data_reg = ddc_line; i2c.a_clk_reg = ddc_line; i2c.a_data_reg = ddc_line; i2c.put_clk_reg = ddc_line; i2c.put_data_reg = ddc_line; i2c.get_clk_reg = ddc_line; i2c.get_data_reg = ddc_line; } if (ddc_line) i2c.valid = TRUE; else i2c.valid = FALSE; return i2c; } RADEONI2CBusRec atom_setup_i2c_bus(int ddc_line) { RADEONI2CBusRec i2c; i2c.hw_line = 0; i2c.hw_capable = FALSE; if (ddc_line == AVIVO_GPIO_0) { i2c.put_clk_mask = (1 << 19); i2c.put_data_mask = (1 << 18); i2c.get_clk_mask = (1 << 19); i2c.get_data_mask = (1 << 18); i2c.mask_clk_mask = (1 << 19); i2c.mask_data_mask = (1 << 18); i2c.a_clk_mask = (1 << 19); i2c.a_data_mask = (1 << 18); } else { i2c.put_clk_mask = (1 << 0); i2c.put_data_mask = (1 << 8); i2c.get_clk_mask = (1 << 0); i2c.get_data_mask = (1 << 8); i2c.mask_clk_mask = (1 << 0); i2c.mask_data_mask = (1 << 8); i2c.a_clk_mask = (1 << 0); i2c.a_data_mask = (1 << 8); } i2c.mask_clk_reg = ddc_line; i2c.mask_data_reg = ddc_line; i2c.a_clk_reg = ddc_line + 0x4; i2c.a_data_reg = ddc_line + 0x4; i2c.put_clk_reg = ddc_line + 0x8; i2c.put_data_reg = ddc_line + 0x8; i2c.get_clk_reg = ddc_line + 0xc; i2c.get_data_reg = ddc_line + 0xc; if (ddc_line) i2c.valid = TRUE; else i2c.valid = FALSE; return i2c; } static void RADEONGetTVInfo(xf86OutputPtr output) { ScrnInfoPtr pScrn = output->scrn; RADEONInfoPtr info = RADEONPTR(pScrn); RADEONOutputPrivatePtr radeon_output = output->driver_private; radeon_tvout_ptr tvout = &radeon_output->tvout; char *optstr; tvout->hPos = 0; tvout->vPos = 0; tvout->hSize = 0; tvout->tv_on = FALSE; if (!RADEONGetTVInfoFromBIOS(output)) { /* set some reasonable defaults */ tvout->default_tvStd = TV_STD_NTSC; tvout->tvStd = TV_STD_NTSC; tvout->TVRefClk = 27.000000000; tvout->SupportedTVStds = TV_STD_NTSC | TV_STD_PAL; } optstr = (char *)xf86GetOptValString(info->Options, OPTION_TVSTD); if (optstr) { if (!strncmp("ntsc", optstr, strlen("ntsc"))) tvout->tvStd = TV_STD_NTSC; else if (!strncmp("pal", optstr, strlen("pal"))) tvout->tvStd = TV_STD_PAL; else if (!strncmp("pal-m", optstr, strlen("pal-m"))) tvout->tvStd = TV_STD_PAL_M; else if (!strncmp("pal-60", optstr, strlen("pal-60"))) tvout->tvStd = TV_STD_PAL_60; else if (!strncmp("ntsc-j", optstr, strlen("ntsc-j"))) tvout->tvStd = TV_STD_NTSC_J; else if (!strncmp("scart-pal", optstr, strlen("scart-pal"))) tvout->tvStd = TV_STD_SCART_PAL; else { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Invalid TV Standard: %s\n", optstr); } } } void RADEONInitConnector(xf86OutputPtr output) { ScrnInfoPtr pScrn = output->scrn; RADEONInfoPtr info = RADEONPTR(pScrn); RADEONOutputPrivatePtr radeon_output = output->driver_private; if (radeon_output->devices & (ATOM_DEVICE_LCD_SUPPORT)) radeon_output->rmx_type = RMX_FULL; else radeon_output->rmx_type = RMX_OFF; /* dce 3.2 chips have problems with low dot clocks, so use the scaler */ if (IS_DCE32_VARIANT && (radeon_output->devices & (ATOM_DEVICE_DFP_SUPPORT))) radeon_output->rmx_type = RMX_FULL; if (!IS_AVIVO_VARIANT) { if (radeon_output->devices & (ATOM_DEVICE_CRT2_SUPPORT)) { if (xf86ReturnOptValBool(info->Options, OPTION_TVDAC_LOAD_DETECT, FALSE)) radeon_output->load_detection = 1; } } if (radeon_output->devices & (ATOM_DEVICE_TV_SUPPORT)) RADEONGetTVInfo(output); if (radeon_output->devices & (ATOM_DEVICE_DFP_SUPPORT)) radeon_output->coherent_mode = TRUE; if (radeon_output->ConnectorType == CONNECTOR_DISPLAY_PORT) { strcpy(radeon_output->dp_bus_name, output->name); strcat(radeon_output->dp_bus_name, "-DP"); RADEON_DP_I2CInit(pScrn, &radeon_output->dp_pI2CBus, radeon_output->dp_bus_name, output); RADEON_DP_GetSinkType(output); } if (radeon_output->ConnectorType == CONNECTOR_EDP) { strcpy(radeon_output->dp_bus_name, output->name); strcat(radeon_output->dp_bus_name, "-eDP"); RADEON_DP_I2CInit(pScrn, &radeon_output->dp_pI2CBus, radeon_output->dp_bus_name, output); RADEON_DP_GetSinkType(output); } if (radeon_output->ddc_i2c.valid) RADEONI2CInit(pScrn, &radeon_output->pI2CBus, output->name, &radeon_output->ddc_i2c); } #if defined(__powerpc__) static Bool RADEONSetupAppleConnectors(ScrnInfoPtr pScrn) { RADEONInfoPtr info = RADEONPTR(pScrn); switch (info->MacModel) { case RADEON_MAC_IBOOK: info->BiosConnector[0].ddc_i2c = legacy_setup_i2c_bus(RADEON_GPIO_DVI_DDC); info->BiosConnector[0].ConnectorType = CONNECTOR_LVDS; info->BiosConnector[0].valid = TRUE; info->BiosConnector[0].devices = ATOM_DEVICE_LCD1_SUPPORT; if (!radeon_add_encoder(pScrn, radeon_get_encoder_id_from_supported_device(pScrn, ATOM_DEVICE_LCD1_SUPPORT, 0), ATOM_DEVICE_LCD1_SUPPORT)) return FALSE; info->BiosConnector[1].ddc_i2c = legacy_setup_i2c_bus(RADEON_GPIO_VGA_DDC); info->BiosConnector[1].load_detection = FALSE; info->BiosConnector[1].ConnectorType = CONNECTOR_VGA; info->BiosConnector[1].valid = TRUE; info->BiosConnector[1].devices = ATOM_DEVICE_CRT2_SUPPORT; if (!radeon_add_encoder(pScrn, radeon_get_encoder_id_from_supported_device(pScrn, ATOM_DEVICE_CRT2_SUPPORT, 2), ATOM_DEVICE_CRT2_SUPPORT)) return FALSE; info->BiosConnector[2].ConnectorType = CONNECTOR_STV; info->BiosConnector[2].load_detection = FALSE; info->BiosConnector[2].ddc_i2c.valid = FALSE; info->BiosConnector[2].valid = TRUE; info->BiosConnector[2].devices = ATOM_DEVICE_TV1_SUPPORT; if (!radeon_add_encoder(pScrn, radeon_get_encoder_id_from_supported_device(pScrn, ATOM_DEVICE_TV1_SUPPORT, 2), ATOM_DEVICE_TV1_SUPPORT)) return FALSE; return TRUE; case RADEON_MAC_POWERBOOK_EXTERNAL: info->BiosConnector[0].ddc_i2c = legacy_setup_i2c_bus(RADEON_GPIO_DVI_DDC); info->BiosConnector[0].ConnectorType = CONNECTOR_LVDS; info->BiosConnector[0].valid = TRUE; info->BiosConnector[0].devices = ATOM_DEVICE_LCD1_SUPPORT; if (!radeon_add_encoder(pScrn, radeon_get_encoder_id_from_supported_device(pScrn, ATOM_DEVICE_LCD1_SUPPORT, 0), ATOM_DEVICE_LCD1_SUPPORT)) return FALSE; info->BiosConnector[1].ddc_i2c = legacy_setup_i2c_bus(RADEON_GPIO_VGA_DDC); info->BiosConnector[1].ConnectorType = CONNECTOR_DVI_I; info->BiosConnector[1].valid = TRUE; info->BiosConnector[1].devices = ATOM_DEVICE_CRT1_SUPPORT | ATOM_DEVICE_DFP2_SUPPORT; if (!radeon_add_encoder(pScrn, radeon_get_encoder_id_from_supported_device(pScrn, ATOM_DEVICE_CRT1_SUPPORT, 1), ATOM_DEVICE_CRT1_SUPPORT)) return FALSE; if (!radeon_add_encoder(pScrn, radeon_get_encoder_id_from_supported_device(pScrn, ATOM_DEVICE_DFP2_SUPPORT, 0), ATOM_DEVICE_DFP2_SUPPORT)) return FALSE; info->BiosConnector[2].ConnectorType = CONNECTOR_STV; info->BiosConnector[2].load_detection = FALSE; info->BiosConnector[2].ddc_i2c.valid = FALSE; info->BiosConnector[2].valid = TRUE; info->BiosConnector[2].devices = ATOM_DEVICE_TV1_SUPPORT; if (!radeon_add_encoder(pScrn, radeon_get_encoder_id_from_supported_device(pScrn, ATOM_DEVICE_TV1_SUPPORT, 2), ATOM_DEVICE_TV1_SUPPORT)) return FALSE; return TRUE; case RADEON_MAC_POWERBOOK_INTERNAL: info->BiosConnector[0].ddc_i2c = legacy_setup_i2c_bus(RADEON_GPIO_DVI_DDC); info->BiosConnector[0].ConnectorType = CONNECTOR_LVDS; info->BiosConnector[0].valid = TRUE; info->BiosConnector[0].devices = ATOM_DEVICE_LCD1_SUPPORT; if (!radeon_add_encoder(pScrn, radeon_get_encoder_id_from_supported_device(pScrn, ATOM_DEVICE_LCD1_SUPPORT, 0), ATOM_DEVICE_LCD1_SUPPORT)) return FALSE; info->BiosConnector[1].ddc_i2c = legacy_setup_i2c_bus(RADEON_GPIO_VGA_DDC); info->BiosConnector[1].ConnectorType = CONNECTOR_DVI_I; info->BiosConnector[1].valid = TRUE; info->BiosConnector[1].devices = ATOM_DEVICE_CRT1_SUPPORT | ATOM_DEVICE_DFP1_SUPPORT; if (!radeon_add_encoder(pScrn, radeon_get_encoder_id_from_supported_device(pScrn, ATOM_DEVICE_CRT1_SUPPORT, 1), ATOM_DEVICE_CRT1_SUPPORT)) return FALSE; if (!radeon_add_encoder(pScrn, radeon_get_encoder_id_from_supported_device(pScrn, ATOM_DEVICE_DFP1_SUPPORT, 0), ATOM_DEVICE_DFP1_SUPPORT)) return FALSE; info->BiosConnector[2].ConnectorType = CONNECTOR_STV; info->BiosConnector[2].load_detection = FALSE; info->BiosConnector[2].ddc_i2c.valid = FALSE; info->BiosConnector[2].valid = TRUE; info->BiosConnector[2].devices = ATOM_DEVICE_TV1_SUPPORT; if (!radeon_add_encoder(pScrn, radeon_get_encoder_id_from_supported_device(pScrn, ATOM_DEVICE_TV1_SUPPORT, 2), ATOM_DEVICE_TV1_SUPPORT)) return FALSE; return TRUE; case RADEON_MAC_POWERBOOK_VGA: info->BiosConnector[0].ddc_i2c = legacy_setup_i2c_bus(RADEON_GPIO_DVI_DDC); info->BiosConnector[0].ConnectorType = CONNECTOR_LVDS; info->BiosConnector[0].valid = TRUE; info->BiosConnector[0].devices = ATOM_DEVICE_LCD1_SUPPORT; if (!radeon_add_encoder(pScrn, radeon_get_encoder_id_from_supported_device(pScrn, ATOM_DEVICE_LCD1_SUPPORT, 0), ATOM_DEVICE_LCD1_SUPPORT)) return FALSE; info->BiosConnector[1].ddc_i2c = legacy_setup_i2c_bus(RADEON_GPIO_VGA_DDC); info->BiosConnector[1].ConnectorType = CONNECTOR_VGA; info->BiosConnector[1].valid = TRUE; info->BiosConnector[1].devices = ATOM_DEVICE_CRT1_SUPPORT; if (!radeon_add_encoder(pScrn, radeon_get_encoder_id_from_supported_device(pScrn, ATOM_DEVICE_CRT1_SUPPORT, 1), ATOM_DEVICE_CRT1_SUPPORT)) return FALSE; info->BiosConnector[2].ConnectorType = CONNECTOR_STV; info->BiosConnector[2].load_detection = FALSE; info->BiosConnector[2].ddc_i2c.valid = FALSE; info->BiosConnector[2].valid = TRUE; info->BiosConnector[2].devices = ATOM_DEVICE_TV1_SUPPORT; if (!radeon_add_encoder(pScrn, radeon_get_encoder_id_from_supported_device(pScrn, ATOM_DEVICE_TV1_SUPPORT, 2), ATOM_DEVICE_TV1_SUPPORT)) return FALSE; return TRUE; case RADEON_MAC_MINI_EXTERNAL: info->BiosConnector[0].ddc_i2c = legacy_setup_i2c_bus(RADEON_GPIO_CRT2_DDC); info->BiosConnector[0].load_detection = FALSE; info->BiosConnector[0].ConnectorType = CONNECTOR_DVI_I; info->BiosConnector[0].valid = TRUE; info->BiosConnector[0].devices = ATOM_DEVICE_CRT2_SUPPORT | ATOM_DEVICE_DFP2_SUPPORT; if (!radeon_add_encoder(pScrn, radeon_get_encoder_id_from_supported_device(pScrn, ATOM_DEVICE_CRT2_SUPPORT, 2), ATOM_DEVICE_CRT2_SUPPORT)) return FALSE; if (!radeon_add_encoder(pScrn, radeon_get_encoder_id_from_supported_device(pScrn, ATOM_DEVICE_DFP2_SUPPORT, 0), ATOM_DEVICE_DFP2_SUPPORT)) return FALSE; info->BiosConnector[1].ConnectorType = CONNECTOR_STV; info->BiosConnector[1].load_detection = FALSE; info->BiosConnector[1].ddc_i2c.valid = FALSE; info->BiosConnector[1].valid = TRUE; info->BiosConnector[1].devices = ATOM_DEVICE_TV1_SUPPORT; if (!radeon_add_encoder(pScrn, radeon_get_encoder_id_from_supported_device(pScrn, ATOM_DEVICE_TV1_SUPPORT, 2), ATOM_DEVICE_TV1_SUPPORT)) return FALSE; return TRUE; case RADEON_MAC_MINI_INTERNAL: info->BiosConnector[0].ddc_i2c = legacy_setup_i2c_bus(RADEON_GPIO_CRT2_DDC); info->BiosConnector[0].load_detection = FALSE; info->BiosConnector[0].ConnectorType = CONNECTOR_DVI_I; info->BiosConnector[0].valid = TRUE; info->BiosConnector[0].devices = ATOM_DEVICE_CRT2_SUPPORT | ATOM_DEVICE_DFP1_SUPPORT; if (!radeon_add_encoder(pScrn, radeon_get_encoder_id_from_supported_device(pScrn, ATOM_DEVICE_CRT2_SUPPORT, 2), ATOM_DEVICE_CRT2_SUPPORT)) return FALSE; if (!radeon_add_encoder(pScrn, radeon_get_encoder_id_from_supported_device(pScrn, ATOM_DEVICE_DFP1_SUPPORT, 0), ATOM_DEVICE_DFP1_SUPPORT)) return FALSE; info->BiosConnector[1].ConnectorType = CONNECTOR_STV; info->BiosConnector[1].load_detection = FALSE; info->BiosConnector[1].ddc_i2c.valid = FALSE; info->BiosConnector[1].valid = TRUE; info->BiosConnector[1].devices = ATOM_DEVICE_TV1_SUPPORT; if (!radeon_add_encoder(pScrn, radeon_get_encoder_id_from_supported_device(pScrn, ATOM_DEVICE_TV1_SUPPORT, 2), ATOM_DEVICE_TV1_SUPPORT)) return FALSE; return TRUE; case RADEON_MAC_IMAC_G5_ISIGHT: info->BiosConnector[0].ddc_i2c = legacy_setup_i2c_bus(RADEON_GPIO_MONID); info->BiosConnector[0].ConnectorType = CONNECTOR_DVI_D; info->BiosConnector[0].valid = TRUE; info->BiosConnector[0].devices = ATOM_DEVICE_DFP1_SUPPORT; if (!radeon_add_encoder(pScrn, radeon_get_encoder_id_from_supported_device(pScrn, ATOM_DEVICE_DFP1_SUPPORT, 0), ATOM_DEVICE_DFP1_SUPPORT)) return FALSE; info->BiosConnector[1].ddc_i2c = legacy_setup_i2c_bus(RADEON_GPIO_DVI_DDC); info->BiosConnector[1].load_detection = FALSE; info->BiosConnector[1].ConnectorType = CONNECTOR_VGA; info->BiosConnector[1].valid = TRUE; info->BiosConnector[1].devices = ATOM_DEVICE_CRT2_SUPPORT; if (!radeon_add_encoder(pScrn, radeon_get_encoder_id_from_supported_device(pScrn, ATOM_DEVICE_CRT2_SUPPORT, 2), ATOM_DEVICE_CRT2_SUPPORT)) return FALSE; info->BiosConnector[2].ConnectorType = CONNECTOR_STV; info->BiosConnector[2].load_detection = FALSE; info->BiosConnector[2].ddc_i2c.valid = FALSE; info->BiosConnector[2].valid = TRUE; info->BiosConnector[2].devices = ATOM_DEVICE_TV1_SUPPORT; if (!radeon_add_encoder(pScrn, radeon_get_encoder_id_from_supported_device(pScrn, ATOM_DEVICE_TV1_SUPPORT, 2), ATOM_DEVICE_TV1_SUPPORT)) return FALSE; return TRUE; case RADEON_MAC_EMAC: /* eMac G4 800/1.0 with radeon 7500, no EDID on internal monitor * later eMac's (G4 1.25/1.42) with radeon 9200 and 9600 may have * different ddc setups. need to verify */ info->BiosConnector[0].ddc_i2c = legacy_setup_i2c_bus(RADEON_GPIO_VGA_DDC); info->BiosConnector[0].ConnectorType = CONNECTOR_VGA; info->BiosConnector[0].valid = TRUE; info->BiosConnector[0].devices = ATOM_DEVICE_CRT1_SUPPORT; if (!radeon_add_encoder(pScrn, radeon_get_encoder_id_from_supported_device(pScrn, ATOM_DEVICE_CRT1_SUPPORT, 1), ATOM_DEVICE_CRT1_SUPPORT)) return FALSE; info->BiosConnector[1].ddc_i2c = legacy_setup_i2c_bus(RADEON_GPIO_CRT2_DDC); info->BiosConnector[1].load_detection = FALSE; info->BiosConnector[1].ConnectorType = CONNECTOR_VGA; info->BiosConnector[1].valid = TRUE; info->BiosConnector[1].devices = ATOM_DEVICE_CRT2_SUPPORT; if (!radeon_add_encoder(pScrn, radeon_get_encoder_id_from_supported_device(pScrn, ATOM_DEVICE_CRT2_SUPPORT, 2), ATOM_DEVICE_CRT2_SUPPORT)) return FALSE; info->BiosConnector[2].ConnectorType = CONNECTOR_STV; info->BiosConnector[2].load_detection = FALSE; info->BiosConnector[2].ddc_i2c.valid = FALSE; info->BiosConnector[2].valid = TRUE; info->BiosConnector[2].devices = ATOM_DEVICE_TV1_SUPPORT; if (!radeon_add_encoder(pScrn, radeon_get_encoder_id_from_supported_device(pScrn, ATOM_DEVICE_TV1_SUPPORT, 2), ATOM_DEVICE_TV1_SUPPORT)) return FALSE; return TRUE; default: return FALSE; } return FALSE; } #endif static void RADEONSetupGenericConnectors(ScrnInfoPtr pScrn) { RADEONInfoPtr info = RADEONPTR(pScrn); RADEONEntPtr pRADEONEnt = RADEONEntPriv(pScrn); if (IS_AVIVO_VARIANT) return; if (!pRADEONEnt->HasCRTC2) { info->BiosConnector[0].ddc_i2c = legacy_setup_i2c_bus(RADEON_GPIO_VGA_DDC); info->BiosConnector[0].ConnectorType = CONNECTOR_VGA; info->BiosConnector[0].valid = TRUE; info->BiosConnector[0].devices = ATOM_DEVICE_CRT1_SUPPORT; radeon_add_encoder(pScrn, radeon_get_encoder_id_from_supported_device(pScrn, ATOM_DEVICE_CRT1_SUPPORT, 1), ATOM_DEVICE_CRT1_SUPPORT); return; } if (info->IsMobility) { /* Below is the most common setting, but may not be true */ if (info->IsIGP) { info->BiosConnector[0].ddc_i2c = legacy_setup_i2c_bus(RADEON_LCD_GPIO_MASK); info->BiosConnector[0].ConnectorType = CONNECTOR_LVDS; info->BiosConnector[0].valid = TRUE; info->BiosConnector[0].devices = ATOM_DEVICE_LCD1_SUPPORT; radeon_add_encoder(pScrn, radeon_get_encoder_id_from_supported_device(pScrn, ATOM_DEVICE_LCD1_SUPPORT, 0), ATOM_DEVICE_LCD1_SUPPORT); /* IGP only has TVDAC */ if ((info->ChipFamily == CHIP_FAMILY_RS400) || (info->ChipFamily == CHIP_FAMILY_RS480)) info->BiosConnector[1].ddc_i2c = legacy_setup_i2c_bus(RADEON_GPIO_CRT2_DDC); else info->BiosConnector[1].ddc_i2c = legacy_setup_i2c_bus(RADEON_GPIO_VGA_DDC); info->BiosConnector[1].load_detection = FALSE; info->BiosConnector[1].ConnectorType = CONNECTOR_VGA; info->BiosConnector[1].valid = TRUE; info->BiosConnector[1].devices = ATOM_DEVICE_CRT1_SUPPORT; radeon_add_encoder(pScrn, radeon_get_encoder_id_from_supported_device(pScrn, ATOM_DEVICE_CRT1_SUPPORT, 2), ATOM_DEVICE_CRT1_SUPPORT); } else { #if defined(__powerpc__) info->BiosConnector[0].ddc_i2c = legacy_setup_i2c_bus(RADEON_GPIO_DVI_DDC); #else info->BiosConnector[0].ddc_i2c = legacy_setup_i2c_bus(RADEON_LCD_GPIO_MASK); #endif info->BiosConnector[0].ConnectorType = CONNECTOR_LVDS; info->BiosConnector[0].valid = TRUE; info->BiosConnector[0].devices = ATOM_DEVICE_LCD1_SUPPORT; radeon_add_encoder(pScrn, radeon_get_encoder_id_from_supported_device(pScrn, ATOM_DEVICE_LCD1_SUPPORT, 0), ATOM_DEVICE_LCD1_SUPPORT); info->BiosConnector[1].ddc_i2c = legacy_setup_i2c_bus(RADEON_GPIO_VGA_DDC); info->BiosConnector[1].ConnectorType = CONNECTOR_VGA; info->BiosConnector[1].valid = TRUE; info->BiosConnector[1].devices = ATOM_DEVICE_CRT1_SUPPORT; radeon_add_encoder(pScrn, radeon_get_encoder_id_from_supported_device(pScrn, ATOM_DEVICE_CRT1_SUPPORT, 1), ATOM_DEVICE_CRT1_SUPPORT); } } else { /* Below is the most common setting, but may not be true */ if (info->IsIGP) { if ((info->ChipFamily == CHIP_FAMILY_RS400) || (info->ChipFamily == CHIP_FAMILY_RS480)) info->BiosConnector[0].ddc_i2c = legacy_setup_i2c_bus(RADEON_GPIO_CRT2_DDC); else info->BiosConnector[0].ddc_i2c = legacy_setup_i2c_bus(RADEON_GPIO_VGA_DDC); info->BiosConnector[0].load_detection = FALSE; info->BiosConnector[0].ConnectorType = CONNECTOR_VGA; info->BiosConnector[0].valid = TRUE; info->BiosConnector[0].devices = ATOM_DEVICE_CRT1_SUPPORT; radeon_add_encoder(pScrn, radeon_get_encoder_id_from_supported_device(pScrn, ATOM_DEVICE_CRT1_SUPPORT, 1), ATOM_DEVICE_CRT1_SUPPORT); /* not sure what a good default DDCType for DVI on * IGP desktop chips is */ info->BiosConnector[1].ddc_i2c = legacy_setup_i2c_bus(RADEON_GPIO_MONID); /* DDC_DVI? */ info->BiosConnector[1].ConnectorType = CONNECTOR_DVI_D; info->BiosConnector[1].valid = TRUE; info->BiosConnector[1].devices = ATOM_DEVICE_DFP1_SUPPORT; radeon_add_encoder(pScrn, radeon_get_encoder_id_from_supported_device(pScrn, ATOM_DEVICE_DFP1_SUPPORT, 0), ATOM_DEVICE_DFP1_SUPPORT); } else { info->BiosConnector[0].ddc_i2c = legacy_setup_i2c_bus(RADEON_GPIO_DVI_DDC); info->BiosConnector[0].load_detection = FALSE; info->BiosConnector[0].ConnectorType = CONNECTOR_DVI_I; info->BiosConnector[0].valid = TRUE; info->BiosConnector[0].devices = ATOM_DEVICE_CRT2_SUPPORT | ATOM_DEVICE_DFP1_SUPPORT; radeon_add_encoder(pScrn, radeon_get_encoder_id_from_supported_device(pScrn, ATOM_DEVICE_CRT2_SUPPORT, 2), ATOM_DEVICE_CRT2_SUPPORT); radeon_add_encoder(pScrn, radeon_get_encoder_id_from_supported_device(pScrn, ATOM_DEVICE_DFP1_SUPPORT, 0), ATOM_DEVICE_DFP1_SUPPORT); #if defined(__powerpc__) info->BiosConnector[1].ddc_i2c = legacy_setup_i2c_bus(RADEON_GPIO_VGA_DDC); info->BiosConnector[1].ConnectorType = CONNECTOR_DVI_I; info->BiosConnector[1].valid = TRUE; info->BiosConnector[1].devices = ATOM_DEVICE_CRT1_SUPPORT | ATOM_DEVICE_DFP2_SUPPORT; radeon_add_encoder(pScrn, radeon_get_encoder_id_from_supported_device(pScrn, ATOM_DEVICE_CRT1_SUPPORT, 1), ATOM_DEVICE_CRT1_SUPPORT); radeon_add_encoder(pScrn, radeon_get_encoder_id_from_supported_device(pScrn, ATOM_DEVICE_DFP2_SUPPORT, 0), ATOM_DEVICE_DFP2_SUPPORT); #else info->BiosConnector[1].ddc_i2c = legacy_setup_i2c_bus(RADEON_GPIO_VGA_DDC); info->BiosConnector[1].ConnectorType = CONNECTOR_VGA; info->BiosConnector[1].valid = TRUE; info->BiosConnector[1].devices = ATOM_DEVICE_CRT1_SUPPORT; radeon_add_encoder(pScrn, radeon_get_encoder_id_from_supported_device(pScrn, ATOM_DEVICE_CRT1_SUPPORT, 1), ATOM_DEVICE_CRT1_SUPPORT); #endif } } if (info->InternalTVOut) { info->BiosConnector[2].ConnectorType = CONNECTOR_STV; info->BiosConnector[2].load_detection = FALSE; info->BiosConnector[2].ddc_i2c.valid = FALSE; info->BiosConnector[2].valid = TRUE; info->BiosConnector[2].devices = ATOM_DEVICE_TV1_SUPPORT; radeon_add_encoder(pScrn, radeon_get_encoder_id_from_supported_device(pScrn, ATOM_DEVICE_TV1_SUPPORT, 2), ATOM_DEVICE_TV1_SUPPORT); } /* 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_i2c = legacy_setup_i2c_bus(RADEON_GPIO_VGA_DDC); info->BiosConnector[1].ddc_i2c = legacy_setup_i2c_bus(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; } else if (strstr(cpuline, "eMac")) { ret = RADEON_MAC_EMAC; 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__ */ static int radeon_output_clones (ScrnInfoPtr pScrn, xf86OutputPtr output) { RADEONInfoPtr info = RADEONPTR(pScrn); RADEONOutputPrivatePtr radeon_output = output->driver_private; xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR (pScrn); int o; int index_mask = 0; /* DIG routing gets problematic */ if (info->ChipFamily >= CHIP_FAMILY_R600) return index_mask; /* LVDS is too wacky */ if (radeon_output->devices & (ATOM_DEVICE_LCD_SUPPORT)) return index_mask; /* TV requires very specific timing */ if (radeon_output->devices & (ATOM_DEVICE_TV_SUPPORT)) return index_mask; /* DVO requires 2x ppll clocks depending on the tmds chip */ if (radeon_output->devices & (ATOM_DEVICE_DFP2_SUPPORT)) return index_mask; for (o = 0; o < config->num_output; o++) { xf86OutputPtr clone = config->output[o]; RADEONOutputPrivatePtr radeon_clone = clone->driver_private; if (output == clone) /* don't clone yourself */ continue; else if (radeon_clone->devices & (ATOM_DEVICE_LCD_SUPPORT)) /* LVDS */ continue; else if (radeon_clone->devices & (ATOM_DEVICE_TV_SUPPORT)) /* TV */ continue; else index_mask |= (1 << o); } return index_mask; } static xf86OutputPtr RADEONOutputCreate(ScrnInfoPtr pScrn, const char *name, int i) { char buf[32]; sprintf(buf, name, i); return xf86OutputCreate(pScrn, &radeon_output_funcs, buf); } /* * initialise the static data sos we don't have to re-do at randr change */ Bool RADEONSetupConnectors(ScrnInfoPtr pScrn) { xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn); RADEONInfoPtr info = RADEONPTR(pScrn); xf86OutputPtr output; char *optstr; int i; int num_vga = 0; int num_dvi = 0; int num_hdmi = 0; int num_dp = 0; int num_edp = 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->encoders[i] = NULL; info->BiosConnector[i].valid = FALSE; info->BiosConnector[i].load_detection = TRUE; info->BiosConnector[i].shared_ddc = FALSE; info->BiosConnector[i].ddc_i2c.valid = FALSE; info->BiosConnector[i].ConnectorType = CONNECTOR_NONE; info->BiosConnector[i].devices = 0; } #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 if (!strncmp("emac", optstr, strlen("emac"))) info->MacModel = RADEON_MAC_EMAC; 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); } /* parse connector table option */ optstr = (char *)xf86GetOptValString(info->Options, OPTION_CONNECTORTABLE); if (optstr) { unsigned int ddc_line[2]; int DACType[2], TMDSType[2]; for (i = 2; i < RADEON_MAX_BIOS_CONNECTOR; i++) { info->BiosConnector[i].valid = FALSE; } if (sscanf(optstr, "%u,%u,%u,%u,%u,%u,%u,%u", &ddc_line[0], &DACType[0], &TMDSType[0], &info->BiosConnector[0].ConnectorType, &ddc_line[1], &DACType[1], &TMDSType[1], &info->BiosConnector[1].ConnectorType) != 8) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Invalid ConnectorTable option: %s\n", optstr); return FALSE; } for (i = 0; i < 2; i++) { info->BiosConnector[i].valid = TRUE; info->BiosConnector[i].ddc_i2c = legacy_setup_i2c_bus(ddc_line[i]); switch (DACType[i]) { case 1: info->BiosConnector[i].devices |= ATOM_DEVICE_CRT1_SUPPORT; if (!radeon_add_encoder(pScrn, radeon_get_encoder_id_from_supported_device(pScrn, ATOM_DEVICE_CRT1_SUPPORT, 1), ATOM_DEVICE_CRT1_SUPPORT)) return FALSE; info->BiosConnector[i].load_detection = TRUE; break; case 2: info->BiosConnector[i].devices |= ATOM_DEVICE_CRT2_SUPPORT; if (!radeon_add_encoder(pScrn, radeon_get_encoder_id_from_supported_device(pScrn, ATOM_DEVICE_CRT1_SUPPORT, 2), ATOM_DEVICE_CRT1_SUPPORT)) return FALSE; info->BiosConnector[i].load_detection = FALSE; break; } switch (TMDSType[i]) { case 1: info->BiosConnector[i].devices |= ATOM_DEVICE_DFP1_SUPPORT; if (!radeon_add_encoder(pScrn, radeon_get_encoder_id_from_supported_device(pScrn, ATOM_DEVICE_DFP1_SUPPORT, 0), ATOM_DEVICE_DFP1_SUPPORT)) return FALSE; break; case 2: info->BiosConnector[i].devices |= ATOM_DEVICE_DFP2_SUPPORT; if (!radeon_add_encoder(pScrn, radeon_get_encoder_id_from_supported_device(pScrn, ATOM_DEVICE_DFP2_SUPPORT, 0), ATOM_DEVICE_DFP2_SUPPORT)) return FALSE; break; } } } for (i = 0; i < RADEON_MAX_BIOS_CONNECTOR; i++) { if (info->BiosConnector[i].valid) { RADEONConnectorType conntype = info->BiosConnector[i].ConnectorType; if ((conntype == CONNECTOR_DVI_D) || (conntype == CONNECTOR_DVI_I) || (conntype == CONNECTOR_DVI_A) || (conntype == CONNECTOR_HDMI_TYPE_B)) { num_dvi++; } else if (conntype == CONNECTOR_VGA) { num_vga++; } else if (conntype == CONNECTOR_HDMI_TYPE_A) { num_hdmi++; } else if (conntype == CONNECTOR_DISPLAY_PORT) { num_dp++; } else if (conntype == CONNECTOR_EDP) { num_edp++; } } } for (i = 0 ; i < RADEON_MAX_BIOS_CONNECTOR; i++) { if (info->BiosConnector[i].valid) { RADEONOutputPrivatePtr radeon_output; RADEONConnectorType conntype = info->BiosConnector[i].ConnectorType; if (conntype == CONNECTOR_NONE) continue; radeon_output = xnfcalloc(sizeof(RADEONOutputPrivateRec), 1); if (!radeon_output) { return FALSE; } radeon_output->MonType = MT_UNKNOWN; radeon_output->ConnectorType = conntype; radeon_output->devices = info->BiosConnector[i].devices; radeon_output->ddc_i2c = info->BiosConnector[i].ddc_i2c; radeon_output->igp_lane_info = info->BiosConnector[i].igp_lane_info; radeon_output->shared_ddc = info->BiosConnector[i].shared_ddc; radeon_output->load_detection = info->BiosConnector[i].load_detection; radeon_output->linkb = info->BiosConnector[i].linkb; radeon_output->dig_encoder = -1; radeon_output->connector_id = info->BiosConnector[i].connector_object; radeon_output->connector_object_id = info->BiosConnector[i].connector_object_id; radeon_output->ucI2cId = info->BiosConnector[i].ucI2cId; radeon_output->hpd_id = info->BiosConnector[i].hpd_id; /* Technically HDMI-B is a glorfied DL DVI so the bios is correct, * but this can be confusing to users when it comes to output names, * so call it DVI */ if ((conntype == CONNECTOR_DVI_D) || (conntype == CONNECTOR_DVI_I) || (conntype == CONNECTOR_DVI_A) || (conntype == CONNECTOR_HDMI_TYPE_B)) { output = RADEONOutputCreate(pScrn, "DVI-%d", --num_dvi); } else if (conntype == CONNECTOR_VGA) { output = RADEONOutputCreate(pScrn, "VGA-%d", --num_vga); } else if (conntype == CONNECTOR_HDMI_TYPE_A) { output = RADEONOutputCreate(pScrn, "HDMI-%d", --num_hdmi); } else if (conntype == CONNECTOR_DISPLAY_PORT) { output = RADEONOutputCreate(pScrn, "DisplayPort-%d", --num_dp); } else if (conntype == CONNECTOR_EDP) { output = RADEONOutputCreate(pScrn, "eDP-%d", --num_edp); } else { output = RADEONOutputCreate(pScrn, ConnectorTypeName[conntype], 0); } if (!output) { return FALSE; } output->driver_private = radeon_output; if (IS_DCE4_VARIANT) { output->possible_crtcs = 0x3f; } else { output->possible_crtcs = 1; /* crtc2 can drive LVDS, it just doesn't have RMX */ if (!(radeon_output->devices & (ATOM_DEVICE_LCD_SUPPORT))) 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); } } for (i = 0; i < xf86_config->num_output; i++) { xf86OutputPtr output = xf86_config->output[i]; output->possible_clones = radeon_output_clones(pScrn, output); RADEONGetHardCodedEDIDFromFile(output); } return TRUE; }