randr12: Change the way CRTC register 59 is set.
[nouveau] / src / nv50_output.c
1 /*
2  * Copyright (c) 2007 NVIDIA, Corporation
3  *
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:
11  *
12  * The above copyright notice and this permission notice shall be included
13  * in all copies or substantial portions of the Software.
14  *
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.
22  */
23
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28
29 #ifdef ENABLE_RANDR12
30
31 #include <string.h>
32
33
34 #include "nv_include.h"
35 #include "nv50_type.h"
36 #include "nv50_display.h"
37 #include "nv50_output.h"
38
39 #include <xf86DDC.h>
40
41 static Bool NV50ReadPortMapping(int scrnIndex, NVPtr pNv)
42 {
43     unsigned const char *VBIOS = pNv->VBIOS;
44     unsigned char *table2;
45     unsigned char headerSize, entries;
46     int i;
47     CARD16 a;
48     CARD32 b;
49
50     if (!VBIOS)
51             goto fail;
52
53     /* Clear the i2c map to invalid */
54     for(i = 0; i < 4; i++)
55         pNv->i2cMap[i].dac = pNv->i2cMap[i].sor = -1;
56
57     if(*(CARD16*)VBIOS != 0xaa55) goto fail;
58
59     a = *(CARD16*)(VBIOS + 0x36);
60     table2 = (unsigned char*)VBIOS + a;
61
62     if(table2[0] != 0x40) goto fail;
63
64     b = *(CARD32*)(table2 + 6);
65     if(b != 0x4edcbdcb) goto fail;
66
67     headerSize = table2[1];
68     entries = table2[2];
69
70     for(i = 0; i < entries; i++) {
71         int type, port;
72         ORNum or;
73
74         b = *(CARD32*)&table2[headerSize + 8*i];
75         type = b & 0xf;
76         port = (b >> 4) & 0xf;
77         or = ffs((b >> 24) & 0xf) - 1;
78
79         if (type == 0xe)
80                 break;
81
82         if(type < 4) {
83             switch(type) {
84                 case 0: /* CRT */
85                     if(pNv->i2cMap[port].dac != -1) {
86                         xf86DrvMsg(scrnIndex, X_WARNING,
87                                    "DDC routing table corrupt!  DAC %i -> %i "
88                                    "for port %i\n",
89                                    or, pNv->i2cMap[port].dac, port);
90                     }
91                     pNv->i2cMap[port].dac = or;
92                     break;
93                 case 1: /* TV */
94                     break;
95                 case 2: /* TMDS */
96                     if(pNv->i2cMap[port].sor != -1)
97                         xf86DrvMsg(scrnIndex, X_WARNING,
98                                    "DDC routing table corrupt!  SOR %i -> %i "
99                                    "for port %i\n",
100                                    or, pNv->i2cMap[port].sor, port);
101                     pNv->i2cMap[port].sor = or;
102                     break;
103                 case 3: /* LVDS */
104                     pNv->lvds.present = TRUE;
105                     pNv->lvds.or = or;
106                     break;
107             }
108         }
109     }
110
111     xf86DrvMsg(scrnIndex, X_PROBED, "Connector map:\n");
112     if (pNv->lvds.present) {
113             xf86DrvMsg(scrnIndex, X_PROBED,
114                        "  [N/A] -> SOR%i (LVDS)\n", pNv->lvds.or);
115     }
116     for(i = 0; i < 4; i++) {
117         if(pNv->i2cMap[i].dac != -1)
118             xf86DrvMsg(scrnIndex, X_PROBED, "  Bus %i -> DAC%i\n", i, pNv->i2cMap[i].dac);
119         if(pNv->i2cMap[i].sor != -1)
120             xf86DrvMsg(scrnIndex, X_PROBED, "  Bus %i -> SOR%i\n", i, pNv->i2cMap[i].sor);
121     }
122
123     return TRUE;
124
125 fail:
126     xf86DrvMsg(scrnIndex, X_ERROR, "Couldn't find the DDC routing table.  "
127                "Mode setting will probably fail!\n");
128     return FALSE;
129 }
130
131 static void NV50_I2CPutBits(I2CBusPtr b, int clock, int data)
132 {
133     NVPtr pNv = NVPTR(xf86Screens[b->scrnIndex]);
134     const int off = b->DriverPrivate.val * 0x18;
135
136     pNv->REGS[(0x0000E138+off)/4] = 4 | clock | data << 1;
137 }
138
139 static void NV50_I2CGetBits(I2CBusPtr b, int *clock, int *data)
140 {
141     NVPtr pNv = NVPTR(xf86Screens[b->scrnIndex]);
142     const int off = b->DriverPrivate.val * 0x18;
143     unsigned char val;
144
145     val = pNv->REGS[(0x0000E138+off)/4];
146     *clock = !!(val & 1);
147     *data = !!(val & 2);
148 }
149
150 static I2CBusPtr
151 NV50I2CInit(ScrnInfoPtr pScrn, const char *name, const int port)
152 {
153     I2CBusPtr i2c;
154
155     /* Allocate the I2C bus structure */
156     i2c = xf86CreateI2CBusRec();
157     if(!i2c) return NULL;
158
159     i2c->BusName = strdup(name);
160     i2c->scrnIndex = pScrn->scrnIndex;
161     i2c->I2CPutBits = NV50_I2CPutBits;
162     i2c->I2CGetBits = NV50_I2CGetBits;
163     i2c->ByteTimeout = 2200; /* VESA DDC spec 3 p. 43 (+10 %) */
164     i2c->StartTimeout = 550;
165     i2c->BitTimeout = 40;
166     i2c->ByteTimeout = 40;
167     i2c->AcknTimeout = 40;
168     i2c->DriverPrivate.val = port;
169
170     if(xf86I2CBusInit(i2c)) {
171         return i2c;
172     } else {
173         xfree(i2c);
174         return NULL;
175     }
176 }
177
178 void
179 NV50OutputSetPClk(xf86OutputPtr output, int pclk)
180 {
181     NV50OutputPrivPtr pPriv = output->driver_private;
182
183     if (pPriv->set_pclk)
184         pPriv->set_pclk(output, pclk);
185 }
186
187 int
188 NV50OutputModeValid(xf86OutputPtr output, DisplayModePtr mode)
189 {
190     if (mode->Clock > 400000)
191         return MODE_CLOCK_HIGH;
192     if (mode->Clock < 25000)
193         return MODE_CLOCK_LOW;
194
195     return MODE_OK;
196 }
197
198 void
199 NV50OutputPrepare(xf86OutputPtr output)
200 {
201 }
202
203 void
204 NV50OutputCommit(xf86OutputPtr output)
205 {
206 }
207
208 static xf86MonPtr
209 ProbeDDC(I2CBusPtr i2c)
210 {
211     ScrnInfoPtr pScrn = xf86Screens[i2c->scrnIndex];
212     NVPtr pNv = NVPTR(pScrn);
213     xf86MonPtr monInfo = NULL;
214     const int bus = i2c->DriverPrivate.val, off = bus * 0x18;
215
216     xf86DrvMsg(pScrn->scrnIndex, X_INFO,
217             "Probing for EDID on I2C bus %i...\n", bus);
218     pNv->REGS[(0x0000E138+off)/4] = 7;
219     /* Should probably use xf86OutputGetEDID here */
220     monInfo = xf86DoEDID_DDC2(pScrn->scrnIndex, i2c);
221     pNv->REGS[(0x0000E138+off)/4] = 3;
222
223     if(monInfo) {
224         xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
225                 "DDC detected a %s:\n", monInfo->features.input_type ?
226                 "DFP" : "CRT");
227         xf86PrintEDID(monInfo);
228     } else {
229         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "  ... none found\n");
230     }
231
232     return monInfo;
233 }
234
235 /*
236  * Read an EDID from the i2c port.  Perform load detection on the DAC (if
237  * present) to see if the display is connected via VGA.  Sets the cached status
238  * of both outputs.  The status is marked dirty again in the BlockHandler.
239  */
240 void NV50OutputPartnersDetect(xf86OutputPtr dac, xf86OutputPtr sor, I2CBusPtr i2c)
241 {
242     xf86MonPtr monInfo = ProbeDDC(i2c);
243     xf86OutputPtr connected = NULL;
244     Bool load = dac && NV50DacLoadDetect(dac);
245
246     if(dac) {
247         NV50OutputPrivPtr pPriv = dac->driver_private;
248
249         if(load) {
250             pPriv->cached_status = XF86OutputStatusConnected;
251             connected = dac;
252         } else {
253             pPriv->cached_status = XF86OutputStatusDisconnected;
254         }
255     }
256
257     if(sor) {
258         NV50OutputPrivPtr pPriv = sor->driver_private;
259
260         if(monInfo && !load) {
261             pPriv->cached_status = XF86OutputStatusConnected;
262             connected = sor;
263         } else {
264             pPriv->cached_status = XF86OutputStatusDisconnected;
265         }
266     }
267
268     if(connected)
269         xf86OutputSetEDID(connected, monInfo);
270 }
271
272 /*
273  * Reset the cached output status for all outputs.  Called from NV50BlockHandler.
274  */
275 void
276 NV50OutputResetCachedStatus(ScrnInfoPtr pScrn)
277 {
278     xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
279     int i;
280
281     for(i = 0; i < xf86_config->num_output; i++) {
282         NV50OutputPrivPtr pPriv = xf86_config->output[i]->driver_private;
283         pPriv->cached_status = XF86OutputStatusUnknown;
284     }
285 }
286
287 DisplayModePtr
288 NV50OutputGetDDCModes(xf86OutputPtr output)
289 {
290     /* The EDID is read as part of the detect step */
291     output->funcs->detect(output);
292     return xf86OutputGetEDIDModes(output);
293 }
294
295 void
296 NV50OutputDestroy(xf86OutputPtr output)
297 {
298     NV50OutputPrivPtr pPriv = output->driver_private;
299
300     if(pPriv->partner)
301         ((NV50OutputPrivPtr)pPriv->partner->driver_private)->partner = NULL;
302     else
303         xf86DestroyI2CBusRec(pPriv->i2c, TRUE, TRUE);
304     pPriv->i2c = NULL;
305 }
306
307 Bool
308 NV50CreateOutputs(ScrnInfoPtr pScrn)
309 {
310     NVPtr pNv = NVPTR(pScrn);
311     xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
312     int i;
313
314     if(!NV50ReadPortMapping(pScrn->scrnIndex, pNv))
315         return FALSE;
316
317     /* For each DDC port, create an output for the attached ORs */
318     for(i = 0; i < 4; i++) {
319         xf86OutputPtr dac = NULL, sor = NULL;
320         I2CBusPtr i2c;
321         char i2cName[16];
322
323         if(pNv->i2cMap[i].dac == -1 && pNv->i2cMap[i].sor == -1)
324             /* No outputs on this port */
325             continue;
326
327         snprintf(i2cName, sizeof(i2cName), "I2C%i", i);
328         i2c = NV50I2CInit(pScrn, i2cName, i);
329         if(!i2c) {
330             xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
331                        "Failed to initialize I2C for port %i.\n",
332                        i);
333             continue;
334         }
335
336         if(pNv->i2cMap[i].dac != -1)
337             dac = NV50CreateDac(pScrn, pNv->i2cMap[i].dac);
338         if(pNv->i2cMap[i].sor != -1)
339             sor = NV50CreateSor(pScrn, pNv->i2cMap[i].sor, TMDS);
340
341         if(dac) {
342             NV50OutputPrivPtr pPriv = dac->driver_private;
343
344             pPriv->partner = sor;
345             pPriv->i2c = i2c;
346             pPriv->scale = NV50_SCALE_OFF;
347         }
348         if(sor) {
349             NV50OutputPrivPtr pPriv = sor->driver_private;
350
351             pPriv->partner = dac;
352             pPriv->i2c = i2c;
353             pPriv->scale = NV50_SCALE_ASPECT;
354         }
355     }
356
357     if (pNv->lvds.present) {
358         xf86OutputPtr lvds = NV50CreateSor(pScrn, pNv->lvds.or, LVDS);
359         NV50OutputPrivPtr pPriv = lvds->driver_private;
360
361         pPriv->scale = NV50_SCALE_ASPECT;
362     }
363
364     /* For each output, set the crtc and clone masks */
365     for(i = 0; i < xf86_config->num_output; i++) {
366         xf86OutputPtr output = xf86_config->output[i];
367
368         /* Any output can connect to any head */
369         output->possible_crtcs = 0x3;
370         output->possible_clones = 0;
371     }
372
373     return TRUE;
374 }
375
376 #endif /* ENABLE_RANDR12 */