static u16 therm_table(struct nouveau_device *device, u8 *ver, u8 *hdr, u8 *len, u8 *cnt) { struct bit_entry bit_P; u16 therm = 0; if (!nouveau_bit_entry(device, 'P', &bit_P)) { if (bit_P.version == 1) therm = nv_ro16(device, bit_P.offset + 12); else if (bit_P.version == 2) therm = nv_ro16(device, bit_P.offset + 16); else nv_error(device, "unknown offset for thermal in BIT P %d\n", bit_P.version); } /* exit now if we haven't found the thermal table */ if (!therm) return 0x0000; *ver = nv_ro08(device, therm + 0); *hdr = nv_ro08(device, therm + 1); *len = nv_ro08(device, therm + 2); *cnt = nv_ro08(device, therm + 3); return therm + nv_ro08(device, therm + 1); }
u32 nvbios_P0260Te(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *xnr, u8 *xsz) { struct bit_entry bit_P; u32 data = 0x00000000; if (!bit_entry(bios, 'P', &bit_P)) { if (bit_P.version == 2 && bit_P.length > 0x63) data = nv_ro32(bios, bit_P.offset + 0x60); if (data) { *ver = nv_ro08(bios, data + 0); switch (*ver) { case 0x10: *hdr = nv_ro08(bios, data + 1); *cnt = nv_ro08(bios, data + 2); *len = 4; *xnr = nv_ro08(bios, data + 3); *xsz = 4; return data; default: break; } } } return 0x00000000; }
static u16 nvbios_dpout_entry(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) { u16 data = nvbios_dp_table(bios, ver, hdr, cnt, len); if (data && idx < *cnt) { u16 outp = nv_ro16(bios, data + *hdr + idx * *len); switch (*ver * !!outp) { case 0x21: case 0x30: *hdr = nv_ro08(bios, data + 0x04); *len = nv_ro08(bios, data + 0x05); *cnt = nv_ro08(bios, outp + 0x04); break; case 0x40: *hdr = nv_ro08(bios, data + 0x04); *cnt = 0; *len = 0; break; default: break; } return outp; } *ver = 0x00; return 0x0000; }
/** * INIT_IO_RESTRICT_PROG - opcode 0x32 * */ static void init_io_restrict_prog(struct nvbios_init *init) { struct nouveau_bios *bios = init->bios; u16 port = nv_ro16(bios, init->offset + 1); u8 index = nv_ro08(bios, init->offset + 3); u8 mask = nv_ro08(bios, init->offset + 4); u8 shift = nv_ro08(bios, init->offset + 5); u8 count = nv_ro08(bios, init->offset + 6); u32 reg = nv_ro32(bios, init->offset + 7); u8 conf, i; trace("IO_RESTRICT_PROG\tR[0x%06x] = " "((0x%04x[0x%02x] & 0x%02x) >> %d) [{\n", reg, port, index, mask, shift); init->offset += 11; conf = (init_rdvgai(init, port, index) & mask) >> shift; for (i = 0; i < count; i++) { u32 data = nv_ro32(bios, init->offset); if (i == conf) { trace("\t0x%08x *\n", data); init_wr32(init, reg, data); } else { trace("\t0x%08x\n", data); } init->offset += 4; } trace("}]\n"); }
int dcb_outp_foreach(struct nouveau_bios *bios, void *data, int (*exec)(struct nouveau_bios *, void *, int, u16)) { int ret, idx = -1; u8 ver, len; u16 outp; while ((outp = dcb_outp(bios, ++idx, &ver, &len))) { if (nv_ro32(bios, outp) == 0x00000000) break; /* seen on an NV11 with DCB v1.5 */ if (nv_ro32(bios, outp) == 0xffffffff) break; /* seen on an NV17 with DCB v2.0 */ if (nv_ro08(bios, outp) == DCB_OUTPUT_UNUSED) continue; if (nv_ro08(bios, outp) == DCB_OUTPUT_EOL) break; ret = exec(bios, data, idx, outp); if (ret) return ret; } return 0; }
u32 nvbios_M0203Te(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) { struct bit_entry bit_M; u32 data = 0x00000000; if (!bit_entry(bios, 'M', &bit_M)) { if (bit_M.version == 2 && bit_M.length > 0x04) data = nv_ro16(bios, bit_M.offset + 0x03); if (data) { *ver = nv_ro08(bios, data + 0x00); switch (*ver) { case 0x10: *hdr = nv_ro08(bios, data + 0x01); *len = nv_ro08(bios, data + 0x02); *cnt = nv_ro08(bios, data + 0x03); return data; default: break; } } } return 0x00000000; }
static u16 nvbios_dp_table(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) { struct bit_entry d; if (!bit_entry(bios, 'd', &d)) { if (d.version == 1 && d.length >= 2) { u16 data = nv_ro16(bios, d.offset); if (data) { *ver = nv_ro08(bios, data + 0x00); switch (*ver) { case 0x21: case 0x30: case 0x40: *hdr = nv_ro08(bios, data + 0x01); *len = nv_ro08(bios, data + 0x02); *cnt = nv_ro08(bios, data + 0x03); return data; default: break; } } } } return 0x0000; }
u16 nvbios_volt_parse(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_volt *info) { u16 volt = nvbios_volt_table(bios, ver, hdr, cnt, len); memset(info, 0x00, sizeof(*info)); switch (!!volt * *ver) { case 0x12: info->vidmask = nv_ro08(bios, volt + 0x04); break; case 0x20: info->vidmask = nv_ro08(bios, volt + 0x05); break; case 0x30: info->vidmask = nv_ro08(bios, volt + 0x04); break; case 0x40: info->base = nv_ro32(bios, volt + 0x04); info->step = nv_ro16(bios, volt + 0x08); info->vidmask = nv_ro08(bios, volt + 0x0b); /*XXX*/ info->min = 0; info->max = info->base; break; case 0x50: info->vidmask = nv_ro08(bios, volt + 0x06); info->min = nv_ro32(bios, volt + 0x0a); info->max = nv_ro32(bios, volt + 0x0e); info->base = nv_ro32(bios, volt + 0x12) & 0x00ffffff; info->step = nv_ro16(bios, volt + 0x16); break; } return volt; }
u16 nvbios_disp_table(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *sub) { struct bit_entry U; if (!bit_entry(bios, 'U', &U)) { if (U.version == 1) { u16 data = nv_ro16(bios, U.offset); if (data) { *ver = nv_ro08(bios, data + 0x00); switch (*ver) { case 0x20: case 0x21: *hdr = nv_ro08(bios, data + 0x01); *len = nv_ro08(bios, data + 0x02); *cnt = nv_ro08(bios, data + 0x03); *sub = nv_ro08(bios, data + 0x04); return data; default: break; } } } } return 0x0000; }
u16 dcb_i2c_table(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) { u16 i2c = 0x0000; u16 dcb = dcb_table(bios, ver, hdr, cnt, len); if (dcb) { if (*ver >= 0x15) i2c = nv_ro16(bios, dcb + 2); if (*ver >= 0x30) i2c = nv_ro16(bios, dcb + 4); } if (i2c && *ver >= 0x42) { nv_warn(bios, "ccb %02x not supported\n", *ver); return 0x0000; } if (i2c && *ver >= 0x30) { *ver = nv_ro08(bios, i2c + 0); *hdr = nv_ro08(bios, i2c + 1); *cnt = nv_ro08(bios, i2c + 2); *len = nv_ro08(bios, i2c + 3); } else { *ver = *ver; /* use DCB version */ *hdr = 0; *cnt = 16; *len = 4; } return i2c; }
u32 nvbios_connEp(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *len, struct nvbios_connE *info) { u32 data = nvbios_connEe(bios, idx, ver, len); memset(info, 0x00, sizeof(*info)); switch (!!data * *ver) { case 0x30: case 0x40: info->type = nv_ro08(bios, data + 0x00); info->location = nv_ro08(bios, data + 0x01) & 0x0f; info->hpd = (nv_ro08(bios, data + 0x01) & 0x30) >> 4; info->dp = (nv_ro08(bios, data + 0x01) & 0xc0) >> 6; if (*len < 4) return data; info->hpd |= (nv_ro08(bios, data + 0x02) & 0x03) << 2; info->dp |= nv_ro08(bios, data + 0x02) & 0x0c; info->di = (nv_ro08(bios, data + 0x02) & 0xf0) >> 4; info->hpd |= (nv_ro08(bios, data + 0x03) & 0x07) << 4; info->sr = (nv_ro08(bios, data + 0x03) & 0x08) >> 3; info->lcdid = (nv_ro08(bios, data + 0x03) & 0x70) >> 4; return data; default: break; } return 0x00000000; }
int nouveau_fb_bios_memtype(struct nouveau_bios *bios) { struct bit_entry M; u8 ramcfg; ramcfg = (nv_rd32(bios, 0x101000) & 0x0000003c) >> 2; if (!bit_entry(bios, 'M', &M) && M.version == 2 && M.length >= 5) { u16 table = nv_ro16(bios, M.offset + 3); u8 version = nv_ro08(bios, table + 0); u8 header = nv_ro08(bios, table + 1); u8 record = nv_ro08(bios, table + 2); u8 entries = nv_ro08(bios, table + 3); if (table && version == 0x10 && ramcfg < entries) { u16 entry = table + header + (ramcfg * record); switch (nv_ro08(bios, entry) & 0x0f) { case 0: return NV_MEM_TYPE_DDR2; case 1: return NV_MEM_TYPE_DDR3; case 2: return NV_MEM_TYPE_GDDR3; case 3: return NV_MEM_TYPE_GDDR5; default: break; } } } return NV_MEM_TYPE_UNKNOWN; }
u16 dcb_xpio_parse(struct nvkm_bios *bios, u8 idx, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_xpio *info) { u16 data = dcb_xpio_table(bios, idx, ver, hdr, cnt, len); if (data && *len >= 6) { info->type = nv_ro08(bios, data + 0x04); info->addr = nv_ro08(bios, data + 0x05); info->flags = nv_ro08(bios, data + 0x06); } return 0x0000; }
u8 nvbios_ramcfg_count(struct nouveau_bios *bios) { struct bit_entry bit_M; if (!bit_entry(bios, 'M', &bit_M)) { if (bit_M.version == 1 && bit_M.length >= 5) return nv_ro08(bios, bit_M.offset + 2); if (bit_M.version == 2 && bit_M.length >= 3) return nv_ro08(bios, bit_M.offset + 0); } return 0x00; }
u16 nvbios_timingEp(struct nouveau_bios *bios, int idx, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_ramcfg *p) { u16 data = nvbios_timingEe(bios, idx, ver, hdr, cnt, len), temp; p->timing_ver = *ver; p->timing_hdr = *hdr; switch (!!data * *ver) { case 0x10: p->timing_10_WR = nv_ro08(bios, data + 0x00); p->timing_10_CL = nv_ro08(bios, data + 0x02); p->timing_10_ODT = nv_ro08(bios, data + 0x0e) & 0x07; p->timing_10_CWL = nv_ro08(bios, data + 0x13); break; case 0x20: p->timing[0] = nv_ro32(bios, data + 0x00); p->timing[1] = nv_ro32(bios, data + 0x04); p->timing[2] = nv_ro32(bios, data + 0x08); p->timing[3] = nv_ro32(bios, data + 0x0c); p->timing[4] = nv_ro32(bios, data + 0x10); p->timing[5] = nv_ro32(bios, data + 0x14); p->timing[6] = nv_ro32(bios, data + 0x18); p->timing[7] = nv_ro32(bios, data + 0x1c); p->timing[8] = nv_ro32(bios, data + 0x20); p->timing[9] = nv_ro32(bios, data + 0x24); p->timing[10] = nv_ro32(bios, data + 0x28); p->timing_20_2e_03 = (nv_ro08(bios, data + 0x2e) & 0x03) >> 0; p->timing_20_2e_30 = (nv_ro08(bios, data + 0x2e) & 0x30) >> 4; p->timing_20_2e_c0 = (nv_ro08(bios, data + 0x2e) & 0xc0) >> 6; p->timing_20_2f_03 = (nv_ro08(bios, data + 0x2f) & 0x03) >> 0; temp = nv_ro16(bios, data + 0x2c); p->timing_20_2c_003f = (temp & 0x003f) >> 0; p->timing_20_2c_1fc0 = (temp & 0x1fc0) >> 6; p->timing_20_30_07 = (nv_ro08(bios, data + 0x30) & 0x07) >> 0; p->timing_20_30_f8 = (nv_ro08(bios, data + 0x30) & 0xf8) >> 3; temp = nv_ro16(bios, data + 0x31); p->timing_20_31_0007 = (temp & 0x0007) >> 0; p->timing_20_31_0078 = (temp & 0x0078) >> 3; p->timing_20_31_0780 = (temp & 0x0780) >> 7; p->timing_20_31_0800 = (temp & 0x0800) >> 11; p->timing_20_31_7000 = (temp & 0x7000) >> 12; p->timing_20_31_8000 = (temp & 0x8000) >> 15; break; default: data = 0; break; } return data; }
static u8 init_ram_restrict_group_count(struct nvbios_init *init) { struct nouveau_bios *bios = init->bios; struct bit_entry bit_M; if (!bit_entry(bios, 'M', &bit_M)) { if (bit_M.version == 1 && bit_M.length >= 5) return nv_ro08(bios, bit_M.offset + 2); if (bit_M.version == 2 && bit_M.length >= 3) return nv_ro08(bios, bit_M.offset + 0); } return 0x00; }
static u16 nvbios_dpcfg_entry(struct nouveau_bios *bios, u16 outp, u8 idx, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) { if (*ver >= 0x40) { outp = nvbios_dp_table(bios, ver, hdr, cnt, len); *hdr = *hdr + (*len * * cnt); *len = nv_ro08(bios, outp + 0x06); *cnt = nv_ro08(bios, outp + 0x07); } if (idx < *cnt) return outp + *hdr + (idx * *len); return 0x0000; }
u8 nvbios_ramcfg_index(struct nouveau_subdev *subdev) { struct nouveau_bios *bios = nouveau_bios(subdev); u8 strap = nvbios_ramcfg_strap(subdev); u32 xlat = 0x00000000; struct bit_entry bit_M; struct nvbios_M0203E M0203E; u8 ver, hdr; if (!bit_entry(bios, 'M', &bit_M)) { if (bit_M.version == 1 && bit_M.length >= 5) xlat = nv_ro16(bios, bit_M.offset + 3); if (bit_M.version == 2 && bit_M.length >= 3) { /*XXX: is M ever shorter than this? * if not - what is xlat used for now? * also - sigh.. */ if (bit_M.length >= 7 && nvbios_M0203Em(bios, strap, &ver, &hdr, &M0203E)) return M0203E.group; xlat = nv_ro16(bios, bit_M.offset + 1); } } if (xlat) strap = nv_ro08(bios, xlat + strap); return strap; }
static u16 dcb_xpiod_table(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) { u16 data = dcb_gpio_table(bios, ver, hdr, cnt, len); if (data && *ver >= 0x40 && *hdr >= 0x06) { u16 xpio = nv_ro16(bios, data + 0x04); if (xpio) { *ver = nv_ro08(bios, data + 0x00); *hdr = nv_ro08(bios, data + 0x01); *cnt = nv_ro08(bios, data + 0x02); *len = nv_ro08(bios, data + 0x03); return xpio; } } return 0x0000; }
/** * init_reserved - stub for various unknown/unused single-byte opcodes * */ static void init_reserved(struct nvbios_init *init) { u8 opcode = nv_ro08(init->bios, init->offset); trace("RESERVED\t0x%02x\n", opcode); init->offset += 1; }
static bool init_io_condition_met(struct nvbios_init *init, u8 cond) { struct nouveau_bios *bios = init->bios; u16 table = init_io_condition_table(init); if (table) { u16 port = nv_ro16(bios, table + (cond * 5) + 0); u8 index = nv_ro08(bios, table + (cond * 5) + 2); u8 mask = nv_ro08(bios, table + (cond * 5) + 3); u8 value = nv_ro08(bios, table + (cond * 5) + 4); trace("\t[0x%02x] (0x%04x[0x%02x] & 0x%02x) == 0x%02x\n", cond, port, index, mask, value); return (init_rdvgai(init, port, index) & mask) == value; } return false; }
u16 dcb_conntab(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) { u16 dcb = dcb_table(bios, ver, hdr, cnt, len); if (dcb && *ver >= 0x30 && *hdr >= 0x16) { u16 data = nv_ro16(bios, dcb + 0x14); if (data) { *ver = nv_ro08(bios, data + 0); *hdr = nv_ro08(bios, data + 1); *cnt = nv_ro08(bios, data + 2); *len = nv_ro08(bios, data + 3); return data; } } return 0x0000; }
u32 nvbios_M0203Ep(struct nouveau_bios *bios, int idx, u8 *ver, u8 *hdr, struct nvbios_M0203E *info) { u32 data = nvbios_M0203Ee(bios, idx, ver, hdr); memset(info, 0x00, sizeof(*info)); switch (!!data * *ver) { case 0x10: info->type = (nv_ro08(bios, data + 0x00) & 0x0f) >> 0; info->strap = (nv_ro08(bios, data + 0x00) & 0xf0) >> 4; info->group = (nv_ro08(bios, data + 0x01) & 0x0f) >> 0; return data; default: break; } return 0x00000000; }
u16 dcb_xpio_table(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) { u16 data = dcb_xpiod_table(bios, ver, hdr, cnt, len); if (data && idx < *cnt) { u16 xpio = nv_ro16(bios, data + *hdr + (idx * *len)); if (xpio) { *ver = nv_ro08(bios, data + 0x00); *hdr = nv_ro08(bios, data + 0x01); *cnt = nv_ro08(bios, data + 0x02); *len = nv_ro08(bios, data + 0x03); return xpio; } } return 0x0000; }
static u8 init_ram_restrict(struct nvbios_init *init) { u8 strap = init_ram_restrict_strap(init); u16 table = init_ram_restrict_table(init); if (table) return nv_ro08(init->bios, table + strap); return 0x00; }
u32 nvbios_pcirTp(struct nvkm_bios *bios, u32 base, u8 *ver, u16 *hdr, struct nvbios_pcirT *info) { u32 data = nvbios_pcirTe(bios, base, ver, hdr); memset(info, 0x00, sizeof(*info)); if (data) { info->vendor_id = nv_ro16(bios, data + 0x04); info->device_id = nv_ro16(bios, data + 0x06); info->class_code[0] = nv_ro08(bios, data + 0x0d); info->class_code[1] = nv_ro08(bios, data + 0x0e); info->class_code[2] = nv_ro08(bios, data + 0x0f); info->image_size = nv_ro16(bios, data + 0x10) * 512; info->image_rev = nv_ro16(bios, data + 0x12); info->image_type = nv_ro08(bios, data + 0x14); info->last = nv_ro08(bios, data + 0x15) & 0x80; } return data; }
int nve0_ram_init(struct nouveau_object *object) { struct nouveau_fb *pfb = (void *)object->parent; struct nve0_ram *ram = (void *)object; struct nouveau_bios *bios = nouveau_bios(pfb); static const u8 train0[] = { 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, }; static const u32 train1[] = { 0x00000000, 0xffffffff, 0x55555555, 0xaaaaaaaa, 0x33333333, 0xcccccccc, 0xf0f0f0f0, 0x0f0f0f0f, 0x00ff00ff, 0xff00ff00, 0x0000ffff, 0xffff0000, }; u8 ver, hdr, cnt, len, snr, ssz; u32 data, save; int ret, i; ret = nouveau_ram_init(&ram->base); if (ret) return ret; /* run a bunch of tables from rammap table. there's actually * individual pointers for each rammap entry too, but, nvidia * seem to just run the last two entries' scripts early on in * their init, and never again.. we'll just run 'em all once * for now. * * i strongly suspect that each script is for a separate mode * (likely selected by 0x10f65c's lower bits?), and the * binary driver skips the one that's already been setup by * the init tables. */ data = nvbios_rammapTe(bios, &ver, &hdr, &cnt, &len, &snr, &ssz); if (!data || hdr < 0x15) return -EINVAL; cnt = nv_ro08(bios, data + 0x14); /* guess at count */ data = nv_ro32(bios, data + 0x10); /* guess u32... */ save = nv_rd32(pfb, 0x10f65c); for (i = 0; i < cnt; i++) { nv_mask(pfb, 0x10f65c, 0x000000f0, i << 4); nvbios_exec(&(struct nvbios_init) { .subdev = nv_subdev(pfb), .bios = bios, .offset = nv_ro32(bios, data), /* guess u32 */ .execute = 1, }); data += 4; }
static int nve0_ram_calc_data(struct nouveau_fb *pfb, u32 freq, struct nouveau_ram_data *data) { struct nouveau_bios *bios = nouveau_bios(pfb); struct nve0_ram *ram = (void *)pfb->ram; u8 strap, cnt, len; /* lookup memory config data relevant to the target frequency */ ram->base.rammap.data = nvbios_rammapEp(bios, freq / 1000, &ram->base.rammap.version, &ram->base.rammap.size, &cnt, &len, &data->bios); if (!ram->base.rammap.data || ram->base.rammap.version != 0x11 || ram->base.rammap.size < 0x09) { nv_error(pfb, "invalid/missing rammap entry\n"); return -EINVAL; } /* locate specific data set for the attached memory */ strap = nvbios_ramcfg_index(nv_subdev(pfb)); ram->base.ramcfg.data = nvbios_rammapSp(bios, ram->base.rammap.data, ram->base.rammap.version, ram->base.rammap.size, cnt, len, strap, &ram->base.ramcfg.version, &ram->base.ramcfg.size, &data->bios); if (!ram->base.ramcfg.data || ram->base.ramcfg.version != 0x11 || ram->base.ramcfg.size < 0x08) { nv_error(pfb, "invalid/missing ramcfg entry\n"); return -EINVAL; } /* lookup memory timings, if bios says they're present */ strap = nv_ro08(bios, ram->base.ramcfg.data + 0x00); if (strap != 0xff) { ram->base.timing.data = nvbios_timingEp(bios, strap, &ram->base.timing.version, &ram->base.timing.size, &cnt, &len, &data->bios); if (!ram->base.timing.data || ram->base.timing.version != 0x20 || ram->base.timing.size < 0x33) { nv_error(pfb, "invalid/missing timing entry\n"); return -EINVAL; } } else { ram->base.timing.data = 0; } data->freq = freq; return 0; }
static u16 nvbios_volt_entry_parse(struct nouveau_device *device, int idx, u8 *ver, u8 *len, struct nvbios_volt_entry *info) { u16 volt = nvbios_volt_entry(device, idx, ver, len); memset(info, 0x00, sizeof(*info)); switch (!!volt * *ver) { case 0x12: case 0x20: info->voltage = nv_ro08(device, volt + 0x00) * 10000; info->vid = nv_ro08(device, volt + 0x01); break; case 0x30: info->voltage = nv_ro08(device, volt + 0x00) * 10000; info->vid = nv_ro08(device, volt + 0x01) >> 2; break; case 0x40: case 0x50: break; } return volt; }
static inline int nv_memcmp(struct nouveau_device *device, u32 addr, const char *str, u32 len) { unsigned char c1, c2; while (len--) { c1 = nv_ro08(device, addr++); c2 = *(str++); if (c1 != c2) return c1 - c2; } return 0; }