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 NVOutputPrivatePtr nv_output = output->driver_private;
175 if (nv_output->type == OUTPUT_TMDS)
176 NV50SorSetPClk(output, pclk);
178 if (nv_output->type == OUTPUT_ANALOG)
179 NV50DacSetPClk(output, pclk);
183 NV50OutputModeValid(xf86OutputPtr output, DisplayModePtr mode)
185 if (mode->Clock > 400000)
186 return MODE_CLOCK_HIGH;
187 if (mode->Clock < 25000)
188 return MODE_CLOCK_LOW;
194 NV50OutputPrepare(xf86OutputPtr output)
199 NV50OutputCommit(xf86OutputPtr output)
204 ProbeDDC(I2CBusPtr i2c)
206 ScrnInfoPtr pScrn = xf86Screens[i2c->scrnIndex];
207 NVPtr pNv = NVPTR(pScrn);
208 xf86MonPtr monInfo = NULL;
209 const int bus = i2c->DriverPrivate.val, off = bus * 0x18;
211 xf86DrvMsg(pScrn->scrnIndex, X_INFO,
212 "Probing for EDID on I2C bus %i...\n", bus);
213 pNv->REGS[(0x0000E138+off)/4] = 7;
214 /* Should probably use xf86OutputGetEDID here */
215 monInfo = xf86DoEDID_DDC2(pScrn->scrnIndex, i2c);
216 pNv->REGS[(0x0000E138+off)/4] = 3;
219 xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
220 "DDC detected a %s:\n", monInfo->features.input_type ?
222 xf86PrintEDID(monInfo);
224 xf86DrvMsg(pScrn->scrnIndex, X_INFO, " ... none found\n");
231 NV50OutputGetDDCModes(xf86OutputPtr output)
233 /* The EDID is read as part of the detect step */
234 output->funcs->detect(output);
235 return xf86OutputGetEDIDModes(output);
239 NV50OutputDestroy(xf86OutputPtr output)
241 NVOutputPrivatePtr nv_output = output->driver_private;
243 if (nv_output->pDDCBus)
244 xf86DestroyI2CBusRec(nv_output->pDDCBus, TRUE, TRUE);
246 nv_output->pDDCBus = NULL;
250 NV50CreateOutputs(ScrnInfoPtr pScrn)
252 NVPtr pNv = NVPTR(pScrn);
253 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
256 if(!NV50ReadPortMapping(pScrn->scrnIndex, pNv))
259 /* For each DDC port, create an output for the attached ORs */
260 for (i = 0; i < 4; i++) {
261 xf86OutputPtr dac = NULL, sor = NULL;
265 if(pNv->i2cMap[i].dac == -1 && pNv->i2cMap[i].sor == -1) {
266 /* No outputs on this port */
270 snprintf(i2cName, sizeof(i2cName), "I2C%i", i);
271 i2c = NV50I2CInit(pScrn, i2cName, i);
273 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
274 "Failed to initialize I2C for port %i.\n",
279 if (pNv->i2cMap[i].dac != -1)
280 dac = NV50CreateDac(pScrn, pNv->i2cMap[i].dac);
281 if (pNv->i2cMap[i].sor != -1)
282 sor = NV50CreateSor(pScrn, pNv->i2cMap[i].sor, OUTPUT_TMDS);
285 NVOutputPrivatePtr nv_output = dac->driver_private;
287 nv_output->pDDCBus = i2c;
288 nv_output->scaling_mode = SCALE_PANEL;
291 NVOutputPrivatePtr nv_output = sor->driver_private;
293 nv_output->pDDCBus = i2c;
294 nv_output->scaling_mode = SCALE_ASPECT;
298 if (pNv->lvds.present) {
299 xf86OutputPtr lvds = NV50CreateSor(pScrn, pNv->lvds.or, OUTPUT_LVDS);
300 NVOutputPrivatePtr nv_output = lvds->driver_private;
302 nv_output->scaling_mode = SCALE_ASPECT;
305 /* For each output, set the crtc and clone masks */
306 for(i = 0; i < xf86_config->num_output; i++) {
307 xf86OutputPtr output = xf86_config->output[i];
309 /* Any output can connect to any head */
310 output->possible_crtcs = 0x3;
311 output->possible_clones = 0;