diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau/nouveau_mem.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_mem.c | 809 |
1 files changed, 587 insertions, 222 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c index c3a5745e9c79..b08065f981df 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mem.c +++ b/drivers/gpu/drm/nouveau/nouveau_mem.c @@ -26,7 +26,8 @@ * DEALINGS IN THE SOFTWARE. * * Authors: - * Keith Whitwell <keith@tungstengraphics.com> + * Ben Skeggs <bskeggs@redhat.com> + * Roy Spliet <r.spliet@student.tudelft.nl> */ @@ -192,75 +193,6 @@ nouveau_mem_gart_fini(struct drm_device *dev) } } -static uint32_t -nouveau_mem_detect_nv04(struct drm_device *dev) -{ - uint32_t boot0 = nv_rd32(dev, NV04_PFB_BOOT_0); - - if (boot0 & 0x00000100) - return (((boot0 >> 12) & 0xf) * 2 + 2) * 1024 * 1024; - - switch (boot0 & NV04_PFB_BOOT_0_RAM_AMOUNT) { - case NV04_PFB_BOOT_0_RAM_AMOUNT_32MB: - return 32 * 1024 * 1024; - case NV04_PFB_BOOT_0_RAM_AMOUNT_16MB: - return 16 * 1024 * 1024; - case NV04_PFB_BOOT_0_RAM_AMOUNT_8MB: - return 8 * 1024 * 1024; - case NV04_PFB_BOOT_0_RAM_AMOUNT_4MB: - return 4 * 1024 * 1024; - } - - return 0; -} - -static uint32_t -nouveau_mem_detect_nforce(struct drm_device *dev) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct pci_dev *bridge; - uint32_t mem; - - bridge = pci_get_bus_and_slot(0, PCI_DEVFN(0, 1)); - if (!bridge) { - NV_ERROR(dev, "no bridge device\n"); - return 0; - } - - if (dev_priv->flags & NV_NFORCE) { - pci_read_config_dword(bridge, 0x7C, &mem); - return (uint64_t)(((mem >> 6) & 31) + 1)*1024*1024; - } else - if (dev_priv->flags & NV_NFORCE2) { - pci_read_config_dword(bridge, 0x84, &mem); - return (uint64_t)(((mem >> 4) & 127) + 1)*1024*1024; - } - - NV_ERROR(dev, "impossible!\n"); - return 0; -} - -int -nouveau_mem_detect(struct drm_device *dev) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - - if (dev_priv->card_type == NV_04) { - dev_priv->vram_size = nouveau_mem_detect_nv04(dev); - } else - if (dev_priv->flags & (NV_NFORCE | NV_NFORCE2)) { - dev_priv->vram_size = nouveau_mem_detect_nforce(dev); - } else - if (dev_priv->card_type < NV_50) { - dev_priv->vram_size = nv_rd32(dev, NV04_PFB_FIFO_DATA); - dev_priv->vram_size &= NV10_PFB_FIFO_DATA_RAM_AMOUNT_MB_MASK; - } - - if (dev_priv->vram_size) - return 0; - return -ENOMEM; -} - bool nouveau_mem_flags_valid(struct drm_device *dev, u32 tile_flags) { @@ -385,11 +317,29 @@ nouveau_mem_init_agp(struct drm_device *dev) return 0; } +static const struct vram_types { + int value; + const char *name; +} vram_type_map[] = { + { NV_MEM_TYPE_STOLEN , "stolen system memory" }, + { NV_MEM_TYPE_SGRAM , "SGRAM" }, + { NV_MEM_TYPE_SDRAM , "SDRAM" }, + { NV_MEM_TYPE_DDR1 , "DDR1" }, + { NV_MEM_TYPE_DDR2 , "DDR2" }, + { NV_MEM_TYPE_DDR3 , "DDR3" }, + { NV_MEM_TYPE_GDDR2 , "GDDR2" }, + { NV_MEM_TYPE_GDDR3 , "GDDR3" }, + { NV_MEM_TYPE_GDDR4 , "GDDR4" }, + { NV_MEM_TYPE_GDDR5 , "GDDR5" }, + { NV_MEM_TYPE_UNKNOWN, "unknown type" } +}; + int nouveau_mem_vram_init(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct ttm_bo_device *bdev = &dev_priv->ttm.bdev; + const struct vram_types *vram_type; int ret, dma_bits; dma_bits = 32; @@ -427,7 +377,21 @@ nouveau_mem_vram_init(struct drm_device *dev) return ret; } - NV_INFO(dev, "Detected %dMiB VRAM\n", (int)(dev_priv->vram_size >> 20)); + vram_type = vram_type_map; + while (vram_type->value != NV_MEM_TYPE_UNKNOWN) { + if (nouveau_vram_type) { + if (!strcasecmp(nouveau_vram_type, vram_type->name)) + break; + dev_priv->vram_type = vram_type->value; + } else { + if (vram_type->value == dev_priv->vram_type) + break; + } + vram_type++; + } + + NV_INFO(dev, "Detected %dMiB VRAM (%s)\n", + (int)(dev_priv->vram_size >> 20), vram_type->name); if (dev_priv->vram_sys_base) { NV_INFO(dev, "Stolen system memory at: 0x%010llx\n", dev_priv->vram_sys_base); @@ -508,216 +472,617 @@ nouveau_mem_gart_init(struct drm_device *dev) return 0; } -/* XXX: For now a dummy. More samples required, possibly even a card - * Called from nouveau_perf.c */ -void nv30_mem_timing_entry(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr, - struct nouveau_pm_tbl_entry *e, uint8_t magic_number, - struct nouveau_pm_memtiming *timing) { - - NV_DEBUG(dev,"Timing entry format unknown, please contact nouveau developers"); -} - -void nv40_mem_timing_entry(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr, - struct nouveau_pm_tbl_entry *e, uint8_t magic_number, - struct nouveau_pm_memtiming *timing) { - - timing->reg_0 = (e->tRC << 24 | e->tRFC << 16 | e->tRAS << 8 | e->tRP); +static int +nv40_mem_timing_calc(struct drm_device *dev, u32 freq, + struct nouveau_pm_tbl_entry *e, u8 len, + struct nouveau_pm_memtiming *boot, + struct nouveau_pm_memtiming *t) +{ + t->reg[0] = (e->tRP << 24 | e->tRAS << 16 | e->tRFC << 8 | e->tRC); /* XXX: I don't trust the -1's and +1's... they must come * from somewhere! */ - timing->reg_1 = (e->tWR + 2 + magic_number) << 24 | - 1 << 16 | - (e->tUNK_1 + 2 + magic_number) << 8 | - (e->tCL + 2 - magic_number); - timing->reg_2 = (magic_number << 24 | e->tUNK_12 << 16 | e->tUNK_11 << 8 | e->tUNK_10); - timing->reg_2 |= 0x20200000; - - NV_DEBUG(dev, "Entry %d: 220: %08x %08x %08x\n", timing->id, - timing->reg_0, timing->reg_1,timing->reg_2); + t->reg[1] = (e->tWR + 2 + (t->tCWL - 1)) << 24 | + 1 << 16 | + (e->tWTR + 2 + (t->tCWL - 1)) << 8 | + (e->tCL + 2 - (t->tCWL - 1)); + + t->reg[2] = 0x20200000 | + ((t->tCWL - 1) << 24 | + e->tRRD << 16 | + e->tRCDWR << 8 | + e->tRCDRD); + + NV_DEBUG(dev, "Entry %d: 220: %08x %08x %08x\n", t->id, + t->reg[0], t->reg[1], t->reg[2]); + return 0; } -void nv50_mem_timing_entry(struct drm_device *dev, struct bit_entry *P, struct nouveau_pm_tbl_header *hdr, - struct nouveau_pm_tbl_entry *e, uint8_t magic_number,struct nouveau_pm_memtiming *timing) { +static int +nv50_mem_timing_calc(struct drm_device *dev, u32 freq, + struct nouveau_pm_tbl_entry *e, u8 len, + struct nouveau_pm_memtiming *boot, + struct nouveau_pm_memtiming *t) +{ struct drm_nouveau_private *dev_priv = dev->dev_private; + struct bit_entry P; + uint8_t unk18 = 1, unk20 = 0, unk21 = 0, tmp7_3; - uint8_t unk18 = 1, - unk19 = 1, - unk20 = 0, - unk21 = 0; + if (bit_table(dev, 'P', &P)) + return -EINVAL; - switch (min(hdr->entry_len, (u8) 22)) { + switch (min(len, (u8) 22)) { case 22: unk21 = e->tUNK_21; case 21: unk20 = e->tUNK_20; case 20: - unk19 = e->tUNK_19; + if (e->tCWL > 0) + t->tCWL = e->tCWL; case 19: unk18 = e->tUNK_18; break; } - timing->reg_0 = (e->tRC << 24 | e->tRFC << 16 | e->tRAS << 8 | e->tRP); + t->reg[0] = (e->tRP << 24 | e->tRAS << 16 | e->tRFC << 8 | e->tRC); - /* XXX: I don't trust the -1's and +1's... they must come - * from somewhere! */ - timing->reg_1 = (e->tWR + unk19 + 1 + magic_number) << 24 | - max(unk18, (u8) 1) << 16 | - (e->tUNK_1 + unk19 + 1 + magic_number) << 8; - if (dev_priv->chipset == 0xa8) { - timing->reg_1 |= (e->tCL - 1); - } else { - timing->reg_1 |= (e->tCL + 2 - magic_number); - } - timing->reg_2 = (e->tUNK_12 << 16 | e->tUNK_11 << 8 | e->tUNK_10); - - timing->reg_5 = (e->tRAS << 24 | e->tRC); - timing->reg_5 += max(e->tUNK_10, e->tUNK_11) << 16; - - if (P->version == 1) { - timing->reg_2 |= magic_number << 24; - timing->reg_3 = (0x14 + e->tCL) << 24 | - 0x16 << 16 | - (e->tCL - 1) << 8 | - (e->tCL - 1); - timing->reg_4 = (nv_rd32(dev,0x10022c) & 0xffff0000) | e->tUNK_13 << 8 | e->tUNK_13; - timing->reg_5 |= (e->tCL + 2) << 8; - timing->reg_7 = 0x4000202 | (e->tCL - 1) << 16; + t->reg[1] = (e->tWR + 2 + (t->tCWL - 1)) << 24 | + max(unk18, (u8) 1) << 16 | + (e->tWTR + 2 + (t->tCWL - 1)) << 8; + + t->reg[2] = ((t->tCWL - 1) << 24 | + e->tRRD << 16 | + e->tRCDWR << 8 | + e->tRCDRD); + + t->reg[4] = e->tUNK_13 << 8 | e->tUNK_13; + + t->reg[5] = (e->tRFC << 24 | max(e->tRCDRD, e->tRCDWR) << 16 | e->tRP); + + t->reg[8] = boot->reg[8] & 0xffffff00; + + if (P.version == 1) { + t->reg[1] |= (e->tCL + 2 - (t->tCWL - 1)); + + t->reg[3] = (0x14 + e->tCL) << 24 | + 0x16 << 16 | + (e->tCL - 1) << 8 | + (e->tCL - 1); + + t->reg[4] |= boot->reg[4] & 0xffff0000; + + t->reg[6] = (0x33 - t->tCWL) << 16 | + t->tCWL << 8 | + (0x2e + e->tCL - t->tCWL); + + t->reg[7] = 0x4000202 | (e->tCL - 1) << 16; + + /* XXX: P.version == 1 only has DDR2 and GDDR3? */ + if (dev_priv->vram_type == NV_MEM_TYPE_DDR2) { + t->reg[5] |= (e->tCL + 3) << 8; + t->reg[6] |= (t->tCWL - 2) << 8; + t->reg[8] |= (e->tCL - 4); + } else { + t->reg[5] |= (e->tCL + 2) << 8; + t->reg[6] |= t->tCWL << 8; + t->reg[8] |= (e->tCL - 2); + } } else { - timing->reg_2 |= (unk19 - 1) << 24; - /* XXX: reg_10022c for recentish cards pretty much unknown*/ - timing->reg_3 = e->tCL - 1; - timing->reg_4 = (unk20 << 24 | unk21 << 16 | - e->tUNK_13 << 8 | e->tUNK_13); + t->reg[1] |= (5 + e->tCL - (t->tCWL)); + + /* XXX: 0xb? 0x30? */ + t->reg[3] = (0x30 + e->tCL) << 24 | + (boot->reg[3] & 0x00ff0000)| + (0xb + e->tCL) << 8 | + (e->tCL - 1); + + t->reg[4] |= (unk20 << 24 | unk21 << 16); + /* XXX: +6? */ - timing->reg_5 |= (unk19 + 6) << 8; + t->reg[5] |= (t->tCWL + 6) << 8; - /* XXX: reg_10023c currently unknown - * 10023c seen as 06xxxxxx, 0bxxxxxx or 0fxxxxxx */ - timing->reg_7 = 0x202; + t->reg[6] = (0x5a + e->tCL) << 16 | + (6 - e->tCL + t->tCWL) << 8 | + (0x50 + e->tCL - t->tCWL); + + tmp7_3 = (boot->reg[7] & 0xff000000) >> 24; + t->reg[7] = (tmp7_3 << 24) | + ((tmp7_3 - 6 + e->tCL) << 16) | + 0x202; } - NV_DEBUG(dev, "Entry %d: 220: %08x %08x %08x %08x\n", timing->id, - timing->reg_0, timing->reg_1, - timing->reg_2, timing->reg_3); + NV_DEBUG(dev, "Entry %d: 220: %08x %08x %08x %08x\n", t->id, + t->reg[0], t->reg[1], t->reg[2], t->reg[3]); NV_DEBUG(dev, " 230: %08x %08x %08x %08x\n", - timing->reg_4, timing->reg_5, - timing->reg_6, timing->reg_7); - NV_DEBUG(dev, " 240: %08x\n", timing->reg_8); -} - -void nvc0_mem_timing_entry(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr, - struct nouveau_pm_tbl_entry *e, struct nouveau_pm_memtiming *timing) { - timing->reg_0 = (e->tRC << 24 | (e->tRFC & 0x7f) << 17 | e->tRAS << 8 | e->tRP); - timing->reg_1 = (nv_rd32(dev,0x10f294) & 0xff000000) | (e->tUNK_11&0x0f) << 20 | (e->tUNK_19 << 7) | (e->tCL & 0x0f); - timing->reg_2 = (nv_rd32(dev,0x10f298) & 0xff0000ff) | e->tWR << 16 | e->tUNK_1 << 8; - timing->reg_3 = e->tUNK_20 << 9 | e->tUNK_13; - timing->reg_4 = (nv_rd32(dev,0x10f2a0) & 0xfff000ff) | e->tUNK_12 << 15; - NV_DEBUG(dev, "Entry %d: 290: %08x %08x %08x %08x\n", timing->id, - timing->reg_0, timing->reg_1, - timing->reg_2, timing->reg_3); - NV_DEBUG(dev, " 2a0: %08x %08x %08x %08x\n", - timing->reg_4, timing->reg_5, - timing->reg_6, timing->reg_7); + t->reg[4], t->reg[5], t->reg[6], t->reg[7]); + NV_DEBUG(dev, " 240: %08x\n", t->reg[8]); + return 0; +} + +static int +nvc0_mem_timing_calc(struct drm_device *dev, u32 freq, + struct nouveau_pm_tbl_entry *e, u8 len, + struct nouveau_pm_memtiming *boot, + struct nouveau_pm_memtiming *t) +{ + if (e->tCWL > 0) + t->tCWL = e->tCWL; + + t->reg[0] = (e->tRP << 24 | (e->tRAS & 0x7f) << 17 | + e->tRFC << 8 | e->tRC); + + t->reg[1] = (boot->reg[1] & 0xff000000) | + (e->tRCDWR & 0x0f) << 20 | + (e->tRCDRD & 0x0f) << 14 | + (t->tCWL << 7) | + (e->tCL & 0x0f); + + t->reg[2] = (boot->reg[2] & 0xff0000ff) | + e->tWR << 16 | e->tWTR << 8; + + t->reg[3] = (e->tUNK_20 & 0x1f) << 9 | + (e->tUNK_21 & 0xf) << 5 | + (e->tUNK_13 & 0x1f); + + t->reg[4] = (boot->reg[4] & 0xfff00fff) | + (e->tRRD&0x1f) << 15; + + NV_DEBUG(dev, "Entry %d: 290: %08x %08x %08x %08x\n", t->id, + t->reg[0], t->reg[1], t->reg[2], t->reg[3]); + NV_DEBUG(dev, " 2a0: %08x\n", t->reg[4]); + return 0; } /** - * Processes the Memory Timing BIOS table, stores generated - * register values - * @pre init scripts were run, memtiming regs are initialized + * MR generation methods */ -void -nouveau_mem_timing_init(struct drm_device *dev) + +static int +nouveau_mem_ddr2_mr(struct drm_device *dev, u32 freq, + struct nouveau_pm_tbl_entry *e, u8 len, + struct nouveau_pm_memtiming *boot, + struct nouveau_pm_memtiming *t) +{ + t->drive_strength = 0; + if (len < 15) { + t->odt = boot->odt; + } else { + t->odt = e->RAM_FT1 & 0x07; + } + + if (e->tCL >= NV_MEM_CL_DDR2_MAX) { + NV_WARN(dev, "(%u) Invalid tCL: %u", t->id, e->tCL); + return -ERANGE; + } + + if (e->tWR >= NV_MEM_WR_DDR2_MAX) { + NV_WARN(dev, "(%u) Invalid tWR: %u", t->id, e->tWR); + return -ERANGE; + } + + if (t->odt > 3) { + NV_WARN(dev, "(%u) Invalid odt value, assuming disabled: %x", + t->id, t->odt); + t->odt = 0; + } + + t->mr[0] = (boot->mr[0] & 0x100f) | + (e->tCL) << 4 | + (e->tWR - 1) << 9; + t->mr[1] = (boot->mr[1] & 0x101fbb) | + (t->odt & 0x1) << 2 | + (t->odt & 0x2) << 5; + + NV_DEBUG(dev, "(%u) MR: %08x", t->id, t->mr[0]); + return 0; +} + +uint8_t nv_mem_wr_lut_ddr3[NV_MEM_WR_DDR3_MAX] = { + 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 5, 6, 6, 7, 7, 0, 0}; + +static int +nouveau_mem_ddr3_mr(struct drm_device *dev, u32 freq, + struct nouveau_pm_tbl_entry *e, u8 len, + struct nouveau_pm_memtiming *boot, + struct nouveau_pm_memtiming *t) +{ + u8 cl = e->tCL - 4; + + t->drive_strength = 0; + if (len < 15) { + t->odt = boot->odt; + } else { + t->odt = e->RAM_FT1 & 0x07; + } + + if (e->tCL >= NV_MEM_CL_DDR3_MAX || e->tCL < 4) { + NV_WARN(dev, "(%u) Invalid tCL: %u", t->id, e->tCL); + return -ERANGE; + } + + if (e->tWR >= NV_MEM_WR_DDR3_MAX || e->tWR < 4) { + NV_WARN(dev, "(%u) Invalid tWR: %u", t->id, e->tWR); + return -ERANGE; + } + + if (e->tCWL < 5) { + NV_WARN(dev, "(%u) Invalid tCWL: %u", t->id, e->tCWL); + return -ERANGE; + } + + t->mr[0] = (boot->mr[0] & 0x180b) | + /* CAS */ + (cl & 0x7) << 4 | + (cl & 0x8) >> 1 | + (nv_mem_wr_lut_ddr3[e->tWR]) << 9; + t->mr[1] = (boot->mr[1] & 0x101dbb) | + (t->odt & 0x1) << 2 | + (t->odt & 0x2) << 5 | + (t->odt & 0x4) << 7; + t->mr[2] = (boot->mr[2] & 0x20ffb7) | (e->tCWL - 5) << 3; + + NV_DEBUG(dev, "(%u) MR: %08x %08x", t->id, t->mr[0], t->mr[2]); + return 0; +} + +uint8_t nv_mem_cl_lut_gddr3[NV_MEM_CL_GDDR3_MAX] = { + 0, 0, 0, 0, 4, 5, 6, 7, 0, 1, 2, 3, 8, 9, 10, 11}; +uint8_t nv_mem_wr_lut_gddr3[NV_MEM_WR_GDDR3_MAX] = { + 0, 0, 0, 0, 0, 2, 3, 8, 9, 10, 11, 0, 0, 1, 1, 0, 3}; + +static int +nouveau_mem_gddr3_mr(struct drm_device *dev, u32 freq, + struct nouveau_pm_tbl_entry *e, u8 len, + struct nouveau_pm_memtiming *boot, + struct nouveau_pm_memtiming *t) +{ + if (len < 15) { + t->drive_strength = boot->drive_strength; + t->odt = boot->odt; + } else { + t->drive_strength = (e->RAM_FT1 & 0x30) >> 4; + t->odt = e->RAM_FT1 & 0x07; + } + + if (e->tCL >= NV_MEM_CL_GDDR3_MAX) { + NV_WARN(dev, "(%u) Invalid tCL: %u", t->id, e->tCL); + return -ERANGE; + } + + if (e->tWR >= NV_MEM_WR_GDDR3_MAX) { + NV_WARN(dev, "(%u) Invalid tWR: %u", t->id, e->tWR); + return -ERANGE; + } + + if (t->odt > 3) { + NV_WARN(dev, "(%u) Invalid odt value, assuming autocal: %x", + t->id, t->odt); + t->odt = 0; + } + + t->mr[0] = (boot->mr[0] & 0xe0b) | + /* CAS */ + ((nv_mem_cl_lut_gddr3[e->tCL] & 0x7) << 4) | + ((nv_mem_cl_lut_gddr3[e->tCL] & 0x8) >> 2); + t->mr[1] = (boot->mr[1] & 0x100f40) | t->drive_strength | + (t->odt << 2) | + (nv_mem_wr_lut_gddr3[e->tWR] & 0xf) << 4; + t->mr[2] = boot->mr[2]; + + NV_DEBUG(dev, "(%u) MR: %08x %08x %08x", t->id, + t->mr[0], t->mr[1], t->mr[2]); + return 0; +} + +static int +nouveau_mem_gddr5_mr(struct drm_device *dev, u32 freq, + struct nouveau_pm_tbl_entry *e, u8 len, + struct nouveau_pm_memtiming *boot, + struct nouveau_pm_memtiming *t) +{ + if (len < 15) { + t->drive_strength = boot->drive_strength; + t->odt = boot->odt; + } else { + t->drive_strength = (e->RAM_FT1 & 0x30) >> 4; + t->odt = e->RAM_FT1 & 0x03; + } + + if (e->tCL >= NV_MEM_CL_GDDR5_MAX) { + NV_WARN(dev, "(%u) Invalid tCL: %u", t->id, e->tCL); + return -ERANGE; + } + + if (e->tWR >= NV_MEM_WR_GDDR5_MAX) { + NV_WARN(dev, "(%u) Invalid tWR: %u", t->id, e->tWR); + return -ERANGE; + } + + if (t->odt > 3) { + NV_WARN(dev, "(%u) Invalid odt value, assuming autocal: %x", + t->id, t->odt); + t->odt = 0; + } + + t->mr[0] = (boot->mr[0] & 0x007) | + ((e->tCL - 5) << 3) | + ((e->tWR - 4) << 8); + t->mr[1] = (boot->mr[1] & 0x1007f0) | + t->drive_strength | + (t->odt << 2); + + NV_DEBUG(dev, "(%u) MR: %08x %08x", t->id, t->mr[0], t->mr[1]); + return 0; +} + +int +nouveau_mem_timing_calc(struct drm_device *dev, u32 freq, + struct nouveau_pm_memtiming *t) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_pm_engine *pm = &dev_priv->engine.pm; - struct nouveau_pm_memtimings *memtimings = &pm->memtimings; - struct nvbios *bios = &dev_priv->vbios; - struct bit_entry P; - struct nouveau_pm_tbl_header *hdr = NULL; - uint8_t magic_number; - u8 *entry; - int i; + struct nouveau_pm_memtiming *boot = &pm->boot.timing; + struct nouveau_pm_tbl_entry *e; + u8 ver, len, *ptr, *ramcfg; + int ret; + + ptr = nouveau_perf_timing(dev, freq, &ver, &len); + if (!ptr || ptr[0] == 0x00) { + *t = *boot; + return 0; + } + e = (struct nouveau_pm_tbl_entry *)ptr; + + t->tCWL = boot->tCWL; + + switch (dev_priv->card_type) { + case NV_40: + ret = nv40_mem_timing_calc(dev, freq, e, len, boot, t); + break; + case NV_50: + ret = nv50_mem_timing_calc(dev, freq, e, len, boot, t); + break; + case NV_C0: + ret = nvc0_mem_timing_calc(dev, freq, e, len, boot, t); + break; + default: + ret = -ENODEV; + break; + } - if (bios->type == NVBIOS_BIT) { - if (bit_table(dev, 'P', &P)) - return; + switch (dev_priv->vram_type * !ret) { + case NV_MEM_TYPE_GDDR3: + ret = nouveau_mem_gddr3_mr(dev, freq, e, len, boot, t); + break; + case NV_MEM_TYPE_GDDR5: + ret = nouveau_mem_gddr5_mr(dev, freq, e, len, boot, t); + break; + case NV_MEM_TYPE_DDR2: + ret = nouveau_mem_ddr2_mr(dev, freq, e, len, boot, t); + break; + case NV_MEM_TYPE_DDR3: + ret = nouveau_mem_ddr3_mr(dev, freq, e, len, boot, t); + break; + default: + ret = -EINVAL; + break; + } + + ramcfg = nouveau_perf_ramcfg(dev, freq, &ver, &len); + if (ramcfg) { + int dll_off; - if (P.version == 1) - hdr = (struct nouveau_pm_tbl_header *) ROMPTR(dev, P.data[4]); + if (ver == 0x00) + dll_off = !!(ramcfg[3] & 0x04); else - if (P.version == 2) - hdr = (struct nouveau_pm_tbl_header *) ROMPTR(dev, P.data[8]); - else { - NV_WARN(dev, "unknown mem for BIT P %d\n", P.version); + dll_off = !!(ramcfg[2] & 0x40); + + switch (dev_priv->vram_type) { + case NV_MEM_TYPE_GDDR3: + t->mr[1] &= ~0x00000040; + t->mr[1] |= 0x00000040 * dll_off; + break; + default: + t->mr[1] &= ~0x00000001; + t->mr[1] |= 0x00000001 * dll_off; + break; } + } + + return ret; +} + +void +nouveau_mem_timing_read(struct drm_device *dev, struct nouveau_pm_memtiming *t) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + u32 timing_base, timing_regs, mr_base; + int i; + + if (dev_priv->card_type >= 0xC0) { + timing_base = 0x10f290; + mr_base = 0x10f300; } else { - NV_DEBUG(dev, "BMP version too old for memory\n"); - return; + timing_base = 0x100220; + mr_base = 0x1002c0; } - if (!hdr) { - NV_DEBUG(dev, "memory timing table pointer invalid\n"); + t->id = -1; + + switch (dev_priv->card_type) { + case NV_50: + timing_regs = 9; + break; + case NV_C0: + case NV_D0: + timing_regs = 5; + break; + case NV_30: + case NV_40: + timing_regs = 3; + break; + default: + timing_regs = 0; return; } + for(i = 0; i < timing_regs; i++) + t->reg[i] = nv_rd32(dev, timing_base + (0x04 * i)); + + t->tCWL = 0; + if (dev_priv->card_type < NV_C0) { + t->tCWL = ((nv_rd32(dev, 0x100228) & 0x0f000000) >> 24) + 1; + } else if (dev_priv->card_type <= NV_D0) { + t->tCWL = ((nv_rd32(dev, 0x10f294) & 0x00000f80) >> 7); + } - if (hdr->version != 0x10) { - NV_WARN(dev, "memory timing table 0x%02x unknown\n", hdr->version); - return; + t->mr[0] = nv_rd32(dev, mr_base); + t->mr[1] = nv_rd32(dev, mr_base + 0x04); + t->mr[2] = nv_rd32(dev, mr_base + 0x20); + t->mr[3] = nv_rd32(dev, mr_base + 0x24); + + t->odt = 0; + t->drive_strength = 0; + + switch (dev_priv->vram_type) { + case NV_MEM_TYPE_DDR3: + t->odt |= (t->mr[1] & 0x200) >> 7; + case NV_MEM_TYPE_DDR2: + t->odt |= (t->mr[1] & 0x04) >> 2 | + (t->mr[1] & 0x40) >> 5; + break; + case NV_MEM_TYPE_GDDR3: + case NV_MEM_TYPE_GDDR5: + t->drive_strength = t->mr[1] & 0x03; + t->odt = (t->mr[1] & 0x0c) >> 2; + break; + default: + break; } +} - /* validate record length */ - if (hdr->entry_len < 15) { - NV_ERROR(dev, "mem timing table length unknown: %d\n", hdr->entry_len); - return; +int +nouveau_mem_exec(struct nouveau_mem_exec_func *exec, + struct nouveau_pm_level *perflvl) +{ + struct drm_nouveau_private *dev_priv = exec->dev->dev_private; + struct nouveau_pm_memtiming *info = &perflvl->timing; + u32 tMRD = 1000, tCKSRE = 0, tCKSRX = 0, tXS = 0, tDLLK = 0; + u32 mr[3] = { info->mr[0], info->mr[1], info->mr[2] }; + u32 mr1_dlloff; + + switch (dev_priv->vram_type) { + case NV_MEM_TYPE_DDR2: + tDLLK = 2000; + mr1_dlloff = 0x00000001; + break; + case NV_MEM_TYPE_DDR3: + tDLLK = 12000; + mr1_dlloff = 0x00000001; + break; + case NV_MEM_TYPE_GDDR3: + tDLLK = 40000; + mr1_dlloff = 0x00000040; + break; + default: + NV_ERROR(exec->dev, "cannot reclock unsupported memtype\n"); + return -ENODEV; } - /* parse vbios entries into common format */ - memtimings->timing = - kcalloc(hdr->entry_cnt, sizeof(*memtimings->timing), GFP_KERNEL); - if (!memtimings->timing) - return; + /* fetch current MRs */ + switch (dev_priv->vram_type) { + case NV_MEM_TYPE_GDDR3: + case NV_MEM_TYPE_DDR3: + mr[2] = exec->mrg(exec, 2); + default: + mr[1] = exec->mrg(exec, 1); + mr[0] = exec->mrg(exec, 0); + break; + } - /* Get "some number" from the timing reg for NV_40 and NV_50 - * Used in calculations later... source unknown */ - magic_number = 0; - if (P.version == 1) { - magic_number = (nv_rd32(dev, 0x100228) & 0x0f000000) >> 24; + /* DLL 'on' -> DLL 'off' mode, disable before entering self-refresh */ + if (!(mr[1] & mr1_dlloff) && (info->mr[1] & mr1_dlloff)) { + exec->precharge(exec); + exec->mrs (exec, 1, mr[1] | mr1_dlloff); + exec->wait(exec, tMRD); } - entry = (u8*) hdr + hdr->header_len; - for (i = 0; i < hdr->entry_cnt; i++, entry += hdr->entry_len) { - struct nouveau_pm_memtiming *timing = &pm->memtimings.timing[i]; - if (entry[0] == 0) - continue; + /* enter self-refresh mode */ + exec->precharge(exec); + exec->refresh(exec); + exec->refresh(exec); + exec->refresh_auto(exec, false); + exec->refresh_self(exec, true); + exec->wait(exec, tCKSRE); + + /* modify input clock frequency */ + exec->clock_set(exec); + + /* exit self-refresh mode */ + exec->wait(exec, tCKSRX); + exec->precharge(exec); + exec->refresh_self(exec, false); + exec->refresh_auto(exec, true); + exec->wait(exec, tXS); + + /* update MRs */ + if (mr[2] != info->mr[2]) { + exec->mrs (exec, 2, info->mr[2]); + exec->wait(exec, tMRD); + } + + if (mr[1] != info->mr[1]) { + /* need to keep DLL off until later, at least on GDDR3 */ + exec->mrs (exec, 1, info->mr[1] | (mr[1] & mr1_dlloff)); + exec->wait(exec, tMRD); + } + + if (mr[0] != info->mr[0]) { + exec->mrs (exec, 0, info->mr[0]); + exec->wait(exec, tMRD); + } - timing->id = i; - timing->WR = entry[0]; - timing->CL = entry[2]; + /* update PFB timing registers */ + exec->timing_set(exec); - if(dev_priv->card_type <= NV_40) { - nv40_mem_timing_entry(dev,hdr,(struct nouveau_pm_tbl_entry*) entry,magic_number,&pm->memtimings.timing[i]); - } else if(dev_priv->card_type == NV_50){ - nv50_mem_timing_entry(dev,&P,hdr,(struct nouveau_pm_tbl_entry*) entry,magic_number,&pm->memtimings.timing[i]); - } else if(dev_priv->card_type == NV_C0) { - nvc0_mem_timing_entry(dev,hdr,(struct nouveau_pm_tbl_entry*) entry,&pm->memtimings.timing[i]); + /* DLL (enable + ) reset */ + if (!(info->mr[1] & mr1_dlloff)) { + if (mr[1] & mr1_dlloff) { + exec->mrs (exec, 1, info->mr[1]); + exec->wait(exec, tMRD); } + exec->mrs (exec, 0, info->mr[0] | 0x00000100); + exec->wait(exec, tMRD); + exec->mrs (exec, 0, info->mr[0] | 0x00000000); + exec->wait(exec, tMRD); + exec->wait(exec, tDLLK); + if (dev_priv->vram_type == NV_MEM_TYPE_GDDR3) + exec->precharge(exec); } - memtimings->nr_timing = hdr->entry_cnt; - memtimings->supported = P.version == 1; + return 0; } -void -nouveau_mem_timing_fini(struct drm_device *dev) +int +nouveau_mem_vbios_type(struct drm_device *dev) { - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_pm_memtimings *mem = &dev_priv->engine.pm.memtimings; + struct bit_entry M; + u8 ramcfg = (nv_rd32(dev, 0x101000) & 0x0000003c) >> 2; + if (!bit_table(dev, 'M', &M) || M.version != 2 || M.length < 5) { + u8 *table = ROMPTR(dev, M.data[3]); + if (table && table[0] == 0x10 && ramcfg < table[3]) { + u8 *entry = table + table[1] + (ramcfg * table[2]); + switch (entry[0] & 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; + } - if(mem->timing) { - kfree(mem->timing); - mem->timing = NULL; + } } + return NV_MEM_TYPE_UNKNOWN; } static int |