The first patch should allow the generation of correct memtimings (r100240 put aside) on the nv40-nv98.a1 interval. The second one is associating the memtimings with the performance levels. Please comment on it or push it
Martin Peres
2011-Mar-29 00:18 UTC
[Nouveau] [PATCH 1/2] PM: NV50-NV98.a1 correct/implement many additional memtimings Nearly consistent with envytools. Added stepping to drm_nouveau_private to make sure newer NV98 (105M) is zero rather than incorrect.
From: Roy Spliet <r.spliet at student.tudelft.nl>
---
drivers/gpu/drm/nouveau/nouveau_drv.h | 2 +
drivers/gpu/drm/nouveau/nouveau_mem.c | 42 +++++++++++++++++++++---------
drivers/gpu/drm/nouveau/nouveau_state.c | 2 +
3 files changed, 33 insertions(+), 13 deletions(-)
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h
b/drivers/gpu/drm/nouveau/nouveau_drv.h
index 383093e..aca960e 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -466,6 +466,7 @@ struct nouveau_pm_memtiming {
u32 reg_100234;
u32 reg_100238;
u32 reg_10023c;
+ u32 reg_100240;
};
struct nouveau_pm_memtimings {
@@ -637,6 +638,7 @@ struct drm_nouveau_private {
enum nouveau_card_type card_type;
/* exact chipset, derived from NV_PMC_BOOT_0 */
int chipset;
+ int stepping;
int flags;
void __iomem *mmio;
diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c
b/drivers/gpu/drm/nouveau/nouveau_mem.c
index 405f990..3636529 100644
--- a/drivers/gpu/drm/nouveau/nouveau_mem.c
+++ b/drivers/gpu/drm/nouveau/nouveau_mem.c
@@ -597,9 +597,9 @@ nouveau_mem_timing_init(struct drm_device *dev)
if (!memtimings->timing)
return;
- /* Get "some number" from the timing reg for NV_40
+ /* Get "some number" from the timing reg for NV_40 and NV_50
* Used in calculations later */
- if (dev_priv->card_type == NV_40) {
+ if (dev_priv->card_type >= NV_40 && dev_priv->chipset <
0x98) {
magic_number = (nv_rd32(dev, 0x100228) & 0x0f000000) >> 24;
}
@@ -643,7 +643,7 @@ nouveau_mem_timing_init(struct drm_device *dev)
/* XXX: I don't trust the -1's and +1's... they must come
* from somewhere! */
timing->reg_100224 = (tUNK_0 + tUNK_19 + 1 + magic_number) << 24 |
- tUNK_18 << 16 |
+ max(tUNK_18, (u8) 1) << 16 |
(tUNK_1 + tUNK_19 + 1 + magic_number) << 8;
if (dev_priv->chipset == 0xa8) {
timing->reg_100224 |= (tUNK_2 - 1);
@@ -654,15 +654,25 @@ nouveau_mem_timing_init(struct drm_device *dev)
timing->reg_100228 = (tUNK_12 << 16 | tUNK_11 << 8 | tUNK_10);
if (dev_priv->chipset >= 0xa3 && dev_priv->chipset <
0xaa) {
timing->reg_100228 |= (tUNK_19 - 1) << 24;
- }
+ } else {
+ timing->reg_100228 |= magic_number << 24;
+ }
if (dev_priv->card_type == NV_40) {
/* NV40: don't know what the rest of the regs are..
* And don't need to know either */
- timing->reg_100228 |= 0x20200000 | magic_number << 24;
+ timing->reg_100228 |= 0x20200000;
} else if (dev_priv->card_type >= NV_50) {
- /* XXX: reg_10022c */
- timing->reg_10022c = tUNK_2 - 1;
+ if(dev_priv->chipset < 0x98 ||
+ (dev_priv->chipset == 0x98 && dev_priv->stepping <= 0xa1))
{
+ timing->reg_10022c = (0x14 + tUNK_2) << 24 |
+ 0x16 << 16 |
+ (tUNK_2 - 1) << 8 |
+ (tUNK_2 - 1);
+ } else {
+ /* XXX: reg_10022c for recentish cards */
+ timing->reg_10022c = tUNK_2 - 1;
+ }
timing->reg_100230 = (tUNK_20 << 24 | tUNK_21 << 16 |
tUNK_13 << 8 | tUNK_13);
@@ -670,23 +680,27 @@ nouveau_mem_timing_init(struct drm_device *dev)
timing->reg_100234 = (tRAS << 24 | tRC);
timing->reg_100234 += max(tUNK_10, tUNK_11) << 16;
- if (dev_priv->chipset < 0xa3) {
+ if (dev_priv->chipset < 0x98 ||
+ (dev_priv->chipset == 0x98 && dev_priv->stepping <= 0xa1))
{
timing->reg_100234 |= (tUNK_2 + 2) << 8;
} else {
/* XXX: +6? */
timing->reg_100234 |= (tUNK_19 + 6) << 8;
}
- /* XXX; reg_100238, reg_10023c
- * reg_100238: 0x00??????
- * reg_10023c: 0x!!??0202 for NV50+ cards (empirical evidence) */
+ /* XXX; reg_100238
+ * reg_100238: 0x00?????? */
timing->reg_10023c = 0x202;
- if (dev_priv->chipset < 0xa3) {
+ if (dev_priv->chipset < 0x98 ||
+ (dev_priv->chipset == 0x98 && dev_priv->stepping <= 0xa1))
{
timing->reg_10023c |= 0x4000000 | (tUNK_2 - 1) << 16;
} else {
- /* currently unknown
+ /* XXX: reg_10023c
+ * currently unknown
* 10023c seen as 06xxxxxx, 0bxxxxxx or 0fxxxxxx */
}
+
+ /* XXX: reg_100240? */
}
NV_DEBUG(dev, "Entry %d: 220: %08x %08x %08x %08x\n", i,
@@ -695,6 +709,8 @@ nouveau_mem_timing_init(struct drm_device *dev)
NV_DEBUG(dev, " 230: %08x %08x %08x %08x\n",
timing->reg_100230, timing->reg_100234,
timing->reg_100238, timing->reg_10023c);
+ NV_DEBUG(dev, " 240: %08x\n",
+ timing->reg_100240);
}
memtimings->nr_timing = entries;
diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c
b/drivers/gpu/drm/nouveau/nouveau_state.c
index e162514..d8297c0 100644
--- a/drivers/gpu/drm/nouveau/nouveau_state.c
+++ b/drivers/gpu/drm/nouveau/nouveau_state.c
@@ -922,11 +922,13 @@ int nouveau_load(struct drm_device *dev, unsigned long
flags)
/* Time to determine the card architecture */
reg0 = nv_rd32(dev, NV03_PMC_BOOT_0);
+ dev_priv->stepping = 0; /* XXX: add stepping for pre-NV10? */
/* We're dealing with >=NV10 */
if ((reg0 & 0x0f000000) > 0) {
/* Bit 27-20 contain the architecture in hex */
dev_priv->chipset = (reg0 & 0xff00000) >> 20;
+ dev_priv->stepping = (reg0 & 0xff);
/* NV04 or NV05 */
} else if ((reg0 & 0xff00fff0) == 0x20004000) {
if (reg0 & 0x00f00000)
--
1.7.4.1
Martin Peres
2011-Mar-29 00:18 UTC
[Nouveau] [PATCH 2/2] Associate memtimings with performance levels
Signed-off-by: Martin Peres <martin.peres at ensi-bourges.fr>
---
drivers/gpu/drm/nouveau/nouveau_drv.h | 26 ++++++++++++++------------
drivers/gpu/drm/nouveau/nouveau_mem.c | 1 +
drivers/gpu/drm/nouveau/nouveau_perf.c | 25 +++++++++++++++++++++++++
drivers/gpu/drm/nouveau/nouveau_pm.c | 14 +++++++++-----
4 files changed, 49 insertions(+), 17 deletions(-)
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h
b/drivers/gpu/drm/nouveau/nouveau_drv.h
index aca960e..0d23671 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -426,6 +426,19 @@ struct nouveau_pm_voltage {
int nr_level;
};
+struct nouveau_pm_memtiming {
+ int id;
+ u32 reg_100220;
+ u32 reg_100224;
+ u32 reg_100228;
+ u32 reg_10022c;
+ u32 reg_100230;
+ u32 reg_100234;
+ u32 reg_100238;
+ u32 reg_10023c;
+ u32 reg_100240;
+};
+
#define NOUVEAU_PM_MAX_LEVEL 8
struct nouveau_pm_level {
struct device_attribute dev_attr;
@@ -441,6 +454,7 @@ struct nouveau_pm_level {
u8 fanspeed;
u16 memscript;
+ struct nouveau_pm_memtiming *timing;
};
struct nouveau_pm_temp_sensor_constants {
@@ -457,18 +471,6 @@ 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;
- u32 reg_100240;
-};
-
struct nouveau_pm_memtimings {
bool supported;
struct nouveau_pm_memtiming *timing;
diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c
b/drivers/gpu/drm/nouveau/nouveau_mem.c
index 3636529..45b315c 100644
--- a/drivers/gpu/drm/nouveau/nouveau_mem.c
+++ b/drivers/gpu/drm/nouveau/nouveau_mem.c
@@ -702,6 +702,7 @@ nouveau_mem_timing_init(struct drm_device *dev)
/* XXX: reg_100240? */
}
+ timing->id = i;
NV_DEBUG(dev, "Entry %d: 220: %08x %08x %08x %08x\n", i,
timing->reg_100220, timing->reg_100224,
diff --git a/drivers/gpu/drm/nouveau/nouveau_perf.c
b/drivers/gpu/drm/nouveau/nouveau_perf.c
index 670e3cb..904d680 100644
--- a/drivers/gpu/drm/nouveau/nouveau_perf.c
+++ b/drivers/gpu/drm/nouveau/nouveau_perf.c
@@ -82,6 +82,7 @@ nouveau_perf_init(struct drm_device *dev)
u8 version, headerlen, recordlen, entries;
u8 *perf, *entry;
int vid, i;
+ uint8_t timing_entry = (nv_rd32(dev, NV_PEXTDEV_BOOT_0) & 0x1c) >>
2;
if (bios->type == NVBIOS_BIT) {
if (bit_table(dev, 'P', &P))
@@ -124,6 +125,8 @@ nouveau_perf_init(struct drm_device *dev)
for (i = 0; i < entries; i++) {
struct nouveau_pm_level *perflvl = &pm->perflvl[pm->nr_perflvl];
+ perflvl->timing = NULL;
+
if (entry[0] == 0xff) {
entry += recordlen;
continue;
@@ -190,6 +193,28 @@ nouveau_perf_init(struct drm_device *dev)
}
}
+ /* get the corresponding memory timings */
+ if (pm->memtimings.supported) {
+ uint8_t timing_id = 0xff;
+
+ if (version > 0x15 && version < 0x40 &&
+ timing_entry < perf[4]) {
+ uint16_t extra_data;
+ extra_data = perf[3] + (timing_entry * perf[5]);
+
+ timing_id = entry[extra_data + 1];
+ } else if (version == 0x40 && timing_entry < perf[4]) {
+ uint16_t extra_data;
+ extra_data = perf[2] + (timing_entry * perf[3]);
+
+ timing_id = entry[extra_data + 1];
+ }
+
+ if (pm->memtimings.nr_timing > timing_id)
+ perflvl->timing + &pm->memtimings.timing[timing_id];
+ }
+
snprintf(perflvl->name, sizeof(perflvl->name),
"performance_level_%d", i);
perflvl->id = i;
diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c
b/drivers/gpu/drm/nouveau/nouveau_pm.c
index 0b1caeb..4e51404 100644
--- a/drivers/gpu/drm/nouveau/nouveau_pm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_pm.c
@@ -156,7 +156,7 @@ nouveau_pm_perflvl_get(struct drm_device *dev, struct
nouveau_pm_level *perflvl)
static void
nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len)
{
- char c[16], s[16], v[16], f[16];
+ char c[16], s[16], v[16], f[16], t[16];
c[0] = '\0';
if (perflvl->core)
@@ -174,8 +174,12 @@ nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl,
char *ptr, int len)
if (perflvl->fanspeed)
snprintf(f, sizeof(f), " fanspeed %d%%", perflvl->fanspeed);
- snprintf(ptr, len, "memory %dMHz%s%s%s%s\n", perflvl->memory /
1000,
- c, s, v, f);
+ t[0] = '\0';
+ if (perflvl->timing)
+ snprintf(t, sizeof(t), " timing %d", perflvl->timing->id);
+
+ snprintf(ptr, len, "memory %dMHz%s%s%s%s%s\n", perflvl->memory /
1000,
+ c, s, v, f, t);
}
static ssize_t
@@ -476,10 +480,10 @@ nouveau_pm_init(struct drm_device *dev)
char info[256];
int ret, i;
+ nouveau_mem_timing_init(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++) {
@@ -525,10 +529,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_mem_timing_fini(dev);
#ifdef CONFIG_ACPI
unregister_acpi_notifier(&pm->acpi_nb);
--
1.7.4.1