Skip to content

Commit

Permalink
drm/nouveau: implement parsing of DCB 2.2 GPIO table
Browse files Browse the repository at this point in the history
Found on NV3x boards, this should allow voltage modifications to work
on these chipsets.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
  • Loading branch information
Ben Skeggs committed Sep 24, 2010
1 parent 07b1266 commit e49f70f
Showing 1 changed file with 63 additions and 93 deletions.
156 changes: 63 additions & 93 deletions drivers/gpu/drm/nouveau/nouveau_bios.c
Original file line number Diff line number Diff line change
Expand Up @@ -5762,8 +5762,14 @@ static uint16_t findstr(uint8_t *data, int n, const uint8_t *str, int len)
static struct dcb_gpio_entry *
new_gpio_entry(struct nvbios *bios)
{
struct drm_device *dev = bios->dev;
struct dcb_gpio_table *gpio = &bios->dcb.gpio;

if (gpio->entries >= DCB_MAX_NUM_GPIO_ENTRIES) {
NV_ERROR(dev, "exceeded maximum number of gpio entries!!\n");
return NULL;
}

return &gpio->entry[gpio->entries++];
}

Expand All @@ -5784,114 +5790,78 @@ nouveau_bios_gpio_entry(struct drm_device *dev, enum dcb_gpio_tag tag)
return NULL;
}

static void
parse_dcb30_gpio_entry(struct nvbios *bios, uint16_t offset)
{
struct dcb_gpio_entry *gpio;
uint16_t ent = ROM16(bios->data[offset]);
uint8_t line = ent & 0x1f,
tag = ent >> 5 & 0x3f,
flags = ent >> 11 & 0x1f;

if (tag == 0x3f)
return;

gpio = new_gpio_entry(bios);

gpio->tag = tag;
gpio->line = line;
gpio->invert = flags != 4;
gpio->entry = ent;
}

static void
parse_dcb40_gpio_entry(struct nvbios *bios, uint16_t offset)
{
uint32_t entry = ROM32(bios->data[offset]);
struct dcb_gpio_entry *gpio;

if ((entry & 0x0000ff00) == 0x0000ff00)
return;

gpio = new_gpio_entry(bios);
gpio->tag = (entry & 0x0000ff00) >> 8;
gpio->line = (entry & 0x0000001f) >> 0;
gpio->state_default = (entry & 0x01000000) >> 24;
gpio->state[0] = (entry & 0x18000000) >> 27;
gpio->state[1] = (entry & 0x60000000) >> 29;
gpio->entry = entry;
}

static void
parse_dcb_gpio_table(struct nvbios *bios)
{
struct drm_device *dev = bios->dev;
uint16_t gpio_table_ptr = bios->dcb.gpio_table_ptr;
uint8_t *gpio_table = &bios->data[gpio_table_ptr];
int header_len = gpio_table[1],
entries = gpio_table[2],
entry_len = gpio_table[3];
void (*parse_entry)(struct nvbios *, uint16_t) = NULL;
struct dcb_gpio_entry *e;
u8 headerlen, entries, recordlen;
u8 *dcb, *gpio = NULL, *entry;
int i;

if (bios->dcb.version >= 0x40) {
if (gpio_table_ptr && entry_len != 4) {
NV_WARN(dev, "Invalid DCB GPIO table entry length.\n");
return;
}

parse_entry = parse_dcb40_gpio_entry;

} else if (bios->dcb.version >= 0x30) {
if (gpio_table_ptr && entry_len != 2) {
NV_WARN(dev, "Invalid DCB GPIO table entry length.\n");
return;
}

parse_entry = parse_dcb30_gpio_entry;
dcb = ROMPTR(bios, bios->data[0x36]);
if (dcb[0] >= 0x30) {
gpio = ROMPTR(bios, dcb[10]);
if (!gpio)
goto no_table;

} else if (bios->dcb.version >= 0x22) {
/*
* DCBs older than v3.0 don't really have a GPIO
* table, instead they keep some GPIO info at fixed
* locations.
*/
uint16_t dcbptr = ROM16(bios->data[0x36]);
uint8_t *tvdac_gpio = &bios->data[dcbptr - 5];
headerlen = gpio[1];
entries = gpio[2];
recordlen = gpio[3];
} else
if (dcb[0] >= 0x22) {
gpio = ROMPTR(bios, dcb[-15]);
if (!gpio)
goto no_table;

headerlen = 3;
entries = gpio[2];
recordlen = gpio[1];
} else {
NV_DEBUG(dev, "no/unknown gpio table on DCB 0x%02x\n", dcb[0]);
goto no_table;
}

if (tvdac_gpio[0] & 1) {
struct dcb_gpio_entry *gpio = new_gpio_entry(bios);
entry = gpio + headerlen;
for (i = 0; i < entries; i++, entry += recordlen) {
e = new_gpio_entry(bios);
if (!e)
break;

gpio->tag = DCB_GPIO_TVDAC0;
gpio->line = tvdac_gpio[1] >> 4;
gpio->invert = tvdac_gpio[0] & 2;
}
} else {
/*
* No systematic way to store GPIO info on pre-v2.2
* DCBs, try to match the PCI device IDs.
*/
if (gpio[0] < 0x40) {
e->entry = ROM16(entry[0]);
e->tag = (e->entry & 0x07e0) >> 5;
if (e->tag == 0x3f) {
bios->dcb.gpio.entries--;
continue;
}

/* Apple iMac G4 NV18 */
if (nv_match_device(dev, 0x0189, 0x10de, 0x0010)) {
struct dcb_gpio_entry *gpio = new_gpio_entry(bios);
e->line = (e->entry & 0x001f);
e->invert = ((e->entry & 0xf800) >> 11) != 4;
} else {
e->entry = ROM32(entry[0]);
e->tag = (e->entry & 0x0000ff00) >> 8;
if (e->tag == 0xff) {
bios->dcb.gpio.entries--;
continue;
}

gpio->tag = DCB_GPIO_TVDAC0;
gpio->line = 4;
e->line = (e->entry & 0x0000001f) >> 0;
e->state_default = (e->entry & 0x01000000) >> 24;
e->state[0] = (e->entry & 0x18000000) >> 27;
e->state[1] = (e->entry & 0x60000000) >> 29;
}

}

if (!gpio_table_ptr)
return;

if (entries > DCB_MAX_NUM_GPIO_ENTRIES) {
NV_WARN(dev, "Too many entries in the DCB GPIO table.\n");
entries = DCB_MAX_NUM_GPIO_ENTRIES;
no_table:
/* Apple iMac G4 NV18 */
if (nv_match_device(dev, 0x0189, 0x10de, 0x0010)) {
e = new_gpio_entry(bios);
if (e) {
e->tag = DCB_GPIO_TVDAC0;
e->line = 4;
}
}

for (i = 0; i < entries; i++)
parse_entry(bios, gpio_table_ptr + header_len + entry_len * i);
}

struct dcb_connector_table_entry *
Expand Down

0 comments on commit e49f70f

Please sign in to comment.