diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau/core/subdev/bios')
-rw-r--r-- | drivers/gpu/drm/nouveau/core/subdev/bios/base.c | 5 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/core/subdev/bios/gpio.c | 128 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/core/subdev/bios/init.c | 65 |
3 files changed, 140 insertions, 58 deletions
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/base.c b/drivers/gpu/drm/nouveau/core/subdev/bios/base.c index dd111947eb86..f621f69fa1a2 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/bios/base.c +++ b/drivers/gpu/drm/nouveau/core/subdev/bios/base.c @@ -447,6 +447,7 @@ nouveau_bios_ctor(struct nouveau_object *parent, bios->version.chip = nv_ro08(bios, bit_i.offset + 2); bios->version.minor = nv_ro08(bios, bit_i.offset + 1); bios->version.micro = nv_ro08(bios, bit_i.offset + 0); + bios->version.patch = nv_ro08(bios, bit_i.offset + 4); } else if (bmp_version(bios)) { bios->version.major = nv_ro08(bios, bios->bmp_offset + 13); @@ -455,9 +456,9 @@ nouveau_bios_ctor(struct nouveau_object *parent, bios->version.micro = nv_ro08(bios, bios->bmp_offset + 10); } - nv_info(bios, "version %02x.%02x.%02x.%02x\n", + nv_info(bios, "version %02x.%02x.%02x.%02x.%02x\n", bios->version.major, bios->version.chip, - bios->version.minor, bios->version.micro); + bios->version.minor, bios->version.micro, bios->version.patch); return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/gpio.c b/drivers/gpu/drm/nouveau/core/subdev/bios/gpio.c index c90d4aa3ae4f..c84e93fa6d95 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/bios/gpio.c +++ b/drivers/gpu/drm/nouveau/core/subdev/bios/gpio.c @@ -27,84 +27,105 @@ #include <subdev/bios/gpio.h> u16 -dcb_gpio_table(struct nouveau_bios *bios) +dcb_gpio_table(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) { - u8 ver, hdr, cnt, len; - u16 dcb = dcb_table(bios, &ver, &hdr, &cnt, &len); + u16 data = 0x0000; + u16 dcb = dcb_table(bios, ver, hdr, cnt, len); if (dcb) { - if (ver >= 0x30 && hdr >= 0x0c) - return nv_ro16(bios, dcb + 0x0a); - if (ver >= 0x22 && nv_ro08(bios, dcb - 1) >= 0x13) - return nv_ro16(bios, dcb - 0x0f); + if (*ver >= 0x30 && *hdr >= 0x0c) + data = nv_ro16(bios, dcb + 0x0a); + else + if (*ver >= 0x22 && nv_ro08(bios, dcb - 1) >= 0x13) + data = nv_ro16(bios, dcb - 0x0f); + + if (data) { + *ver = nv_ro08(bios, data + 0x00); + if (*ver < 0x30) { + *hdr = 3; + *cnt = nv_ro08(bios, data + 0x02); + *len = nv_ro08(bios, data + 0x01); + } else + if (*ver <= 0x41) { + *hdr = nv_ro08(bios, data + 0x01); + *cnt = nv_ro08(bios, data + 0x02); + *len = nv_ro08(bios, data + 0x03); + } else { + data = 0x0000; + } + } } - return 0x0000; + return data; } u16 -dcb_gpio_entry(struct nouveau_bios *bios, int idx, int ent, u8 *ver) +dcb_gpio_entry(struct nouveau_bios *bios, int idx, int ent, u8 *ver, u8 *len) { - u16 gpio = dcb_gpio_table(bios); - if (gpio) { - *ver = nv_ro08(bios, gpio); - if (*ver < 0x30 && ent < nv_ro08(bios, gpio + 2)) - return gpio + 3 + (ent * nv_ro08(bios, gpio + 1)); - else if (ent < nv_ro08(bios, gpio + 2)) - return gpio + nv_ro08(bios, gpio + 1) + - (ent * nv_ro08(bios, gpio + 3)); - } + u8 hdr, cnt; + u16 gpio = !idx ? dcb_gpio_table(bios, ver, &hdr, &cnt, len) : 0x0000; + if (gpio && ent < cnt) + return gpio + hdr + (ent * *len); return 0x0000; } -int -dcb_gpio_parse(struct nouveau_bios *bios, int idx, u8 func, u8 line, +u16 +dcb_gpio_parse(struct nouveau_bios *bios, int idx, int ent, u8 *ver, u8 *len, struct dcb_gpio_func *gpio) { - u8 ver, hdr, cnt, len; - u16 entry; - int i = -1; - - while ((entry = dcb_gpio_entry(bios, idx, ++i, &ver))) { - if (ver < 0x40) { - u16 data = nv_ro16(bios, entry); + u16 data = dcb_gpio_entry(bios, idx, ent, ver, len); + if (data) { + if (*ver < 0x40) { + u16 info = nv_ro16(bios, data); *gpio = (struct dcb_gpio_func) { - .line = (data & 0x001f) >> 0, - .func = (data & 0x07e0) >> 5, - .log[0] = (data & 0x1800) >> 11, - .log[1] = (data & 0x6000) >> 13, - .param = !!(data & 0x8000), + .line = (info & 0x001f) >> 0, + .func = (info & 0x07e0) >> 5, + .log[0] = (info & 0x1800) >> 11, + .log[1] = (info & 0x6000) >> 13, + .param = !!(info & 0x8000), }; } else - if (ver < 0x41) { - u32 data = nv_ro32(bios, entry); + if (*ver < 0x41) { + u32 info = nv_ro32(bios, data); *gpio = (struct dcb_gpio_func) { - .line = (data & 0x0000001f) >> 0, - .func = (data & 0x0000ff00) >> 8, - .log[0] = (data & 0x18000000) >> 27, - .log[1] = (data & 0x60000000) >> 29, - .param = !!(data & 0x80000000), + .line = (info & 0x0000001f) >> 0, + .func = (info & 0x0000ff00) >> 8, + .log[0] = (info & 0x18000000) >> 27, + .log[1] = (info & 0x60000000) >> 29, + .param = !!(info & 0x80000000), }; } else { - u32 data = nv_ro32(bios, entry + 0); - u8 data1 = nv_ro32(bios, entry + 4); + u32 info = nv_ro32(bios, data + 0); + u8 info1 = nv_ro32(bios, data + 4); *gpio = (struct dcb_gpio_func) { - .line = (data & 0x0000003f) >> 0, - .func = (data & 0x0000ff00) >> 8, - .log[0] = (data1 & 0x30) >> 4, - .log[1] = (data1 & 0xc0) >> 6, - .param = !!(data & 0x80000000), + .line = (info & 0x0000003f) >> 0, + .func = (info & 0x0000ff00) >> 8, + .log[0] = (info1 & 0x30) >> 4, + .log[1] = (info1 & 0xc0) >> 6, + .param = !!(info & 0x80000000), }; } + } + + return data; +} +u16 +dcb_gpio_match(struct nouveau_bios *bios, int idx, u8 func, u8 line, + u8 *ver, u8 *len, struct dcb_gpio_func *gpio) +{ + u8 hdr, cnt, i = 0; + u16 data; + + while ((data = dcb_gpio_parse(bios, idx, i++, ver, len, gpio))) { if ((line == 0xff || line == gpio->line) && (func == 0xff || func == gpio->func)) - return 0; + return data; } /* DCB 2.2, fixed TVDAC GPIO data */ - if ((entry = dcb_table(bios, &ver, &hdr, &cnt, &len))) { - if (ver >= 0x22 && ver < 0x30 && func == DCB_GPIO_TVDAC0) { - u8 conf = nv_ro08(bios, entry - 5); - u8 addr = nv_ro08(bios, entry - 4); + if ((data = dcb_table(bios, ver, &hdr, &cnt, len))) { + if (*ver >= 0x22 && *ver < 0x30 && func == DCB_GPIO_TVDAC0) { + u8 conf = nv_ro08(bios, data - 5); + u8 addr = nv_ro08(bios, data - 4); if (conf & 0x01) { *gpio = (struct dcb_gpio_func) { .func = DCB_GPIO_TVDAC0, @@ -112,10 +133,11 @@ dcb_gpio_parse(struct nouveau_bios *bios, int idx, u8 func, u8 line, .log[0] = !!(conf & 0x02), .log[1] = !(conf & 0x02), }; - return 0; + *ver = 0x00; + return data; } } } - return -EINVAL; + return 0x0000; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/init.c b/drivers/gpu/drm/nouveau/core/subdev/bios/init.c index ae168bbb86d8..2917d552689b 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/bios/init.c +++ b/drivers/gpu/drm/nouveau/core/subdev/bios/init.c @@ -2,11 +2,12 @@ #include <core/device.h> #include <subdev/bios.h> -#include <subdev/bios/conn.h> #include <subdev/bios/bmp.h> #include <subdev/bios/bit.h> +#include <subdev/bios/conn.h> #include <subdev/bios/dcb.h> #include <subdev/bios/dp.h> +#include <subdev/bios/gpio.h> #include <subdev/bios/init.h> #include <subdev/devinit.h> #include <subdev/clock.h> @@ -410,9 +411,25 @@ init_ram_restrict_group_count(struct nvbios_init *init) } static u8 +init_ram_restrict_strap(struct nvbios_init *init) +{ + /* This appears to be the behaviour of the VBIOS parser, and *is* + * important to cache the NV_PEXTDEV_BOOT0 on later chipsets to + * avoid fucking up the memory controller (somehow) by reading it + * on every INIT_RAM_RESTRICT_ZM_GROUP opcode. + * + * Preserving the non-caching behaviour on earlier chipsets just + * in case *not* re-reading the strap causes similar breakage. + */ + if (!init->ramcfg || init->bios->version.major < 0x70) + init->ramcfg = init_rd32(init, 0x101000); + return (init->ramcfg & 0x00000003c) >> 2; +} + +static u8 init_ram_restrict(struct nvbios_init *init) { - u32 strap = (init_rd32(init, 0x101000) & 0x0000003c) >> 2; + u8 strap = init_ram_restrict_strap(init); u16 table = init_ram_restrict_table(init); if (table) return nv_ro08(init->bios, table + strap); @@ -1781,7 +1798,7 @@ init_gpio(struct nvbios_init *init) init->offset += 1; if (init_exec(init) && gpio && gpio->reset) - gpio->reset(gpio); + gpio->reset(gpio, DCB_GPIO_UNUSED); } /** @@ -1995,6 +2012,47 @@ init_i2c_long_if(struct nvbios_init *init) init_exec_set(init, false); } +/** + * INIT_GPIO_NE - opcode 0xa9 + * + */ +static void +init_gpio_ne(struct nvbios_init *init) +{ + struct nouveau_bios *bios = init->bios; + struct nouveau_gpio *gpio = nouveau_gpio(bios); + struct dcb_gpio_func func; + u8 count = nv_ro08(bios, init->offset + 1); + u8 idx = 0, ver, len; + u16 data, i; + + trace("GPIO_NE\t"); + init->offset += 2; + + for (i = init->offset; i < init->offset + count; i++) + cont("0x%02x ", nv_ro08(bios, i)); + cont("\n"); + + while ((data = dcb_gpio_parse(bios, 0, idx++, &ver, &len, &func))) { + if (func.func != DCB_GPIO_UNUSED) { + for (i = init->offset; i < init->offset + count; i++) { + if (func.func == nv_ro08(bios, i)) + break; + } + + trace("\tFUNC[0x%02x]", func.func); + if (i == (init->offset + count)) { + cont(" *"); + if (init_exec(init) && gpio && gpio->reset) + gpio->reset(gpio, func.func); + } + cont("\n"); + } + } + + init->offset += count; +} + static struct nvbios_init_opcode { void (*exec)(struct nvbios_init *); } init_opcode[] = { @@ -2059,6 +2117,7 @@ static struct nvbios_init_opcode { [0x98] = { init_auxch }, [0x99] = { init_zm_auxch }, [0x9a] = { init_i2c_long_if }, + [0xa9] = { init_gpio_ne }, }; #define init_opcode_nr (sizeof(init_opcode) / sizeof(init_opcode[0])) |