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