Enable nv30 exa on PPC.
[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 #include <string.h>
25
26 #include "nv_include.h"
27 #include "nv50_type.h"
28 #include "nv50_display.h"
29 #include "nv50_output.h"
30
31 #include <xf86DDC.h>
32
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)
36 {
37         NV50DisplayWrite(pScrn, addr + or * 0x800, value);
38 }
39
40 CARD32 NV50OrRead(ScrnInfoPtr pScrn, int or, CARD32 addr)
41 {
42         return NV50DisplayRead(pScrn, addr + or * 0x800);
43 }
44
45 void NV50OutputWrite(xf86OutputPtr output, CARD32 addr, CARD32 value)
46 {
47         NV50OutputPrivPtr nv_output = output->driver_private;
48         ScrnInfoPtr pScrn = output->scrn;
49         NV50OrWrite(pScrn, nv_output->or, addr, value);
50 }
51
52 CARD32 NV50OutputRead(xf86OutputPtr output, CARD32 addr)
53 {
54         NV50OutputPrivPtr nv_output = output->driver_private;
55         ScrnInfoPtr pScrn = output->scrn;
56         return NV50OrRead(pScrn, nv_output->or, addr);
57 }
58
59 static Bool NV50ReadPortMapping(int scrnIndex, NVPtr pNv)
60 {
61          unsigned const char *VBIOS = pNv->VBIOS;
62          unsigned char *table2;
63          unsigned char headerSize, entries;
64          int i;
65          CARD16 a;
66          CARD32 b;
67
68          if (!VBIOS)
69                 goto fail;
70
71          /* Clear the i2c map to invalid */
72          for (i = 0; i < 4; i++)
73                 pNv->i2cMap[i].dac = pNv->i2cMap[i].sor = -1;
74
75         if (*(CARD16*)VBIOS != 0xaa55) goto fail;
76
77         a = *(CARD16*)(VBIOS + 0x36);
78         table2 = (unsigned char*)VBIOS + a;
79
80         if (table2[0] != 0x40) goto fail;
81
82         b = *(CARD32*)(table2 + 6);
83         if (b != 0x4edcbdcb) goto fail;
84
85         headerSize = table2[1];
86         entries = table2[2];
87
88         for(i = 0; i < entries; i++) {
89                 int type, port;
90                 ORNum or;
91
92                 b = *(CARD32*)&table2[headerSize + 8*i];
93                 type = b & 0xf;
94                 port = (b >> 4) & 0xf;
95                 or = ffs((b >> 24) & 0xf) - 1;
96
97                 if (type == 0xe)
98                         break;
99
100                 if(type < 4) {
101                         switch(type) {
102                                 case 0: /* CRT */
103                                         if(pNv->i2cMap[port].dac != -1) {
104                                                 xf86DrvMsg(scrnIndex, X_WARNING,
105                                                         "DDC routing table corrupt!  DAC %i -> %i "
106                                                         "for port %i\n",
107                                                         or, pNv->i2cMap[port].dac, port);
108                                         }
109                                         pNv->i2cMap[port].dac = or;
110                                         break;
111                                 case 1: /* TV */
112                                         break;
113                                 case 2: /* TMDS */
114                                         if(pNv->i2cMap[port].sor != -1)
115                                                 xf86DrvMsg(scrnIndex, X_WARNING,
116                                                         "DDC routing table corrupt!  SOR %i -> %i "
117                                                         "for port %i\n",
118                                                         or, pNv->i2cMap[port].sor, port);
119                                         pNv->i2cMap[port].sor = or;
120                                         break;
121                                 case 3: /* LVDS */
122                                         pNv->lvds.present = TRUE;
123                                         pNv->lvds.or = or;
124                                         break;
125                         }
126                 }
127         }
128
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);
133         }
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);
139         }
140
141         return TRUE;
142
143         fail:
144                 xf86DrvMsg(scrnIndex, X_ERROR, "Couldn't find the DDC routing table.  "
145                         "Mode setting will probably fail!\n");
146                 return FALSE;
147 }
148
149 static void NV50_I2CPutBits(I2CBusPtr b, int clock, int data)
150 {
151         NVPtr pNv = NVPTR(xf86Screens[b->scrnIndex]);
152         const int off = b->DriverPrivate.val * 0x18;
153
154         pNv->REGS[(0x0000E138+off)/4] = 4 | clock | data << 1;
155 }
156
157 static void NV50_I2CGetBits(I2CBusPtr b, int *clock, int *data)
158 {
159         NVPtr pNv = NVPTR(xf86Screens[b->scrnIndex]);
160         const int off = b->DriverPrivate.val * 0x18;
161         unsigned char val;
162
163         val = pNv->REGS[(0x0000E138+off)/4];
164         *clock = !!(val & 1);
165         *data = !!(val & 2);
166 }
167
168 static I2CBusPtr
169 NV50I2CInit(ScrnInfoPtr pScrn, const char *name, const int port)
170 {
171         I2CBusPtr i2c;
172
173         /* Allocate the I2C bus structure */
174         i2c = xf86CreateI2CBusRec();
175         if(!i2c) return NULL;
176
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;
187
188         if(xf86I2CBusInit(i2c)) {
189                 return i2c;
190         } else {
191                 xfree(i2c);
192                 return NULL;
193         }
194 }
195
196 void
197 NV50OutputSetPClk(xf86OutputPtr output, int pclk)
198 {
199         NV50OutputPrivPtr nv_output = output->driver_private;
200
201         if (nv_output->set_pclk)
202                 nv_output->set_pclk(output, pclk);
203 }
204
205 int
206 NV50OutputModeValid(xf86OutputPtr output, DisplayModePtr mode)
207 {
208         if (mode->Clock > 400000)
209                 return MODE_CLOCK_HIGH;
210         if (mode->Clock < 25000)
211                 return MODE_CLOCK_LOW;
212
213         return MODE_OK;
214 }
215
216 void
217 NV50OutputPrepare(xf86OutputPtr output)
218 {
219 }
220
221 void
222 NV50OutputCommit(xf86OutputPtr output)
223 {
224 }
225
226 static xf86MonPtr
227 ProbeDDC(I2CBusPtr i2c)
228 {
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;
233
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;
240
241         if(monInfo) {
242                 xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
243                         "DDC detected a %s:\n", monInfo->features.input_type ?
244                         "DFP" : "CRT");
245                 xf86PrintEDID(monInfo);
246         } else {
247                 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "  ... none found\n");
248         }
249
250         return monInfo;
251 }
252
253 /*
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.
257  */
258 void NV50OutputPartnersDetect(xf86OutputPtr dac, xf86OutputPtr sor, I2CBusPtr i2c)
259 {
260         xf86MonPtr monInfo = ProbeDDC(i2c);
261         xf86OutputPtr connected = NULL;
262         Bool load = dac && NV50DacLoadDetect(dac);
263
264         if(dac) {
265                 NV50OutputPrivPtr nv_output = dac->driver_private;
266
267                 if(load) {
268                         nv_output->cached_status = XF86OutputStatusConnected;
269                         connected = dac;
270                 } else {
271                         nv_output->cached_status = XF86OutputStatusDisconnected;
272                 }
273         }
274
275         if(sor) {
276                 NV50OutputPrivPtr nv_output = sor->driver_private;
277
278                 if(monInfo && !load) {
279                         nv_output->cached_status = XF86OutputStatusConnected;
280                         connected = sor;
281                 } else {
282                         nv_output->cached_status = XF86OutputStatusDisconnected;
283                 }
284         }
285
286         if(connected)
287                 xf86OutputSetEDID(connected, monInfo);
288 }
289
290 /*
291  * Reset the cached output status for all outputs.  Called from NV50BlockHandler.
292  */
293 void
294 NV50OutputResetCachedStatus(ScrnInfoPtr pScrn)
295 {
296         xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
297         int i;
298
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;
302         }
303 }
304
305 DisplayModePtr
306 NV50OutputGetDDCModes(xf86OutputPtr output)
307 {
308         /* The EDID is read as part of the detect step */
309         output->funcs->detect(output);
310         return xf86OutputGetEDIDModes(output);
311 }
312
313 void
314 NV50OutputDestroy(xf86OutputPtr output)
315 {
316         NV50OutputPrivPtr nv_output = output->driver_private;
317
318         if(nv_output->partner)
319                 ((NV50OutputPrivPtr)nv_output->partner->driver_private)->partner = NULL;
320         else
321                 xf86DestroyI2CBusRec(nv_output->i2c, TRUE, TRUE);
322         nv_output->i2c = NULL;
323 }
324
325 Bool
326 NV50CreateOutputs(ScrnInfoPtr pScrn)
327 {
328         NVPtr pNv = NVPTR(pScrn);
329         xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
330         int i;
331
332         if(!NV50ReadPortMapping(pScrn->scrnIndex, pNv))
333                 return FALSE;
334
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;
338                 I2CBusPtr i2c;
339                 char i2cName[16];
340
341                 if(pNv->i2cMap[i].dac == -1 && pNv->i2cMap[i].sor == -1) {
342                         /* No outputs on this port */
343                         continue;
344                 }
345
346                 snprintf(i2cName, sizeof(i2cName), "I2C%i", i);
347                 i2c = NV50I2CInit(pScrn, i2cName, i);
348                 if (!i2c) {
349                 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
350                                 "Failed to initialize I2C for port %i.\n",
351                                 i);
352                         continue;
353                 }
354
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);
359
360                 if (dac) {
361                         NV50OutputPrivPtr nv_output = dac->driver_private;
362
363                         nv_output->partner = sor;
364                         nv_output->i2c = i2c;
365                         nv_output->scale = NV50_SCALE_OFF;
366                 }
367                 if (sor) {
368                         NV50OutputPrivPtr nv_output = sor->driver_private;
369
370                         nv_output->partner = dac;
371                         nv_output->i2c = i2c;
372                         nv_output->scale = NV50_SCALE_ASPECT;
373                 }
374         }
375
376         if (pNv->lvds.present) {
377                 xf86OutputPtr lvds = NV50CreateSor(pScrn, pNv->lvds.or, LVDS);
378                 NV50OutputPrivPtr nv_output = lvds->driver_private;
379
380                 nv_output->scale = NV50_SCALE_ASPECT;
381         }
382
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];
386
387                 /* Any output can connect to any head */
388                 output->possible_crtcs = 0x3;
389                 output->possible_clones = 0;
390         }
391
392         return TRUE;
393 }
394