diff options
author | Dave Airlie <airlied@linux.ie> | 2007-03-25 12:12:55 +1000 |
---|---|---|
committer | Dave Airlie <airlied@linux.ie> | 2007-03-25 12:12:55 +1000 |
commit | 4c2d386aef46a10541ab3d43165226dda8060b0d (patch) | |
tree | 73640c92d1b92703f7de9777b473b54f2d56a338 | |
parent | 5f23553ab361e586a366e9e3cc9a483f8c5621de (diff) | |
download | xorg-driver-xf86-video-nouveau-4c2d386aef46a10541ab3d43165226dda8060b0d.tar.gz |
randr12: attempt to parse DCB table from nv40 bios
-rw-r--r-- | src/nv_bios.c | 57 | ||||
-rw-r--r-- | src/nv_output.c | 137 | ||||
-rw-r--r-- | src/nv_type.h | 6 |
3 files changed, 155 insertions, 45 deletions
diff --git a/src/nv_bios.c b/src/nv_bios.c index 286e85f..a43a4fb 100644 --- a/src/nv_bios.c +++ b/src/nv_bios.c @@ -1498,6 +1498,52 @@ static unsigned int findstr(bios_t* bios, unsigned char *str, int len) return 0; } +#define G5_FIXED_LOC 0xe2f8 + + +static unsigned int nv_find_dcb_table(ScrnInfoPtr pScrn, bios_t *bios) +{ + NVPtr pNv = NVPTR(pScrn); + CARD16 bufloc; + int is_g5; + CARD32 sig; + char *table2; + unsigned char headerSize, entries; + CARD32 header_word; + int i; + + /* get the offset from 0x36 */ + + bufloc = *(CARD16 *)&bios->data[0x36]; + if (bufloc == 0x0) { + if ((pNv->Chipset & 0x0ff0) == CHIPSET_NV43) { + is_g5 = 1; + bufloc = G5_FIXED_LOC; + } else { + return 0; + } + } + + table2 = &bios->data[bufloc]; + sig = *(uint32_t*)(table2 + 6); + if ((sig != 0x4edcbdcb) && (sig!=0xcbbddc4e)) + return 0; + + header_word = *(uint32_t *)table2; + headerSize = (header_word >> 8) & 0xff; + entries = (header_word >> 16) & 0xff; + + if (entries >= NV40_NUM_DCB_ENTRIES) + entries = NV40_NUM_DCB_ENTRIES; + + for (i = 0; i < entries; i++) { + pNv->dcb_table[i] = *(uint32_t *)&table2[headerSize + 8 * i]; + } + + return entries; +} + + unsigned int NVParseBios(ScrnInfoPtr pScrn) { unsigned int bit_offset; @@ -1507,7 +1553,7 @@ unsigned int NVParseBios(ScrnInfoPtr pScrn) unsigned char nv_signature[]={0xff,0x7f,'N','V',0x0}; unsigned char bit_signature[]={'B','I','T'}; NVPtr pNv; - int i; + int i, ret; pNv = NVPTR(pScrn); bios.data=xalloc(NV_PROM_SIZE); @@ -1546,6 +1592,15 @@ unsigned int NVParseBios(ScrnInfoPtr pScrn) xf86DrvMsg(pScrn->scrnIndex, X_INFO, "No known script signature found.\n"); } + /* look for NV40+ DCB table - and make a copy somewhere for output setup code */ + ret = nv_find_dcb_table(pScrn, &bios); + if (ret) + { + pNv->dcb_entries = ret; + xf86DrvMsg(pScrn->scrnIndex, X_INFO, "DCB found %d entries.\n", ret); + } + else + xf86DrvMsg(pScrn->scrnIndex, X_INFO, "No DCB table found\n"); xfree(bios.data); return 1; } diff --git a/src/nv_output.c b/src/nv_output.c index eeebab0..1faf3b5 100644 --- a/src/nv_output.c +++ b/src/nv_output.c @@ -682,6 +682,71 @@ static const xf86OutputFuncsRec nv_lvds_output_funcs = { .commit = nv_output_commit, }; + +static void nv_add_analog_output(ScrnInfoPtr pScrn, int i2c_index) +{ + NVPtr pNv = NVPTR(pScrn); + xf86OutputPtr output; + NVOutputPrivatePtr nv_output; + char outputname[20]; + int crtc_mask = (1<<0) | (1<<1); + + sprintf(outputname, "Analog-%d", pNv->analog_count); + output = xf86OutputCreate (pScrn, &nv_analog_output_funcs, outputname); + if (!output) + return; + nv_output = xnfcalloc (sizeof (NVOutputPrivateRec), 1); + if (!nv_output) + { + xf86OutputDestroy (output); + return; + } + + output->driver_private = nv_output; + nv_output->type = OUTPUT_ANALOG; + + nv_output->ramdac = pNv->analog_count; + + nv_output->pDDCBus = pNv->pI2CBus[i2c_index]; + + output->possible_crtcs = crtc_mask; + xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "Adding output %s\n", outputname); + + pNv->analog_count++; +} + + +static void nv_add_digital_output(ScrnInfoPtr pScrn, int i2c_index) +{ + NVPtr pNv = NVPTR(pScrn); + xf86OutputPtr output; + NVOutputPrivatePtr nv_output; + char outputname[20]; + int crtc_mask = (1<<0) | (1<<1); + + sprintf(outputname, "Digital-%d", pNv->digital_count); + output = xf86OutputCreate (pScrn, &nv_digital_output_funcs, outputname); + if (!output) + return; + nv_output = xnfcalloc (sizeof (NVOutputPrivateRec), 1); + if (!nv_output) + { + xf86OutputDestroy (output); + return; + } + + output->driver_private = nv_output; + nv_output->type = OUTPUT_DIGITAL; + + nv_output->ramdac = pNv->digital_count; + + nv_output->pDDCBus = pNv->pI2CBus[i2c_index]; + + output->possible_crtcs = crtc_mask; + xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "Adding output %s\n", outputname); + + pNv->digital_count++; +} /** * Set up the outputs according to what type of chip we are. * @@ -697,61 +762,45 @@ void Nv20SetupOutputs(ScrnInfoPtr pScrn) int i; int num_analog_outputs = pNv->twoHeads ? 2 : 1; int num_digital_outputs = 1; - char outputname[20]; - int crtc_mask = (1<<0) | (1<<1); for (i = 0 ; i < num_analog_outputs; i++) { - sprintf(outputname, "Analog-%d", i); - output = xf86OutputCreate (pScrn, &nv_analog_output_funcs, outputname); - if (!output) - return; - nv_output = xnfcalloc (sizeof (NVOutputPrivateRec), 1); - if (!nv_output) - { - xf86OutputDestroy (output); - return; - } - - output->driver_private = nv_output; - nv_output->type = OUTPUT_ANALOG; - - nv_output->ramdac = i; - - nv_output->pDDCBus = pNv->pI2CBus[i]; - - output->possible_crtcs = crtc_mask; - xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "Adding output %s\n", outputname); - + nv_add_analog_output(pScrn, i); } for (i = 0 ; i < num_digital_outputs; i++) { - sprintf(outputname, "Digital-%d", i); - output = xf86OutputCreate (pScrn, &nv_digital_output_funcs, outputname); - if (!output) - return; - nv_output = xnfcalloc (sizeof (NVOutputPrivateRec), 1); - if (!nv_output) - { - xf86OutputDestroy (output); - return; - } - - output->driver_private = nv_output; - nv_output->type = OUTPUT_DIGITAL; - - nv_output->ramdac = i; - - nv_output->pDDCBus = pNv->pI2CBus[i]; - - output->possible_crtcs = crtc_mask; - xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "Adding output %s\n", outputname); + nv_add_digital_output(pScrn, i); } - } void Nv40SetupOutputs(ScrnInfoPtr pScrn) { + unsigned char type, port, or; + NVPtr pNv = NVPTR(pScrn); + int i; + /* we setup the outputs up from the BIOS table */ + if (pNv->dcb_entries) { + for (i = 0 ; i < pNv->dcb_entries; i++) { + type = pNv->dcb_table[i] & 0xf; + port = (pNv->dcb_table[i] >> 4) & 0xf; + or = ffs((pNv->dcb_table[i] >> 24) & 0xf) - 1; + + if (type < 4) + xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "DCB entry: %d: %08X type: %d, port %d:, or %d\n", i, pNv->dcb_table[i], type, port, or); + if (type < 4 && port != 0xf) { + switch(type) { + case 0: /* analog */ + nv_add_analog_output(pScrn, port); + break; + case 2: + nv_add_digital_output(pScrn, port); + default: + break; + } + } + } + } else + Nv20SetupOutputs(pScrn); } diff --git a/src/nv_type.h b/src/nv_type.h index 70d3798..36ec7b5 100644 --- a/src/nv_type.h +++ b/src/nv_type.h @@ -71,6 +71,7 @@ #define Set8Bits(value) ((value)&0xff) #define NV_I2C_BUSES 3 +#define NV40_NUM_DCB_ENTRIES 10 typedef enum { @@ -303,6 +304,11 @@ typedef struct _NVRec { /* we know about 3 i2c buses */ I2CBusPtr pI2CBus[3]; + int dcb_entries; + + int analog_count; + int digital_count; + CARD32 dcb_table[NV40_NUM_DCB_ENTRIES]; /* 10 is a good limit */ } NVRec; #define NVPTR(p) ((NVPtr)((p)->driverPrivate)) |