/* * Copyright 2007, 2008 Luc Verhaegen * Copyright 2007, 2008 Matthias Hopf * Copyright 2007, 2008 Egbert Eich * Copyright 2007, 2008 Advanced Micro Devices, Inc. * * 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 * 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 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 NONINFRINGEMENT. IN NO EVENT SHALL * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. */ /* * RandR interface. * * Only supports RandR 1.2 ATM or no RandR at all. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif /* The _AtomBIOS property is experimental for now - only enable if needed. * Beware - there be dragons and crashes */ #define ENABLE_PROPERTY_ATOMBIOS 0 /* Xserver interface */ #include "xf86.h" #ifdef RANDR_12_SUPPORT /* Xserver interface */ # include "randrstr.h" # include "xf86i2c.h" /* Missing in old versions of xf86Crtc.h */ # include "xf86Crtc.h" # include "xf86RandR12.h" # include "xaa.h" # include "exa.h" #ifdef HAVE_XEXTPROTO_71 # include "X11/extensions/dpmsconst.h" #else # define DPMS_SERVER # include "X11/extensions/dpms.h" #endif # include "X11/Xatom.h" #endif /* RANDR_12_SUPPORT */ #if HAVE_XF86_ANSIC_H # include "xf86_ansic.h" #endif /* Driver specific headers */ #include "rhd.h" #include "rhd_atombios.h" #include "rhd_randr.h" #ifdef RANDR_12_SUPPORT # include "rhd_crtc.h" # include "rhd_cursor.h" # include "rhd_connector.h" # include "rhd_output.h" # include "rhd_modes.h" # include "rhd_monitor.h" # include "rhd_vga.h" # include "rhd_pll.h" # include "rhd_lut.h" # include "rhd_mc.h" # include "rhd_card.h" # include "rhd_i2c.h" # include "rhd_audio.h" /* * Driver internal */ # define WRAP_SCRNINFO(func,new) \ do { \ rhdPtr->randr->func = pScrn->func; \ pScrn->func = new; \ } while (0) # define UNWRAP_SCRNINFO(func) \ pScrn->func = rhdPtr->randr->func struct rhdRandr { xf86CrtcPtr RandrCrtc[2]; xf86OutputPtr *RandrOutput; /* NULL-terminated */ void (*PointerMoved)(int, int, int); } ; #define MAX_CONNECTORS_PER_RR_OUTPUT 4 /* Outputs and Connectors are combined for RandR due to missing abstraction */ typedef struct _rhdRandrOutput { char Name[64]; struct rhdConnector *Connector; struct rhdOutput *Output; DisplayModePtr ScaledToMode; struct rhdCrtc *Crtc; struct rhdConnector *AllConnectors[MAX_CONNECTORS_PER_RR_OUTPUT]; Bool OutputActive; } rhdRandrOutputRec, *rhdRandrOutputPtr; struct rhdRandrCrtc { struct rhdCrtc *rhdCrtc; union { FBLinearPtr MemXAA; ExaOffscreenArea *MemEXA; } u; }; #define ATOM_EDID "EDID" #define ATOM_EDID2 "EDID_DATA" #define ATOM_SIGNAL_FORMAT "SignalFormat" #define ATOM_CONNECTOR_TYPE "ConnectorType" #define ATOM_CONNECTOR_NUMBER "ConnectorNumber" #define ATOM_OUTPUT_NUMBER "_OutputNumber" #define ATOM_PANNING_AREA "_PanningArea" #define ATOM_BACKLIGHT "Backlight" #define ATOM_COHERENT "_Coherent" #define ATOM_HDMI "_HDMI" #define ATOM_AUDIO_WORKAROUND "_AudioStreamSilence" #define ATOM_ATOMBIOS "_AtomBIOS" static Atom atom_SignalFormat, atom_ConnectorType, atom_ConnectorNumber, atom_OutputNumber, atom_PanningArea, atom_Backlight, atom_Coherent, atom_HdmiProperty, atom_AudioWorkaround; static Atom atom_unknown, atom_VGA, atom_TMDS, atom_LVDS, atom_DisplayPort, atom_TV; static Atom atom_DVI, atom_DVII, atom_DVID, atom_DVIA, atom_HDMI, atom_Panel; static Atom atom_EDID, atom_EDID2, atom_AtomBIOS; /* Get RandR property values */ static Atom rhdGetSignalFormat(rhdRandrOutputPtr ro) { switch (ro->Output->Id) { case RHD_OUTPUT_DACA: case RHD_OUTPUT_DACB: switch (ro->Connector->Type) { case RHD_CONNECTOR_VGA: case RHD_CONNECTOR_DVI: case RHD_CONNECTOR_DVI_SINGLE: return atom_VGA; case RHD_CONNECTOR_TV: default: return atom_unknown; /* TODO */ } case RHD_OUTPUT_LVTMA: case RHD_OUTPUT_KLDSKP_LVTMA: case RHD_OUTPUT_LVDS: case RHD_OUTPUT_UNIPHYA: case RHD_OUTPUT_UNIPHYB: case RHD_OUTPUT_UNIPHYC: case RHD_OUTPUT_UNIPHYD: case RHD_OUTPUT_UNIPHYE: case RHD_OUTPUT_UNIPHYF: #if RHD_OUTPUT_LVTMB != RHD_OUTPUT_LVDS case RHD_OUTPUT_LVTMB: #endif switch (ro->Connector->Type) { case RHD_CONNECTOR_DVI: case RHD_CONNECTOR_DVI_SINGLE: return atom_TMDS; case RHD_CONNECTOR_PANEL: return atom_LVDS; default: return atom_unknown; } case RHD_OUTPUT_TMDSA: #if RHD_OUTPUT_TMDSB != RHD_OUTPUT_LVDS case RHD_OUTPUT_TMDSB: #endif return atom_TMDS; default: return atom_unknown; } } static Atom rhdGetConnectorType(rhdRandrOutputPtr ro) { switch (ro->Connector->Type) { case RHD_CONNECTOR_VGA: return atom_VGA; case RHD_CONNECTOR_DVI: case RHD_CONNECTOR_DVI_SINGLE: #ifdef NOTYET if (ro->Output->Connector == RHD_CONNECTOR_HDMI || ro->Output->Connector == RHD_CONNECTOR_HDMI_DUAL) return atom_HDMI; #endif if (strncmp (ro->Connector->Name, "DVI-I", 5) == 0) return atom_DVII; if (strncmp (ro->Connector->Name, "DVI-D", 5) == 0) return atom_DVID; if (strncmp (ro->Connector->Name, "DVI-A", 5) == 0) return atom_DVIA; return atom_DVI; case RHD_CONNECTOR_PANEL: return atom_Panel; case RHD_CONNECTOR_TV: return atom_TV; /* TODO */ default: return atom_unknown; } } /* Set crtc pos according to mouse pos and panning information */ static void rhdUpdateCrtcPos(RHDPtr rhdPtr, struct rhdCrtc *Crtc, int x, int y) { int i; if (Crtc->MaxX > 0) { int cx = Crtc->X, cy = Crtc->Y; int w = Crtc->CurrentMode->HDisplay; int h = Crtc->CurrentMode->VDisplay; if (x < cx) cx = x > Crtc->MinX ? x : Crtc->MinX; if (x >= cx + w) cx = x < Crtc->MaxX ? x-w+1 : Crtc->MaxX-w; if (y < cy) cy = y > Crtc->MinY ? y : Crtc->MinY; if (y >= cy + h) cy = y < Crtc->MaxY ? y-h+1 : Crtc->MaxY-h; if (cx != Crtc->X || cy != Crtc->Y) Crtc->FrameSet(Crtc, cx, cy); for (i = 0; i < 2; i++) { xf86CrtcPtr crtc = (xf86CrtcPtr) rhdPtr->randr->RandrCrtc[i]; if (Crtc == ((struct rhdRandrCrtc *)crtc->driver_private)->rhdCrtc) { crtc->x = cx; crtc->y = cy; } } } } /* Debug: print out state */ void RHDDebugRandrState (RHDPtr rhdPtr, const char *msg) { int i; xf86OutputPtr *ro; RHDDebug(rhdPtr->scrnIndex, "State at %s:\n", msg); for (i = 0; i < 2; i++) { xf86CrtcPtr crtc = rhdPtr->randr->RandrCrtc[i]; struct rhdCrtc *c = ((struct rhdRandrCrtc *)crtc->driver_private)->rhdCrtc; RHDDebugCont(" RRCrtc #%d [rhd %s]: active %d [%d] " "mode %s (%dx%d) +%d+%d\n", i, c->Name, crtc->enabled, c->Active, crtc->mode.name ? crtc->mode.name : "unnamed", crtc->mode.HDisplay, crtc->mode.VDisplay, crtc->x, crtc->y); } for (ro = rhdPtr->randr->RandrOutput; *ro; ro++) { rhdRandrOutputPtr o = (rhdRandrOutputPtr) (*ro)->driver_private; ASSERT(!strcmp((*ro)->name,o->Name)); RHDDebugCont(" RROut %s [Out %s Conn %s] Crtc %s [%s] " "[%sactive] %s\n", (*ro)->name, o->Output->Name, o->Connector->Name, (*ro)->crtc ? ((struct rhdRandrCrtc *)((*ro)->crtc->driver_private))->rhdCrtc->Name : "null", o->Output->Crtc ? o->Output->Crtc->Name : "null", o->Output->Active ? "" : "in", (*ro)->status == XF86OutputStatusConnected ? "connected" : (*ro)->status == XF86OutputStatusDisconnected ? "disconnected" : (*ro)->status == XF86OutputStatusUnknown ? "unknownState" : "badState" ); } } /* * xf86CrtcConfig callback functions */ static Bool rhdRRXF86CrtcResize(ScrnInfoPtr pScrn, int width, int height) { RHDFUNC(pScrn); /* This is strange... if we set virtualX/virtualY like the intel driver * does, we limit ourself in the future to this maximum size. * The check for this is internally in RandR, no idea why the intel driver * actually works this way... * Even more curious: if we DON'T update virtual, everything seems to * work as expected... */ #if 0 pScrn->virtualX = width; pScrn->virtualY = height; #endif return TRUE; } /* * xf86Crtc callback functions */ /* Turns the crtc on/off, or sets intermediate power levels if available. */ static void rhdRRCrtcDpms(xf86CrtcPtr Crtc, int mode) { RHDPtr rhdPtr = RHDPTR(Crtc->scrn); struct rhdCrtc *rhdCrtc = ((struct rhdRandrCrtc *)(Crtc->driver_private))->rhdCrtc; RHDDebug(rhdCrtc->scrnIndex, "%s: %s: %s\n", __func__, rhdCrtc->Name, mode==DPMSModeOn ? "On" : mode==DPMSModeOff ? "Off" : "Other"); switch (mode) { case DPMSModeOn: if (rhdCrtc->PLL) rhdCrtc->PLL->Power(rhdCrtc->PLL, RHD_POWER_ON); rhdCrtc->Power(rhdCrtc, RHD_POWER_ON); rhdCrtc->Active = TRUE; break; case DPMSModeSuspend: case DPMSModeStandby: rhdCrtc->Power(rhdCrtc, RHD_POWER_RESET); if (rhdCrtc->PLL) rhdCrtc->PLL->Power(rhdCrtc->PLL, RHD_POWER_RESET); rhdCrtc->Active = FALSE; break; case DPMSModeOff: rhdCrtc->Power(rhdCrtc, RHD_POWER_SHUTDOWN); if (rhdCrtc->PLL) rhdCrtc->PLL->Power(rhdCrtc->PLL, RHD_POWER_SHUTDOWN); rhdCrtc->Active = FALSE; break; default: ASSERT(!"Unknown DPMS mode"); } RHDDebugRandrState(rhdPtr, "POST-CrtcDpms"); } /* Lock CRTC prior to mode setting. Returns whether unlock is needed */ static Bool rhdRRCrtcLock(xf86CrtcPtr crtc) { /* Looks like we don't have to lock for mode setting. Only as long as * buffers are fixed, of course */ return FALSE; } /* Unlock CRTC after mode setting */ static void rhdRRCrtcUnlock (xf86CrtcPtr crtc) { } /* Helper: setup PLL and LUT for Crtc */ static void setupCrtc(RHDPtr rhdPtr, struct rhdCrtc *Crtc) { int i; /* PLL & LUT setup - static at the moment */ if (Crtc->PLL) return; for (i = 0; i < 2; i++) if (Crtc == rhdPtr->Crtc[i]) break; ASSERT(i<2); Crtc->PLL = rhdPtr->PLLs[i]; Crtc->LUT = rhdPtr->LUT[i]; } /* Set video mode. * randr calls for Crtc and Output separately, while order should be * up to the driver. Well. */ static void rhdRRCrtcPrepare(xf86CrtcPtr crtc) { RHDPtr rhdPtr = RHDPTR(crtc->scrn); ScrnInfoPtr pScrn = xf86Screens[rhdPtr->scrnIndex]; struct rhdCrtc *rhdCrtc = ((struct rhdRandrCrtc *)(crtc->driver_private))->rhdCrtc; RHDFUNC(rhdPtr); setupCrtc(rhdPtr, rhdCrtc); pScrn->vtSema = TRUE; /* Disable CRTCs to stop noise from appearing. */ rhdCrtc->Power(rhdCrtc, RHD_POWER_RESET); /* Verify panning area */ if (rhdCrtc->MaxX > rhdCrtc->Width) rhdCrtc->MaxX = rhdCrtc->Width; if (rhdCrtc->MaxY > rhdCrtc->Height) rhdCrtc->MaxY = rhdCrtc->Height; } static void rhdRRCrtcModeSet(xf86CrtcPtr crtc, DisplayModePtr OrigMode, DisplayModePtr Mode, int x, int y) { RHDPtr rhdPtr = RHDPTR(crtc->scrn); ScrnInfoPtr pScrn = xf86Screens[rhdPtr->scrnIndex]; struct rhdCrtc *rhdCrtc = ((struct rhdRandrCrtc*) (crtc->driver_private))->rhdCrtc; xf86CrtcConfigPtr xf86CrtcConfig = XF86_CRTC_CONFIG_PTR(crtc->scrn); CARD32 ScanOutOffset; int i; /* RandR may give us a mode without a name... (xf86RandRModeConvert) */ if (!Mode->name && crtc->mode.name) Mode->name = xstrdup(crtc->mode.name); RHDDebug(rhdPtr->scrnIndex, "%s: %s : %s at %d/%d\n", __func__, rhdCrtc->Name, Mode->name, x, y); /* * for AtomBIOS SetPixelClock we need information about the outputs. * So find the output(s) matching this Crtc and assign the Crtc to it. */ for (i = 0; i < xf86CrtcConfig->num_output; i++) { if (xf86CrtcConfig->output[i]->crtc == crtc) { rhdRandrOutputPtr rout = xf86CrtcConfig->output[i]->driver_private; rout->Output->Crtc = rhdCrtc; } } if (rhdPtr->verbosity >= 3) { xf86DrvMsg(rhdPtr->scrnIndex, X_INFO, "On Crtc %i Setting %3.1f Hz Mode: ", rhdCrtc->Id, Mode->VRefresh); RHDPrintModeline(Mode); if (OrigMode->VDisplay != Mode->VDisplay || OrigMode->HDisplay != Mode->HDisplay) { xf86DrvMsg(-1, X_NONE, "Scaled from: "); RHDPrintModeline(OrigMode); } } /* Set up mode */ if (crtc->rotatedData != NULL) { ScanOutOffset = (unsigned long) crtc->rotatedData - (unsigned long)rhdPtr->FbBase; x = y = 0; } else { ScanOutOffset = rhdPtr->FbScanoutStart; } rhdCrtc->FBSet(rhdCrtc, pScrn->displayWidth, pScrn->virtualX, pScrn->virtualY, pScrn->depth, ScanOutOffset); rhdCrtc->ModeSet(rhdCrtc, Mode); if (OrigMode->VDisplay != Mode->VDisplay || OrigMode->HDisplay != Mode->HDisplay) rhdCrtc->ScaleSet(rhdCrtc, rhdCrtc->ScaleType, OrigMode, Mode); else rhdCrtc->ScaleSet(rhdCrtc, RHD_CRTC_SCALE_TYPE_NONE, Mode, NULL); rhdCrtc->FrameSet(rhdCrtc, x, y); rhdUpdateCrtcPos(rhdPtr, rhdCrtc, rhdCrtc->Cursor->X, rhdCrtc->Cursor->Y); RHDPLLSet(rhdCrtc->PLL, Mode->Clock); /* This also powers up PLL */ rhdCrtc->LUTSelect(rhdCrtc, rhdCrtc->LUT); /* * RandR is able to bring up new Crtcs, but can't be bothered to set up * a cmap on them. * * The pScreen check tells us whether we are still in PreInit. If we are * still in PreInit, the xserver will still do the right thing and call * LoadPalette accordingly after the modeset. VT switch will also do the * right thing still, but at that time no new CRTC gets initialised, so * LUT->Initialised is either set, or the current function isn't called. */ if (!rhdCrtc->LUT->Initialised && pScrn->pScreen) RHDLUTCopyForRR(rhdCrtc->LUT); } static void rhdRRCrtcCommit(xf86CrtcPtr crtc) { RHDPtr rhdPtr = RHDPTR(crtc->scrn); struct rhdCrtc *rhdCrtc = ((struct rhdRandrCrtc *)(crtc->driver_private))->rhdCrtc; RHDFUNC(rhdPtr); rhdCrtc->Active = TRUE; rhdCrtc->Power(rhdCrtc, RHD_POWER_ON); if (crtc->scrn->pScreen != NULL) xf86_reload_cursors(crtc->scrn->pScreen); RHDDebugRandrState(rhdPtr, rhdCrtc->Name); } static void rhdRRCrtcGammaSet(xf86CrtcPtr crtc, CARD16 *red, CARD16 *green, CARD16 *blue, int size) { struct rhdCrtc *rhdCrtc = ((struct rhdRandrCrtc *)(crtc->driver_private))->rhdCrtc; RHDDebug(rhdCrtc->scrnIndex, "%s: %s.\n", __func__, rhdCrtc->Name); if (size < 256) { xf86DrvMsg(rhdCrtc->scrnIndex, X_ERROR, "LUT: only %d rows of LUT given, when 256 expected; aborting", size); return; } else if (size > 256) { xf86DrvMsg(rhdCrtc->scrnIndex, X_WARNING, "LUT: %d rows of LUT given, when 256 expected; some rows are ignored", size); } rhdCrtc->LUT->Set(rhdCrtc->LUT, red, green, blue); } /* Dummy, because not tested for NULL */ static Bool rhdRRCrtcModeFixupDUMMY(xf86CrtcPtr crtc, DisplayModePtr mode, DisplayModePtr adjusted_mode) { return TRUE; } #if XF86_CRTC_VERSION >= 2 /* Entry point for RandR 1.3 panning */ static void rhdRRCrtcSetOrigin(xf86CrtcPtr crtc, int x, int y) { struct rhdCrtc *rhdCrtc = ((struct rhdRandrCrtc*) (crtc->driver_private))->rhdCrtc; rhdCrtc->FrameSet(rhdCrtc, x, y); } #endif /* * xf86Output callback functions */ static void rhdRROutputCreateResources(xf86OutputPtr out) { RHDPtr rhdPtr = RHDPTR(out->scrn); rhdRandrOutputPtr rout = (rhdRandrOutputPtr) out->driver_private; struct rhdOutput *o; INT32 val; CARD32 num; int err; INT32 range[2]; static xf86OutputPtr first_output = NULL; RHDFUNC(rhdPtr); if (! first_output) first_output = out; /* Create atoms for RandR 1.3 properties */ atom_EDID = MakeAtom(ATOM_EDID, sizeof(ATOM_EDID)-1, TRUE); atom_EDID2 = MakeAtom(ATOM_EDID2, sizeof(ATOM_EDID2)-1, TRUE); atom_SignalFormat = MakeAtom(ATOM_SIGNAL_FORMAT, sizeof(ATOM_SIGNAL_FORMAT)-1, TRUE); atom_ConnectorType = MakeAtom(ATOM_CONNECTOR_TYPE, sizeof(ATOM_CONNECTOR_TYPE)-1, TRUE); atom_ConnectorNumber = MakeAtom(ATOM_CONNECTOR_NUMBER, sizeof(ATOM_CONNECTOR_NUMBER)-1, TRUE); atom_OutputNumber = MakeAtom(ATOM_OUTPUT_NUMBER, sizeof(ATOM_OUTPUT_NUMBER)-1, TRUE); atom_PanningArea = MakeAtom(ATOM_PANNING_AREA, sizeof(ATOM_PANNING_AREA)-1, TRUE); atom_AtomBIOS = MakeAtom(ATOM_ATOMBIOS, sizeof(ATOM_ATOMBIOS)-1, TRUE); /* Create atoms for RandR 1.3 property values */ atom_unknown = MakeAtom("unknown", 7, TRUE); atom_VGA = MakeAtom("VGA", 3, TRUE); atom_TMDS = MakeAtom("TMDS", 4, TRUE); atom_LVDS = MakeAtom("LVDS", 4, TRUE); atom_DisplayPort = MakeAtom("DisplayPort", 11, TRUE); atom_TV = MakeAtom("TV", 2, TRUE); atom_DVI = MakeAtom("DVI", 3, TRUE); atom_DVII = MakeAtom("DVI-I", 5, TRUE); atom_DVID = MakeAtom("DVI-D", 5, TRUE); atom_DVIA = MakeAtom("DVI-A", 5, TRUE); atom_HDMI = MakeAtom("HDMI", 4, TRUE); atom_Panel = MakeAtom("Panel", 5, TRUE); /* Set up properties */ val = rhdGetSignalFormat(rout); /* TODO: for TV multiple signal formats will be possible */ RRConfigureOutputProperty(out->randr_output, atom_SignalFormat, FALSE, FALSE, TRUE, 1, &val); RRChangeOutputProperty(out->randr_output, atom_SignalFormat, XA_ATOM, 32, PropModeReplace, 1, &val, FALSE, FALSE); val = rhdGetConnectorType(rout); RRConfigureOutputProperty(out->randr_output, atom_ConnectorType, FALSE, FALSE, TRUE, 0, NULL); RRChangeOutputProperty(out->randr_output, atom_ConnectorType, XA_ATOM, 32, PropModeReplace, 1, &val, FALSE, FALSE); for (num = 0; num < RHD_CONNECTORS_MAX; num++) if (rout->Connector == rhdPtr->Connector[num]) break; ASSERT(num < RHD_CONNECTORS_MAX); num++; /* For RANDR_CONNECTOR_NUMBER 0 is unknown */ RRConfigureOutputProperty(out->randr_output, atom_ConnectorNumber, FALSE, FALSE, TRUE, 0, NULL); RRChangeOutputProperty(out->randr_output, atom_ConnectorNumber, XA_INTEGER, 32, PropModeReplace, 1, &num, FALSE, FALSE); for (num = 1, o = rhdPtr->Outputs; o; num++, o = o->Next) if (rout->Output == o) break; ASSERT(o); RRConfigureOutputProperty(out->randr_output, atom_OutputNumber, FALSE, FALSE, FALSE, 0, NULL); RRChangeOutputProperty(out->randr_output, atom_OutputNumber, XA_INTEGER, 32, PropModeReplace, 1, &num, FALSE, FALSE); RRConfigureOutputProperty(out->randr_output, atom_PanningArea, FALSE, FALSE, FALSE, 0, NULL); RRChangeOutputProperty(out->randr_output, atom_PanningArea, XA_STRING, 8, PropModeReplace, 0, NULL, FALSE, FALSE); #if ENABLE_PROPERTY_ATOMBIOS /* AtomBIOS usage */ /* We don't have per-CRTC or even per-GPU properties; * so fake this by only applying to first output */ if (out == first_output) { char *string; RRConfigureOutputProperty(out->randr_output, atom_AtomBIOS, FALSE, FALSE, FALSE, 0, NULL); string = rhdReturnAtomBIOSUsage(rhdPtr); RRChangeOutputProperty(out->randr_output, atom_AtomBIOS, XA_STRING, 8, PropModeReplace, strlen(string), string, FALSE, FALSE); free (string); } #endif if (rout->Output->Property) { if (rout->Output->Property(rout->Output, rhdPropertyCheck, RHD_OUTPUT_BACKLIGHT, NULL)) { atom_Backlight = MakeAtom(ATOM_BACKLIGHT, sizeof(ATOM_BACKLIGHT)-1, TRUE); range[0] = 0; range[1] = RHD_BACKLIGHT_PROPERTY_MAX; err = RRConfigureOutputProperty(out->randr_output, atom_Backlight, FALSE, TRUE, FALSE, 2, range); if (err != 0) xf86DrvMsg(rhdPtr->scrnIndex, X_ERROR, "RRConfigureOutputProperty error: %d\n", err); else { union rhdPropertyData val; if (!rout->Output->Property(rout->Output, rhdPropertyGet, RHD_OUTPUT_BACKLIGHT, &val)) val.integer = RHD_BACKLIGHT_PROPERTY_MAX; /* max */ err = RRChangeOutputProperty(out->randr_output, atom_Backlight, XA_INTEGER, 32, PropModeReplace, 1, &val.integer, FALSE, FALSE); if (err != 0) xf86DrvMsg(rhdPtr->scrnIndex, X_ERROR, "In %s RRChangeOutputProperty error: %d\n", __func__, err); } } if (rout->Output->Property(rout->Output, rhdPropertyCheck, RHD_OUTPUT_COHERENT, NULL)) { atom_Coherent = MakeAtom(ATOM_COHERENT, sizeof(ATOM_COHERENT)-1, TRUE); range[0] = 0; range[1] = 1; err = RRConfigureOutputProperty(out->randr_output, atom_Coherent, FALSE, TRUE, FALSE, 2, range); if (err != 0) xf86DrvMsg(rhdPtr->scrnIndex, X_ERROR, "RRConfigureOutputProperty error: %d\n", err); else { union rhdPropertyData val; if (!rout->Output->Property(rout->Output, rhdPropertyGet, RHD_OUTPUT_COHERENT, &val)) val.Bool = 1; err = RRChangeOutputProperty(out->randr_output, atom_Coherent, XA_INTEGER, 32, PropModeReplace, 1, &val.Bool, FALSE, FALSE); if (err != 0) xf86DrvMsg(rhdPtr->scrnIndex, X_ERROR, "In %s RRChangeOutputProperty error: %d\n", __func__, err); } } if (rout->Output->Property(rout->Output, rhdPropertyCheck, RHD_OUTPUT_HDMI, NULL)) { atom_HdmiProperty = MakeAtom(ATOM_HDMI, sizeof(ATOM_HDMI)-1, TRUE); range[0] = 0; range[1] = 1; err = RRConfigureOutputProperty(out->randr_output, atom_HdmiProperty, FALSE, TRUE, FALSE, 2, range); if (err != 0) xf86DrvMsg(rhdPtr->scrnIndex, X_ERROR, "RRConfigureOutputProperty error: %d\n", err); else { union rhdPropertyData val; if (!rout->Output->Property(rout->Output, rhdPropertyGet, RHD_OUTPUT_HDMI, &val)) val.Bool = 1; err = RRChangeOutputProperty(out->randr_output, atom_HdmiProperty, XA_INTEGER, 32, PropModeReplace, 1, &val.Bool, FALSE, FALSE); if (err != 0) xf86DrvMsg(rhdPtr->scrnIndex, X_ERROR, "In %s RRChangeOutputProperty error: %d\n", __func__, err); } } if (rout->Output->Property(rout->Output, rhdPropertyCheck, RHD_OUTPUT_AUDIO_WORKAROUND, NULL)) { atom_AudioWorkaround = MakeAtom(ATOM_AUDIO_WORKAROUND, sizeof(ATOM_AUDIO_WORKAROUND)-1, TRUE); range[0] = 0; range[1] = 1; err = RRConfigureOutputProperty(out->randr_output, atom_AudioWorkaround, FALSE, TRUE, FALSE, 2, range); if (err != 0) xf86DrvMsg(rhdPtr->scrnIndex, X_ERROR, "RRConfigureOutputProperty error: %d\n", err); else { union rhdPropertyData val; if (!rout->Output->Property(rout->Output, rhdPropertyGet, RHD_OUTPUT_AUDIO_WORKAROUND, &val)) val.Bool = 1; err = RRChangeOutputProperty(out->randr_output, atom_AudioWorkaround, XA_INTEGER, 32, PropModeReplace, 1, &val.Bool, FALSE, FALSE); if (err != 0) xf86DrvMsg(rhdPtr->scrnIndex, X_ERROR, "In %s RRChangeOutputProperty error: %d\n", __func__, err); } } } } /* Turns the output on/off, or sets intermediate power levels if available. */ /* Something that cannot be solved with the current RandR model is that * multiple connectors might be attached to the same output. They cannot be * driven with different crtcs then, but RandR sees them as different outputs * because of a missing abstraction layer. This implementation will silently * set one crtc or the other, and maybe even won't warn. */ static void rhdRROutputDpms(xf86OutputPtr out, int mode) { RHDPtr rhdPtr = RHDPTR(out->scrn); rhdRandrOutputPtr rout = (rhdRandrOutputPtr) out->driver_private; xf86OutputPtr *ro; struct rhdCrtc *rhdCrtc = out->crtc ? ((struct rhdRandrCrtc *)(out->crtc->driver_private))->rhdCrtc : NULL; const char *outUsedBy = NULL; RHDDebug(rhdPtr->scrnIndex, "%s: Output %s : %s\n", __func__, rout->Name, mode==DPMSModeOn ? "On" : mode==DPMSModeOff ? "Off" : "Other"); for (ro = rhdPtr->randr->RandrOutput; *ro; ro++) { rhdRandrOutputPtr o = (rhdRandrOutputPtr) (*ro)->driver_private; if (o != rout && o->Output == rout->Output && (*ro)->crtc) outUsedBy = (*ro)->name; } switch (mode) { case DPMSModeOn: rout->Output->Power(rout->Output, RHD_POWER_ON); rout->Output->Active = TRUE; ASSERT(rhdCrtc); ASSERT(rhdCrtc == rout->Output->Crtc); rout->Crtc = rhdCrtc; break; case DPMSModeSuspend: case DPMSModeStandby: if (outUsedBy) { xf86DrvMsg(rhdPtr->scrnIndex, X_WARNING, "RandR: While resetting %s: output %s is also used " "by %s - ignoring\n", out->name, rout->Output->Name, outUsedBy); break; } rout->Output->Power(rout->Output, RHD_POWER_RESET); rout->Output->Active = FALSE; rout->Crtc = NULL; break; case DPMSModeOff: if (outUsedBy) { xf86DrvMsg(rhdPtr->scrnIndex, X_WARNING, "RandR: While switching off %s: output %s is also used " "by %s - ignoring\n", out->name, rout->Output->Name, outUsedBy); break; } rout->Output->Power(rout->Output, RHD_POWER_SHUTDOWN); rout->Output->Active = FALSE; rout->Crtc = NULL; break; default: ASSERT(!"Unknown DPMS mode"); } RHDDebugRandrState(rhdPtr, "POST-OutputDpms"); } /* RandR has a weird sense of how validation and fixup should be handled: * - Fixup and validation can interfere, they would have to be looped * - Validation should be done after fixup * - There is no crtc validation AT ALL * - The necessity of adjusted_mode is questionable * - Outputs and crtcs are checked independently, and one at a time. * This can interfere, the driver should know the complete setup for * validation and fixup. * We also cannot emulate, because we never know when validation is finished. * Therefore we only use a single function for all and have to work around * not knowing what the other crtcs might be needed for. */ static int rhdRROutputModeValid(xf86OutputPtr out, DisplayModePtr OrigMode) { RHDPtr rhdPtr = RHDPTR(out->scrn); rhdRandrOutputPtr rout = (rhdRandrOutputPtr) out->driver_private; DisplayModePtr Mode = xf86DuplicateMode(OrigMode); int Status; RHDFUNC(rhdPtr); /* RandR may give us a mode without a name... (xf86RandRModeConvert) * xf86DuplicateMode should fill it up, though */ if (!Mode->name) Mode->name = xstrdup("n/a"); RHDDebug(rhdPtr->scrnIndex, "%s: Output %s : %s\n", __func__, rout->Name, Mode->name); if (rhdPtr->verbosity >= LOG_DEBUG) RHDPrintModeline(Mode); ASSERT(rout->Connector); ASSERT(rout->Output); /* If out->crtc is not NULL, it is not necessarily the Crtc that will * be used, so let's better skip crtc based checks... */ /* Monitor is handled by RandR */ if (!rout->Output->Connector) return MODE_OUTPUT_UNDEF; Status = RHDRRModeFixup(out->scrn, Mode, NULL, rout->Connector, rout->Output, NULL, rout->ScaledToMode ? TRUE : FALSE); RHDDebug(rhdPtr->scrnIndex, "%s: %s: %s\n", __func__, Mode->name, RHDModeStatusToString(Status)); xfree(Mode->name); xfree(Mode); return Status; } static void rhdRRModeCopy(DisplayModePtr OrigMode, DisplayModePtr Mode) { memset(Mode, 0, sizeof(DisplayModeRec)); Mode->name = xstrdup(OrigMode->name ? OrigMode->name : "n/a"); Mode->status = OrigMode->status; Mode->type = OrigMode->type; Mode->Clock = OrigMode->Clock; Mode->HDisplay = OrigMode->HDisplay; Mode->HSyncStart = OrigMode->HSyncStart; Mode->HSyncEnd = OrigMode->HSyncEnd; Mode->HTotal = OrigMode->HTotal; Mode->HSkew = OrigMode->HSkew; Mode->VDisplay = OrigMode->VDisplay; Mode->VSyncStart = OrigMode->VSyncStart; Mode->VSyncEnd = OrigMode->VSyncEnd; Mode->VTotal = OrigMode->VTotal; Mode->VScan = OrigMode->VScan; Mode->Flags = OrigMode->Flags; if ((Mode->type & M_T_CRTC_C) == M_T_BUILTIN) { Mode->CrtcHDisplay = OrigMode->CrtcHDisplay; Mode->CrtcHBlankStart = OrigMode->CrtcHBlankStart; Mode->CrtcHSyncStart = OrigMode->CrtcHSyncStart; Mode->CrtcHBlankEnd = OrigMode->CrtcHBlankEnd; Mode->CrtcHSyncEnd = OrigMode->CrtcHSyncEnd; Mode->CrtcHTotal = OrigMode->CrtcHTotal; Mode->CrtcVDisplay = OrigMode->CrtcVDisplay; Mode->CrtcVBlankStart = OrigMode->CrtcVBlankStart; Mode->CrtcVSyncStart = OrigMode->CrtcVSyncStart; Mode->CrtcVSyncEnd = OrigMode->CrtcVSyncEnd; Mode->CrtcVBlankEnd = OrigMode->CrtcVBlankEnd; Mode->CrtcVTotal = OrigMode->CrtcVTotal; } } /* * */ static void rhdRRSanitizeMode(DisplayModePtr Mode) { if (!Mode->name) Mode->name = xstrdup("n/a"); Mode->status = MODE_OK; if ((Mode->type & M_T_CRTC_C) != M_T_BUILTIN) { Mode->CrtcHDisplay = Mode->CrtcHBlankStart = Mode->CrtcHSyncStart = Mode->CrtcHBlankEnd = Mode->CrtcHSyncEnd = Mode->CrtcHTotal = 0; Mode->CrtcVDisplay = Mode->CrtcVBlankStart = Mode->CrtcVSyncStart = Mode->CrtcVSyncEnd = Mode->CrtcVBlankEnd = Mode->CrtcVTotal = 0; Mode->SynthClock = 0; } } /* * */ static void rhdRRFreeOutputs( RHDPtr rhdPtr) { xf86OutputPtr *ro; struct rhdOutput *Output; /* * modesUpdated indicates if we are entering here for a first time after a * OutputsModeValid(). In this case a new mode setting round starts, thus * we need to free all outputs so that all inactive outputs are freed. * We later on allocate each output. */ for (Output = rhdPtr->Outputs; Output; Output = Output->Next) { if (Output->AllocFree) { for (ro = rhdPtr->randr->RandrOutput; *ro; ro++) { rhdRandrOutputPtr o = (rhdRandrOutputPtr) (*ro)->driver_private; if (o->Output == Output) { if (!(*ro)->crtc) { Output->AllocFree(Output, RHD_OUTPUT_FREE); RHDDebug(rhdPtr->scrnIndex, "%s: Freeing Output: %s\n",__func__, Output->Name); } } } } } } /* The crtc is only known on fixup time. Now it's actually to late to reject a * mode and give a reasonable answer why (return is bool), but we'll better not * set a mode than scrap our hardware */ static Bool rhdRROutputModeFixup(xf86OutputPtr out, DisplayModePtr OrigMode, DisplayModePtr Mode) { RHDPtr rhdPtr = RHDPTR(out->scrn); rhdRandrOutputPtr rout = (rhdRandrOutputPtr) out->driver_private; struct rhdCrtc *rhdCrtc = NULL; int Status; DisplayModePtr DisplayedMode; Bool Scaled = FALSE; RHDFUNC(rhdPtr); ASSERT(out->crtc); rhdCrtc = ((struct rhdRandrCrtc *)(out->crtc->driver_private))->rhdCrtc; rhdRRFreeOutputs(rhdPtr); xfree(Mode->name); if (rout->ScaledToMode) { DisplayModePtr tmp = RHDModeCopy(rout->ScaledToMode); /* validate against CRTC. */ if ((Status = RHDValidateScaledToMode(rhdCrtc, tmp))!= MODE_OK) { RHDDebug(rhdPtr->scrnIndex, "%s: %s ScaledToMode INVALID: [0x%x] %s\n", __func__, tmp->name, Status, RHDModeStatusToString(Status)); xfree(tmp); return FALSE; /* failing here doesn't help */ } memcpy(Mode, tmp, sizeof(DisplayModeRec)); Mode->name = xstrdup(tmp->name); Mode->prev = Mode->next = NULL; xfree(tmp->name); xfree(tmp); /* sanitize OrigMode */ rhdRRSanitizeMode(OrigMode); DisplayedMode = OrigMode; rhdCrtc->ScaledToMode = Mode; Scaled = TRUE; } else { /* !@#$ xf86RandRModeConvert doesn't initialize Mode with 0 * Fixed in xserver git c6c284e6 */ rhdRRModeCopy(OrigMode, Mode); DisplayedMode = Mode; } /* RHDRRModeFixup will set up the remaining bits */ RHDDebug(rhdPtr->scrnIndex, "%s: Output %s : %s\n", __func__, rout->Name, Mode->name); ASSERT(rout->Connector); ASSERT(rout->Output); setupCrtc(rhdPtr, rhdCrtc); /* Monitor is handled by RandR */ if (!rout->Output->AllocFree || rout->Output->AllocFree(rout->Output, RHD_OUTPUT_ALLOC)) { Status = RHDRRModeFixup(out->scrn, DisplayedMode, rhdCrtc, rout->Connector, rout->Output, NULL, Scaled); } else Status = MODE_NO_ENCODER; if (Status != MODE_OK) { rout->OutputActive = FALSE; RHDDebug(rhdPtr->scrnIndex, "%s: %s FAILED: [0x%x] %s\n", __func__, Mode->name, Status, RHDModeStatusToString(Status)); return FALSE; } rout->OutputActive = TRUE; return TRUE; } /* Set output mode. * Again, randr calls for Crtc and Output separately, while order should be * up to the driver. There wouldn't be a need for prepare/set/commit then. * We cannot do everything in OutputModeSet, because the viewport isn't known * here. */ static void rhdRROutputPrepare(xf86OutputPtr out) { RHDPtr rhdPtr = RHDPTR(out->scrn); ScrnInfoPtr pScrn = xf86Screens[rhdPtr->scrnIndex]; rhdRandrOutputPtr rout = (rhdRandrOutputPtr) out->driver_private; RHDFUNC(rhdPtr); pScrn->vtSema = TRUE; /* no active output == no mess */ rout->Output->Power(rout->Output, RHD_POWER_RESET); rout->Output->Crtc = rout->Crtc = NULL; } static void rhdRROutputModeSet(xf86OutputPtr out, DisplayModePtr OrigMode, DisplayModePtr Mode) { RHDPtr rhdPtr = RHDPTR(out->scrn); rhdRandrOutputPtr rout = (rhdRandrOutputPtr) out->driver_private; struct rhdCrtc *rhdCrtc = ((struct rhdRandrCrtc *)(out->crtc->driver_private))->rhdCrtc; RHDFUNC(rhdPtr); /* RandR may give us a mode without a name... (xf86RandRModeConvert) */ if (!Mode->name && out->crtc->mode.name) Mode->name = xstrdup(out->crtc->mode.name); RHDDebug(rhdPtr->scrnIndex, "%s: Output %s : %s to %s\n", __func__, rout->Name, Mode->name, rhdCrtc->Name); /* RandR might want to set up several outputs (RandR speech) with different * crtcs, while the outputs differ in fact only by the connector and thus * cannot be used by different crtcs */ if (rout->Crtc && rout->Crtc != rhdCrtc) xf86DrvMsg(rhdPtr->scrnIndex, X_ERROR, "RandR: Output %s has already CRTC attached - " "assuming ouput/connector clash\n", rout->Name); /* Set up mode */ rout->Crtc = rhdCrtc; ASSERT(rhdCrtc == rout->Output->Crtc); rout->Output->Mode(rout->Output, Mode); } static void rhdRROutputCommit(xf86OutputPtr out) { RHDPtr rhdPtr = RHDPTR(out->scrn); rhdRandrOutputPtr rout = (rhdRandrOutputPtr) out->driver_private; CARD32 val; char buf[32]; struct rhdCrtc *rhdCrtc = ((struct rhdRandrCrtc *)(out->crtc->driver_private))->rhdCrtc; RHDFUNC(rhdPtr); ASSERT(rhdCrtc == rout->Output->Crtc); rout->Output->Active = TRUE; RHDOutputAttachConnector(rout->Output, rout->Connector); /* @@@ */ rout->Output->Power(rout->Output, RHD_POWER_ON); /* Some outputs may have physical protocol changes (e.g. TV) */ val = rhdGetSignalFormat(rout); RRChangeOutputProperty(out->randr_output, atom_SignalFormat, XA_ATOM, 32, PropModeReplace, 1, &val, TRUE, FALSE); /* Should be a crtc property */ if (rhdCrtc->MaxX > rhdCrtc->MinX && rhdCrtc->MaxY > rhdCrtc->MinY) sprintf(buf, "%dx%d+%d+%d", rhdCrtc->MaxX - rhdCrtc->MinX, rhdCrtc->MaxY - rhdCrtc->MinY, rhdCrtc->MinX, rhdCrtc->MinY); else buf[0] = 0; RRChangeOutputProperty(out->randr_output, atom_PanningArea, XA_STRING, 8, PropModeReplace, strlen(buf), buf, TRUE, FALSE); RHDDebugRandrState(rhdPtr, rout->Name); } /* * This function looks for other outputs on the connector rout is connected to. * If one of those outputs can be sensed and is sensed the function will return * one of those. */ static rhdRandrOutputPtr rhdRROtherOutputOnConnectorHelper(RHDPtr rhdPtr, rhdRandrOutputPtr rout) { xf86OutputPtr *ro; for (ro = rhdPtr->randr->RandrOutput; *ro; ro++) { rhdRandrOutputPtr o = (rhdRandrOutputPtr) (*ro)->driver_private; if (o != rout && o->Connector == rout->Connector && o->Output->Sense) { /* Yes, this looks wrong, but is correct */ enum rhdSensedOutput SensedType = o->Output->Sense(o->Output, o->Connector); if (SensedType != RHD_SENSED_NONE) { RHDOutputPrintSensedType(o->Output); return o; } } } return NULL; } /* Probe for a connected output. */ static xf86OutputStatus rhdRROutputDetect(xf86OutputPtr output) { RHDPtr rhdPtr = RHDPTR(output->scrn); rhdRandrOutputPtr rout = (rhdRandrOutputPtr) output->driver_private; RHDDebug(rhdPtr->scrnIndex, "%s: Output %s\n", __func__, rout->Name); /* Assume that a panel is always connected */ if (rout->Connector->Type == RHD_CONNECTOR_PANEL) { RHDOutputAttachConnector(rout->Output, rout->Connector); /* @@@ */ return XF86OutputStatusConnected; } else if (rout->Connector->Type == RHD_CONNECTOR_TV) /* until TV_OUT is fixed we bail here */ return XF86OutputStatusDisconnected; if (rout->Connector->HPDCheck) { /* Hot Plug Detection available, use it */ if (rout->Connector->HPDCheck(rout->Connector)) { /* * HPD returned true */ if (rout->Output->Sense) { if ((rout->Output->SensedType = rout->Output->Sense(rout->Output, rout->Connector)) != RHD_SENSED_NONE) { RHDOutputPrintSensedType(rout->Output); RHDOutputAttachConnector(rout->Output, rout->Connector); /* @@@ */ return XF86OutputStatusConnected; } else return XF86OutputStatusDisconnected; } else { /* HPD returned true, but no Sense() available * Typically the case on TMDSB. * Check if there is another output attached to this connector * and use Sense() on that one to verify whether something * is attached to this one */ if (rhdRROtherOutputOnConnectorHelper(rhdPtr, rout)) return XF86OutputStatusDisconnected; RHDOutputAttachConnector(rout->Output, rout->Connector); /* @@@ */ return XF86OutputStatusConnected; } } else { /* * HPD returned false */ /* There is the infamous DMS-59 connector, on which HPD returns * false when 'only' VGA is connected. */ if (rhdPtr->Card && (rhdPtr->Card->flags & RHD_CARD_FLAG_DMS59)) { xf86DrvMsg(rhdPtr->scrnIndex, X_INFO, "RandR: Verifying state of DMS-59 VGA connector.\n"); if (rout->Output->Sense) { rout->Output->SensedType = rout->Output->Sense(rout->Output, rout->Connector); if (rout->Output->SensedType != RHD_SENSED_NONE) { RHDOutputPrintSensedType(rout->Output); RHDOutputAttachConnector(rout->Output, rout->Connector); /* @@@ */ return XF86OutputStatusConnected; } } } return XF86OutputStatusDisconnected; } } else { /* * No HPD available, Sense() if possible */ if (rout->Output->Sense) { rout->Output->SensedType = rout->Output->Sense(rout->Output, rout->Connector); if (rout->Output->SensedType != RHD_SENSED_NONE) { RHDOutputPrintSensedType(rout->Output); RHDOutputAttachConnector(rout->Output, rout->Connector); /* @@@ */ return XF86OutputStatusConnected; } else return XF86OutputStatusDisconnected; } /* Use DDC address probing if possible otherwise */ if (rout->Connector->DDC) { RHDI2CDataArg i2cRec; i2cRec.probe.slave = 0xa0; i2cRec.probe.i2cBusPtr = rout->Connector->DDC; if (RHDI2CFunc(rhdPtr->scrnIndex, rhdPtr->I2C,RHD_I2C_PROBE_ADDR,&i2cRec) == RHD_I2C_SUCCESS) { rhdRandrOutputPtr rout_tmp; RHDDebug(rout->Output->scrnIndex, "DDC Probing for Output %s returned connected\n", rout->Output->Name); if ((rout_tmp = rhdRROtherOutputOnConnectorHelper(rhdPtr, rout))) { RHDDebug(rout->Output->scrnIndex, "Output %s on same connector already connected\n", rout_tmp->Output->Name); return XF86OutputStatusDisconnected; } RHDOutputAttachConnector(rout->Output, rout->Connector); /* @@@ */ return XF86OutputStatusConnected; } else { RHDDebug(rout->Output->scrnIndex, "DDC Probing for Output %s returned disconnected\n", rout->Output->Name); return XF86OutputStatusDisconnected; } } RHDOutputAttachConnector(rout->Output, rout->Connector); /* @@@ */ return XF86OutputStatusUnknown; } } /* * RHDRRMonitorInit(): adds synthesized modes for scaling to list generated by RHDMonitorInit() * as with RandR this is the only chance we have access to the full modes list. */ static struct rhdMonitor * RHDRRMonitorInit(struct rhdConnector *Connector) { struct rhdMonitor *m = RHDMonitorInit(Connector); RHDFUNC(Connector); if (RHDScalePolicy(m, Connector)) RHDSynthModes(Connector->scrnIndex, m->Modes); return m; } /* Query the device for the modes it provides. Set MonInfo, mm_width/height. */ static DisplayModePtr rhdRROutputGetModes(xf86OutputPtr output) { RHDPtr rhdPtr = RHDPTR(output->scrn); rhdRandrOutputPtr rout = (rhdRandrOutputPtr) output->driver_private; struct rhdOutput *o; RHDDebug(rhdPtr->scrnIndex, "%s: Output %s\n", __func__, rout->Name); /* TODO: per-output options ForceReduced & UseXF86Edid */ /* Nuke old monitor */ if (rout->Connector->Monitor) { /* EDID is already freed by RandR (OutputSetEDID+return) */ rout->Connector->Monitor->EDID = NULL; RHDMonitorDestroy(rout->Connector->Monitor); } /* Get new one */ if (! (rout->Connector->Monitor = RHDRRMonitorInit(rout->Connector)) ) { xf86OutputSetEDID (output, NULL); return NULL; } ASSERT(rout->Output); o = rout->Output; if (RHDScalePolicy(rout->Connector->Monitor, rout->Connector)) { if (o->Connector->Monitor) { int Status; rout->ScaledToMode = RHDModeCopy(o->Connector->Monitor->NativeMode); xf86DrvMsg(rhdPtr->scrnIndex, X_INFO, "Found native mode: "); RHDPrintModeline(rout->ScaledToMode); if ((Status = RHDRRValidateScaledToMode(rout->Output, rout->ScaledToMode)) != MODE_OK) { xf86DrvMsg(rhdPtr->scrnIndex, X_ERROR, "Native mode doesn't validate [%s]: deleting\n", RHDModeStatusToString(Status)); xfree(rout->ScaledToMode->name); xfree(rout->ScaledToMode); rout->ScaledToMode = NULL; } } } else rout->ScaledToMode = NULL; /* If digitally attached, enable reduced blanking */ if (rout->Output->Id == RHD_OUTPUT_TMDSA || rout->Output->Id == RHD_OUTPUT_LVTMA || rout->Output->Id == RHD_OUTPUT_KLDSKP_LVTMA || rout->Output->Id == RHD_OUTPUT_UNIPHYA || rout->Output->Id == RHD_OUTPUT_UNIPHYB || rout->Output->Id == RHD_OUTPUT_UNIPHYC || rout->Output->Id == RHD_OUTPUT_UNIPHYD || rout->Output->Id == RHD_OUTPUT_UNIPHYE || rout->Output->Id == RHD_OUTPUT_UNIPHYF ) rout->Connector->Monitor->ReducedAllowed = TRUE; /* Allow user overrides */ if (rhdPtr->forceReduced.set) rout->Connector->Monitor->ReducedAllowed = rhdPtr->forceReduced.val.bool; xf86OutputSetEDID (output, rout->Connector->Monitor->EDID); /* If no EDID data is available, calculate mm_size by assuming 96dpi * for the preferred (or first if no preferred) mode */ if (! rout->Connector->Monitor->EDID) { DisplayModePtr m; for (m = rout->Connector->Monitor->Modes; m; m = m->next) if (m->type & M_T_PREFERRED) break; if (! m) m = rout->Connector->Monitor->Modes; if (m) { #define DEFAULT_MM_PER_DOT (25.4 / 96) output->mm_width = m->HDisplay * DEFAULT_MM_PER_DOT; output->mm_height = m->VDisplay * DEFAULT_MM_PER_DOT; xf86DrvMsg(rhdPtr->scrnIndex, X_WARNING, "No monitor size info, assuming 96dpi.\n"); } } RHDDebug(rhdPtr->scrnIndex, "%s: Adding Output Modes:\n",__func__); if (rhdPtr->verbosity >= 7) { DisplayModePtr mode = rout->Connector->Monitor->Modes; while (mode) { RHDPrintModeline(mode); mode = mode->next; } } return xf86DuplicateModes(output->scrn, rout->Connector->Monitor->Modes); } /* An output's property has changed. */ static Bool rhdRROutputSetProperty(xf86OutputPtr out, Atom property, RRPropertyValuePtr value) { RHDPtr rhdPtr = RHDPTR(out->scrn); rhdRandrOutputPtr rout = (rhdRandrOutputPtr) out->driver_private; char buf[256]; RHDFUNC(rhdPtr); /* Unfortunately, strings are not necessarily 0 terminated */ if (value->type == XA_STRING && value->format == 8) { int len = value->size < 255 ? value->size : 255; memcpy (buf, value->data, len); buf[len] = 0; } if (property == atom_PanningArea) { int w = 0, h = 0, x = 0, y = 0; struct rhdCrtc *Crtc = rout->Output->Crtc; int i; if (!Crtc) return FALSE; for (i = 0; i < 2; i++) { xf86CrtcPtr crtc = (xf86CrtcPtr) rhdPtr->randr->RandrCrtc[i]; if (Crtc == ((struct rhdRandrCrtc *)crtc->driver_private)->rhdCrtc) { /* Don't allow panning while rotated */ if (crtc->rotation != RR_Rotate_0) return FALSE; else break; } } if (value->type != XA_STRING || value->format != 8) return FALSE; switch (sscanf(buf, "%dx%d+%d+%d", &w, &h, &x, &y)) { case 0: case 2: case 4: if (w < 0 || h < 0 || x < 0 || y < 0 || x+w > Crtc->Width || y+h > Crtc->Height) return FALSE; Crtc->MinX = x; Crtc->MinY = y; Crtc->MaxX = x + w; Crtc->MaxY = y + h; rhdUpdateCrtcPos(rhdPtr, Crtc, Crtc->Cursor->X, Crtc->Cursor->Y); RHDDebug(rhdPtr->scrnIndex, "%s: PanningArea %d/%d - %d/%d\n", x, y, x+w, y+h); return TRUE; default: return FALSE; } } else if (property == atom_Backlight) { if (value->type != XA_INTEGER || value->format != 32) { xf86DrvMsg(rhdPtr->scrnIndex, X_ERROR, "%s: wrong value\n", __func__); return FALSE; } if (rout->Output->Property) { union rhdPropertyData val; val.integer = *(int*)(value->data); if(rout->Output->Property(rout->Output, rhdPropertySet, RHD_OUTPUT_BACKLIGHT, &val)) return rout->Output->Property(rout->Output, rhdPropertyCommit, RHD_OUTPUT_BACKLIGHT, NULL); } return FALSE; } else if (property == atom_Coherent) { if (value->type != XA_INTEGER || value->format != 32) { xf86DrvMsg(rhdPtr->scrnIndex, X_ERROR, "%s: wrong value\n", __func__); return FALSE; } if (rout->Output->Property) { union rhdPropertyData val; val.Bool = *(int*)(value->data); if(rout->Output->Property(rout->Output, rhdPropertySet, RHD_OUTPUT_COHERENT, &val)) return rout->Output->Property(rout->Output, rhdPropertyCommit, RHD_OUTPUT_COHERENT, NULL); } return FALSE; } else if (property == atom_HdmiProperty) { if (value->type != XA_INTEGER || value->format != 32) { xf86DrvMsg(rhdPtr->scrnIndex, X_ERROR, "%s: wrong value\n", __func__); return FALSE; } if (rout->Output->Property) { union rhdPropertyData val; val.Bool = *(int*)(value->data); if(rout->Output->Property(rout->Output, rhdPropertySet, RHD_OUTPUT_HDMI, &val)) return rout->Output->Property(rout->Output, rhdPropertyCommit, RHD_OUTPUT_HDMI, NULL); } return FALSE; } else if (property == atom_AudioWorkaround) { if (value->type != XA_INTEGER || value->format != 32) { xf86DrvMsg(rhdPtr->scrnIndex, X_ERROR, "%s: wrong value\n", __func__); return FALSE; } if (rout->Output->Property) { union rhdPropertyData val; val.Bool = *(int*)(value->data); if(rout->Output->Property(rout->Output, rhdPropertySet, RHD_OUTPUT_AUDIO_WORKAROUND, &val)) return rout->Output->Property(rout->Output, rhdPropertyCommit, RHD_OUTPUT_AUDIO_WORKAROUND, NULL); } return FALSE; #if ENABLE_PROPERTY_ATOMBIOS } else if (property == atom_AtomBIOS) { if (value->type != XA_STRING || value->format != 8) return FALSE; if (rhdUpdateAtomBIOSUsage(rhdPtr, buf)) { free (value->data); value->data = rhdReturnAtomBIOSUsage(rhdPtr); value->size = strlen(value->data); return TRUE; } return FALSE; #endif } else if (property == atom_EDID || property == atom_EDID2) { /* Don't do anything, but allow change */ return TRUE; } return FALSE; /* Others are not mutable */ } /* * */ static void * rhdRRCrtcShadowAllocate(xf86CrtcPtr crtc, int Width, int Height) { ScrnInfoPtr pScrn = crtc->scrn; RHDPtr rhdPtr = RHDPTR(crtc->scrn); ScreenPtr pScreen = screenInfo.screens[pScrn->scrnIndex]; struct rhdRandrCrtc *rhdRRCrtc = (struct rhdRandrCrtc*) crtc->driver_private; int OctPerPixel = pScrn->bitsPerPixel >> 3; int Size = (pScrn->displayWidth * OctPerPixel) * Height; if (rhdPtr->AccelMethod == RHD_ACCEL_SHADOWFB || rhdPtr->AccelMethod == RHD_ACCEL_NONE) return NULL; #ifdef USE_EXA if (rhdPtr->AccelMethod == RHD_ACCEL_EXA) { ASSERT(rhdRRCrtc->u.MemEXA == NULL); rhdRRCrtc->u.MemEXA = exaOffscreenAlloc(pScreen, Size, 4096, TRUE, NULL, NULL); if (rhdRRCrtc->u.MemEXA == NULL) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Unable to allocate shadow memory for rotated CRTC\n"); return NULL; } return ((char *)rhdPtr->FbBase + rhdRRCrtc->u.MemEXA->offset); } #endif /* USE_EXA */ if (rhdPtr->AccelMethod == RHD_ACCEL_XAA) { int Align = (4096 + OctPerPixel - 1) / OctPerPixel; Size = (Size + OctPerPixel - 1) / OctPerPixel; ASSERT(rhdRRCrtc->u.MemXAA == NULL); rhdRRCrtc->u.MemXAA = xf86AllocateOffscreenLinear(pScreen, Size, Align, /* @@@ */ NULL, NULL, NULL); if (rhdRRCrtc->u.MemXAA == NULL) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Unable to allocate shadow memory for rotated CRTC\n"); return NULL; } return ((char *)rhdPtr->FbBase + rhdPtr->FbScanoutStart + rhdRRCrtc->u.MemXAA->offset * OctPerPixel); } return NULL; } /* * */ static PixmapPtr rhdRRCrtcShadowCreate(xf86CrtcPtr Crtc, void *Data, int Width, int Height) { ScrnInfoPtr pScrn = Crtc->scrn; PixmapPtr RPixmap; if (!Data) Data = rhdRRCrtcShadowAllocate(Crtc, Width, Height); RPixmap = GetScratchPixmapHeader(pScrn->pScreen, Width, Height, pScrn->depth, pScrn->bitsPerPixel, pScrn->displayWidth * pScrn->bitsPerPixel >> 3, Data); if (RPixmap == NULL) xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Unable to allocate shadow pixmap for rotated CRTC\n"); return RPixmap; } /* * */ static void rhdRRCrtcShadowDestroy(xf86CrtcPtr crtc, PixmapPtr RPixmap, void *Data) { ScrnInfoPtr pScrn = crtc->scrn; RHDPtr rhdPtr = RHDPTR(crtc->scrn); struct rhdRandrCrtc *rhdRRCrtc = (struct rhdRandrCrtc*) crtc->driver_private; if (RPixmap) FreeScratchPixmapHeader(RPixmap); if (Data) { #ifdef USE_EXA if (rhdPtr->AccelMethod == RHD_ACCEL_EXA) { exaOffscreenFree(pScrn->pScreen, rhdRRCrtc->u.MemEXA); rhdRRCrtc->u.MemEXA = NULL; } #endif /* USE_EXA */ if (rhdPtr->AccelMethod == RHD_ACCEL_XAA) { xf86FreeOffscreenLinear(rhdRRCrtc->u.MemXAA); rhdRRCrtc->u.MemXAA = NULL; } } } #ifdef RANDR_13_INTERFACE static Bool rhdRROutputGetProperty(xf86OutputPtr out, Atom property) { RHDPtr rhdPtr = RHDPTR(out->scrn); rhdRandrOutputPtr rout = (rhdRandrOutputPtr) out->driver_private; int err = BadValue; union rhdPropertyData val; RHDFUNC(rhdPtr); if (property == atom_Backlight) { if (rout->Output->Property == NULL) return FALSE; if (!rout->Output->Property(rout->Output, rhdPropertyGet, RHD_OUTPUT_BACKLIGHT, &val)) return FALSE; err = RRChangeOutputProperty(out->randr_output, atom_Backlight, XA_INTEGER, 32, PropModeReplace, 1, &val.integer, FALSE, FALSE); } else if (property == atom_Coherent) { if (rout->Output->Property == NULL) return FALSE; if (!rout->Output->Property(rout->Output, rhdPropertyGet, RHD_OUTPUT_COHERENT, &val)) return FALSE; err = RRChangeOutputProperty(out->randr_output, atom_Coherent, XA_INTEGER, 32, PropModeReplace, 1, &val.Bool, FALSE, FALSE); } else if (property == atom_HdmiProperty) { if (rout->Output->Property == NULL) return FALSE; if (!rout->Output->Property(rout->Output, rhdPropertyGet, RHD_OUTPUT_HDMI, &val)) return FALSE; err = RRChangeOutputProperty(out->randr_output, atom_HdmiProperty, XA_INTEGER, 32, PropModeReplace, 1, &val.Bool, FALSE, FALSE); } else if (property == atom_AudioWorkaround) { if (rout->Output->Property == NULL) return FALSE; if (!rout->Output->Property(rout->Output, rhdPropertyGet, RHD_OUTPUT_AUDIO_WORKAROUND, &val)) return FALSE; err = RRChangeOutputProperty(out->randr_output, atom_AudioWorkaround, XA_INTEGER, 32, PropModeReplace, 1, &val.Bool, FALSE, FALSE); } RHDDebug(rhdPtr->scrnIndex, "%s 0x%x returns %d\n", __func__, property, err); if (err != 0) return FALSE; return TRUE; } #endif /* * */ Bool RHDRRInitCursor(ScreenPtr pScreen) { RHDFUNCI(pScreen->myNum); /* still need to alloc fb mem for cursors */ return xf86_cursors_init(pScreen, MAX_CURSOR_WIDTH, MAX_CURSOR_HEIGHT, HARDWARE_CURSOR_TRUECOLOR_AT_8BPP | HARDWARE_CURSOR_UPDATE_UNHIDDEN | HARDWARE_CURSOR_AND_SOURCE_WITH_MASK | HARDWARE_CURSOR_SOURCE_MASK_INTERLEAVE_1 | HARDWARE_CURSOR_ARGB); } /* * */ static void rhdRRShowCursor(xf86CrtcPtr crtc) { struct rhdCrtc *rhdCrtc = ((struct rhdRandrCrtc *)crtc->driver_private)->rhdCrtc; rhdCrtcShowCursor(rhdCrtc); } /* * */ static void rhdRRHideCursor(xf86CrtcPtr crtc) { struct rhdCrtc *rhdCrtc = ((struct rhdRandrCrtc *)crtc->driver_private)->rhdCrtc; rhdCrtcHideCursor(rhdCrtc); } /* * */ static void rhdRRLoadCursorARGB(xf86CrtcPtr crtc, CARD32 *Image) { struct rhdCrtc *rhdCrtc = ((struct rhdRandrCrtc *)crtc->driver_private)->rhdCrtc; rhdCrtcLoadCursorARGB(rhdCrtc, Image); } /* * */ static void rhdRRSetCursorColors(xf86CrtcPtr crtc, int bg, int fg) { struct rhdCrtc *rhdCrtc = ((struct rhdRandrCrtc *)crtc->driver_private)->rhdCrtc; rhdCrtcSetCursorColors(rhdCrtc, bg, fg); } /* * */ static void rhdRRSetCursorPosition(xf86CrtcPtr crtc, int x, int y) { struct rhdCrtc *rhdCrtc = ((struct rhdRandrCrtc *)crtc->driver_private)->rhdCrtc; /* * Given cursor pos is always relative to frame - make absolute * NOTE: This is hardware specific, it doesn't really fit here, * but it's the only place where the relevant information is * available. */ if (!crtc->rotatedData) { x += crtc->x; y += crtc->y; } rhdCrtcSetCursorPosition(rhdCrtc, x, y); } /* * Xorg Interface */ static const xf86CrtcConfigFuncsRec rhdRRCrtcConfigFuncs = { rhdRRXF86CrtcResize }; static xf86CrtcFuncsRec rhdRRCrtcFuncs = { rhdRRCrtcDpms, NULL, NULL, /* Save,Restore */ rhdRRCrtcLock, rhdRRCrtcUnlock, rhdRRCrtcModeFixupDUMMY, rhdRRCrtcPrepare, rhdRRCrtcModeSet, rhdRRCrtcCommit, rhdRRCrtcGammaSet, rhdRRCrtcShadowAllocate, rhdRRCrtcShadowCreate, rhdRRCrtcShadowDestroy, rhdRRSetCursorColors, rhdRRSetCursorPosition, rhdRRShowCursor, rhdRRHideCursor, NULL, rhdRRLoadCursorARGB, NULL /* SetCursorColors,SetCursorPosition,ShowCursor,HideCursor, * LoadCursorImage,LoadCursorArgb,CrtcDestroy */ #ifdef XF86CRTCFUNCS_HAS_SETMODEMAJOR /* set_mode_major */ , NULL #endif #if XF86_CRTC_VERSION >= 2 /* set_origin */ , rhdRRCrtcSetOrigin #endif }; /* * */ void RHDRRFreeShadow(ScrnInfoPtr pScrn) { #ifndef HAVE_FREE_SHADOW int i; xf86CrtcConfigPtr CrtcConfig = XF86_CRTC_CONFIG_PTR(pScrn); for (i = 0; i < CrtcConfig->num_crtc; i++) { xf86CrtcPtr Crtc = CrtcConfig->crtc[i]; if (Crtc->rotatedPixmap || Crtc->rotatedData) { Crtc->funcs->shadow_destroy(Crtc, Crtc->rotatedPixmap, Crtc->rotatedData); Crtc->rotatedPixmap = NULL; Crtc->rotatedData = NULL; } } #else xf86RotateFreeShadow(pScrn); #endif } static const xf86OutputFuncsRec rhdRROutputFuncs = { rhdRROutputCreateResources, rhdRROutputDpms, NULL, NULL, /* Save,Restore */ rhdRROutputModeValid, rhdRROutputModeFixup, rhdRROutputPrepare, rhdRROutputCommit, rhdRROutputModeSet, rhdRROutputDetect, rhdRROutputGetModes, #ifdef RANDR_12_INTERFACE rhdRROutputSetProperty, /* Only(!) RANDR_12_INTERFACE */ #endif #ifdef RANDR_13_INTERFACE rhdRROutputGetProperty, /* get_property */ #endif #ifdef RANDR_GET_CRTC_INTERFACE NULL, #endif NULL /* Destroy */ }; /* Helper: Create rhdRandrOutput with unique name per Connector/Output combo */ static rhdRandrOutputPtr createRandrOutput(ScrnInfoPtr pScrn, struct rhdConnector *conn, struct rhdOutput *out) { rhdRandrOutputPtr rro; char *c; rro = xnfcalloc(1, sizeof(rhdRandrOutputRec)); rro->Connector = conn; rro->Output = out; sprintf(rro->Name, "%.30s", conn->Name); for (c = rro->Name; *c; c++) if (isspace(*c)) *c='_'; return rro; } /* Helper: Consolidate names, improve duplicates */ static void consolidateRandrOutputNames(rhdRandrOutputPtr *rop, int num) { int i, j, changed; const char *outname; char *c; /* First try to come up with something sensible */ for (i = 0; i < num; i++) { for (j = i+1; j < num; j++) if (strcmp(rop[i]->Name, rop[j]->Name) == 0) break; if (j < num) { for (j = num-1; j >= i; j--) /* Include i */ if (strcmp(rop[i]->Name, rop[j]->Name) == 0) { switch (rop[j]->Output->Id) { case RHD_OUTPUT_DACA: case RHD_OUTPUT_DACB: outname = "analog"; break; case RHD_OUTPUT_TMDSA: case RHD_OUTPUT_LVTMA: case RHD_OUTPUT_KLDSKP_LVTMA: case RHD_OUTPUT_UNIPHYA: case RHD_OUTPUT_UNIPHYB: case RHD_OUTPUT_UNIPHYC: case RHD_OUTPUT_UNIPHYD: case RHD_OUTPUT_UNIPHYE: case RHD_OUTPUT_UNIPHYF: outname = "digital"; break; default: outname = rop[j]->Output->Name; } sprintf(rop[j]->Name, "%.30s/%.30s", rop[j]->Connector->Name, outname); for (c = rop[j]->Name; *c; c++) if (isspace(*c)) *c='_'; } } } /* This shouldn't happen, but better be safe: * fix up every remaining name with the old naming scheme */ for (i = 0; i < num; i++) { changed=0; for (j = i+1; j < num; j++) if (strcmp(rop[i]->Name, rop[j]->Name) == 0) { changed++; sprintf(rop[j]->Name, "%.30s/%.30s", rop[j]->Connector->Name, rop[j]->Output->Name); for (c = rop[j]->Name; *c; c++) if (isspace(*c)) *c='_'; } if (changed) { sprintf(rop[i]->Name, "%.30s/%.30s", rop[i]->Connector->Name, rop[i]->Output->Name); for (c = rop[i]->Name; *c; c++) if (isspace(*c)) *c='_'; } } } /* Helper: Create and set up xf86Output */ static xf86OutputPtr createXF86Output(ScrnInfoPtr pScrn, rhdRandrOutputPtr rro) { xf86OutputPtr xo; xo = xf86OutputCreate(pScrn, &rhdRROutputFuncs, rro->Name); ASSERT(xo); xo->driver_private = rro; xo->possible_crtcs = ~0; /* No limitations */ xo->possible_clones = ~0; /* No limitations */ xo->interlaceAllowed = TRUE; xo->doubleScanAllowed = TRUE; xo->subpixel_order = SubPixelUnknown; xf86OutputUseScreenMonitor (xo, FALSE); return xo; } /* Call in PreInit after memory + io has been set up */ Bool RHDRandrPreInit(ScrnInfoPtr pScrn) { RHDPtr rhdPtr = RHDPTR(pScrn); struct rhdRandr *randr; int i, j, k, numCombined = 0; rhdRandrOutputPtr *RandrOutput, *r; char *outputorder; RHDFUNC(rhdPtr); if (rhdPtr->noRandr.val.bool) { xf86DrvMsg(pScrn->scrnIndex, X_INFO, "RandR 1.2 support disabled due to configuration\n"); return FALSE; } randr = xnfcalloc(1, sizeof(struct rhdRandr)); xf86CrtcConfigInit(pScrn, &rhdRRCrtcConfigFuncs); /* Maximum for D1GRPH_X/Y_END: 8k */ xf86CrtcSetSizeRange (pScrn, 320, 200, 8000, 8000); for (i = 0; i < 2; i++) { randr->RandrCrtc[i] = xf86CrtcCreate(pScrn, &rhdRRCrtcFuncs); ASSERT(randr->RandrCrtc[i]); randr->RandrCrtc[i]->driver_private = xnfcalloc(sizeof(struct rhdRandrCrtc),1); ((struct rhdRandrCrtc*) randr->RandrCrtc[i]->driver_private)->rhdCrtc = rhdPtr->Crtc[i]; /* TODO: not cleaning up correctly */ } /* First count, then allocate */ for (i = 0; i < RHD_CONNECTORS_MAX; i++) { struct rhdConnector *conn = rhdPtr->Connector[i]; if (conn) for (j = 0; j < MAX_OUTPUTS_PER_CONNECTOR; j++) { struct rhdOutput *out = conn->Output[j]; if (out) numCombined++; } } RandrOutput = r = xnfcalloc(numCombined+1, sizeof(struct rhdRandrOutputPtr *)); /* Create combined unique output names */ for (i = 0; i < RHD_CONNECTORS_MAX; i++) { struct rhdConnector *conn = rhdPtr->Connector[i]; if (conn) { for (j = 0; j < MAX_OUTPUTS_PER_CONNECTOR; j++) { struct rhdOutput *out = conn->Output[j]; if (out) *r++ = createRandrOutput(pScrn, conn, out); } } } /* * Each rhdRandrOutputRec carries a list of all connectors this output belongs to. * Since the output can only drive one connector but RandR doesn't seem to have * any notion of 'shared outputs' we need to probe them all and decide which * output that reports connected we pass to RandR as connected. */ for (i = 0; i < numCombined; i++) { int cnt = 0; for (j = 0; j < RHD_CONNECTORS_MAX; j++) { struct rhdConnector *conn = rhdPtr->Connector[j]; if (!conn) continue; for (k = 0; k < MAX_OUTPUTS_PER_CONNECTOR; k++) { if (conn->Output[k] == RandrOutput[i]->Output) { if (cnt >= MAX_CONNECTORS_PER_RR_OUTPUT) xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "%s: Number of Connectors for Output %s exceeds %i\n", __func__,RandrOutput[i]->Name, MAX_CONNECTORS_PER_RR_OUTPUT); else RandrOutput[i]->AllConnectors[cnt++] = conn; break; } } } } consolidateRandrOutputNames(RandrOutput, numCombined); for (i = 0; i < numCombined; i++) xf86DrvMsg(pScrn->scrnIndex, X_INFO, "RandR: Adding RRoutput %s for Output %s\n", RandrOutput[i]->Name, RandrOutput[i]->Output->Name); /* Reorder for xinerama screen enumeration if requested */ outputorder = rhdPtr->rrOutputOrder.val.string; if (outputorder && *outputorder) { rhdRandrOutputPtr *RandrOutputReorder = r = xnfcalloc(numCombined+1, sizeof(struct xf86OutputPtr *)); while (*outputorder) { char *end = strchr(outputorder, ' '); int len = end ? end-outputorder : (signed) strlen(outputorder); end = strchr(outputorder, ','); if (end) len = end-outputorder < len ? end-outputorder : len; for (i = 0; i < numCombined; i++) { if (RandrOutput[i] && strncmp(RandrOutput[i]->Name, outputorder, len) == 0 && RandrOutput[i]->Name[len] == 0) { xf86DrvMsg(pScrn->scrnIndex, X_INFO, "RandR: Reordering output %s\n", RandrOutput[i]->Name); *r++ = RandrOutput[i]; RandrOutput[i] = NULL; } } outputorder += len; while (*outputorder == ' ' || *outputorder == ',') outputorder++; } for (i = 0; i < numCombined; i++) if (RandrOutput[i]) *r++ = RandrOutput[i]; ASSERT(r - RandrOutputReorder == numCombined); xfree(RandrOutput); RandrOutput = RandrOutputReorder; } /* Initialize RandR structures */ randr->RandrOutput = xnfcalloc(numCombined+1, sizeof(struct xf86OutputPtr *)); for (i = 0; i < numCombined; i++) { randr->RandrOutput[i] = createXF86Output(pScrn, RandrOutput[i]); } xfree(RandrOutput); rhdPtr->randr = randr; /* Unless we're able to shrink/enlarge FB on the fly (GEM etc.), allocate * large enough (TM) virtual size */ if (!pScrn->display->virtualX || !pScrn->display->virtualY) { /* Have at least enough space for double buffering and z Buffer + some textures */ if (2 * 1920*1920 * ((unsigned)pScrn->bitsPerPixel/8) <= rhdPtr->FbFreeSize / 4) { /* Fits on 128MB and up */ pScrn->display->virtualX = 2 * 1920; pScrn->display->virtualY = 1920; } else if (2 * 1680*1280 * ((unsigned)pScrn->bitsPerPixel/8) <= rhdPtr->FbFreeSize / 4) { /* Fits on 64MB and up */ pScrn->display->virtualX = 2 * 1680; pScrn->display->virtualY = 1280; } } if (!xf86InitialConfiguration(pScrn, FALSE)) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "RandR: No valid modes. Disabling RandR support.\n"); for (i = 0; i < 2; i++) xfree(randr->RandrCrtc[i]->driver_private); xfree(randr); rhdPtr->randr = NULL; /* TODO: not cleaning up correctly */ return FALSE; } xf86DrvMsg(pScrn->scrnIndex, X_INFO, "RandR 1.2 support enabled\n"); /* No docs, but radeon, avivo, and nv drivers calling this * after(!) xf86InitialConfiguration */ if (!xf86RandR12PreInit (pScrn)) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "RandR: xf86RandR12PreInit failed. Disabled.\n"); for (i = 0; i < 2; i++) xfree(randr->RandrCrtc[i]->driver_private); xfree(randr); rhdPtr->randr = NULL; /* TODO: not cleaning up correctly */ return FALSE; } return TRUE; } /* Wrapped PointerMoved for driver-level panning */ static void rhdRRPointerMoved(int scrnIndex, int x, int y) { ScrnInfoPtr pScrn = xf86Screens[scrnIndex]; RHDPtr rhdPtr = RHDPTR(xf86Screens[scrnIndex]); int i; for (i = 0; i < 2; i++) { struct rhdCrtc *Crtc = rhdPtr->Crtc[i]; if (Crtc->scrnIndex == scrnIndex && Crtc->Active) rhdUpdateCrtcPos(rhdPtr, Crtc, x + pScrn->frameX0, y + pScrn->frameY0); } UNWRAP_SCRNINFO(PointerMoved); pScrn->PointerMoved(scrnIndex, x, y); WRAP_SCRNINFO(PointerMoved, rhdRRPointerMoved); } /* Call in ScreenInit after frame buffer + acceleration layers */ Bool RHDRandrScreenInit(ScreenPtr pScreen) { ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; RHDPtr rhdPtr = RHDPTR(pScrn); RHDFUNC(rhdPtr); if (rhdPtr->AccelMethod == RHD_ACCEL_NONE || rhdPtr->AccelMethod == RHD_ACCEL_SHADOWFB) { rhdRRCrtcFuncs.shadow_allocate = NULL; rhdRRCrtcFuncs.shadow_create = NULL; rhdRRCrtcFuncs.shadow_destroy = NULL; } if (!xf86CrtcScreenInit(pScreen)) return FALSE; /* Wrap cursor for driver-level panning */ WRAP_SCRNINFO(PointerMoved, rhdRRPointerMoved); RHDDebugRandrState(rhdPtr, "POST-ScreenInit"); return TRUE; } /* ModeInit: Set modes according to current layout */ Bool RHDRandrModeInit(ScrnInfoPtr pScrn) { Bool ret; RHDPtr rhdPtr = RHDPTR(pScrn); RHDFUNC(rhdPtr); /* Stop crap from being shown: gets reenabled through SaveScreen */ rhdPtr->Crtc[0]->Blank(rhdPtr->Crtc[0], TRUE); rhdPtr->Crtc[1]->Blank(rhdPtr->Crtc[1], TRUE); RHDPrepareMode(rhdPtr); ret = xf86SetDesiredModes(pScrn); RHDDebugRandrState(rhdPtr, "POST-ModeInit"); return ret; } /* SwitchMode: Legacy: Set one mode */ Bool RHDRandrSwitchMode(ScrnInfoPtr pScrn, DisplayModePtr mode) { return xf86SetSingleMode(pScrn, mode, RR_Rotate_0); } #else /* RANDR_12_SUPPORT */ Bool RHDRandrPreInit(ScrnInfoPtr pScrn) { xf86DrvMsg(pScrn->scrnIndex, X_INFO, "RandR 1.2 support disabled (not available at compile time)\n"); return FALSE; } Bool RHDRandrScreenInit(ScreenPtr pScreen) { ASSERT(0); return FALSE; } Bool RHDRandrModeInit(ScrnInfoPtr pScrn) { ASSERT(0); return FALSE; } Bool RHDRandrSwitchMode(ScrnInfoPtr pScrn, DisplayModePtr mode) { ASSERT(0); return FALSE; } #endif /* RANDR_12_SUPPORT */