diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 799cd149745d000246dfe4a4bbc92c444cfde0f7..e1fb2c95eb900c9c950e029cd9d084d2816d0fc6 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -401,10 +401,28 @@ struct nouveau_pm_threshold_temp { s16 fan_boost; }; +struct nouveau_pm_memtiming { + u32 reg_100220; + u32 reg_100224; + u32 reg_100228; + u32 reg_10022c; + u32 reg_100230; + u32 reg_100234; + u32 reg_100238; + u32 reg_10023c; +}; + +struct nouveau_pm_memtimings { + bool supported; + struct nouveau_pm_memtiming *timing; + int nr_timing; +}; + struct nouveau_pm_engine { struct nouveau_pm_voltage voltage; struct nouveau_pm_level perflvl[NOUVEAU_PM_MAX_LEVEL]; int nr_perflvl; + struct nouveau_pm_memtimings memtimings; struct nouveau_pm_temp_sensor_constants sensor_constants; struct nouveau_pm_threshold_temp threshold_temp; diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c index 2db01f80f38e56ae62c9afd9ae2b6633d6ff4bb2..00b31b5e16cd1c8e69c34c153959ede9a10e285a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mem.c +++ b/drivers/gpu/drm/nouveau/nouveau_mem.c @@ -648,3 +648,147 @@ nouveau_mem_gart_init(struct drm_device *dev) return 0; } +void +nouveau_mem_timing_init(struct drm_device *dev) +{ + 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; + u8 tUNK_0, tUNK_1, tUNK_2; + u8 tRP; /* Byte 3 */ + u8 tRAS; /* Byte 5 */ + u8 tRFC; /* Byte 7 */ + u8 tRC; /* Byte 9 */ + u8 tUNK_10, tUNK_11, tUNK_12, tUNK_13, tUNK_14; + u8 tUNK_18, tUNK_19, tUNK_20, tUNK_21; + u8 *mem = NULL, *entry; + int i, recordlen, entries; + + if (bios->type == NVBIOS_BIT) { + if (bit_table(dev, 'P', &P)) + return; + + if (P.version == 1) + mem = ROMPTR(bios, P.data[4]); + else + if (P.version == 2) + mem = ROMPTR(bios, P.data[8]); + else { + NV_WARN(dev, "unknown mem for BIT P %d\n", P.version); + } + } else { + NV_DEBUG(dev, "BMP version too old for memory\n"); + return; + } + + if (!mem) { + NV_DEBUG(dev, "memory timing table pointer invalid\n"); + return; + } + + if (mem[0] != 0x10) { + NV_WARN(dev, "memory timing table 0x%02x unknown\n", mem[0]); + return; + } + + /* validate record length */ + entries = mem[2]; + recordlen = mem[3]; + if (recordlen < 15) { + NV_ERROR(dev, "mem timing table length unknown: %d\n", mem[3]); + return; + } + + /* parse vbios entries into common format */ + memtimings->timing = + kcalloc(entries, sizeof(*memtimings->timing), GFP_KERNEL); + if (!memtimings->timing) + return; + + entry = mem + mem[1]; + for (i = 0; i < entries; i++, entry += recordlen) { + struct nouveau_pm_memtiming *timing = &pm->memtimings.timing[i]; + if (entry[0] == 0) + continue; + + tUNK_18 = 1; + tUNK_19 = 1; + tUNK_20 = 0; + tUNK_21 = 0; + switch (recordlen) { + case 0x21: + tUNK_21 = entry[21]; + case 0x20: + tUNK_20 = entry[20]; + case 0x19: + tUNK_19 = entry[19]; + case 0x18: + tUNK_18 = entry[18]; + default: + tUNK_0 = entry[0]; + tUNK_1 = entry[1]; + tUNK_2 = entry[2]; + tRP = entry[3]; + tRAS = entry[5]; + tRFC = entry[7]; + tRC = entry[9]; + tUNK_10 = entry[10]; + tUNK_11 = entry[11]; + tUNK_12 = entry[12]; + tUNK_13 = entry[13]; + tUNK_14 = entry[14]; + break; + } + + timing->reg_100220 = (tRC << 24 | tRFC << 16 | tRAS << 8 | tRP); + + /* XXX: I don't trust the -1's and +1's... they must come + * from somewhere! */ + timing->reg_100224 = ((tUNK_0 + tUNK_19 + 1) << 24 | + tUNK_18 << 16 | + (tUNK_1 + tUNK_19 + 1) << 8 | + (tUNK_2 - 1)); + + timing->reg_100228 = (tUNK_12 << 16 | tUNK_11 << 8 | tUNK_10); + if(recordlen > 19) { + timing->reg_100228 += (tUNK_19 - 1) << 24; + } else { + timing->reg_100228 += tUNK_12 << 24; + } + + /* XXX: reg_10022c */ + + timing->reg_100230 = (tUNK_20 << 24 | tUNK_21 << 16 | + tUNK_13 << 8 | tUNK_13); + + /* XXX: +6? */ + timing->reg_100234 = (tRAS << 24 | (tUNK_19 + 6) << 8 | tRC); + if(tUNK_10 > tUNK_11) { + timing->reg_100234 += tUNK_10 << 16; + } else { + timing->reg_100234 += tUNK_11 << 16; + } + + /* XXX; reg_100238, reg_10023c */ + NV_DEBUG(dev, "Entry %d: 220: %08x %08x %08x %08x\n", i, + timing->reg_100220, timing->reg_100224, + timing->reg_100228, timing->reg_10022c); + NV_DEBUG(dev, " 230: %08x %08x %08x %08x\n", + timing->reg_100230, timing->reg_100234, + timing->reg_100238, timing->reg_10023c); + } + + memtimings->nr_timing = entries; + memtimings->supported = true; +} + +void +nouveau_mem_timing_fini(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_pm_memtimings *mem = &dev_priv->engine.pm.memtimings; + + kfree(mem->timing); +} diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index b1d3f4b26ebd61291bbf6fab7b04c16b701a3765..01437f1753a745c8de03c2dfcadf5abcc71e93b9 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -451,6 +451,7 @@ nouveau_pm_init(struct drm_device *dev) nouveau_volt_init(dev); nouveau_perf_init(dev); nouveau_temp_init(dev); + nouveau_mem_timing_init(dev); NV_INFO(dev, "%d available performance level(s)\n", pm->nr_perflvl); for (i = 0; i < pm->nr_perflvl; i++) { @@ -491,9 +492,10 @@ nouveau_pm_fini(struct drm_device *dev) if (pm->cur != &pm->boot) nouveau_pm_perflvl_set(dev, &pm->boot); + nouveau_mem_timing_fini(dev); + nouveau_temp_fini(dev); nouveau_perf_fini(dev); nouveau_volt_fini(dev); - nouveau_temp_fini(dev); nouveau_hwmon_fini(dev); nouveau_sysfs_fini(dev); diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.h b/drivers/gpu/drm/nouveau/nouveau_pm.h index 6ad0ca9db88f0d060e1af0395fd24a5f26702053..7504e3b8c023d83e4f089904d8b789c3abc16d82 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.h +++ b/drivers/gpu/drm/nouveau/nouveau_pm.h @@ -42,6 +42,10 @@ int nouveau_voltage_gpio_set(struct drm_device *, int voltage); void nouveau_perf_init(struct drm_device *); void nouveau_perf_fini(struct drm_device *); +/* nouveau_mem.c */ +void nouveau_mem_timing_init(struct drm_device *); +void nouveau_mem_timing_fini(struct drm_device *); + /* nv04_pm.c */ int nv04_pm_clock_get(struct drm_device *, u32 id); void *nv04_pm_clock_pre(struct drm_device *, u32 id, int khz);