g80_output.c revision 6086d97e
1/*
2 * Copyright (c) 2007-2008 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 <strings.h>
30
31#include "g80_type.h"
32#include "g80_display.h"
33#include "g80_output.h"
34
35static unsigned G80FindLoadVal(const unsigned char *table1)
36{
37    const unsigned char *p = table1;
38    int count;
39    const CARD32 def = 340;
40
41    for(p = table1; *(CARD16*)p != 0xb8ff && p < table1 + 64000; p += 2);
42    if(p == table1 + 64000)
43        return def;
44    p += 2;
45    if(*(CARD32*)p != 0x544942)
46        return def;
47    p += 4;
48    if(*(CARD16*)p != 0x100)
49        return def;
50    p += 2;
51    if(*p != 12)
52        return def;
53    p++;
54    if(*p != 6)
55        return def;
56    p++;
57    count = *p;
58    p += 2;
59    for(; *p != 'A' && count >= 0; count--, p += 6);
60    if(count == -1)
61        return def;
62    p += 4;
63    p = table1 + *(CARD16*)p;
64    p = table1 + *(CARD16*)p;
65    if(p[0] != 0x10 || p[1] != 4 || p[2] != 4 || p[3] != 2)
66        return def;
67    return *(CARD32*)(p + 4) & 0x3ff;
68}
69
70static Bool G80ReadPortMapping(int scrnIndex, G80Ptr pNv)
71{
72    unsigned char *table2, *table3;
73    unsigned char headerSize, entries, table3Entries, table3EntSize;
74    int i;
75    CARD16 a;
76    CARD32 b;
77
78    /* Clear the i2c map to invalid */
79    for(i = 0; i < G80_NUM_I2C_PORTS; i++)
80        pNv->i2cMap[i].dac = pNv->i2cMap[i].sor = -1;
81
82    if(*(CARD16*)pNv->table1 != 0xaa55) goto fail;
83
84    a = *(CARD16*)(pNv->table1 + 0x36);
85    table2 = (unsigned char*)pNv->table1 + a;
86
87    if(table2[0] != 0x40) goto fail;
88
89    b = *(CARD32*)(table2 + 6);
90    if(b != 0x4edcbdcb) goto fail;
91
92    table3 = (unsigned char*)pNv->table1 + *(CARD16*)(table2 + 4);
93    table3Entries = table3[2];
94    table3EntSize = table3[3];
95    table3 += table3[1];
96
97    headerSize = table2[1];
98    entries = table2[2];
99
100    for(i = 0; i < entries; i++) {
101        int type, port, portType;
102        ORNum or;
103
104        b = *(CARD32*)&table2[headerSize + 8*i];
105        type = b & 0xf;
106        port = (b >> 4) & 0xf;
107        or = ffs((b >> 24) & 0xf) - 1;
108
109        if(b & 0x300000) {
110            /* Can't handle this type of output yet */
111            xf86DrvMsg(scrnIndex, X_INFO,
112                       "Ignoring unsupported external output type %d at output "
113                       "%d\n", type, or);
114            continue;
115        }
116
117        if(type == 0xe) break;
118
119        switch(type) {
120            case 0: /* CRT */
121                if(port >= table3Entries) {
122                    xf86DrvMsg(scrnIndex, X_WARNING,
123                               "VGA%d: invalid port %d\n", or, port);
124                    break;
125                }
126                b = *(CARD32*)&table3[table3EntSize * port];
127                port = b & 0xff;
128                portType = b >> 24;
129                if(portType != 5) {
130                    xf86DrvMsg(scrnIndex, X_WARNING,
131                               "VGA%d: invalid port type %d\n", or, portType);
132                    break;
133                }
134                if(port >= G80_NUM_I2C_PORTS) {
135                    xf86DrvMsg(scrnIndex, X_WARNING,
136                               "VGA%d: unrecognized port %d\n", or, port);
137                    break;
138                }
139                if(pNv->i2cMap[port].dac != -1) {
140                    xf86DrvMsg(scrnIndex, X_WARNING,
141                               "DDC routing table corrupt!  DAC %i -> %i for "
142                               "port %i\n", or, pNv->i2cMap[port].dac, port);
143                }
144                pNv->i2cMap[port].dac = or;
145                break;
146            case 1: /* TV */
147                xf86DrvMsg(scrnIndex, X_INFO,
148                           "Ignoring unsupported TV output %d\n", or);
149                break;
150
151            case 2: /* TMDS */
152                if(port >= table3Entries) {
153                    xf86DrvMsg(scrnIndex, X_WARNING,
154                               "DVI%d: invalid port %d\n", or, port);
155                    break;
156                }
157                b = *(CARD32*)&table3[table3EntSize * port];
158                port = b & 0xff;
159                portType = b >> 24;
160                if(portType != 5) {
161                    xf86DrvMsg(scrnIndex, X_WARNING,
162                               "DVI%d: invalid port type %d\n", or, portType);
163                    break;
164                }
165                if(port >= G80_NUM_I2C_PORTS) {
166                    xf86DrvMsg(scrnIndex, X_WARNING,
167                               "DVI%d: unrecognized port %d\n", or, port);
168                    break;
169                }
170                if(pNv->i2cMap[port].sor != -1)
171                    xf86DrvMsg(scrnIndex, X_WARNING,
172                               "DDC routing table corrupt!  SOR %i -> %i for "
173                               "port %i\n", or, pNv->i2cMap[port].sor, port);
174                pNv->i2cMap[port].sor = or;
175                break;
176
177            case 3: /* LVDS */
178                pNv->lvds.present = TRUE;
179                pNv->lvds.or = or;
180                pNv->lvds.i2cPort = -1;
181
182                if(port == 15) {
183                    xf86DrvMsg(scrnIndex, X_INFO, "LVDS has no I2C port\n");
184                    break;
185                }
186                if(port >= table3Entries) {
187                    xf86DrvMsg(scrnIndex, X_WARNING,
188                               "LVDS: invalid port %d\n", port);
189                    break;
190                }
191                b = *(CARD32*)&table3[table3EntSize * port];
192                port = b & 0xff;
193                portType = b >> 24;
194                if(portType != 5) {
195                    xf86DrvMsg(scrnIndex, X_WARNING,
196                               "LVDS: invalid port type %d\n", portType);
197                    break;
198                }
199                if(port >= G80_NUM_I2C_PORTS) {
200                    xf86DrvMsg(scrnIndex, X_WARNING,
201                               "LVDS: unrecognized port %d\n", port);
202                    break;
203                }
204                pNv->lvds.i2cPort = port;
205
206                break;
207
208            case 6: /* DisplayPort */
209                xf86DrvMsg(scrnIndex, X_INFO,
210                           "Ignoring unsupported DisplayPort output %d\n", or);
211                break;
212
213            default:
214                xf86DrvMsg(scrnIndex, X_INFO,
215                           "Ignoring unsupported output type %d at port %d\n",
216                           type, or);
217                break;
218        }
219    }
220
221    xf86DrvMsg(scrnIndex, X_PROBED, "Connector map:\n");
222    if(pNv->lvds.present) {
223        if (pNv->lvds.i2cPort != -1)
224            xf86DrvMsg(scrnIndex, X_PROBED, "  Bus %i -> SOR%i (LVDS)\n", pNv->lvds.i2cPort, pNv->lvds.or);
225        else
226            xf86DrvMsg(scrnIndex, X_PROBED, "  [N/A] -> SOR%i (LVDS)\n", pNv->lvds.or);
227    }
228    for(i = 0; i < G80_NUM_I2C_PORTS; i++) {
229        if(pNv->i2cMap[i].dac != -1)
230            xf86DrvMsg(scrnIndex, X_PROBED, "  Bus %i -> DAC%i\n", i, pNv->i2cMap[i].dac);
231        if(pNv->i2cMap[i].sor != -1)
232            xf86DrvMsg(scrnIndex, X_PROBED, "  Bus %i -> SOR%i\n", i, pNv->i2cMap[i].sor);
233    }
234
235    pNv->loadVal = G80FindLoadVal(pNv->table1);
236    xf86DrvMsg(scrnIndex, X_PROBED, "Load detection: %d\n", pNv->loadVal);
237
238    return TRUE;
239
240fail:
241    xf86DrvMsg(scrnIndex, X_ERROR, "Couldn't find the DDC routing table.  "
242               "Mode setting will probably fail!\n");
243    return FALSE;
244}
245
246static CARD32 i2cAddr(const int port)
247{
248    const CARD32 addrs[G80_NUM_I2C_PORTS] = {
249        0xE138, 0xE150, 0xE168, 0xE180, 0xE254, 0xE274, 0xE764, 0xE780, 0xE79C,
250        0xE7B8
251    };
252    return addrs[port];
253}
254
255static void G80_I2CPutBits(I2CBusPtr b, int clock, int data)
256{
257    G80Ptr pNv = G80PTR(xf86Screens[b->scrnIndex]);
258    pNv->reg[i2cAddr(b->DriverPrivate.val)/4] = 4 | clock | data << 1;
259}
260
261static void G80_I2CGetBits(I2CBusPtr b, int *clock, int *data)
262{
263    G80Ptr pNv = G80PTR(xf86Screens[b->scrnIndex]);
264    unsigned char val;
265
266    val = pNv->reg[i2cAddr(b->DriverPrivate.val)/4];
267    *clock = !!(val & 1);
268    *data = !!(val & 2);
269}
270
271static I2CBusPtr
272G80I2CInit(ScrnInfoPtr pScrn, const char *name, const int port)
273{
274    I2CBusPtr i2c;
275
276    /* Allocate the I2C bus structure */
277    i2c = xf86CreateI2CBusRec();
278    if(!i2c) return NULL;
279
280    i2c->BusName = strdup(name);
281    i2c->scrnIndex = pScrn->scrnIndex;
282    i2c->I2CPutBits = G80_I2CPutBits;
283    i2c->I2CGetBits = G80_I2CGetBits;
284    i2c->ByteTimeout = 2200; /* VESA DDC spec 3 p. 43 (+10 %) */
285    i2c->StartTimeout = 550;
286    i2c->BitTimeout = 40;
287    i2c->ByteTimeout = 40;
288    i2c->AcknTimeout = 40;
289    i2c->DriverPrivate.val = port;
290
291    if(xf86I2CBusInit(i2c)) {
292        return i2c;
293    } else {
294        free(i2c);
295        return NULL;
296    }
297}
298
299void
300G80OutputSetPClk(xf86OutputPtr output, int pclk)
301{
302    G80OutputPrivPtr pPriv = output->driver_private;
303    if(pPriv->set_pclk)
304        pPriv->set_pclk(output, pclk);
305}
306
307int
308G80OutputModeValid(xf86OutputPtr output, DisplayModePtr mode)
309{
310    if(mode->Clock > 400000)
311        return MODE_CLOCK_HIGH;
312    if(mode->Clock < 25000)
313        return MODE_CLOCK_LOW;
314
315    return MODE_OK;
316}
317
318void
319G80OutputPrepare(xf86OutputPtr output)
320{
321}
322
323void
324G80OutputCommit(xf86OutputPtr output)
325{
326}
327
328static xf86MonPtr
329ProbeDDC(I2CBusPtr i2c)
330{
331    ScrnInfoPtr pScrn = xf86Screens[i2c->scrnIndex];
332    G80Ptr pNv = G80PTR(pScrn);
333    xf86MonPtr monInfo = NULL;
334    const int bus = i2c->DriverPrivate.val;
335    const CARD32 addr = i2cAddr(bus);
336
337    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
338            "Probing for EDID on I2C bus %i...\n", bus);
339    pNv->reg[addr/4] = 7;
340    /* Should probably use xf86OutputGetEDID here */
341#ifdef EDID_COMPLETE_RAWDATA
342    monInfo = xf86DoEEDID(pScrn->scrnIndex, i2c, TRUE);
343#else
344    monInfo = xf86DoEDID_DDC2(pScrn->scrnIndex, i2c);
345#endif
346    pNv->reg[addr/4] = 3;
347
348    if(monInfo) {
349        xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
350                "DDC detected a %s:\n", monInfo->features.input_type ?
351                "DFP" : "CRT");
352        xf86PrintEDID(monInfo);
353    } else {
354        xf86DrvMsg(pScrn->scrnIndex, X_INFO, "  ... none found\n");
355    }
356
357    return monInfo;
358}
359
360/*
361 * Read an EDID from the i2c port.  Perform load detection on the DAC (if
362 * present) to see if the display is connected via VGA.  Sets the cached status
363 * of both outputs.  The status is marked dirty again in the BlockHandler.
364 */
365void G80OutputPartnersDetect(xf86OutputPtr dac, xf86OutputPtr sor, I2CBusPtr i2c)
366{
367    xf86MonPtr monInfo = ProbeDDC(i2c);
368    xf86OutputPtr connected = NULL;
369    Bool load = dac && G80DacLoadDetect(dac);
370
371    if(dac) {
372        G80OutputPrivPtr pPriv = dac->driver_private;
373
374        if(load) {
375            pPriv->cached_status = XF86OutputStatusConnected;
376            connected = dac;
377        } else {
378            pPriv->cached_status = XF86OutputStatusDisconnected;
379        }
380    }
381
382    if(sor) {
383        G80OutputPrivPtr pPriv = sor->driver_private;
384
385        if(monInfo && !load) {
386            pPriv->cached_status = XF86OutputStatusConnected;
387            connected = sor;
388        } else {
389            pPriv->cached_status = XF86OutputStatusDisconnected;
390        }
391    }
392
393    if(connected)
394        xf86OutputSetEDID(connected, monInfo);
395}
396
397/*
398 * Reset the cached output status for all outputs.  Called from G80BlockHandler.
399 */
400void
401G80OutputResetCachedStatus(ScrnInfoPtr pScrn)
402{
403    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
404    int i;
405
406    for(i = 0; i < xf86_config->num_output; i++) {
407        G80OutputPrivPtr pPriv = xf86_config->output[i]->driver_private;
408        pPriv->cached_status = XF86OutputStatusUnknown;
409    }
410}
411
412DisplayModePtr
413G80OutputGetDDCModes(xf86OutputPtr output)
414{
415    /* The EDID is read as part of the detect step */
416    output->funcs->detect(output);
417    return xf86OutputGetEDIDModes(output);
418}
419
420void
421G80OutputDestroy(xf86OutputPtr output)
422{
423    G80OutputPrivPtr pPriv = output->driver_private;
424
425    if(pPriv->partner)
426        ((G80OutputPrivPtr)pPriv->partner->driver_private)->partner = NULL;
427    else
428        xf86DestroyI2CBusRec(pPriv->i2c, TRUE, TRUE);
429    pPriv->i2c = NULL;
430}
431
432Bool
433G80CreateOutputs(ScrnInfoPtr pScrn)
434{
435    G80Ptr pNv = G80PTR(pScrn);
436    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
437    int i;
438
439    if(!G80ReadPortMapping(pScrn->scrnIndex, pNv))
440        return FALSE;
441
442    /* For each DDC port, create an output for the attached ORs */
443    for(i = 0; i < G80_NUM_I2C_PORTS; i++) {
444        xf86OutputPtr dac = NULL, sor = NULL;
445        I2CBusPtr i2c;
446        char i2cName[16];
447
448        if(pNv->i2cMap[i].dac == -1 && pNv->i2cMap[i].sor == -1)
449            /* No outputs on this port */
450            continue;
451
452        snprintf(i2cName, sizeof(i2cName), "I2C%i", i);
453        i2c = G80I2CInit(pScrn, i2cName, i);
454        if(!i2c) {
455            xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
456                       "Failed to initialize I2C for port %i.\n",
457                       i);
458            continue;
459        }
460
461        if(pNv->i2cMap[i].dac != -1)
462            dac = G80CreateDac(pScrn, pNv->i2cMap[i].dac);
463        if(pNv->i2cMap[i].sor != -1)
464            sor = G80CreateSor(pScrn, pNv->i2cMap[i].sor, TMDS);
465
466        if(dac) {
467            G80OutputPrivPtr pPriv = dac->driver_private;
468
469            pPriv->partner = sor;
470            pPriv->i2c = i2c;
471            pPriv->scale = G80_SCALE_OFF;
472        }
473        if(sor) {
474            G80OutputPrivPtr pPriv = sor->driver_private;
475
476            pPriv->partner = dac;
477            pPriv->i2c = i2c;
478            pPriv->scale = G80_SCALE_ASPECT;
479        }
480    }
481
482    if(pNv->lvds.present) {
483        xf86OutputPtr lvds = G80CreateSor(pScrn, pNv->lvds.or, LVDS);
484
485        if (lvds) {
486            G80OutputPrivPtr pPriv = lvds->driver_private;
487
488            pPriv->scale = G80_SCALE_ASPECT;
489
490            if(pNv->lvds.i2cPort != -1) {
491                char i2cName[16];
492
493                snprintf(i2cName, sizeof(i2cName), "I2C%i (LVDS)", pNv->lvds.i2cPort);
494                pPriv->i2c = G80I2CInit(pScrn, i2cName, pNv->lvds.i2cPort);
495                if(!pPriv->i2c) {
496                    xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
497                               "Failed to initialize I2C for port %i (LVDS)!\n",
498                               pNv->lvds.i2cPort);
499                }
500            }
501        }
502    }
503
504    /* For each output, set the crtc and clone masks */
505    for(i = 0; i < xf86_config->num_output; i++) {
506        xf86OutputPtr output = xf86_config->output[i];
507
508        /* Any output can connect to any head */
509        output->possible_crtcs = 0x3;
510        output->possible_clones = 0;
511    }
512
513    return TRUE;
514}
515