2 * Copyright (c) 2007 NVIDIA, Corporation
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
12 * The above copyright notice and this permission notice shall be included
13 * in all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 #include "nv_include.h"
27 #include "nv50_type.h"
28 #include "nv50_display.h"
29 #include "nv50_output.h"
33 static Bool NV50ReadPortMapping(int scrnIndex, NVPtr pNv)
35 unsigned const char *VBIOS = (unsigned const char *)pNv->VBIOS.data;
36 unsigned char *table2;
37 unsigned char headerSize, entries;
45 /* Clear the i2c map to invalid */
46 for (i = 0; i < 4; i++)
47 pNv->i2cMap[i].dac = pNv->i2cMap[i].sor = -1;
49 if (*(CARD16*)VBIOS != 0xaa55) goto fail;
51 a = *(CARD16*)(VBIOS + 0x36);
52 table2 = (unsigned char*)VBIOS + a;
54 if (table2[0] != 0x40) goto fail;
56 b = *(CARD32*)(table2 + 6);
57 if (b != 0x4edcbdcb) goto fail;
59 headerSize = table2[1];
62 for(i = 0; i < entries; i++) {
66 b = *(CARD32*)&table2[headerSize + 8*i];
68 port = (b >> 4) & 0xf;
69 or = ffs((b >> 24) & 0xf) - 1;
77 if(pNv->i2cMap[port].dac != -1) {
78 xf86DrvMsg(scrnIndex, X_WARNING,
79 "DDC routing table corrupt! DAC %i -> %i "
81 or, pNv->i2cMap[port].dac, port);
83 pNv->i2cMap[port].dac = or;
88 if(pNv->i2cMap[port].sor != -1)
89 xf86DrvMsg(scrnIndex, X_WARNING,
90 "DDC routing table corrupt! SOR %i -> %i "
92 or, pNv->i2cMap[port].sor, port);
93 pNv->i2cMap[port].sor = or;
96 pNv->lvds.present = TRUE;
103 xf86DrvMsg(scrnIndex, X_PROBED, "Connector map:\n");
104 if (pNv->lvds.present) {
105 xf86DrvMsg(scrnIndex, X_PROBED,
106 " [N/A] -> SOR%i (LVDS)\n", pNv->lvds.or);
108 for(i = 0; i < 4; i++) {
109 if(pNv->i2cMap[i].dac != -1)
110 xf86DrvMsg(scrnIndex, X_PROBED, " Bus %i -> DAC%i\n", i, pNv->i2cMap[i].dac);
111 if(pNv->i2cMap[i].sor != -1)
112 xf86DrvMsg(scrnIndex, X_PROBED, " Bus %i -> SOR%i\n", i, pNv->i2cMap[i].sor);
118 xf86DrvMsg(scrnIndex, X_ERROR, "Couldn't find the DDC routing table. "
119 "Mode setting will probably fail!\n");
123 static void NV50_I2CPutBits(I2CBusPtr b, int clock, int data)
125 NVPtr pNv = NVPTR(xf86Screens[b->scrnIndex]);
126 const int off = b->DriverPrivate.val * 0x18;
128 pNv->REGS[(0x0000E138+off)/4] = 4 | clock | data << 1;
131 static void NV50_I2CGetBits(I2CBusPtr b, int *clock, int *data)
133 NVPtr pNv = NVPTR(xf86Screens[b->scrnIndex]);
134 const int off = b->DriverPrivate.val * 0x18;
137 val = pNv->REGS[(0x0000E138+off)/4];
138 *clock = !!(val & 1);
143 NV50I2CInit(ScrnInfoPtr pScrn, const char *name, const int port)
147 /* Allocate the I2C bus structure */
148 i2c = xf86CreateI2CBusRec();
149 if(!i2c) return NULL;
151 i2c->BusName = strdup(name);
152 i2c->scrnIndex = pScrn->scrnIndex;
153 i2c->I2CPutBits = NV50_I2CPutBits;
154 i2c->I2CGetBits = NV50_I2CGetBits;
155 i2c->ByteTimeout = 2200; /* VESA DDC spec 3 p. 43 (+10 %) */
156 i2c->StartTimeout = 550;
157 i2c->BitTimeout = 40;
158 i2c->ByteTimeout = 40;
159 i2c->AcknTimeout = 40;
160 i2c->DriverPrivate.val = port;
162 if(xf86I2CBusInit(i2c)) {
171 NV50OutputSetPClk(xf86OutputPtr output, int pclk)
173 NV50OutputPrivPtr nv_output = output->driver_private;
175 if (nv_output->set_pclk)
176 nv_output->set_pclk(output, pclk);
180 NV50OutputModeValid(xf86OutputPtr output, DisplayModePtr mode)
182 if (mode->Clock > 400000)
183 return MODE_CLOCK_HIGH;
184 if (mode->Clock < 25000)
185 return MODE_CLOCK_LOW;
191 NV50OutputPrepare(xf86OutputPtr output)
196 NV50OutputCommit(xf86OutputPtr output)
201 ProbeDDC(I2CBusPtr i2c)
203 ScrnInfoPtr pScrn = xf86Screens[i2c->scrnIndex];
204 NVPtr pNv = NVPTR(pScrn);
205 xf86MonPtr monInfo = NULL;
206 const int bus = i2c->DriverPrivate.val, off = bus * 0x18;
208 xf86DrvMsg(pScrn->scrnIndex, X_INFO,
209 "Probing for EDID on I2C bus %i...\n", bus);
210 pNv->REGS[(0x0000E138+off)/4] = 7;
211 /* Should probably use xf86OutputGetEDID here */
212 monInfo = xf86DoEDID_DDC2(pScrn->scrnIndex, i2c);
213 pNv->REGS[(0x0000E138+off)/4] = 3;
216 xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
217 "DDC detected a %s:\n", monInfo->features.input_type ?
219 xf86PrintEDID(monInfo);
221 xf86DrvMsg(pScrn->scrnIndex, X_INFO, " ... none found\n");
228 * Read an EDID from the i2c port. Perform load detection on the DAC (if
229 * present) to see if the display is connected via VGA. Sets the cached status
230 * of both outputs. The status is marked dirty again in the BlockHandler.
232 void NV50OutputPartnersDetect(xf86OutputPtr dac, xf86OutputPtr sor, I2CBusPtr i2c)
234 xf86MonPtr monInfo = ProbeDDC(i2c);
235 xf86OutputPtr connected = NULL;
236 Bool load = dac && NV50DacLoadDetect(dac);
239 NV50OutputPrivPtr nv_output = dac->driver_private;
242 nv_output->cached_status = XF86OutputStatusConnected;
245 nv_output->cached_status = XF86OutputStatusDisconnected;
250 NV50OutputPrivPtr nv_output = sor->driver_private;
252 if(monInfo && !load) {
253 nv_output->cached_status = XF86OutputStatusConnected;
256 nv_output->cached_status = XF86OutputStatusDisconnected;
261 xf86OutputSetEDID(connected, monInfo);
265 * Reset the cached output status for all outputs. Called from NV50BlockHandler.
268 NV50OutputResetCachedStatus(ScrnInfoPtr pScrn)
270 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
273 for(i = 0; i < xf86_config->num_output; i++) {
274 NV50OutputPrivPtr nv_output = xf86_config->output[i]->driver_private;
275 nv_output->cached_status = XF86OutputStatusUnknown;
280 NV50OutputGetDDCModes(xf86OutputPtr output)
282 /* The EDID is read as part of the detect step */
283 output->funcs->detect(output);
284 return xf86OutputGetEDIDModes(output);
288 NV50OutputDestroy(xf86OutputPtr output)
290 NV50OutputPrivPtr nv_output = output->driver_private;
292 if(nv_output->partner)
293 ((NV50OutputPrivPtr)nv_output->partner->driver_private)->partner = NULL;
295 xf86DestroyI2CBusRec(nv_output->i2c, TRUE, TRUE);
296 nv_output->i2c = NULL;
300 NV50CreateOutputs(ScrnInfoPtr pScrn)
302 NVPtr pNv = NVPTR(pScrn);
303 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
306 if(!NV50ReadPortMapping(pScrn->scrnIndex, pNv))
309 /* For each DDC port, create an output for the attached ORs */
310 for (i = 0; i < 4; i++) {
311 xf86OutputPtr dac = NULL, sor = NULL;
315 if(pNv->i2cMap[i].dac == -1 && pNv->i2cMap[i].sor == -1) {
316 /* No outputs on this port */
320 snprintf(i2cName, sizeof(i2cName), "I2C%i", i);
321 i2c = NV50I2CInit(pScrn, i2cName, i);
323 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
324 "Failed to initialize I2C for port %i.\n",
329 if (pNv->i2cMap[i].dac != -1)
330 dac = NV50CreateDac(pScrn, pNv->i2cMap[i].dac);
331 if (pNv->i2cMap[i].sor != -1)
332 sor = NV50CreateSor(pScrn, pNv->i2cMap[i].sor, TMDS);
335 NV50OutputPrivPtr nv_output = dac->driver_private;
337 nv_output->partner = sor;
338 nv_output->i2c = i2c;
339 nv_output->scale = NV50_SCALE_OFF;
342 NV50OutputPrivPtr nv_output = sor->driver_private;
344 nv_output->partner = dac;
345 nv_output->i2c = i2c;
346 nv_output->scale = NV50_SCALE_ASPECT;
350 if (pNv->lvds.present) {
351 xf86OutputPtr lvds = NV50CreateSor(pScrn, pNv->lvds.or, LVDS);
352 NV50OutputPrivPtr nv_output = lvds->driver_private;
354 nv_output->scale = NV50_SCALE_ASPECT;
357 /* For each output, set the crtc and clone masks */
358 for(i = 0; i < xf86_config->num_output; i++) {
359 xf86OutputPtr output = xf86_config->output[i];
361 /* Any output can connect to any head */
362 output->possible_crtcs = 0x3;
363 output->possible_clones = 0;