From ddb2005516949dc50d117cb8381d7a3f8f0614b0 Mon Sep 17 00:00:00 2001 From: Martin Peres Date: Sat, 17 Dec 2011 12:24:59 +0100 Subject: drm/nouveau/pm: style fixes Signed-off-by: Martin Peres Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_pm.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'drivers/gpu/drm/nouveau/nouveau_pm.c') diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index 9064d7f19794..1770f1569c90 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -162,6 +162,7 @@ nouveau_pm_profile_set(struct drm_device *dev, const char *profile) struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_pm_engine *pm = &dev_priv->engine.pm; struct nouveau_pm_level *perflvl = NULL; + long pl; /* safety precaution, for now */ if (nouveau_perflvl_wr != 7777) @@ -170,8 +171,9 @@ nouveau_pm_profile_set(struct drm_device *dev, const char *profile) if (!strncmp(profile, "boot", 4)) perflvl = &pm->boot; else { - int pl = simple_strtol(profile, NULL, 10); int i; + if (kstrtol(profile, 10, &pl) == -EINVAL) + return -EINVAL; for (i = 0; i < pm->nr_perflvl; i++) { if (pm->perflvl[i].id == pl) { @@ -397,7 +399,7 @@ nouveau_hwmon_set_max_temp(struct device *d, struct device_attribute *a, struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp; long value; - if (strict_strtol(buf, 10, &value) == -EINVAL) + if (kstrtol(buf, 10, &value) == -EINVAL) return count; temp->down_clock = value/1000; @@ -432,7 +434,7 @@ nouveau_hwmon_set_critical_temp(struct device *d, struct device_attribute *a, struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp; long value; - if (strict_strtol(buf, 10, &value) == -EINVAL) + if (kstrtol(buf, 10, &value) == -EINVAL) return count; temp->critical = value/1000; @@ -529,7 +531,7 @@ nouveau_hwmon_set_pwm0(struct device *d, struct device_attribute *a, if (nouveau_perflvl_wr != 7777) return -EPERM; - if (strict_strtol(buf, 10, &value) == -EINVAL) + if (kstrtol(buf, 10, &value) == -EINVAL) return -EINVAL; if (value < pm->fan.min_duty) @@ -568,7 +570,7 @@ nouveau_hwmon_set_pwm0_min(struct device *d, struct device_attribute *a, struct nouveau_pm_engine *pm = &dev_priv->engine.pm; long value; - if (strict_strtol(buf, 10, &value) == -EINVAL) + if (kstrtol(buf, 10, &value) == -EINVAL) return -EINVAL; if (value < 0) @@ -609,7 +611,7 @@ nouveau_hwmon_set_pwm0_max(struct device *d, struct device_attribute *a, struct nouveau_pm_engine *pm = &dev_priv->engine.pm; long value; - if (strict_strtol(buf, 10, &value) == -EINVAL) + if (kstrtol(buf, 10, &value) == -EINVAL) return -EINVAL; if (value < 0) @@ -731,8 +733,10 @@ nouveau_hwmon_fini(struct drm_device *dev) if (pm->hwmon) { sysfs_remove_group(&dev->pdev->dev.kobj, &hwmon_attrgroup); - sysfs_remove_group(&dev->pdev->dev.kobj, &hwmon_pwm_fan_attrgroup); - sysfs_remove_group(&dev->pdev->dev.kobj, &hwmon_fan_rpm_attrgroup); + sysfs_remove_group(&dev->pdev->dev.kobj, + &hwmon_pwm_fan_attrgroup); + sysfs_remove_group(&dev->pdev->dev.kobj, + &hwmon_fan_rpm_attrgroup); hwmon_device_unregister(pm->hwmon); } -- cgit v1.2.3 From bc6389e4fa1a6535021ffc4d3b37d48f9a0542e2 Mon Sep 17 00:00:00 2001 From: Martin Peres Date: Fri, 7 Oct 2011 01:33:12 +0200 Subject: drm/nouveau/pm: restore fan speed after suspend Signed-off-by: Martin Peres Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_drv.h | 1 + drivers/gpu/drm/nouveau/nouveau_pm.c | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm/nouveau/nouveau_pm.c') diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 760f85c85ccd..378c64c2973e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -519,6 +519,7 @@ struct nouveau_pm_memtimings { }; struct nouveau_pm_fan { + u32 percent; u32 min_duty; u32 max_duty; u32 pwm_freq; diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index 1770f1569c90..0c79f060f7f1 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -89,7 +89,10 @@ nouveau_pwmfan_set(struct drm_device *dev, int percent) if (dev_priv->card_type <= NV_40 || (gpio.log[0] & 1)) duty = divs - duty; - return pm->pwm_set(dev, gpio.line, divs, duty); + ret = pm->pwm_set(dev, gpio.line, divs, duty); + if (!ret) + pm->fan.percent = percent; + return ret; } return -ENODEV; @@ -800,6 +803,9 @@ nouveau_pm_init(struct drm_device *dev) } } + /* determine the current fan speed */ + pm->fan.percent = nouveau_pwmfan_get(dev); + nouveau_sysfs_init(dev); nouveau_hwmon_init(dev); #if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY) @@ -844,4 +850,5 @@ nouveau_pm_resume(struct drm_device *dev) perflvl = pm->cur; pm->cur = &pm->boot; nouveau_pm_perflvl_set(dev, perflvl); + nouveau_pwmfan_set(dev, pm->fan.percent); } -- cgit v1.2.3 From b1aa5531cc74ea023ad35e9cf5872112a15b4f70 Mon Sep 17 00:00:00 2001 From: Martin Peres Date: Sat, 20 Aug 2011 16:37:06 +0200 Subject: drm/nouveau: move pwm_divisor to the nouveau_pm_fan struct Signed-off-by: Martin Peres Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_drv.h | 2 +- drivers/gpu/drm/nouveau/nouveau_perf.c | 2 +- drivers/gpu/drm/nouveau/nouveau_pm.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/gpu/drm/nouveau/nouveau_pm.c') diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 378c64c2973e..78302cccf6fd 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -523,6 +523,7 @@ struct nouveau_pm_fan { u32 min_duty; u32 max_duty; u32 pwm_freq; + u32 pwm_divisor; }; struct nouveau_pm_engine { @@ -533,7 +534,6 @@ struct nouveau_pm_engine { struct nouveau_pm_temp_sensor_constants sensor_constants; struct nouveau_pm_threshold_temp threshold_temp; struct nouveau_pm_fan fan; - u32 pwm_divisor; struct nouveau_pm_level boot; struct nouveau_pm_level *cur; diff --git a/drivers/gpu/drm/nouveau/nouveau_perf.c b/drivers/gpu/drm/nouveau/nouveau_perf.c index 54895cc60d2a..2ca26f39292a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_perf.c +++ b/drivers/gpu/drm/nouveau/nouveau_perf.c @@ -206,7 +206,7 @@ nouveau_perf_init(struct drm_device *dev) recordlen = perf[3] + (perf[4] * perf[5]); entries = perf[2]; - pm->pwm_divisor = ROM16(perf[6]); + pm->fan.pwm_divisor = ROM16(perf[6]); } else { recordlen = perf[2] + (perf[3] * perf[4]); entries = perf[5]; diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index 0c79f060f7f1..5ee5ca6166aa 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -77,7 +77,7 @@ nouveau_pwmfan_set(struct drm_device *dev, int percent) ret = nouveau_gpio_find(dev, 0, DCB_GPIO_PWM_FAN, 0xff, &gpio); if (ret == 0) { - divs = pm->pwm_divisor; + divs = pm->fan.pwm_divisor; if (pm->fan.pwm_freq) { /*XXX: PNVIO clock more than likely... */ divs = 135000 / pm->fan.pwm_freq; -- cgit v1.2.3 From b0103747094b62231fc951dfaca4897f67670874 Mon Sep 17 00:00:00 2001 From: Martin Peres Date: Thu, 3 Nov 2011 00:03:06 +0100 Subject: drm/nouveau/pm: improve the reclocking logs' readability Signed-off-by: Martin Peres Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_pm.c | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) (limited to 'drivers/gpu/drm/nouveau/nouveau_pm.c') diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index 5ee5ca6166aa..08c563dfa28e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -147,9 +147,13 @@ nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl) return ret; state = pm->clocks_pre(dev, perflvl); - if (IS_ERR(state)) - return PTR_ERR(state); - pm->clocks_set(dev, state); + if (IS_ERR(state)) { + ret = PTR_ERR(state); + goto error; + } + ret = pm->clocks_set(dev, state); + if (ret) + goto error; ret = nouveau_pm_perflvl_aux(dev, perflvl, perflvl, pm->cur); if (ret) @@ -157,6 +161,11 @@ nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl) pm->cur = perflvl; return 0; + +error: + /* restore the fan speed and voltage before leaving */ + nouveau_pm_perflvl_aux(dev, perflvl, perflvl, pm->cur); + return ret; } static int @@ -165,6 +174,8 @@ nouveau_pm_profile_set(struct drm_device *dev, const char *profile) struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_pm_engine *pm = &dev_priv->engine.pm; struct nouveau_pm_level *perflvl = NULL; + u64 start_time; + int ret = 0; long pl; /* safety precaution, for now */ @@ -189,8 +200,17 @@ nouveau_pm_profile_set(struct drm_device *dev, const char *profile) return -EINVAL; } - NV_INFO(dev, "setting performance level: %s\n", profile); - return nouveau_pm_perflvl_set(dev, perflvl); + NV_INFO(dev, "setting performance level: %s", profile); + start_time = nv04_timer_read(dev); + ret = nouveau_pm_perflvl_set(dev, perflvl); + if (!ret) { + NV_INFO(dev, "> reclocking took %lluns\n\n", + (nv04_timer_read(dev) - start_time)); + } else { + NV_INFO(dev, "> reclocking failed\n\n"); + } + + return ret; } static int -- cgit v1.2.3 From 3d8a408c43828d586629204fe22134ae8f39b8be Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 23 Jan 2012 12:58:33 +1000 Subject: drm/nouveau/pm: avoid potential divide-by-zero Signed-off-by: Ben Skeggs Signed-off-by: Martin Peres --- drivers/gpu/drm/nouveau/nouveau_pm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu/drm/nouveau/nouveau_pm.c') diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index 08c563dfa28e..6e3851e1a1d7 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -50,7 +50,7 @@ nouveau_pwmfan_get(struct drm_device *dev) ret = nouveau_gpio_find(dev, 0, DCB_GPIO_PWM_FAN, 0xff, &gpio); if (ret == 0) { ret = pm->pwm_get(dev, gpio.line, &divs, &duty); - if (ret == 0) { + if (ret == 0 && divs) { divs = max(divs, duty); if (dev_priv->card_type <= NV_40 || (gpio.log[0] & 1)) duty = divs - duty; -- cgit v1.2.3 From c7c039fd31be82ecb8d48477955e76badd38141a Mon Sep 17 00:00:00 2001 From: Roy Spliet Date: Mon, 9 Jan 2012 15:23:07 +1000 Subject: drm/nouveau/pm: implement DDR2/DDR3/GDDR3/GDDR5 MR generation and validation Roy Spliet: - Implement according to specs - Simplify - Make array for mc latency registers Martin Peres: - squash and split all the commits from Roy - rework following Ben Skeggs comments - add a form of timings validation - store the initial timings for later use Ben Skeggs - merge slightly modified tidy-up patch with this one - remove perflvl-dropping logic for the moment Signed-off-by: Roy Spliet Signed-off-by: Martin Peres Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_drv.h | 38 +-- drivers/gpu/drm/nouveau/nouveau_mem.c | 588 ++++++++++++++++++++++++++-------- drivers/gpu/drm/nouveau/nouveau_pm.c | 1 + drivers/gpu/drm/nouveau/nv50_vram.c | 1 + 4 files changed, 476 insertions(+), 152 deletions(-) (limited to 'drivers/gpu/drm/nouveau/nouveau_pm.c') diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index c5105326db7f..f5700418da3f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -432,25 +432,26 @@ struct nouveau_pm_voltage { int nr_level; }; +/* Exclusive upper limits */ +#define NV_MEM_CL_DDR2_MAX 8 +#define NV_MEM_WR_DDR2_MAX 9 +#define NV_MEM_CL_DDR3_MAX 17 +#define NV_MEM_WR_DDR3_MAX 17 +#define NV_MEM_CL_GDDR3_MAX 16 +#define NV_MEM_WR_GDDR3_MAX 18 +#define NV_MEM_CL_GDDR5_MAX 21 +#define NV_MEM_WR_GDDR5_MAX 20 + struct nouveau_pm_memtiming { int id; - u32 reg_0; /* 0x10f290 on Fermi, 0x100220 for older */ - u32 reg_1; - u32 reg_2; - u32 reg_3; - u32 reg_4; - u32 reg_5; - u32 reg_6; - u32 reg_7; - u32 reg_8; - /* To be written to 0x1002c0 */ - u8 CL; - u8 WR; + + u32 reg[9]; + u32 mr[4]; + u8 tCWL; - bool odt; - bool dll_disable; - bool ron_pull; + u8 odt; + u8 drive_strength; }; struct nouveau_pm_tbl_header { @@ -527,8 +528,10 @@ struct nouveau_pm_threshold_temp { struct nouveau_pm_memtimings { bool supported; + struct nouveau_pm_memtiming boot; struct nouveau_pm_memtiming *timing; int nr_timing; + int nr_timing_valid; }; struct nouveau_pm_fan { @@ -796,6 +799,7 @@ struct drm_nouveau_private { } vram_type; uint64_t vram_size; uint64_t vram_sys_base; + bool vram_rank_B; uint64_t fb_available_size; uint64_t fb_mappable_pages; @@ -927,10 +931,6 @@ extern void nv10_mem_put_tile_region(struct drm_device *dev, struct nouveau_fence *fence); extern const struct ttm_mem_type_manager_func nouveau_vram_manager; extern const struct ttm_mem_type_manager_func nouveau_gart_manager; -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); /* nouveau_notifier.c */ extern int nouveau_notifier_init_channel(struct nouveau_channel *); diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c index 3337f2cfb5f6..1cd29c02a7e9 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mem.c +++ b/drivers/gpu/drm/nouveau/nouveau_mem.c @@ -471,50 +471,41 @@ 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, - struct nouveau_pm_memtiming *timing) +static void +nv40_mem_timing_entry(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr, + struct nouveau_pm_tbl_entry *e, + struct nouveau_pm_memtiming *t, + struct nouveau_pm_memtiming *boot) { - timing->reg_0 = (e->tRP << 24 | e->tRAS << 16 | e->tRFC << 8 | e->tRC); + 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 + (timing->tCWL - 1)) << 24 | - 1 << 16 | - (e->tWTR + 2 + (timing->tCWL - 1)) << 8 | - (e->tCL + 2 - (timing->tCWL - 1)); - - timing->reg_2 = 0x20200000 | ((timing->tCWL - 1) << 24 | - e->tRRD << 16 | e->tRCDWR << 8 | e->tRCDRD); - - 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]); } -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, - struct nouveau_pm_memtiming *timing) +static 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, + struct nouveau_pm_memtiming *t, + struct nouveau_pm_memtiming *boot) { struct drm_nouveau_private *dev_priv = dev->dev_private; - uint8_t unk18 = 1, - unk20 = 0, - unk21 = 0, - tmp7_3; + uint8_t unk18 = 1, unk20 = 0, unk21 = 0, tmp7_3; switch (min(hdr->entry_len, (u8) 22)) { case 22: @@ -523,132 +514,414 @@ void nv50_mem_timing_entry(struct drm_device *dev, struct bit_entry *P, unk20 = e->tUNK_20; case 20: if (e->tCWL > 0) - timing->tCWL = e->tCWL; + t->tCWL = e->tCWL; case 19: unk18 = e->tUNK_18; break; } - timing->reg_0 = (e->tRP << 24 | e->tRAS << 16 | e->tRFC << 8 | e->tRC); + t->reg[0] = (e->tRP << 24 | e->tRAS << 16 | e->tRFC << 8 | e->tRC); - timing->reg_1 = (e->tWR + 2 + (timing->tCWL - 1)) << 24 | + t->reg[1] = (e->tWR + 2 + (t->tCWL - 1)) << 24 | max(unk18, (u8) 1) << 16 | - (e->tWTR + 2 + (timing->tCWL - 1)) << 8; + (e->tWTR + 2 + (t->tCWL - 1)) << 8; - timing->reg_2 = ((timing->tCWL - 1) << 24 | e->tRRD << 16 | - e->tRCDWR << 8 | e->tRCDRD); + t->reg[2] = ((t->tCWL - 1) << 24 | + e->tRRD << 16 | + e->tRCDWR << 8 | + e->tRCDRD); - timing->reg_4 = e->tUNK_13 << 8 | e->tUNK_13; + t->reg[4] = e->tUNK_13 << 8 | e->tUNK_13; - timing->reg_5 = (e->tRFC << 24 | max(e->tRCDRD, e->tRCDWR) << 16 | - e->tRP); + t->reg[5] = (e->tRFC << 24 | max(e->tRCDRD, e->tRCDWR) << 16 | e->tRP); - timing->reg_8 = (nv_rd32(dev, 0x100240) & 0xffffff00); + t->reg[8] = boot->reg[8] & 0xffffff00; if (P->version == 1) { - timing->reg_1 |= (e->tCL + 2 - (timing->tCWL - 1)); + t->reg[1] |= (e->tCL + 2 - (t->tCWL - 1)); - timing->reg_3 = (0x14 + e->tCL) << 24 | - 0x16 << 16 | - (e->tCL - 1) << 8 | - (e->tCL - 1); + t->reg[3] = (0x14 + e->tCL) << 24 | + 0x16 << 16 | + (e->tCL - 1) << 8 | + (e->tCL - 1); - timing->reg_4 |= (nv_rd32(dev, 0x100230) & 0xffff0000); + t->reg[4] |= boot->reg[4] & 0xffff0000; - timing->reg_6 = (0x33 - timing->tCWL) << 16 | - timing->tCWL << 8 | - (0x2E + e->tCL - timing->tCWL); + t->reg[6] = (0x33 - t->tCWL) << 16 | + t->tCWL << 8 | + (0x2e + e->tCL - t->tCWL); - timing->reg_7 = 0x4000202 | (e->tCL - 1) << 16; + 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) { - timing->reg_5 |= (e->tCL + 3) << 8; - timing->reg_6 |= (timing->tCWL - 2) << 8; - timing->reg_8 |= (e->tCL - 4); + t->reg[5] |= (e->tCL + 3) << 8; + t->reg[6] |= (t->tCWL - 2) << 8; + t->reg[8] |= (e->tCL - 4); } else { - timing->reg_5 |= (e->tCL + 2) << 8; - timing->reg_6 |= timing->tCWL << 8; - timing->reg_8 |= (e->tCL - 2); + t->reg[5] |= (e->tCL + 2) << 8; + t->reg[6] |= t->tCWL << 8; + t->reg[8] |= (e->tCL - 2); } } else { - timing->reg_1 |= (5 + e->tCL - (timing->tCWL)); + t->reg[1] |= (5 + e->tCL - (t->tCWL)); /* XXX: 0xb? 0x30? */ - timing->reg_3 = (0x30 + e->tCL) << 24 | - (nv_rd32(dev, 0x10022c) & 0x00ff0000) | - (0xB + e->tCL) << 8 | - (e->tCL - 1); + t->reg[3] = (0x30 + e->tCL) << 24 | + (boot->reg[3] & 0x00ff0000)| + (0xb + e->tCL) << 8 | + (e->tCL - 1); - timing->reg_4 |= (unk20 << 24 | unk21 << 16); + t->reg[4] |= (unk20 << 24 | unk21 << 16); /* XXX: +6? */ - timing->reg_5 |= (timing->tCWL + 6) << 8; + t->reg[5] |= (t->tCWL + 6) << 8; - timing->reg_6 = (0x5A + e->tCL) << 16 | - (6 - e->tCL + timing->tCWL) << 8 | - (0x50 + e->tCL - timing->tCWL); + t->reg[6] = (0x5a + e->tCL) << 16 | + (6 - e->tCL + t->tCWL) << 8 | + (0x50 + e->tCL - t->tCWL); - tmp7_3 = (nv_rd32(dev, 0x10023c) & 0xff000000) >> 24; - timing->reg_7 = (tmp7_3 << 24) | - ((tmp7_3 - 6 + e->tCL) << 16) | - 0x202; + 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); + t->reg[4], t->reg[5], t->reg[6], t->reg[7]); + NV_DEBUG(dev, " 240: %08x\n", t->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) +static 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 *t, + struct nouveau_pm_memtiming *boot) { - timing->tCWL = e->tCWL; + if (e->tCWL > 0) + t->tCWL = e->tCWL; - timing->reg_0 = (e->tRP << 24 | (e->tRAS & 0x7f) << 17 | - e->tRFC << 8 | e->tRC); + t->reg[0] = (e->tRP << 24 | (e->tRAS & 0x7f) << 17 | + e->tRFC << 8 | e->tRC); - timing->reg_1 = (nv_rd32(dev, 0x10f294) & 0xff000000) | - (e->tRCDWR & 0x0f) << 20 | - (e->tRCDRD & 0x0f) << 14 | - (e->tCWL << 7) | - (e->tCL & 0x0f); + t->reg[1] = (boot->reg[1] & 0xff000000) | + (e->tRCDWR & 0x0f) << 20 | + (e->tRCDRD & 0x0f) << 14 | + (e->tCWL << 7) | + (e->tCL & 0x0f); - timing->reg_2 = (nv_rd32(dev, 0x10f298) & 0xff0000ff) | - e->tWR << 16 | e->tWTR << 8; + t->reg[2] = (boot->reg[2] & 0xff0000ff) | + e->tWR << 16 | e->tWTR << 8; - timing->reg_3 = (e->tUNK_20&0xf) << 9 | - (e->tUNK_21 & 0xf) << 5 | - (e->tUNK_13 & 0x1f); + t->reg[3] = (e->tUNK_20 & 0xf) << 9 | + (e->tUNK_21 & 0xf) << 5 | + (e->tUNK_13 & 0x1f); - timing->reg_4 = (nv_rd32(dev, 0x10f2a0) & 0xfff00fff) | - (e->tRRD&0x1f) << 15; + t->reg[4] = (boot->reg[4] & 0xfff00fff) | + (e->tRRD&0x1f) << 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\n", - timing->reg_4); + 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]); } -void -nouveau_mem_features_entry(uint8_t p_version, struct nouveau_pm_tbl_header *hdr, - struct nouveau_pm_tbl_entry *e, - struct nouveau_pm_memtiming *timing) +/** + * MR generation methods + */ + +static bool +nouveau_mem_ddr2_mr(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr, + struct nouveau_pm_tbl_entry *e, + struct nouveau_pm_memtiming *t, + struct nouveau_pm_memtiming *boot) +{ + t->drive_strength = 0; + if (hdr->entry_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 false; + } + + if (e->tWR >= NV_MEM_WR_DDR2_MAX) { + NV_WARN(dev, "(%u) Invalid tWR: %u", t->id, e->tWR); + return false; + } + + 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 true; +} + +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 bool +nouveau_mem_ddr3_mr(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr, + struct nouveau_pm_tbl_entry *e, + struct nouveau_pm_memtiming *t, + struct nouveau_pm_memtiming *boot) +{ + u8 cl = e->tCL - 4; + + t->drive_strength = 0; + if (hdr->entry_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 false; + } + + if (e->tWR >= NV_MEM_WR_DDR3_MAX || e->tWR < 4) { + NV_WARN(dev, "(%u) Invalid tWR: %u", t->id, e->tWR); + return false; + } + + if (e->tCWL < 5) { + NV_WARN(dev, "(%u) Invalid tCWL: %u", t->id, e->tCWL); + return false; + } + + 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 true; +} + +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 bool +nouveau_mem_gddr3_mr(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr, + struct nouveau_pm_tbl_entry *e, + struct nouveau_pm_memtiming *t, + struct nouveau_pm_memtiming *boot) { - if (p_version == 1) { - /* XXX: Todo */ - } else if (p_version == 2) { - timing->odt = e->RAM_FT1 & 0x1; - timing->dll_disable = (e->RAM_FT1 & 0x2) >> 1; - timing->ron_pull = (e->RAM_FT1 & 0x4) >> 2; + if (hdr->entry_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 false; + } + + if (e->tWR >= NV_MEM_WR_GDDR3_MAX) { + NV_WARN(dev, "(%u) Invalid tWR: %u", t->id, e->tWR); + return false; + } + + 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; + + NV_DEBUG(dev, "(%u) MR: %08x %08x", t->id, t->mr[0], t->mr[1]); + return true; +} + +static bool +nouveau_mem_gddr5_mr(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr, + struct nouveau_pm_tbl_entry *e, + struct nouveau_pm_memtiming *t, + struct nouveau_pm_memtiming *boot) +{ + if (hdr->entry_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 false; + } + + if (e->tWR >= NV_MEM_WR_GDDR5_MAX) { + NV_WARN(dev, "(%u) Invalid tWR: %u", t->id, e->tWR); + return false; + } + + 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 true; +} + +static void +nouveau_mem_copy_current_timings(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 { + timing_base = 0x100220; + mr_base = 0x1002c0; + } + + 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; + } + + 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; + } +} + +static bool +nouveau_mem_compare_timings(struct drm_device *dev, + struct nouveau_pm_memtiming *t1, + struct nouveau_pm_memtiming *t2) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + + switch (dev_priv->card_type) { + case 0x50: + if (t1->reg[8] != t2->reg[8] || + t1->reg[7] != t2->reg[7] || + t1->reg[6] != t2->reg[6] || + t1->reg[5] != t2->reg[5]) + return false; + case 0xC0: + if (t1->reg[4] != t2->reg[4] || + t1->reg[3] != t2->reg[3]) + return false; + case 0x40: + if (t1->reg[2] != t2->reg[2] || + t1->reg[1] != t2->reg[1] || + t1->reg[0] != t2->reg[0]) + return false; + break; + default: + return false; + } + + /* RSpliet: may generate many false negatives */ + switch (dev_priv->vram_type) { + case NV_MEM_TYPE_GDDR3: + case NV_MEM_TYPE_GDDR5: + if (t1->mr[0] == t2->mr[0] || + t1->mr[1] != t2->mr[1]) + return true; + break; + case NV_MEM_TYPE_DDR3: + if (t1->mr[2] == t2->mr[2]) + return true; + case NV_MEM_TYPE_DDR2: + if (t1->mr[0] == t2->mr[0]) + return true; + break; + default: + return false; + } + + return false; } /** @@ -665,10 +938,24 @@ nouveau_mem_timing_init(struct drm_device *dev) struct nvbios *bios = &dev_priv->vbios; struct bit_entry P; struct nouveau_pm_tbl_header *hdr = NULL; - uint8_t tCWL; + bool valid_generation = false; u8 *entry; int i; + memtimings->nr_timing = 0; + memtimings->nr_timing_valid = 0; + memtimings->supported = 0; + + if (dev_priv->card_type < NV_40) { + NV_ERROR(dev, "Timing entry format unknown for card_type %x. " + "please contact nouveau developers", + dev_priv->card_type); + return; + } + + /* Copy the current timings */ + nouveau_mem_copy_current_timings(dev, &memtimings->boot); + if (bios->type == NVBIOS_BIT) { if (bit_table(dev, 'P', &P)) return; @@ -710,12 +997,6 @@ nouveau_mem_timing_init(struct drm_device *dev) if (!memtimings->timing) return; - /* Get tCWL from the timing reg for NV_40 and NV_50 - * Used in calculations later... source unknown */ - tCWL = 0; - if (dev_priv->card_type < NV_C0) - tCWL = ((nv_rd32(dev, 0x100228) & 0x0f000000) >> 24) + 1; - 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]; @@ -723,29 +1004,70 @@ nouveau_mem_timing_init(struct drm_device *dev) (struct nouveau_pm_tbl_entry *) entry; if (entry[0] == 0) continue; + memtimings->nr_timing_valid++; timing->id = i; - timing->WR = entry[0]; - timing->CL = entry[2]; - timing->tCWL = tCWL; - - nouveau_mem_features_entry(P.version, hdr, entry_struct, - &pm->memtimings.timing[i]); + timing->tCWL = memtimings->boot.tCWL; - if (dev_priv->card_type <= NV_40) { + /* generate the timngs */ + if (dev_priv->card_type == NV_40) { nv40_mem_timing_entry(dev, hdr, entry_struct, - &pm->memtimings.timing[i]); + &pm->memtimings.timing[i], + &memtimings->boot); } else if (dev_priv->card_type == NV_50) { nv50_mem_timing_entry(dev, &P, hdr, entry_struct, - &pm->memtimings.timing[i]); + &pm->memtimings.timing[i], + &memtimings->boot); } else if (dev_priv->card_type == NV_C0) { nvc0_mem_timing_entry(dev, hdr, entry_struct, - &pm->memtimings.timing[i]); + &pm->memtimings.timing[i], + &memtimings->boot); + } + + /* generate the MR/EMR/... */ + switch (dev_priv->vram_type) { + case NV_MEM_TYPE_GDDR3: + nouveau_mem_gddr3_mr(dev, hdr, entry_struct, timing, + &memtimings->boot); + break; + case NV_MEM_TYPE_GDDR5: + nouveau_mem_gddr5_mr(dev, hdr, entry_struct, timing, + &memtimings->boot); + break; + case NV_MEM_TYPE_DDR2: + nouveau_mem_ddr2_mr(dev, hdr, entry_struct, timing, + &memtimings->boot); + break; + case NV_MEM_TYPE_DDR3: + nouveau_mem_ddr3_mr(dev, hdr, entry_struct, timing, + &memtimings->boot); + break; + default: + valid_generation = false; + break; + } + + /* some kind of validation */ + if (nouveau_mem_compare_timings(dev, timing, + &memtimings->boot)) { + NV_DEBUG(dev, "Copy boot timings from entry %d\n", + timing->id); + memtimings->boot = *timing; + valid_generation = true; } } memtimings->nr_timing = hdr->entry_cnt; - memtimings->supported = (P.version == 1); + memtimings->supported = (P.version == 1) && valid_generation; + + /* if there are no timing entries that cannot + * re-generate the current timings + */ + if (memtimings->nr_timing_valid > 0 && !valid_generation) { + NV_INFO(dev, + "Memory timings management may not be working." + " please report to nouveau devs\n"); + } } void diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index 6e3851e1a1d7..8a742596d095 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -808,6 +808,7 @@ nouveau_pm_init(struct drm_device *dev) ret = nouveau_pm_perflvl_get(dev, &pm->boot); if (ret == 0) { strncpy(pm->boot.name, "boot", 4); + pm->boot.timing = &pm->memtimings.boot; pm->cur = &pm->boot; nouveau_pm_perflvl_info(&pm->boot, info, sizeof(info)); diff --git a/drivers/gpu/drm/nouveau/nv50_vram.c b/drivers/gpu/drm/nouveau/nv50_vram.c index 1467efc6d61d..a52b9a51e010 100644 --- a/drivers/gpu/drm/nouveau/nv50_vram.c +++ b/drivers/gpu/drm/nouveau/nv50_vram.c @@ -207,6 +207,7 @@ nv50_vram_init(struct drm_device *dev) break; } + dev_priv->vram_rank_B = (nv_rd32(dev, NV04_PFB_CFG0) & 0x100) >> 8; dev_priv->vram_size = nv_rd32(dev, 0x10020c); dev_priv->vram_size |= (dev_priv->vram_size & 0xff) << 32; dev_priv->vram_size &= 0xffffffff00ULL; -- cgit v1.2.3 From 68a64cad07057c3395b3b18c282df884e87ff3d6 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 23 Jan 2012 13:47:02 +1000 Subject: drm/nouveau/pm: readback boot perflvl *before* parsing vbios We might want/need the boot data to generate the other perflevels. Signed-off-by: Ben Skeggs Signed-off-by: Martin Peres --- drivers/gpu/drm/nouveau/nouveau_pm.c | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) (limited to 'drivers/gpu/drm/nouveau/nouveau_pm.c') diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index 8a742596d095..a9a2e367f9d1 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -794,26 +794,34 @@ nouveau_pm_init(struct drm_device *dev) int ret, i; nouveau_mem_timing_init(dev); + + /* parse aux tables from vbios */ nouveau_volt_init(dev); - nouveau_perf_init(dev); nouveau_temp_init(dev); + /* determine current ("boot") performance level */ + ret = nouveau_pm_perflvl_get(dev, &pm->boot); + if (ret) { + NV_ERROR(dev, "failed to determine boot perflvl\n"); + return ret; + } + + strncpy(pm->boot.name, "boot", 4); + pm->boot.timing = &pm->memtimings.boot; + pm->cur = &pm->boot; + + /* add performance levels from vbios */ + nouveau_perf_init(dev); + + /* display available performance levels */ NV_INFO(dev, "%d available performance level(s)\n", pm->nr_perflvl); for (i = 0; i < pm->nr_perflvl; i++) { nouveau_pm_perflvl_info(&pm->perflvl[i], info, sizeof(info)); NV_INFO(dev, "%d:%s", pm->perflvl[i].id, info); } - /* determine current ("boot") performance level */ - ret = nouveau_pm_perflvl_get(dev, &pm->boot); - if (ret == 0) { - strncpy(pm->boot.name, "boot", 4); - pm->boot.timing = &pm->memtimings.boot; - pm->cur = &pm->boot; - - nouveau_pm_perflvl_info(&pm->boot, info, sizeof(info)); - NV_INFO(dev, "c:%s", info); - } + nouveau_pm_perflvl_info(&pm->boot, info, sizeof(info)); + NV_INFO(dev, "c:%s", info); /* switch performance levels now if requested */ if (nouveau_perflvl != NULL) { -- cgit v1.2.3 From fd99fd6100d3b7aaa8dc76888a38bbb15e8041bc Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 17 Jan 2012 21:10:58 +1000 Subject: drm/nouveau/pm: calculate memory timings at perflvl creation time Statically generating the PFB register and MR values for each timing set turns out to be insufficient. There's at least one (so far) known piece of information which effects MR values which is stored in the perflvl entry on some chipsets (and in another table on later ones), which is disconnected from the timing table entries. After this change we will generate a timing set based on an input clock frequency instead, and have this data stored in the performance level data. Signed-off-by: Ben Skeggs Signed-off-by: Martin Peres --- drivers/gpu/drm/nouveau/nouveau_drv.h | 14 +- drivers/gpu/drm/nouveau/nouveau_mem.c | 387 +++++++++++---------------------- drivers/gpu/drm/nouveau/nouveau_perf.c | 23 +- drivers/gpu/drm/nouveau/nouveau_pm.c | 5 +- drivers/gpu/drm/nouveau/nouveau_pm.h | 1 + 5 files changed, 132 insertions(+), 298 deletions(-) (limited to 'drivers/gpu/drm/nouveau/nouveau_pm.c') diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index f5700418da3f..a8344c321ab5 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -526,14 +526,6 @@ struct nouveau_pm_threshold_temp { s16 fan_boost; }; -struct nouveau_pm_memtimings { - bool supported; - struct nouveau_pm_memtiming boot; - struct nouveau_pm_memtiming *timing; - int nr_timing; - int nr_timing_valid; -}; - struct nouveau_pm_fan { u32 percent; u32 min_duty; @@ -546,11 +538,11 @@ 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; struct nouveau_pm_fan fan; + struct nouveau_pm_memtiming boot_timing; struct nouveau_pm_level boot; struct nouveau_pm_level *cur; @@ -922,6 +914,10 @@ extern int nouveau_mem_init_agp(struct drm_device *); extern int nouveau_mem_reset_agp(struct drm_device *); extern void nouveau_mem_close(struct drm_device *); extern bool nouveau_mem_flags_valid(struct drm_device *, u32 tile_flags); +extern void nouveau_mem_timing_read(struct drm_device *, + struct nouveau_pm_memtiming *); +extern struct nouveau_pm_memtiming * +nouveau_mem_timing(struct drm_device *, u32 freq); extern int nouveau_mem_vbios_type(struct drm_device *); extern struct nouveau_tile_reg *nv10_mem_set_tiling( struct drm_device *dev, uint32_t addr, uint32_t size, diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c index 1cd29c02a7e9..33de77211639 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mem.c +++ b/drivers/gpu/drm/nouveau/nouveau_mem.c @@ -471,13 +471,12 @@ nouveau_mem_gart_init(struct drm_device *dev) return 0; } -static void -nv40_mem_timing_entry(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr, - struct nouveau_pm_tbl_entry *e, - struct nouveau_pm_memtiming *t, - struct nouveau_pm_memtiming *boot) +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 @@ -495,19 +494,23 @@ nv40_mem_timing_entry(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr, NV_DEBUG(dev, "Entry %d: 220: %08x %08x %08x\n", t->id, t->reg[0], t->reg[1], t->reg[2]); + return 0; } -static 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, - struct nouveau_pm_memtiming *t, - struct nouveau_pm_memtiming *boot) +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; - switch (min(hdr->entry_len, (u8) 22)) { + if (bit_table(dev, 'P', &P)) + return -EINVAL; + + switch (min(len, (u8) 22)) { case 22: unk21 = e->tUNK_21; case 21: @@ -537,7 +540,7 @@ nv50_mem_timing_entry(struct drm_device *dev, struct bit_entry *P, t->reg[8] = boot->reg[8] & 0xffffff00; - if (P->version == 1) { + if (P.version == 1) { t->reg[1] |= (e->tCL + 2 - (t->tCWL - 1)); t->reg[3] = (0x14 + e->tCL) << 24 | @@ -592,13 +595,14 @@ nv50_mem_timing_entry(struct drm_device *dev, struct bit_entry *P, NV_DEBUG(dev, " 230: %08x %08x %08x %08x\n", t->reg[4], t->reg[5], t->reg[6], t->reg[7]); NV_DEBUG(dev, " 240: %08x\n", t->reg[8]); + return 0; } -static 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 *t, - struct nouveau_pm_memtiming *boot) +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; @@ -625,20 +629,21 @@ nvc0_mem_timing_entry(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr, 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; } /** * MR generation methods */ -static bool -nouveau_mem_ddr2_mr(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr, - struct nouveau_pm_tbl_entry *e, - struct nouveau_pm_memtiming *t, - struct nouveau_pm_memtiming *boot) +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 (hdr->entry_len < 15) { + if (len < 15) { t->odt = boot->odt; } else { t->odt = e->RAM_FT1 & 0x07; @@ -646,12 +651,12 @@ nouveau_mem_ddr2_mr(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr, if (e->tCL >= NV_MEM_CL_DDR2_MAX) { NV_WARN(dev, "(%u) Invalid tCL: %u", t->id, e->tCL); - return false; + return -ERANGE; } if (e->tWR >= NV_MEM_WR_DDR2_MAX) { NV_WARN(dev, "(%u) Invalid tWR: %u", t->id, e->tWR); - return false; + return -ERANGE; } if (t->odt > 3) { @@ -668,22 +673,22 @@ nouveau_mem_ddr2_mr(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr, (t->odt & 0x2) << 5; NV_DEBUG(dev, "(%u) MR: %08x", t->id, t->mr[0]); - return true; + 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 bool -nouveau_mem_ddr3_mr(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr, - struct nouveau_pm_tbl_entry *e, - struct nouveau_pm_memtiming *t, - struct nouveau_pm_memtiming *boot) +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 (hdr->entry_len < 15) { + if (len < 15) { t->odt = boot->odt; } else { t->odt = e->RAM_FT1 & 0x07; @@ -691,17 +696,17 @@ nouveau_mem_ddr3_mr(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr, if (e->tCL >= NV_MEM_CL_DDR3_MAX || e->tCL < 4) { NV_WARN(dev, "(%u) Invalid tCL: %u", t->id, e->tCL); - return false; + 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 false; + return -ERANGE; } if (e->tCWL < 5) { NV_WARN(dev, "(%u) Invalid tCWL: %u", t->id, e->tCWL); - return false; + return -ERANGE; } t->mr[0] = (boot->mr[0] & 0x180b) | @@ -716,7 +721,7 @@ nouveau_mem_ddr3_mr(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr, 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 true; + return 0; } uint8_t nv_mem_cl_lut_gddr3[NV_MEM_CL_GDDR3_MAX] = { @@ -724,13 +729,13 @@ uint8_t nv_mem_cl_lut_gddr3[NV_MEM_CL_GDDR3_MAX] = { 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 bool -nouveau_mem_gddr3_mr(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr, - struct nouveau_pm_tbl_entry *e, - struct nouveau_pm_memtiming *t, - struct nouveau_pm_memtiming *boot) +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 (hdr->entry_len < 15) { + if (len < 15) { t->drive_strength = boot->drive_strength; t->odt = boot->odt; } else { @@ -740,12 +745,12 @@ nouveau_mem_gddr3_mr(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr, if (e->tCL >= NV_MEM_CL_GDDR3_MAX) { NV_WARN(dev, "(%u) Invalid tCL: %u", t->id, e->tCL); - return false; + return -ERANGE; } if (e->tWR >= NV_MEM_WR_GDDR3_MAX) { NV_WARN(dev, "(%u) Invalid tWR: %u", t->id, e->tWR); - return false; + return -ERANGE; } if (t->odt > 3) { @@ -763,16 +768,16 @@ nouveau_mem_gddr3_mr(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr, (nv_mem_wr_lut_gddr3[e->tWR] & 0xf) << 4; NV_DEBUG(dev, "(%u) MR: %08x %08x", t->id, t->mr[0], t->mr[1]); - return true; + return 0; } -static bool -nouveau_mem_gddr5_mr(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr, - struct nouveau_pm_tbl_entry *e, - struct nouveau_pm_memtiming *t, - struct nouveau_pm_memtiming *boot) +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 (hdr->entry_len < 15) { + if (len < 15) { t->drive_strength = boot->drive_strength; t->odt = boot->odt; } else { @@ -782,12 +787,12 @@ nouveau_mem_gddr5_mr(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr, if (e->tCL >= NV_MEM_CL_GDDR5_MAX) { NV_WARN(dev, "(%u) Invalid tCL: %u", t->id, e->tCL); - return false; + return -ERANGE; } if (e->tWR >= NV_MEM_WR_GDDR5_MAX) { NV_WARN(dev, "(%u) Invalid tWR: %u", t->id, e->tWR); - return false; + return -ERANGE; } if (t->odt > 3) { @@ -804,12 +809,70 @@ nouveau_mem_gddr5_mr(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr, (t->odt << 2); NV_DEBUG(dev, "(%u) MR: %08x %08x", t->id, t->mr[0], t->mr[1]); - return true; + return 0; } -static void -nouveau_mem_copy_current_timings(struct drm_device *dev, - struct nouveau_pm_memtiming *t) +struct nouveau_pm_memtiming * +nouveau_mem_timing(struct drm_device *dev, u32 freq) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_pm_engine *pm = &dev_priv->engine.pm; + struct nouveau_pm_memtiming *boot = &pm->boot_timing; + struct nouveau_pm_memtiming *t; + struct nouveau_pm_tbl_entry *e; + u8 ver, len, *ptr; + int ret; + + ptr = nouveau_perf_timing(dev, freq, &ver, &len); + if (!ptr || ptr[0] == 0x00) + return boot; + e = (struct nouveau_pm_tbl_entry *)ptr; + + t = kzalloc(sizeof(*t), GFP_KERNEL); + if (t) { + 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; + } + + 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; + } + + if (ret) { + kfree(t); + t = NULL; + } + } + + return t; +} + +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; @@ -874,212 +937,6 @@ nouveau_mem_copy_current_timings(struct drm_device *dev, } } -static bool -nouveau_mem_compare_timings(struct drm_device *dev, - struct nouveau_pm_memtiming *t1, - struct nouveau_pm_memtiming *t2) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - - switch (dev_priv->card_type) { - case 0x50: - if (t1->reg[8] != t2->reg[8] || - t1->reg[7] != t2->reg[7] || - t1->reg[6] != t2->reg[6] || - t1->reg[5] != t2->reg[5]) - return false; - case 0xC0: - if (t1->reg[4] != t2->reg[4] || - t1->reg[3] != t2->reg[3]) - return false; - case 0x40: - if (t1->reg[2] != t2->reg[2] || - t1->reg[1] != t2->reg[1] || - t1->reg[0] != t2->reg[0]) - return false; - break; - default: - return false; - } - - /* RSpliet: may generate many false negatives */ - switch (dev_priv->vram_type) { - case NV_MEM_TYPE_GDDR3: - case NV_MEM_TYPE_GDDR5: - if (t1->mr[0] == t2->mr[0] || - t1->mr[1] != t2->mr[1]) - return true; - break; - case NV_MEM_TYPE_DDR3: - if (t1->mr[2] == t2->mr[2]) - return true; - case NV_MEM_TYPE_DDR2: - if (t1->mr[0] == t2->mr[0]) - return true; - break; - default: - return false; - } - - return false; -} - -/** - * Processes the Memory Timing BIOS table, stores generated - * register values - * @pre init scripts were run, memtiming regs are initialized - */ -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; - struct nouveau_pm_tbl_header *hdr = NULL; - bool valid_generation = false; - u8 *entry; - int i; - - memtimings->nr_timing = 0; - memtimings->nr_timing_valid = 0; - memtimings->supported = 0; - - if (dev_priv->card_type < NV_40) { - NV_ERROR(dev, "Timing entry format unknown for card_type %x. " - "please contact nouveau developers", - dev_priv->card_type); - return; - } - - /* Copy the current timings */ - nouveau_mem_copy_current_timings(dev, &memtimings->boot); - - if (bios->type == NVBIOS_BIT) { - if (bit_table(dev, 'P', &P)) - return; - - if (P.version == 1) - hdr = (struct nouveau_pm_tbl_header *) ROMPTR(dev, - P.data[4]); - 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); - } else { - NV_DEBUG(dev, "BMP version too old for memory\n"); - return; - } - - if (!hdr) { - NV_DEBUG(dev, "memory timing table pointer invalid\n"); - return; - } - - if (hdr->version != 0x10) { - NV_WARN(dev, "memory timing table 0x%02x unknown\n", - hdr->version); - return; - } - - /* validate record length */ - if (hdr->entry_len < 15) { - NV_ERROR(dev, "mem timing table length unknown: %d\n", - hdr->entry_len); - return; - } - - /* parse vbios entries into common format */ - memtimings->timing = kcalloc(hdr->entry_cnt, - sizeof(*memtimings->timing), GFP_KERNEL); - if (!memtimings->timing) - return; - - 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]; - struct nouveau_pm_tbl_entry *entry_struct = - (struct nouveau_pm_tbl_entry *) entry; - if (entry[0] == 0) - continue; - memtimings->nr_timing_valid++; - - timing->id = i; - timing->tCWL = memtimings->boot.tCWL; - - /* generate the timngs */ - if (dev_priv->card_type == NV_40) { - nv40_mem_timing_entry(dev, hdr, entry_struct, - &pm->memtimings.timing[i], - &memtimings->boot); - } else if (dev_priv->card_type == NV_50) { - nv50_mem_timing_entry(dev, &P, hdr, entry_struct, - &pm->memtimings.timing[i], - &memtimings->boot); - } else if (dev_priv->card_type == NV_C0) { - nvc0_mem_timing_entry(dev, hdr, entry_struct, - &pm->memtimings.timing[i], - &memtimings->boot); - } - - /* generate the MR/EMR/... */ - switch (dev_priv->vram_type) { - case NV_MEM_TYPE_GDDR3: - nouveau_mem_gddr3_mr(dev, hdr, entry_struct, timing, - &memtimings->boot); - break; - case NV_MEM_TYPE_GDDR5: - nouveau_mem_gddr5_mr(dev, hdr, entry_struct, timing, - &memtimings->boot); - break; - case NV_MEM_TYPE_DDR2: - nouveau_mem_ddr2_mr(dev, hdr, entry_struct, timing, - &memtimings->boot); - break; - case NV_MEM_TYPE_DDR3: - nouveau_mem_ddr3_mr(dev, hdr, entry_struct, timing, - &memtimings->boot); - break; - default: - valid_generation = false; - break; - } - - /* some kind of validation */ - if (nouveau_mem_compare_timings(dev, timing, - &memtimings->boot)) { - NV_DEBUG(dev, "Copy boot timings from entry %d\n", - timing->id); - memtimings->boot = *timing; - valid_generation = true; - } - } - - memtimings->nr_timing = hdr->entry_cnt; - memtimings->supported = (P.version == 1) && valid_generation; - - /* if there are no timing entries that cannot - * re-generate the current timings - */ - if (memtimings->nr_timing_valid > 0 && !valid_generation) { - NV_INFO(dev, - "Memory timings management may not be working." - " please report to nouveau devs\n"); - } -} - -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); - mem->timing = NULL; -} - int nouveau_mem_vbios_type(struct drm_device *dev) { diff --git a/drivers/gpu/drm/nouveau/nouveau_perf.c b/drivers/gpu/drm/nouveau/nouveau_perf.c index ad990553d115..150ff415a172 100644 --- a/drivers/gpu/drm/nouveau/nouveau_perf.c +++ b/drivers/gpu/drm/nouveau/nouveau_perf.c @@ -89,7 +89,7 @@ nouveau_perf_rammap(struct drm_device *dev, u32 freq, { struct drm_nouveau_private *dev_priv = dev->dev_private; struct bit_entry P; - u8 *perf, i; + u8 *perf, i = 0; if (!bit_table(dev, 'P', &P) && P.version == 2) { u8 *rammap = ROMPTR(dev, P.data[4]); @@ -158,7 +158,7 @@ nouveau_perf_ramcfg(struct drm_device *dev, u32 freq, u8 *ver, u8 *len) return NULL; } -static u8 * +u8 * nouveau_perf_timing(struct drm_device *dev, u32 freq, u8 *ver, u8 *len) { struct drm_nouveau_private *dev_priv = dev->dev_private; @@ -384,24 +384,7 @@ nouveau_perf_init(struct drm_device *dev) } /* get the corresponding memory timings */ -#if 0 - if (version == 0x15) { - memtimings->timing[i].id = i; - nv30_mem_timing_entry(dev, &mt_hdr, - (struct nouveau_pm_tbl_entry *) &entry[41], - 0, &memtimings->timing[i]); - perflvl->timing = &memtimings->timing[i]; - } else if (version > 0x15) { - /* last 3 args are for < 0x40, ignored for >= 0x40 */ - perflvl->timing = - nouveau_perf_timing(dev, &P, - perflvl->memory / 1000, - entry + perf[3], - perf[5], perf[4]); - } -#else - perflvl->timing = NULL; -#endif + perflvl->timing = nouveau_mem_timing(dev, perflvl->memory); snprintf(perflvl->name, sizeof(perflvl->name), "performance_level_%d", i); diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index a9a2e367f9d1..4f299f4df71c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -238,6 +238,7 @@ nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) if (ret > 0) perflvl->fanspeed = ret; + nouveau_mem_timing_read(dev, &perflvl->timing); return 0; } @@ -793,8 +794,6 @@ nouveau_pm_init(struct drm_device *dev) char info[256]; int ret, i; - nouveau_mem_timing_init(dev); - /* parse aux tables from vbios */ nouveau_volt_init(dev); nouveau_temp_init(dev); @@ -807,7 +806,6 @@ nouveau_pm_init(struct drm_device *dev) } strncpy(pm->boot.name, "boot", 4); - pm->boot.timing = &pm->memtimings.boot; pm->cur = &pm->boot; /* add performance levels from vbios */ @@ -857,7 +855,6 @@ nouveau_pm_fini(struct drm_device *dev) nouveau_temp_fini(dev); nouveau_perf_fini(dev); nouveau_volt_fini(dev); - nouveau_mem_timing_fini(dev); #if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY) unregister_acpi_notifier(&pm->acpi_nb); diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.h b/drivers/gpu/drm/nouveau/nouveau_pm.h index 2f8e14fbcff8..9e7ad33aa091 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.h +++ b/drivers/gpu/drm/nouveau/nouveau_pm.h @@ -41,6 +41,7 @@ int nouveau_voltage_gpio_set(struct drm_device *, int voltage); /* nouveau_perf.c */ void nouveau_perf_init(struct drm_device *); void nouveau_perf_fini(struct drm_device *); +u8 *nouveau_perf_timing(struct drm_device *, u32 freq, u8 *ver, u8 *len); /* nouveau_mem.c */ void nouveau_mem_timing_init(struct drm_device *); -- cgit v1.2.3 From 085028ce3bf7136c5ab2eeb8bf012024d88905c8 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Wed, 18 Jan 2012 09:02:28 +1000 Subject: drm/nouveau/pm: embed timings into perflvl structs Signed-off-by: Ben Skeggs Signed-off-by: Martin Peres --- drivers/gpu/drm/nouveau/nouveau_drv.h | 13 +++--- drivers/gpu/drm/nouveau/nouveau_mem.c | 82 ++++++++++++++++------------------ drivers/gpu/drm/nouveau/nouveau_perf.c | 9 +++- drivers/gpu/drm/nouveau/nouveau_pm.c | 8 +--- 4 files changed, 54 insertions(+), 58 deletions(-) (limited to 'drivers/gpu/drm/nouveau/nouveau_pm.c') diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index a8344c321ab5..531e435d9fb3 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -491,8 +491,11 @@ struct nouveau_pm_level { char name[32]; int id; - u32 core; u32 memory; + u16 memscript; + struct nouveau_pm_memtiming timing; + + u32 core; u32 shader; u32 rop; u32 copy; @@ -507,9 +510,6 @@ struct nouveau_pm_level { u32 volt_min; /* microvolts */ u32 volt_max; u8 fanspeed; - - u16 memscript; - struct nouveau_pm_memtiming *timing; }; struct nouveau_pm_temp_sensor_constants { @@ -542,7 +542,6 @@ struct nouveau_pm_engine { struct nouveau_pm_threshold_temp threshold_temp; struct nouveau_pm_fan fan; - struct nouveau_pm_memtiming boot_timing; struct nouveau_pm_level boot; struct nouveau_pm_level *cur; @@ -914,10 +913,10 @@ extern int nouveau_mem_init_agp(struct drm_device *); extern int nouveau_mem_reset_agp(struct drm_device *); extern void nouveau_mem_close(struct drm_device *); extern bool nouveau_mem_flags_valid(struct drm_device *, u32 tile_flags); +extern int nouveau_mem_timing_calc(struct drm_device *, u32 freq, + struct nouveau_pm_memtiming *); extern void nouveau_mem_timing_read(struct drm_device *, struct nouveau_pm_memtiming *); -extern struct nouveau_pm_memtiming * -nouveau_mem_timing(struct drm_device *, u32 freq); extern int nouveau_mem_vbios_type(struct drm_device *); extern struct nouveau_tile_reg *nv10_mem_set_tiling( struct drm_device *dev, uint32_t addr, uint32_t size, diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c index 33de77211639..cc46811e2d37 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mem.c +++ b/drivers/gpu/drm/nouveau/nouveau_mem.c @@ -812,63 +812,59 @@ nouveau_mem_gddr5_mr(struct drm_device *dev, u32 freq, return 0; } -struct nouveau_pm_memtiming * -nouveau_mem_timing(struct drm_device *dev, u32 freq) +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_memtiming *boot = &pm->boot_timing; - struct nouveau_pm_memtiming *t; + struct nouveau_pm_memtiming *boot = &pm->boot.timing; struct nouveau_pm_tbl_entry *e; u8 ver, len, *ptr; int ret; ptr = nouveau_perf_timing(dev, freq, &ver, &len); - if (!ptr || ptr[0] == 0x00) - return boot; + if (!ptr || ptr[0] == 0x00) { + *t = *boot; + return 0; + } e = (struct nouveau_pm_tbl_entry *)ptr; - t = kzalloc(sizeof(*t), GFP_KERNEL); - if (t) { - 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; - } + t->tCWL = boot->tCWL; - 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; - } + 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 (ret) { - kfree(t); - t = NULL; - } + 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; } - return t; + return ret; } void diff --git a/drivers/gpu/drm/nouveau/nouveau_perf.c b/drivers/gpu/drm/nouveau/nouveau_perf.c index 150ff415a172..bd6071f10008 100644 --- a/drivers/gpu/drm/nouveau/nouveau_perf.c +++ b/drivers/gpu/drm/nouveau/nouveau_perf.c @@ -293,7 +293,7 @@ nouveau_perf_init(struct drm_device *dev) struct nouveau_pm_engine *pm = &dev_priv->engine.pm; struct nvbios *bios = &dev_priv->vbios; u8 *perf, ver, hdr, cnt, len; - int vid, i = -1; + int ret, vid, i = -1; if (bios->type == NVBIOS_BMP && bios->data[bios->offset + 6] < 0x25) { legacy_perf_init(dev); @@ -384,7 +384,12 @@ nouveau_perf_init(struct drm_device *dev) } /* get the corresponding memory timings */ - perflvl->timing = nouveau_mem_timing(dev, perflvl->memory); + ret = nouveau_mem_timing_calc(dev, perflvl->memory, + &perflvl->timing); + if (ret) { + NV_DEBUG(dev, "perflvl %d, bad timing: %d\n", i, ret); + continue; + } snprintf(perflvl->name, sizeof(perflvl->name), "performance_level_%d", i); diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index 4f299f4df71c..7c25567fb56a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -245,7 +245,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[32], f[16], t[16], m[16]; + char c[16], s[16], v[32], f[16], m[16]; c[0] = '\0'; if (perflvl->core) @@ -273,11 +273,7 @@ nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len) if (perflvl->fanspeed) snprintf(f, sizeof(f), " fanspeed %d%%", perflvl->fanspeed); - t[0] = '\0'; - if (perflvl->timing) - snprintf(t, sizeof(t), " timing %d", perflvl->timing->id); - - snprintf(ptr, len, "%s%s%s%s%s%s\n", c, s, m, t, v, f); + snprintf(ptr, len, "%s%s%s%s%s\n", c, s, m, v, f); } static ssize_t -- cgit v1.2.3 From 8d7bb400638906075c38cb07891993cf95076aa7 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 24 Jan 2012 15:59:07 +1000 Subject: drm/nouveau/pm: rework to allow selecting separate profiles for ac/battery Signed-off-by: Ben Skeggs Signed-off-by: Martin Peres --- drivers/gpu/drm/nouveau/nouveau_drv.h | 18 ++++- drivers/gpu/drm/nouveau/nouveau_perf.c | 7 ++ drivers/gpu/drm/nouveau/nouveau_pm.c | 139 ++++++++++++++++++++++----------- drivers/gpu/drm/nouveau/nouveau_pm.h | 2 + 4 files changed, 120 insertions(+), 46 deletions(-) (limited to 'drivers/gpu/drm/nouveau/nouveau_pm.c') diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 531e435d9fb3..009089e093f3 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -485,15 +485,27 @@ struct nouveau_pm_tbl_entry { u8 tUNK_20, tUNK_21; }; +struct nouveau_pm_profile; +struct nouveau_pm_profile_func { + struct nouveau_pm_level *(*select)(struct nouveau_pm_profile *); +}; + +struct nouveau_pm_profile { + const struct nouveau_pm_profile_func *func; + struct list_head head; + char name[8]; +}; + #define NOUVEAU_PM_MAX_LEVEL 8 struct nouveau_pm_level { + struct nouveau_pm_profile profile; struct device_attribute dev_attr; char name[32]; int id; + struct nouveau_pm_memtiming timing; u32 memory; u16 memscript; - struct nouveau_pm_memtiming timing; u32 core; u32 shader; @@ -542,6 +554,10 @@ struct nouveau_pm_engine { struct nouveau_pm_threshold_temp threshold_temp; struct nouveau_pm_fan fan; + struct nouveau_pm_profile *profile_ac; + struct nouveau_pm_profile *profile_dc; + struct list_head profiles; + struct nouveau_pm_level boot; struct nouveau_pm_level *cur; diff --git a/drivers/gpu/drm/nouveau/nouveau_perf.c b/drivers/gpu/drm/nouveau/nouveau_perf.c index cc8beb8e2f06..e64901509f90 100644 --- a/drivers/gpu/drm/nouveau/nouveau_perf.c +++ b/drivers/gpu/drm/nouveau/nouveau_perf.c @@ -394,6 +394,13 @@ nouveau_perf_init(struct drm_device *dev) snprintf(perflvl->name, sizeof(perflvl->name), "performance_level_%d", i); perflvl->id = i; + + snprintf(perflvl->profile.name, sizeof(perflvl->profile.name), + "%d", perflvl->id); + perflvl->profile.func = &nouveau_pm_static_profile_func; + list_add_tail(&perflvl->profile.head, &pm->profiles); + + pm->nr_perflvl++; } } diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index 7c25567fb56a..4fff09e3f592 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -168,51 +168,99 @@ error: return ret; } +void +nouveau_pm_trigger(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_profile *profile = NULL; + struct nouveau_pm_level *perflvl = NULL; + int ret; + + /* select power profile based on current power source */ + if (power_supply_is_system_supplied()) + profile = pm->profile_ac; + else + profile = pm->profile_dc; + + /* select performance level based on profile */ + perflvl = profile->func->select(profile); + + /* change perflvl, if necessary */ + if (perflvl != pm->cur) { + struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer; + u64 time0 = ptimer->read(dev); + + NV_INFO(dev, "setting performance level: %d", perflvl->id); + ret = nouveau_pm_perflvl_set(dev, perflvl); + if (ret) + NV_INFO(dev, "> reclocking failed: %d\n\n", ret); + + NV_INFO(dev, "> reclocking took %lluns\n\n", + ptimer->read(dev) - time0); + } +} + +static struct nouveau_pm_profile * +profile_find(struct drm_device *dev, const char *string) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_pm_engine *pm = &dev_priv->engine.pm; + struct nouveau_pm_profile *profile; + + list_for_each_entry(profile, &pm->profiles, head) { + if (!strncmp(profile->name, string, sizeof(profile->name))) + return profile; + } + + return NULL; +} + static int nouveau_pm_profile_set(struct drm_device *dev, const char *profile) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_pm_engine *pm = &dev_priv->engine.pm; - struct nouveau_pm_level *perflvl = NULL; - u64 start_time; - int ret = 0; - long pl; + struct nouveau_pm_profile *ac = NULL, *dc = NULL; + char string[16], *cur = string, *ptr; /* safety precaution, for now */ if (nouveau_perflvl_wr != 7777) return -EPERM; - if (!strncmp(profile, "boot", 4)) - perflvl = &pm->boot; - else { - int i; - if (kstrtol(profile, 10, &pl) == -EINVAL) - return -EINVAL; - - for (i = 0; i < pm->nr_perflvl; i++) { - if (pm->perflvl[i].id == pl) { - perflvl = &pm->perflvl[i]; - break; - } - } + strncpy(string, profile, sizeof(string)); + if ((ptr = strchr(string, '\n'))) + *ptr = '\0'; - if (!perflvl) - return -EINVAL; - } + ptr = strsep(&cur, ","); + if (ptr) + ac = profile_find(dev, ptr); - NV_INFO(dev, "setting performance level: %s", profile); - start_time = nv04_timer_read(dev); - ret = nouveau_pm_perflvl_set(dev, perflvl); - if (!ret) { - NV_INFO(dev, "> reclocking took %lluns\n\n", - (nv04_timer_read(dev) - start_time)); - } else { - NV_INFO(dev, "> reclocking failed\n\n"); - } + ptr = strsep(&cur, ","); + if (ptr) + dc = profile_find(dev, ptr); + else + dc = ac; - return ret; + if (ac == NULL || dc == NULL) + return -EINVAL; + + pm->profile_ac = ac; + pm->profile_dc = dc; + nouveau_pm_trigger(dev); + return 0; +} + +static struct nouveau_pm_level * +nouveau_pm_static_select(struct nouveau_pm_profile *profile) +{ + return container_of(profile, struct nouveau_pm_level, profile); } +const struct nouveau_pm_profile_func nouveau_pm_static_profile_func = { + .select = nouveau_pm_static_select, +}; + static int nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) { @@ -273,14 +321,15 @@ 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, "%s%s%s%s%s\n", c, s, m, v, f); + snprintf(ptr, len, "%s%s%s%s%s\n", c, s, m, v, f); } static ssize_t nouveau_pm_get_perflvl_info(struct device *d, struct device_attribute *a, char *buf) { - struct nouveau_pm_level *perflvl = (struct nouveau_pm_level *)a; + struct nouveau_pm_level *perflvl = + container_of(a, struct nouveau_pm_level, dev_attr); char *ptr = buf; int len = PAGE_SIZE; @@ -302,12 +351,8 @@ nouveau_pm_get_perflvl(struct device *d, struct device_attribute *a, char *buf) int len = PAGE_SIZE, ret; char *ptr = buf; - if (!pm->cur) - snprintf(ptr, len, "setting: boot\n"); - else if (pm->cur == &pm->boot) - snprintf(ptr, len, "setting: boot\nc:"); - else - snprintf(ptr, len, "setting: static %d\nc:", pm->cur->id); + snprintf(ptr, len, "profile: %s, %s\nc:", + pm->profile_ac->name, pm->profile_dc->name); ptr += strlen(buf); len -= strlen(buf); @@ -776,6 +821,7 @@ nouveau_pm_acpi_event(struct notifier_block *nb, unsigned long val, void *data) bool ac = power_supply_is_system_supplied(); NV_DEBUG(dev, "power supply changed: %s\n", ac ? "AC" : "DC"); + nouveau_pm_trigger(dev); } return NOTIFY_OK; @@ -802,6 +848,14 @@ nouveau_pm_init(struct drm_device *dev) } strncpy(pm->boot.name, "boot", 4); + strncpy(pm->boot.profile.name, "boot", 4); + pm->boot.profile.func = &nouveau_pm_static_profile_func; + + INIT_LIST_HEAD(&pm->profiles); + list_add(&pm->boot.profile.head, &pm->profiles); + + pm->profile_ac = &pm->boot.profile; + pm->profile_dc = &pm->boot.profile; pm->cur = &pm->boot; /* add performance levels from vbios */ @@ -818,13 +872,8 @@ nouveau_pm_init(struct drm_device *dev) NV_INFO(dev, "c:%s", info); /* switch performance levels now if requested */ - if (nouveau_perflvl != NULL) { - ret = nouveau_pm_profile_set(dev, nouveau_perflvl); - if (ret) { - NV_ERROR(dev, "error setting perflvl \"%s\": %d\n", - nouveau_perflvl, ret); - } - } + if (nouveau_perflvl != NULL) + nouveau_pm_profile_set(dev, nouveau_perflvl); /* determine the current fan speed */ pm->fan.percent = nouveau_pwmfan_get(dev); diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.h b/drivers/gpu/drm/nouveau/nouveau_pm.h index 1a8ae15803a0..3f82dfea61dd 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.h +++ b/drivers/gpu/drm/nouveau/nouveau_pm.h @@ -47,6 +47,8 @@ int nouveau_mem_exec(struct nouveau_mem_exec_func *, int nouveau_pm_init(struct drm_device *dev); void nouveau_pm_fini(struct drm_device *dev); void nouveau_pm_resume(struct drm_device *dev); +extern const struct nouveau_pm_profile_func nouveau_pm_static_profile_func; +void nouveau_pm_trigger(struct drm_device *dev); /* nouveau_volt.c */ void nouveau_volt_init(struct drm_device *); -- cgit v1.2.3 From 25c53c1068a804c6b51f86e937cebab6274dc056 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 24 Jan 2012 18:03:25 +1000 Subject: drm/nouveau/pm: extend profile interface for destroy/init/fini Signed-off-by: Ben Skeggs Signed-off-by: Martin Peres --- drivers/gpu/drm/nouveau/nouveau_drv.h | 4 ++++ drivers/gpu/drm/nouveau/nouveau_pm.c | 21 +++++++++++++++++++++ 2 files changed, 25 insertions(+) (limited to 'drivers/gpu/drm/nouveau/nouveau_pm.c') diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 009089e093f3..63e77fc282f2 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -487,6 +487,9 @@ struct nouveau_pm_tbl_entry { struct nouveau_pm_profile; struct nouveau_pm_profile_func { + void (*destroy)(struct nouveau_pm_profile *); + void (*init)(struct nouveau_pm_profile *); + void (*fini)(struct nouveau_pm_profile *); struct nouveau_pm_level *(*select)(struct nouveau_pm_profile *); }; @@ -556,6 +559,7 @@ struct nouveau_pm_engine { struct nouveau_pm_profile *profile_ac; struct nouveau_pm_profile *profile_dc; + struct nouveau_pm_profile *profile; struct list_head profiles; struct nouveau_pm_level boot; diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index 4fff09e3f592..dcc16927716f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -183,6 +183,12 @@ nouveau_pm_trigger(struct drm_device *dev) else profile = pm->profile_dc; + if (profile != pm->profile) { + pm->profile->func->fini(pm->profile); + pm->profile = profile; + pm->profile->func->init(pm->profile); + } + /* select performance level based on profile */ perflvl = profile->func->select(profile); @@ -251,6 +257,11 @@ nouveau_pm_profile_set(struct drm_device *dev, const char *profile) return 0; } +static void +nouveau_pm_static_dummy(struct nouveau_pm_profile *profile) +{ +} + static struct nouveau_pm_level * nouveau_pm_static_select(struct nouveau_pm_profile *profile) { @@ -258,6 +269,9 @@ nouveau_pm_static_select(struct nouveau_pm_profile *profile) } const struct nouveau_pm_profile_func nouveau_pm_static_profile_func = { + .destroy = nouveau_pm_static_dummy, + .init = nouveau_pm_static_dummy, + .fini = nouveau_pm_static_dummy, .select = nouveau_pm_static_select, }; @@ -856,6 +870,7 @@ nouveau_pm_init(struct drm_device *dev) pm->profile_ac = &pm->boot.profile; pm->profile_dc = &pm->boot.profile; + pm->profile = &pm->boot.profile; pm->cur = &pm->boot; /* add performance levels from vbios */ @@ -893,6 +908,12 @@ nouveau_pm_fini(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_profile *profile, *tmp; + + list_for_each_entry_safe(profile, tmp, &pm->profiles, head) { + list_del(&profile->head); + profile->func->destroy(profile); + } if (pm->cur != &pm->boot) nouveau_pm_perflvl_set(dev, &pm->boot); -- cgit v1.2.3 From c11dd0da5277596d0ccdccb745b273d69a94f2d7 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Wed, 7 Mar 2012 14:18:49 +1000 Subject: drm/nouveau/pm: fix oops if chipset has no pm support at all Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_pm.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers/gpu/drm/nouveau/nouveau_pm.c') diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index dcc16927716f..34d591b7d4ef 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -284,9 +284,11 @@ nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) memset(perflvl, 0, sizeof(*perflvl)); - ret = pm->clocks_get(dev, perflvl); - if (ret) - return ret; + if (pm->clocks_get) { + ret = pm->clocks_get(dev, perflvl); + if (ret) + return ret; + } if (pm->voltage.supported && pm->voltage_get) { ret = pm->voltage_get(dev); -- cgit v1.2.3 From 5799d9e2eab20ef694fb92a7636f451e1b0e456c Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Tue, 17 Apr 2012 21:27:54 +0200 Subject: drm/nouveau/pm: don't read/write beyond end of stack buffer NUL-terminate after strncpy. If the parameter "profile" has length 16 or more, then strncpy leaves "string" with no NUL terminator, so the following search for '\n' may read beyond the end of that 16-byte buffer. If it finds a newline there, then it will also write beyond the end of that stack buffer. Signed-off-by: Jim Meyering Signed-off-by: Dave Airlie --- drivers/gpu/drm/nouveau/nouveau_pm.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/gpu/drm/nouveau/nouveau_pm.c') diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index 34d591b7d4ef..da3e7c3abab7 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -235,6 +235,7 @@ nouveau_pm_profile_set(struct drm_device *dev, const char *profile) return -EPERM; strncpy(string, profile, sizeof(string)); + string[sizeof(string) - 1] = 0; if ((ptr = strchr(string, '\n'))) *ptr = '\0'; -- cgit v1.2.3