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.
32 #include "nv_include.h"
33 #include "nv50_type.h"
34 #include "nv50_output.h"
36 static unsigned const char *
37 NV50GetVBIOSImage(NVPtr pNv)
40 uint32_t old_bar0_pramin;
42 VBIOS = xalloc(65536);
44 old_bar0_pramin = pNv->REGS[0x1700/4];
45 pNv->REGS[0x1700/4] = pNv->REGS[0x00619f04/4] >> 8;
47 memcpy(VBIOS, (const void *)&pNv->REGS[0x700000/4], 65536);
49 pNv->REGS[0x1700/4] = old_bar0_pramin;
52 return (unsigned const char *)VBIOS;
55 static Bool NV50ReadPortMapping(int scrnIndex, NVPtr pNv)
57 unsigned const char *VBIOS;
58 unsigned char *table2;
59 unsigned char headerSize, entries;
64 VBIOS = NV50GetVBIOSImage(pNv);
68 /* Clear the i2c map to invalid */
69 for(i = 0; i < 4; i++)
70 pNv->i2cMap[i].dac = pNv->i2cMap[i].sor = -1;
72 if(*(CARD16*)VBIOS != 0xaa55) goto fail;
74 a = *(CARD16*)(VBIOS + 0x36);
75 table2 = (unsigned char*)VBIOS + a;
77 if(table2[0] != 0x40) goto fail;
79 b = *(CARD32*)(table2 + 6);
80 if(b != 0x4edcbdcb) goto fail;
82 headerSize = table2[1];
85 for(i = 0; i < entries; i++) {
89 b = *(CARD32*)&table2[headerSize + 8*i];
91 port = (b >> 4) & 0xf;
92 or = ffs((b >> 24) & 0xf) - 1;
94 if(type < 4 && port != 0xf) {
98 if(pNv->i2cMap[port].dac != -1) {
99 xf86DrvMsg(scrnIndex, X_WARNING,
100 "DDC routing table corrupt! DAC %i -> %i "
102 or, pNv->i2cMap[port].dac, port);
104 pNv->i2cMap[port].dac = or;
108 if(pNv->i2cMap[port].sor != -1)
109 xf86DrvMsg(scrnIndex, X_WARNING,
110 "DDC routing table corrupt! SOR %i -> %i "
112 or, pNv->i2cMap[port].sor, port);
113 pNv->i2cMap[port].sor = or;
119 xf86DrvMsg(scrnIndex, X_PROBED, "I2C map:\n");
120 for(i = 0; i < 4; i++) {
121 if(pNv->i2cMap[i].dac != -1)
122 xf86DrvMsg(scrnIndex, X_PROBED, " Bus %i -> DAC%i\n", i, pNv->i2cMap[i].dac);
123 if(pNv->i2cMap[i].sor != -1)
124 xf86DrvMsg(scrnIndex, X_PROBED, " Bus %i -> SOR%i\n", i, pNv->i2cMap[i].sor);
130 xf86DrvMsg(scrnIndex, X_ERROR, "Couldn't find the DDC routing table. "
131 "Mode setting will probably fail!\n");
135 static void NV50_I2CPutBits(I2CBusPtr b, int clock, int data)
137 NVPtr pNv = NVPTR(xf86Screens[b->scrnIndex]);
138 const int off = b->DriverPrivate.val * 0x18;
140 pNv->REGS[(0x0000E138+off)/4] = 4 | clock | data << 1;
143 static void NV50_I2CGetBits(I2CBusPtr b, int *clock, int *data)
145 NVPtr pNv = NVPTR(xf86Screens[b->scrnIndex]);
146 const int off = b->DriverPrivate.val * 0x18;
149 val = pNv->REGS[(0x0000E138+off)/4];
150 *clock = !!(val & 1);
155 NV50I2CInit(ScrnInfoPtr pScrn, const char *name, const int port)
159 /* Allocate the I2C bus structure */
160 i2c = xf86CreateI2CBusRec();
161 if(!i2c) return NULL;
163 i2c->BusName = strdup(name);
164 i2c->scrnIndex = pScrn->scrnIndex;
165 i2c->I2CPutBits = NV50_I2CPutBits;
166 i2c->I2CGetBits = NV50_I2CGetBits;
167 i2c->ByteTimeout = 2200; /* VESA DDC spec 3 p. 43 (+10 %) */
168 i2c->StartTimeout = 550;
169 i2c->BitTimeout = 40;
170 i2c->ByteTimeout = 40;
171 i2c->AcknTimeout = 40;
172 i2c->DriverPrivate.val = port;
174 if(xf86I2CBusInit(i2c)) {
183 NV50OutputSetPClk(xf86OutputPtr output, int pclk)
185 NV50OutputPrivPtr pPriv = output->driver_private;
186 pPriv->set_pclk(output, pclk);
190 NV50OutputModeValid(xf86OutputPtr output, DisplayModePtr mode)
192 if(mode->Clock > 400000 || mode->Clock < 25000)
193 return MODE_CLOCK_RANGE;
199 NV50OutputModeFixup(xf86OutputPtr output, DisplayModePtr mode,
200 DisplayModePtr adjusted_mode)
206 NV50OutputPrepare(xf86OutputPtr output)
211 NV50OutputCommit(xf86OutputPtr output)
216 ProbeDDC(I2CBusPtr i2c)
218 ScrnInfoPtr pScrn = xf86Screens[i2c->scrnIndex];
219 NVPtr pNv = NVPTR(pScrn);
220 xf86MonPtr monInfo = NULL;
221 const int bus = i2c->DriverPrivate.val, off = bus * 0x18;
223 xf86DrvMsg(pScrn->scrnIndex, X_INFO,
224 "Probing for EDID on I2C bus %i...\n", bus);
225 pNv->REGS[(0x0000E138+off)/4] = 7;
226 /* Should probably use xf86OutputGetEDID here */
227 monInfo = xf86DoEDID_DDC2(pScrn->scrnIndex, i2c);
228 pNv->REGS[(0x0000E138+off)/4] = 3;
231 xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
232 "DDC detected a %s:\n", monInfo->features.input_type ?
234 xf86PrintEDID(monInfo);
236 xf86DrvMsg(pScrn->scrnIndex, X_INFO, " ... none found\n");
243 * Read an EDID from the i2c port. Perform load detection on the DAC (if
244 * present) to see if the display is connected via VGA. Sets the cached status
245 * of both outputs. The status is marked dirty again in the BlockHandler.
247 void NV50OutputPartnersDetect(xf86OutputPtr dac, xf86OutputPtr sor, I2CBusPtr i2c)
249 xf86MonPtr monInfo = ProbeDDC(i2c);
250 xf86OutputPtr connected = NULL;
251 Bool load = dac && NV50DacLoadDetect(dac);
254 NV50OutputPrivPtr pPriv = dac->driver_private;
257 pPriv->cached_status = XF86OutputStatusConnected;
260 pPriv->cached_status = XF86OutputStatusDisconnected;
265 NV50OutputPrivPtr pPriv = sor->driver_private;
267 if(monInfo && !load) {
268 pPriv->cached_status = XF86OutputStatusConnected;
271 pPriv->cached_status = XF86OutputStatusDisconnected;
276 xf86OutputSetEDID(connected, monInfo);
280 * Reset the cached output status for all outputs. Called from NV50BlockHandler.
283 NV50OutputResetCachedStatus(ScrnInfoPtr pScrn)
285 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
288 for(i = 0; i < xf86_config->num_output; i++) {
289 NV50OutputPrivPtr pPriv = xf86_config->output[i]->driver_private;
290 pPriv->cached_status = XF86OutputStatusUnknown;
295 NV50OutputGetDDCModes(xf86OutputPtr output)
297 /* The EDID is read as part of the detect step */
298 output->funcs->detect(output);
299 return xf86OutputGetEDIDModes(output);
303 NV50OutputDestroy(xf86OutputPtr output)
305 NV50OutputPrivPtr pPriv = output->driver_private;
308 ((NV50OutputPrivPtr)pPriv->partner->driver_private)->partner = NULL;
310 xf86DestroyI2CBusRec(pPriv->i2c, TRUE, TRUE);
315 NV50CreateOutputs(ScrnInfoPtr pScrn)
317 NVPtr pNv = NVPTR(pScrn);
318 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
321 if(!NV50ReadPortMapping(pScrn->scrnIndex, pNv))
324 /* For each DDC port, create an output for the attached ORs */
325 for(i = 0; i < 4; i++) {
326 xf86OutputPtr dac = NULL, sor = NULL;
330 if(pNv->i2cMap[i].dac == -1 && pNv->i2cMap[i].sor == -1)
331 /* No outputs on this port */
334 snprintf(i2cName, sizeof(i2cName), "I2C%i", i);
335 i2c = NV50I2CInit(pScrn, i2cName, i);
337 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
338 "Failed to initialize I2C for port %i.\n",
343 if(pNv->i2cMap[i].dac != -1)
344 dac = NV50CreateDac(pScrn, pNv->i2cMap[i].dac);
345 if(pNv->i2cMap[i].sor != -1)
346 sor = NV50CreateSor(pScrn, pNv->i2cMap[i].sor);
349 NV50OutputPrivPtr pPriv = dac->driver_private;
351 pPriv->partner = sor;
355 NV50OutputPrivPtr pPriv = sor->driver_private;
357 pPriv->partner = dac;
362 /* For each output, set the crtc and clone masks */
363 for(i = 0; i < xf86_config->num_output; i++) {
364 xf86OutputPtr output = xf86_config->output[i];
366 /* Any output can connect to any head */
367 output->possible_crtcs = 0x3;
368 output->possible_clones = 0;