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