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 /* Register writes related to outputs also come at a 0x800 offset. */
34 /* So called DisplayCommand's do not use that offset */
35 void NV50OrWrite(ScrnInfoPtr pScrn, int or, CARD32 addr, CARD32 value)
37 NV50DisplayWrite(pScrn, addr + or * 0x800, value);
40 CARD32 NV50OrRead(ScrnInfoPtr pScrn, int or, CARD32 addr)
42 return NV50DisplayRead(pScrn, addr + or * 0x800);
45 void NV50OutputWrite(xf86OutputPtr output, CARD32 addr, CARD32 value)
47 NV50OutputPrivPtr nv_output = output->driver_private;
48 ScrnInfoPtr pScrn = output->scrn;
49 NV50OrWrite(pScrn, nv_output->or, addr, value);
52 CARD32 NV50OutputRead(xf86OutputPtr output, CARD32 addr)
54 NV50OutputPrivPtr nv_output = output->driver_private;
55 ScrnInfoPtr pScrn = output->scrn;
56 return NV50OrRead(pScrn, nv_output->or, addr);
59 static Bool NV50ReadPortMapping(int scrnIndex, NVPtr pNv)
61 unsigned const char *VBIOS = (unsigned const char *)pNv->VBIOS;
62 unsigned char *table2;
63 unsigned char headerSize, entries;
71 /* Clear the i2c map to invalid */
72 for (i = 0; i < 4; i++)
73 pNv->i2cMap[i].dac = pNv->i2cMap[i].sor = -1;
75 if (*(CARD16*)VBIOS != 0xaa55) goto fail;
77 a = *(CARD16*)(VBIOS + 0x36);
78 table2 = (unsigned char*)VBIOS + a;
80 if (table2[0] != 0x40) goto fail;
82 b = *(CARD32*)(table2 + 6);
83 if (b != 0x4edcbdcb) goto fail;
85 headerSize = table2[1];
88 for(i = 0; i < entries; i++) {
92 b = *(CARD32*)&table2[headerSize + 8*i];
94 port = (b >> 4) & 0xf;
95 or = ffs((b >> 24) & 0xf) - 1;
103 if(pNv->i2cMap[port].dac != -1) {
104 xf86DrvMsg(scrnIndex, X_WARNING,
105 "DDC routing table corrupt! DAC %i -> %i "
107 or, pNv->i2cMap[port].dac, port);
109 pNv->i2cMap[port].dac = or;
114 if(pNv->i2cMap[port].sor != -1)
115 xf86DrvMsg(scrnIndex, X_WARNING,
116 "DDC routing table corrupt! SOR %i -> %i "
118 or, pNv->i2cMap[port].sor, port);
119 pNv->i2cMap[port].sor = or;
122 pNv->lvds.present = TRUE;
129 xf86DrvMsg(scrnIndex, X_PROBED, "Connector map:\n");
130 if (pNv->lvds.present) {
131 xf86DrvMsg(scrnIndex, X_PROBED,
132 " [N/A] -> SOR%i (LVDS)\n", pNv->lvds.or);
134 for(i = 0; i < 4; i++) {
135 if(pNv->i2cMap[i].dac != -1)
136 xf86DrvMsg(scrnIndex, X_PROBED, " Bus %i -> DAC%i\n", i, pNv->i2cMap[i].dac);
137 if(pNv->i2cMap[i].sor != -1)
138 xf86DrvMsg(scrnIndex, X_PROBED, " Bus %i -> SOR%i\n", i, pNv->i2cMap[i].sor);
144 xf86DrvMsg(scrnIndex, X_ERROR, "Couldn't find the DDC routing table. "
145 "Mode setting will probably fail!\n");
149 static void NV50_I2CPutBits(I2CBusPtr b, int clock, int data)
151 NVPtr pNv = NVPTR(xf86Screens[b->scrnIndex]);
152 const int off = b->DriverPrivate.val * 0x18;
154 pNv->REGS[(0x0000E138+off)/4] = 4 | clock | data << 1;
157 static void NV50_I2CGetBits(I2CBusPtr b, int *clock, int *data)
159 NVPtr pNv = NVPTR(xf86Screens[b->scrnIndex]);
160 const int off = b->DriverPrivate.val * 0x18;
163 val = pNv->REGS[(0x0000E138+off)/4];
164 *clock = !!(val & 1);
169 NV50I2CInit(ScrnInfoPtr pScrn, const char *name, const int port)
173 /* Allocate the I2C bus structure */
174 i2c = xf86CreateI2CBusRec();
175 if(!i2c) return NULL;
177 i2c->BusName = strdup(name);
178 i2c->scrnIndex = pScrn->scrnIndex;
179 i2c->I2CPutBits = NV50_I2CPutBits;
180 i2c->I2CGetBits = NV50_I2CGetBits;
181 i2c->ByteTimeout = 2200; /* VESA DDC spec 3 p. 43 (+10 %) */
182 i2c->StartTimeout = 550;
183 i2c->BitTimeout = 40;
184 i2c->ByteTimeout = 40;
185 i2c->AcknTimeout = 40;
186 i2c->DriverPrivate.val = port;
188 if(xf86I2CBusInit(i2c)) {
197 NV50OutputSetPClk(xf86OutputPtr output, int pclk)
199 NV50OutputPrivPtr nv_output = output->driver_private;
201 if (nv_output->set_pclk)
202 nv_output->set_pclk(output, pclk);
206 NV50OutputModeValid(xf86OutputPtr output, DisplayModePtr mode)
208 if (mode->Clock > 400000)
209 return MODE_CLOCK_HIGH;
210 if (mode->Clock < 25000)
211 return MODE_CLOCK_LOW;
217 NV50OutputPrepare(xf86OutputPtr output)
222 NV50OutputCommit(xf86OutputPtr output)
227 ProbeDDC(I2CBusPtr i2c)
229 ScrnInfoPtr pScrn = xf86Screens[i2c->scrnIndex];
230 NVPtr pNv = NVPTR(pScrn);
231 xf86MonPtr monInfo = NULL;
232 const int bus = i2c->DriverPrivate.val, off = bus * 0x18;
234 xf86DrvMsg(pScrn->scrnIndex, X_INFO,
235 "Probing for EDID on I2C bus %i...\n", bus);
236 pNv->REGS[(0x0000E138+off)/4] = 7;
237 /* Should probably use xf86OutputGetEDID here */
238 monInfo = xf86DoEDID_DDC2(pScrn->scrnIndex, i2c);
239 pNv->REGS[(0x0000E138+off)/4] = 3;
242 xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
243 "DDC detected a %s:\n", monInfo->features.input_type ?
245 xf86PrintEDID(monInfo);
247 xf86DrvMsg(pScrn->scrnIndex, X_INFO, " ... none found\n");
254 * Read an EDID from the i2c port. Perform load detection on the DAC (if
255 * present) to see if the display is connected via VGA. Sets the cached status
256 * of both outputs. The status is marked dirty again in the BlockHandler.
258 void NV50OutputPartnersDetect(xf86OutputPtr dac, xf86OutputPtr sor, I2CBusPtr i2c)
260 xf86MonPtr monInfo = ProbeDDC(i2c);
261 xf86OutputPtr connected = NULL;
262 Bool load = dac && NV50DacLoadDetect(dac);
265 NV50OutputPrivPtr nv_output = dac->driver_private;
268 nv_output->cached_status = XF86OutputStatusConnected;
271 nv_output->cached_status = XF86OutputStatusDisconnected;
276 NV50OutputPrivPtr nv_output = sor->driver_private;
278 if(monInfo && !load) {
279 nv_output->cached_status = XF86OutputStatusConnected;
282 nv_output->cached_status = XF86OutputStatusDisconnected;
287 xf86OutputSetEDID(connected, monInfo);
291 * Reset the cached output status for all outputs. Called from NV50BlockHandler.
294 NV50OutputResetCachedStatus(ScrnInfoPtr pScrn)
296 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
299 for(i = 0; i < xf86_config->num_output; i++) {
300 NV50OutputPrivPtr nv_output = xf86_config->output[i]->driver_private;
301 nv_output->cached_status = XF86OutputStatusUnknown;
306 NV50OutputGetDDCModes(xf86OutputPtr output)
308 /* The EDID is read as part of the detect step */
309 output->funcs->detect(output);
310 return xf86OutputGetEDIDModes(output);
314 NV50OutputDestroy(xf86OutputPtr output)
316 NV50OutputPrivPtr nv_output = output->driver_private;
318 if(nv_output->partner)
319 ((NV50OutputPrivPtr)nv_output->partner->driver_private)->partner = NULL;
321 xf86DestroyI2CBusRec(nv_output->i2c, TRUE, TRUE);
322 nv_output->i2c = NULL;
326 NV50CreateOutputs(ScrnInfoPtr pScrn)
328 NVPtr pNv = NVPTR(pScrn);
329 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
332 if(!NV50ReadPortMapping(pScrn->scrnIndex, pNv))
335 /* For each DDC port, create an output for the attached ORs */
336 for (i = 0; i < 4; i++) {
337 xf86OutputPtr dac = NULL, sor = NULL;
341 if(pNv->i2cMap[i].dac == -1 && pNv->i2cMap[i].sor == -1) {
342 /* No outputs on this port */
346 snprintf(i2cName, sizeof(i2cName), "I2C%i", i);
347 i2c = NV50I2CInit(pScrn, i2cName, i);
349 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
350 "Failed to initialize I2C for port %i.\n",
355 if (pNv->i2cMap[i].dac != -1)
356 dac = NV50CreateDac(pScrn, pNv->i2cMap[i].dac);
357 if (pNv->i2cMap[i].sor != -1)
358 sor = NV50CreateSor(pScrn, pNv->i2cMap[i].sor, TMDS);
361 NV50OutputPrivPtr nv_output = dac->driver_private;
363 nv_output->partner = sor;
364 nv_output->i2c = i2c;
365 nv_output->scale = NV50_SCALE_OFF;
368 NV50OutputPrivPtr nv_output = sor->driver_private;
370 nv_output->partner = dac;
371 nv_output->i2c = i2c;
372 nv_output->scale = NV50_SCALE_ASPECT;
376 if (pNv->lvds.present) {
377 xf86OutputPtr lvds = NV50CreateSor(pScrn, pNv->lvds.or, LVDS);
378 NV50OutputPrivPtr nv_output = lvds->driver_private;
380 nv_output->scale = NV50_SCALE_ASPECT;
383 /* For each output, set the crtc and clone masks */
384 for(i = 0; i < xf86_config->num_output; i++) {
385 xf86OutputPtr output = xf86_config->output[i];
387 /* Any output can connect to any head */
388 output->possible_crtcs = 0x3;
389 output->possible_clones = 0;