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