From 7ad2d31cb692597f7918fce2f52a4c1949fbcf8d Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Sun, 11 Dec 2011 00:30:05 +1000 Subject: [PATCH 01/57] drm/nouveau: move vram detection funcs to chipset-specific fb code Also, display detected memory type in logs - though, we don't even try to detect this yet. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_drv.c | 4 + drivers/gpu/drm/nouveau/nouveau_drv.h | 18 ++++- drivers/gpu/drm/nouveau/nouveau_mem.c | 103 ++++++++---------------- drivers/gpu/drm/nouveau/nouveau_state.c | 14 ++-- drivers/gpu/drm/nouveau/nv04_fb.c | 29 +++++++ drivers/gpu/drm/nouveau/nv10_fb.c | 35 ++++++++ 6 files changed, 127 insertions(+), 76 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.c b/drivers/gpu/drm/nouveau/nouveau_drv.c index 81d7962e7252..4567e8ca0497 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.c +++ b/drivers/gpu/drm/nouveau/nouveau_drv.c @@ -57,6 +57,10 @@ MODULE_PARM_DESC(vram_notify, "Force DMA notifiers to be in VRAM"); int nouveau_vram_notify = 0; module_param_named(vram_notify, nouveau_vram_notify, int, 0400); +MODULE_PARM_DESC(vram_type, "Override detected VRAM type"); +char *nouveau_vram_type; +module_param_named(vram_type, nouveau_vram_type, charp, 0400); + MODULE_PARM_DESC(duallink, "Allow dual-link TMDS (>=GeForce 8)"); int nouveau_duallink = 1; module_param_named(duallink, nouveau_duallink, int, 0400); diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index b82709828931..36b50e555b92 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -772,6 +772,19 @@ struct drm_nouveau_private { } tile; /* VRAM/fb configuration */ + enum { + NV_MEM_TYPE_UNKNOWN = 0, + NV_MEM_TYPE_STOLEN, + NV_MEM_TYPE_SGRAM, + NV_MEM_TYPE_SDRAM, + NV_MEM_TYPE_DDR1, + NV_MEM_TYPE_DDR2, + NV_MEM_TYPE_DDR3, + NV_MEM_TYPE_GDDR2, + NV_MEM_TYPE_GDDR3, + NV_MEM_TYPE_GDDR4, + NV_MEM_TYPE_GDDR5 + } vram_type; uint64_t vram_size; uint64_t vram_sys_base; @@ -846,6 +859,7 @@ extern int nouveau_uscript_lvds; extern int nouveau_uscript_tmds; extern int nouveau_vram_pushbuf; extern int nouveau_vram_notify; +extern char *nouveau_vram_type; extern int nouveau_fbpercrtc; extern int nouveau_tv_disable; extern char *nouveau_tv_norm; @@ -894,7 +908,6 @@ extern void nouveau_mem_gart_fini(struct drm_device *); 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 int nouveau_mem_detect(struct drm_device *); extern bool nouveau_mem_flags_valid(struct drm_device *, u32 tile_flags); extern struct nouveau_tile_reg *nv10_mem_set_tiling( struct drm_device *dev, uint32_t addr, uint32_t size, @@ -1126,10 +1139,13 @@ void nouveau_dp_tu_update(struct drm_device *, int, int, u32, u32); u8 *nouveau_dp_bios_data(struct drm_device *, struct dcb_entry *, u8 **); /* nv04_fb.c */ +extern int nv04_fb_vram_init(struct drm_device *); extern int nv04_fb_init(struct drm_device *); extern void nv04_fb_takedown(struct drm_device *); /* nv10_fb.c */ +extern int nv10_fb_vram_init(struct drm_device *dev); +extern int nv1a_fb_vram_init(struct drm_device *dev); extern int nv10_fb_init(struct drm_device *); extern void nv10_fb_takedown(struct drm_device *); extern void nv10_fb_init_tile_region(struct drm_device *dev, int i, diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c index c3a5745e9c79..4a658310c29a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mem.c +++ b/drivers/gpu/drm/nouveau/nouveau_mem.c @@ -192,75 +192,6 @@ nouveau_mem_gart_fini(struct drm_device *dev) } } -static uint32_t -nouveau_mem_detect_nv04(struct drm_device *dev) -{ - uint32_t boot0 = nv_rd32(dev, NV04_PFB_BOOT_0); - - if (boot0 & 0x00000100) - return (((boot0 >> 12) & 0xf) * 2 + 2) * 1024 * 1024; - - switch (boot0 & NV04_PFB_BOOT_0_RAM_AMOUNT) { - case NV04_PFB_BOOT_0_RAM_AMOUNT_32MB: - return 32 * 1024 * 1024; - case NV04_PFB_BOOT_0_RAM_AMOUNT_16MB: - return 16 * 1024 * 1024; - case NV04_PFB_BOOT_0_RAM_AMOUNT_8MB: - return 8 * 1024 * 1024; - case NV04_PFB_BOOT_0_RAM_AMOUNT_4MB: - return 4 * 1024 * 1024; - } - - return 0; -} - -static uint32_t -nouveau_mem_detect_nforce(struct drm_device *dev) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct pci_dev *bridge; - uint32_t mem; - - bridge = pci_get_bus_and_slot(0, PCI_DEVFN(0, 1)); - if (!bridge) { - NV_ERROR(dev, "no bridge device\n"); - return 0; - } - - if (dev_priv->flags & NV_NFORCE) { - pci_read_config_dword(bridge, 0x7C, &mem); - return (uint64_t)(((mem >> 6) & 31) + 1)*1024*1024; - } else - if (dev_priv->flags & NV_NFORCE2) { - pci_read_config_dword(bridge, 0x84, &mem); - return (uint64_t)(((mem >> 4) & 127) + 1)*1024*1024; - } - - NV_ERROR(dev, "impossible!\n"); - return 0; -} - -int -nouveau_mem_detect(struct drm_device *dev) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - - if (dev_priv->card_type == NV_04) { - dev_priv->vram_size = nouveau_mem_detect_nv04(dev); - } else - if (dev_priv->flags & (NV_NFORCE | NV_NFORCE2)) { - dev_priv->vram_size = nouveau_mem_detect_nforce(dev); - } else - if (dev_priv->card_type < NV_50) { - dev_priv->vram_size = nv_rd32(dev, NV04_PFB_FIFO_DATA); - dev_priv->vram_size &= NV10_PFB_FIFO_DATA_RAM_AMOUNT_MB_MASK; - } - - if (dev_priv->vram_size) - return 0; - return -ENOMEM; -} - bool nouveau_mem_flags_valid(struct drm_device *dev, u32 tile_flags) { @@ -385,11 +316,29 @@ nouveau_mem_init_agp(struct drm_device *dev) return 0; } +static const struct vram_types { + int value; + const char *name; +} vram_type_map[] = { + { NV_MEM_TYPE_STOLEN , "stolen system memory" }, + { NV_MEM_TYPE_SGRAM , "SGRAM" }, + { NV_MEM_TYPE_SDRAM , "SDRAM" }, + { NV_MEM_TYPE_DDR1 , "DDR1" }, + { NV_MEM_TYPE_DDR2 , "DDR2" }, + { NV_MEM_TYPE_DDR3 , "DDR3" }, + { NV_MEM_TYPE_GDDR2 , "GDDR2" }, + { NV_MEM_TYPE_GDDR3 , "GDDR3" }, + { NV_MEM_TYPE_GDDR4 , "GDDR4" }, + { NV_MEM_TYPE_GDDR5 , "GDDR5" }, + { NV_MEM_TYPE_UNKNOWN, "unknown type" } +}; + int nouveau_mem_vram_init(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct ttm_bo_device *bdev = &dev_priv->ttm.bdev; + const struct vram_types *vram_type; int ret, dma_bits; dma_bits = 32; @@ -427,7 +376,21 @@ nouveau_mem_vram_init(struct drm_device *dev) return ret; } - NV_INFO(dev, "Detected %dMiB VRAM\n", (int)(dev_priv->vram_size >> 20)); + vram_type = vram_type_map; + while (vram_type->value != NV_MEM_TYPE_UNKNOWN) { + if (nouveau_vram_type) { + if (!strcasecmp(nouveau_vram_type, vram_type->name)) + break; + dev_priv->vram_type = vram_type->value; + } else { + if (vram_type->value == dev_priv->vram_type) + break; + } + vram_type++; + } + + NV_INFO(dev, "Detected %dMiB VRAM (%s)\n", + (int)(dev_priv->vram_size >> 20), vram_type->name); if (dev_priv->vram_sys_base) { NV_INFO(dev, "Stolen system memory at: 0x%010llx\n", dev_priv->vram_sys_base); diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index 912839c2bc16..dba3e90188de 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -87,7 +87,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->pm.clocks_get = nv04_pm_clocks_get; engine->pm.clocks_pre = nv04_pm_clocks_pre; engine->pm.clocks_set = nv04_pm_clocks_set; - engine->vram.init = nouveau_mem_detect; + engine->vram.init = nv04_fb_vram_init; engine->vram.takedown = nouveau_stub_takedown; engine->vram.flags_valid = nouveau_mem_flags_valid; break; @@ -134,7 +134,11 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->pm.clocks_get = nv04_pm_clocks_get; engine->pm.clocks_pre = nv04_pm_clocks_pre; engine->pm.clocks_set = nv04_pm_clocks_set; - engine->vram.init = nouveau_mem_detect; + if (dev_priv->chipset == 0x1a || + dev_priv->chipset == 0x1f) + engine->vram.init = nv1a_fb_vram_init; + else + engine->vram.init = nv10_fb_vram_init; engine->vram.takedown = nouveau_stub_takedown; engine->vram.flags_valid = nouveau_mem_flags_valid; break; @@ -181,7 +185,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->pm.clocks_get = nv04_pm_clocks_get; engine->pm.clocks_pre = nv04_pm_clocks_pre; engine->pm.clocks_set = nv04_pm_clocks_set; - engine->vram.init = nouveau_mem_detect; + engine->vram.init = nv10_fb_vram_init; engine->vram.takedown = nouveau_stub_takedown; engine->vram.flags_valid = nouveau_mem_flags_valid; break; @@ -230,7 +234,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->pm.clocks_set = nv04_pm_clocks_set; engine->pm.voltage_get = nouveau_voltage_gpio_get; engine->pm.voltage_set = nouveau_voltage_gpio_set; - engine->vram.init = nouveau_mem_detect; + engine->vram.init = nv10_fb_vram_init; engine->vram.takedown = nouveau_stub_takedown; engine->vram.flags_valid = nouveau_mem_flags_valid; break; @@ -286,7 +290,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->pm.temp_get = nv40_temp_get; engine->pm.pwm_get = nv40_pm_pwm_get; engine->pm.pwm_set = nv40_pm_pwm_set; - engine->vram.init = nouveau_mem_detect; + engine->vram.init = nv10_fb_vram_init; engine->vram.takedown = nouveau_stub_takedown; engine->vram.flags_valid = nouveau_mem_flags_valid; break; diff --git a/drivers/gpu/drm/nouveau/nv04_fb.c b/drivers/gpu/drm/nouveau/nv04_fb.c index 638cf601c427..db16c47fd922 100644 --- a/drivers/gpu/drm/nouveau/nv04_fb.c +++ b/drivers/gpu/drm/nouveau/nv04_fb.c @@ -3,6 +3,35 @@ #include "nouveau_drv.h" #include "nouveau_drm.h" +int +nv04_fb_vram_init(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + u32 boot0 = nv_rd32(dev, NV04_PFB_BOOT_0); + + if (boot0 & 0x00000100) { + dev_priv->vram_size = ((boot0 >> 12) & 0xf) * 2 + 2; + dev_priv->vram_size *= 1024 * 1024; + } else { + switch (boot0 & NV04_PFB_BOOT_0_RAM_AMOUNT) { + case NV04_PFB_BOOT_0_RAM_AMOUNT_32MB: + dev_priv->vram_size = 32 * 1024 * 1024; + break; + case NV04_PFB_BOOT_0_RAM_AMOUNT_16MB: + dev_priv->vram_size = 16 * 1024 * 1024; + break; + case NV04_PFB_BOOT_0_RAM_AMOUNT_8MB: + dev_priv->vram_size = 8 * 1024 * 1024; + break; + case NV04_PFB_BOOT_0_RAM_AMOUNT_4MB: + dev_priv->vram_size = 4 * 1024 * 1024; + break; + } + } + + return 0; +} + int nv04_fb_init(struct drm_device *dev) { diff --git a/drivers/gpu/drm/nouveau/nv10_fb.c b/drivers/gpu/drm/nouveau/nv10_fb.c index f78181a59b4a..7b17dea5f62b 100644 --- a/drivers/gpu/drm/nouveau/nv10_fb.c +++ b/drivers/gpu/drm/nouveau/nv10_fb.c @@ -3,6 +3,41 @@ #include "nouveau_drv.h" #include "nouveau_drm.h" +int +nv1a_fb_vram_init(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct pci_dev *bridge; + uint32_t mem, mib; + + bridge = pci_get_bus_and_slot(0, PCI_DEVFN(0, 1)); + if (!bridge) { + NV_ERROR(dev, "no bridge device\n"); + return 0; + } + + if (dev_priv->chipset == 0x1a) { + pci_read_config_dword(bridge, 0x7c, &mem); + mib = ((mem >> 6) & 31) + 1; + } else { + pci_read_config_dword(bridge, 0x84, &mem); + mib = ((mem >> 4) & 127) + 1; + } + + dev_priv->vram_size = mib * 1024 * 1024; + return 0; +} + +int +nv10_fb_vram_init(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + u32 fifo_data = nv_rd32(dev, NV04_PFB_FIFO_DATA); + + dev_priv->vram_size = fifo_data & NV10_PFB_FIFO_DATA_RAM_AMOUNT_MB_MASK; + return 0; +} + static struct drm_mm_node * nv20_fb_alloc_tag(struct drm_device *dev, uint32_t size) { From ddfd2da48421e6b217e8b4fa7ec967e55555ce65 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Sun, 11 Dec 2011 01:31:17 +1000 Subject: [PATCH 02/57] drm/nouveau: memory type detection for the really old chipsets Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nv04_fb.c | 5 +++++ drivers/gpu/drm/nouveau/nv10_fb.c | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/drivers/gpu/drm/nouveau/nv04_fb.c b/drivers/gpu/drm/nouveau/nv04_fb.c index db16c47fd922..d5eedd67afe5 100644 --- a/drivers/gpu/drm/nouveau/nv04_fb.c +++ b/drivers/gpu/drm/nouveau/nv04_fb.c @@ -29,6 +29,11 @@ nv04_fb_vram_init(struct drm_device *dev) } } + if ((boot0 & 0x00000038) <= 0x10) + dev_priv->vram_type = NV_MEM_TYPE_SGRAM; + else + dev_priv->vram_type = NV_MEM_TYPE_SDRAM; + return 0; } diff --git a/drivers/gpu/drm/nouveau/nv10_fb.c b/drivers/gpu/drm/nouveau/nv10_fb.c index 7b17dea5f62b..447d6f236526 100644 --- a/drivers/gpu/drm/nouveau/nv10_fb.c +++ b/drivers/gpu/drm/nouveau/nv10_fb.c @@ -35,6 +35,14 @@ nv10_fb_vram_init(struct drm_device *dev) u32 fifo_data = nv_rd32(dev, NV04_PFB_FIFO_DATA); dev_priv->vram_size = fifo_data & NV10_PFB_FIFO_DATA_RAM_AMOUNT_MB_MASK; + if (dev_priv->card_type < NV_20) { + u32 cfg0 = nv_rd32(dev, 0x100200); + if (cfg0 & 0x00000001) + dev_priv->vram_type = NV_MEM_TYPE_DDR1; + else + dev_priv->vram_type = NV_MEM_TYPE_SDRAM; + } + return 0; } From d81c19e312a22bdcebef1370f3af30a146670787 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 12 Dec 2011 22:51:33 +1000 Subject: [PATCH 03/57] drm/nv20: split PFB code out of nv10_fb.c Most functions were quite different between NV10/NV20 already, and they're about to get even more so. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/Makefile | 3 +- drivers/gpu/drm/nouveau/nouveau_drv.h | 10 ++ drivers/gpu/drm/nouveau/nouveau_state.c | 16 +-- drivers/gpu/drm/nouveau/nv10_fb.c | 161 ++++++------------------ drivers/gpu/drm/nouveau/nv20_fb.c | 139 ++++++++++++++++++++ 5 files changed, 198 insertions(+), 131 deletions(-) create mode 100644 drivers/gpu/drm/nouveau/nv20_fb.c diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile index 9f27e3d9e69a..1a2ad7eb1734 100644 --- a/drivers/gpu/drm/nouveau/Makefile +++ b/drivers/gpu/drm/nouveau/Makefile @@ -14,7 +14,8 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \ nouveau_mm.o nouveau_vm.o nouveau_mxm.o nouveau_gpio.o \ nv04_timer.o \ nv04_mc.o nv40_mc.o nv50_mc.o \ - nv04_fb.o nv10_fb.o nv30_fb.o nv40_fb.o nv50_fb.o nvc0_fb.o \ + nv04_fb.o nv10_fb.o nv20_fb.o nv30_fb.o nv40_fb.o \ + nv50_fb.o nvc0_fb.o \ nv04_fifo.o nv10_fifo.o nv40_fifo.o nv50_fifo.o nvc0_fifo.o \ nv04_graph.o nv10_graph.o nv20_graph.o \ nv40_graph.o nv50_graph.o nvc0_graph.o \ diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 36b50e555b92..576b0f67a8ba 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -1154,6 +1154,16 @@ extern void nv10_fb_init_tile_region(struct drm_device *dev, int i, extern void nv10_fb_set_tile_region(struct drm_device *dev, int i); extern void nv10_fb_free_tile_region(struct drm_device *dev, int i); +/* nv20_fb.c */ +extern int nv20_fb_vram_init(struct drm_device *dev); +extern int nv20_fb_init(struct drm_device *); +extern void nv20_fb_takedown(struct drm_device *); +extern void nv20_fb_init_tile_region(struct drm_device *dev, int i, + uint32_t addr, uint32_t size, + uint32_t pitch, uint32_t flags); +extern void nv20_fb_set_tile_region(struct drm_device *dev, int i); +extern void nv20_fb_free_tile_region(struct drm_device *dev, int i); + /* nv30_fb.c */ extern int nv30_fb_init(struct drm_device *); extern void nv30_fb_takedown(struct drm_device *); diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index dba3e90188de..6c262e1aaf9e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -157,11 +157,11 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->timer.init = nv04_timer_init; engine->timer.read = nv04_timer_read; engine->timer.takedown = nv04_timer_takedown; - engine->fb.init = nv10_fb_init; - engine->fb.takedown = nv10_fb_takedown; - engine->fb.init_tile_region = nv10_fb_init_tile_region; - engine->fb.set_tile_region = nv10_fb_set_tile_region; - engine->fb.free_tile_region = nv10_fb_free_tile_region; + engine->fb.init = nv20_fb_init; + engine->fb.takedown = nv20_fb_takedown; + engine->fb.init_tile_region = nv20_fb_init_tile_region; + engine->fb.set_tile_region = nv20_fb_set_tile_region; + engine->fb.free_tile_region = nv20_fb_free_tile_region; engine->fifo.channels = 32; engine->fifo.init = nv10_fifo_init; engine->fifo.takedown = nv04_fifo_fini; @@ -185,7 +185,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->pm.clocks_get = nv04_pm_clocks_get; engine->pm.clocks_pre = nv04_pm_clocks_pre; engine->pm.clocks_set = nv04_pm_clocks_set; - engine->vram.init = nv10_fb_vram_init; + engine->vram.init = nv20_fb_vram_init; engine->vram.takedown = nouveau_stub_takedown; engine->vram.flags_valid = nouveau_mem_flags_valid; break; @@ -234,7 +234,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->pm.clocks_set = nv04_pm_clocks_set; engine->pm.voltage_get = nouveau_voltage_gpio_get; engine->pm.voltage_set = nouveau_voltage_gpio_set; - engine->vram.init = nv10_fb_vram_init; + engine->vram.init = nv20_fb_vram_init; engine->vram.takedown = nouveau_stub_takedown; engine->vram.flags_valid = nouveau_mem_flags_valid; break; @@ -290,7 +290,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->pm.temp_get = nv40_temp_get; engine->pm.pwm_get = nv40_pm_pwm_get; engine->pm.pwm_set = nv40_pm_pwm_set; - engine->vram.init = nv10_fb_vram_init; + engine->vram.init = nv20_fb_vram_init; engine->vram.takedown = nouveau_stub_takedown; engine->vram.flags_valid = nouveau_mem_flags_valid; break; diff --git a/drivers/gpu/drm/nouveau/nv10_fb.c b/drivers/gpu/drm/nouveau/nv10_fb.c index 447d6f236526..420b1608536d 100644 --- a/drivers/gpu/drm/nouveau/nv10_fb.c +++ b/drivers/gpu/drm/nouveau/nv10_fb.c @@ -3,6 +3,38 @@ #include "nouveau_drv.h" #include "nouveau_drm.h" +void +nv10_fb_init_tile_region(struct drm_device *dev, int i, uint32_t addr, + uint32_t size, uint32_t pitch, uint32_t flags) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_tile_reg *tile = &dev_priv->tile.reg[i]; + + tile->addr = 0x80000000 | addr; + tile->limit = max(1u, addr + size) - 1; + tile->pitch = pitch; +} + +void +nv10_fb_free_tile_region(struct drm_device *dev, int i) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_tile_reg *tile = &dev_priv->tile.reg[i]; + + tile->addr = tile->limit = tile->pitch = tile->zcomp = 0; +} + +void +nv10_fb_set_tile_region(struct drm_device *dev, int i) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_tile_reg *tile = &dev_priv->tile.reg[i]; + + nv_wr32(dev, NV10_PFB_TLIMIT(i), tile->limit); + nv_wr32(dev, NV10_PFB_TSIZE(i), tile->pitch); + nv_wr32(dev, NV10_PFB_TILE(i), tile->addr); +} + int nv1a_fb_vram_init(struct drm_device *dev) { @@ -33,124 +65,18 @@ nv10_fb_vram_init(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; u32 fifo_data = nv_rd32(dev, NV04_PFB_FIFO_DATA); + u32 cfg0 = nv_rd32(dev, 0x100200); dev_priv->vram_size = fifo_data & NV10_PFB_FIFO_DATA_RAM_AMOUNT_MB_MASK; - if (dev_priv->card_type < NV_20) { - u32 cfg0 = nv_rd32(dev, 0x100200); - if (cfg0 & 0x00000001) - dev_priv->vram_type = NV_MEM_TYPE_DDR1; - else - dev_priv->vram_type = NV_MEM_TYPE_SDRAM; - } + + if (cfg0 & 0x00000001) + dev_priv->vram_type = NV_MEM_TYPE_DDR1; + else + dev_priv->vram_type = NV_MEM_TYPE_SDRAM; return 0; } -static struct drm_mm_node * -nv20_fb_alloc_tag(struct drm_device *dev, uint32_t size) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_fb_engine *pfb = &dev_priv->engine.fb; - struct drm_mm_node *mem; - int ret; - - ret = drm_mm_pre_get(&pfb->tag_heap); - if (ret) - return NULL; - - spin_lock(&dev_priv->tile.lock); - mem = drm_mm_search_free(&pfb->tag_heap, size, 0, 0); - if (mem) - mem = drm_mm_get_block_atomic(mem, size, 0); - spin_unlock(&dev_priv->tile.lock); - - return mem; -} - -static void -nv20_fb_free_tag(struct drm_device *dev, struct drm_mm_node *mem) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - - spin_lock(&dev_priv->tile.lock); - drm_mm_put_block(mem); - spin_unlock(&dev_priv->tile.lock); -} - -void -nv10_fb_init_tile_region(struct drm_device *dev, int i, uint32_t addr, - uint32_t size, uint32_t pitch, uint32_t flags) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_tile_reg *tile = &dev_priv->tile.reg[i]; - int bpp = (flags & NOUVEAU_GEM_TILE_32BPP ? 32 : 16); - - tile->addr = addr; - tile->limit = max(1u, addr + size) - 1; - tile->pitch = pitch; - - if (dev_priv->card_type == NV_20) { - if (flags & NOUVEAU_GEM_TILE_ZETA) { - /* - * Allocate some of the on-die tag memory, - * used to store Z compression meta-data (most - * likely just a bitmap determining if a given - * tile is compressed or not). - */ - tile->tag_mem = nv20_fb_alloc_tag(dev, size / 256); - - if (tile->tag_mem) { - /* Enable Z compression */ - if (dev_priv->chipset >= 0x25) - tile->zcomp = tile->tag_mem->start | - (bpp == 16 ? - NV25_PFB_ZCOMP_MODE_16 : - NV25_PFB_ZCOMP_MODE_32); - else - tile->zcomp = tile->tag_mem->start | - NV20_PFB_ZCOMP_EN | - (bpp == 16 ? 0 : - NV20_PFB_ZCOMP_MODE_32); - } - - tile->addr |= 3; - } else { - tile->addr |= 1; - } - - } else { - tile->addr |= 1 << 31; - } -} - -void -nv10_fb_free_tile_region(struct drm_device *dev, int i) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_tile_reg *tile = &dev_priv->tile.reg[i]; - - if (tile->tag_mem) { - nv20_fb_free_tag(dev, tile->tag_mem); - tile->tag_mem = NULL; - } - - tile->addr = tile->limit = tile->pitch = tile->zcomp = 0; -} - -void -nv10_fb_set_tile_region(struct drm_device *dev, int i) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_tile_reg *tile = &dev_priv->tile.reg[i]; - - nv_wr32(dev, NV10_PFB_TLIMIT(i), tile->limit); - nv_wr32(dev, NV10_PFB_TSIZE(i), tile->pitch); - nv_wr32(dev, NV10_PFB_TILE(i), tile->addr); - - if (dev_priv->card_type == NV_20) - nv_wr32(dev, NV20_PFB_ZCOMP(i), tile->zcomp); -} - int nv10_fb_init(struct drm_device *dev) { @@ -158,14 +84,8 @@ nv10_fb_init(struct drm_device *dev) struct nouveau_fb_engine *pfb = &dev_priv->engine.fb; int i; - pfb->num_tiles = NV10_PFB_TILE__SIZE; - - if (dev_priv->card_type == NV_20) - drm_mm_init(&pfb->tag_heap, 0, - (dev_priv->chipset >= 0x25 ? - 64 * 1024 : 32 * 1024)); - /* Turn all the tiling regions off. */ + pfb->num_tiles = NV10_PFB_TILE__SIZE; for (i = 0; i < pfb->num_tiles; i++) pfb->set_tile_region(dev, i); @@ -181,7 +101,4 @@ nv10_fb_takedown(struct drm_device *dev) for (i = 0; i < pfb->num_tiles; i++) pfb->free_tile_region(dev, i); - - if (dev_priv->card_type == NV_20) - drm_mm_takedown(&pfb->tag_heap); } diff --git a/drivers/gpu/drm/nouveau/nv20_fb.c b/drivers/gpu/drm/nouveau/nv20_fb.c new file mode 100644 index 000000000000..6585c27186da --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv20_fb.c @@ -0,0 +1,139 @@ +#include "drmP.h" +#include "drm.h" +#include "nouveau_drv.h" +#include "nouveau_drm.h" + +static struct drm_mm_node * +nv20_fb_alloc_tag(struct drm_device *dev, uint32_t size) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_fb_engine *pfb = &dev_priv->engine.fb; + struct drm_mm_node *mem; + int ret; + + ret = drm_mm_pre_get(&pfb->tag_heap); + if (ret) + return NULL; + + spin_lock(&dev_priv->tile.lock); + mem = drm_mm_search_free(&pfb->tag_heap, size, 0, 0); + if (mem) + mem = drm_mm_get_block_atomic(mem, size, 0); + spin_unlock(&dev_priv->tile.lock); + + return mem; +} + +static void +nv20_fb_free_tag(struct drm_device *dev, struct drm_mm_node **pmem) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct drm_mm_node *mem = *pmem; + if (mem) { + spin_lock(&dev_priv->tile.lock); + drm_mm_put_block(mem); + spin_unlock(&dev_priv->tile.lock); + *pmem = NULL; + } +} + +void +nv20_fb_init_tile_region(struct drm_device *dev, int i, uint32_t addr, + uint32_t size, uint32_t pitch, uint32_t flags) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_tile_reg *tile = &dev_priv->tile.reg[i]; + int bpp = (flags & NOUVEAU_GEM_TILE_32BPP ? 32 : 16); + + tile->addr = 0x00000001 | addr; + tile->limit = max(1u, addr + size) - 1; + tile->pitch = pitch; + + /* Allocate some of the on-die tag memory, used to store Z + * compression meta-data (most likely just a bitmap determining + * if a given tile is compressed or not). + */ + if (flags & NOUVEAU_GEM_TILE_ZETA) { + tile->tag_mem = nv20_fb_alloc_tag(dev, size / 256); + if (tile->tag_mem) { + /* Enable Z compression */ + tile->zcomp = tile->tag_mem->start; + if (dev_priv->chipset >= 0x25) { + if (bpp == 16) + tile->zcomp |= NV25_PFB_ZCOMP_MODE_16; + else + tile->zcomp |= NV25_PFB_ZCOMP_MODE_32; + } else { + tile->zcomp |= NV20_PFB_ZCOMP_EN; + if (bpp != 16) + tile->zcomp |= NV20_PFB_ZCOMP_MODE_32; + } + } + + tile->addr |= 2; + } +} + +void +nv20_fb_free_tile_region(struct drm_device *dev, int i) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_tile_reg *tile = &dev_priv->tile.reg[i]; + + tile->addr = tile->limit = tile->pitch = tile->zcomp = 0; + nv20_fb_free_tag(dev, &tile->tag_mem); +} + +void +nv20_fb_set_tile_region(struct drm_device *dev, int i) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_tile_reg *tile = &dev_priv->tile.reg[i]; + + nv_wr32(dev, NV10_PFB_TLIMIT(i), tile->limit); + nv_wr32(dev, NV10_PFB_TSIZE(i), tile->pitch); + nv_wr32(dev, NV10_PFB_TILE(i), tile->addr); + nv_wr32(dev, NV20_PFB_ZCOMP(i), tile->zcomp); +} + +int +nv20_fb_vram_init(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + + dev_priv->vram_size = nv_rd32(dev, 0x10020c) & 0xff000000; + return 0; +} + +int +nv20_fb_init(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_fb_engine *pfb = &dev_priv->engine.fb; + int i; + + if (dev_priv->chipset >= 0x25) + drm_mm_init(&pfb->tag_heap, 0, 64 * 1024); + else + drm_mm_init(&pfb->tag_heap, 0, 32 * 1024); + + /* Turn all the tiling regions off. */ + pfb->num_tiles = NV10_PFB_TILE__SIZE; + for (i = 0; i < pfb->num_tiles; i++) + pfb->set_tile_region(dev, i); + + return 0; +} + +void +nv20_fb_takedown(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_fb_engine *pfb = &dev_priv->engine.fb; + int i; + + for (i = 0; i < pfb->num_tiles; i++) + pfb->free_tile_region(dev, i); + + drm_mm_takedown(&pfb->tag_heap); +} From ff92a6cda74c0a51cb723f5e224124cf74e62f25 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 12 Dec 2011 23:03:14 +1000 Subject: [PATCH 04/57] drm/nv20-nv40: add memory type detection NV20/NV30 is partially educated guesswork at this point, based on any information around about available memory types and a horribly unspeakable amount of vbios image scouring. I'm not entirely certain the GDDR3 define is correct, I have not spotted a single vbios with that value yet (though it is mentioned in some 1218-using nv4x vbios), but there are reports that some nv3x did use it.. NV40(100914) confirmed by switching an NV49 to DDR1/DDR2 values and making sure that the binary driver behaviour showed it had detected DDR1/DDR2 instead of GDDR3 before dying horribly. NV40(100474) confirmed by doing much the same task as above on an NV44, except this was *much* easier as changing the values didn't seem to have any noticable effect on the memory controller aside from changing the binary driver's behaviour. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_drv.h | 1 + drivers/gpu/drm/nouveau/nouveau_state.c | 2 +- drivers/gpu/drm/nouveau/nv20_fb.c | 11 +++++- drivers/gpu/drm/nouveau/nv40_fb.c | 45 +++++++++++++++++++++++++ 4 files changed, 57 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 576b0f67a8ba..59031d308fcc 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -1173,6 +1173,7 @@ extern void nv30_fb_init_tile_region(struct drm_device *dev, int i, extern void nv30_fb_free_tile_region(struct drm_device *dev, int i); /* nv40_fb.c */ +extern int nv40_fb_vram_init(struct drm_device *dev); extern int nv40_fb_init(struct drm_device *); extern void nv40_fb_takedown(struct drm_device *); extern void nv40_fb_set_tile_region(struct drm_device *dev, int i); diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index 6c262e1aaf9e..a30d7af58eb3 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -290,7 +290,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->pm.temp_get = nv40_temp_get; engine->pm.pwm_get = nv40_pm_pwm_get; engine->pm.pwm_set = nv40_pm_pwm_set; - engine->vram.init = nv20_fb_vram_init; + engine->vram.init = nv40_fb_vram_init; engine->vram.takedown = nouveau_stub_takedown; engine->vram.flags_valid = nouveau_mem_flags_valid; break; diff --git a/drivers/gpu/drm/nouveau/nv20_fb.c b/drivers/gpu/drm/nouveau/nv20_fb.c index 6585c27186da..19bd64059a66 100644 --- a/drivers/gpu/drm/nouveau/nv20_fb.c +++ b/drivers/gpu/drm/nouveau/nv20_fb.c @@ -100,8 +100,17 @@ int nv20_fb_vram_init(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; + u32 mem_size = nv_rd32(dev, 0x10020c); + u32 pbus1218 = nv_rd32(dev, 0x001218); + + dev_priv->vram_size = mem_size & 0xff000000; + switch (pbus1218 & 0x00000300) { + case 0x00000000: dev_priv->vram_type = NV_MEM_TYPE_SDRAM; break; + case 0x00000100: dev_priv->vram_type = NV_MEM_TYPE_DDR1; break; + case 0x00000200: dev_priv->vram_type = NV_MEM_TYPE_GDDR3; break; + case 0x00000300: dev_priv->vram_type = NV_MEM_TYPE_GDDR2; break; + } - dev_priv->vram_size = nv_rd32(dev, 0x10020c) & 0xff000000; return 0; } diff --git a/drivers/gpu/drm/nouveau/nv40_fb.c b/drivers/gpu/drm/nouveau/nv40_fb.c index f0ac2a768c67..7fbcb334c096 100644 --- a/drivers/gpu/drm/nouveau/nv40_fb.c +++ b/drivers/gpu/drm/nouveau/nv40_fb.c @@ -71,6 +71,51 @@ nv44_fb_init_gart(struct drm_device *dev) nv_wr32(dev, 0x100800, vinst | 0x00000010); } +int +nv40_fb_vram_init(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + + /* 0x001218 is actually present on a few other NV4X I looked at, + * and even contains sane values matching 0x100474. From looking + * at various vbios images however, this isn't the case everywhere. + * So, I chose to use the same regs I've seen NVIDIA reading around + * the memory detection, hopefully that'll get us the right numbers + */ + if (dev_priv->chipset == 0x40) { + u32 pbus1218 = nv_rd32(dev, 0x001218); + switch (pbus1218 & 0x00000300) { + case 0x00000000: dev_priv->vram_type = NV_MEM_TYPE_SDRAM; break; + case 0x00000100: dev_priv->vram_type = NV_MEM_TYPE_DDR1; break; + case 0x00000200: dev_priv->vram_type = NV_MEM_TYPE_GDDR3; break; + case 0x00000300: dev_priv->vram_type = NV_MEM_TYPE_DDR2; break; + } + } else + if (dev_priv->chipset == 0x49 || dev_priv->chipset == 0x4b) { + u32 pfb914 = nv_rd32(dev, 0x100914); + switch (pfb914 & 0x00000003) { + case 0x00000000: dev_priv->vram_type = NV_MEM_TYPE_DDR1; break; + case 0x00000001: dev_priv->vram_type = NV_MEM_TYPE_DDR2; break; + case 0x00000002: dev_priv->vram_type = NV_MEM_TYPE_GDDR3; break; + case 0x00000003: break; + } + } else + if (dev_priv->chipset != 0x4e) { + u32 pfb474 = nv_rd32(dev, 0x100474); + if (pfb474 & 0x00000004) + dev_priv->vram_type = NV_MEM_TYPE_GDDR3; + if (pfb474 & 0x00000002) + dev_priv->vram_type = NV_MEM_TYPE_DDR2; + if (pfb474 & 0x00000001) + dev_priv->vram_type = NV_MEM_TYPE_DDR1; + } else { + dev_priv->vram_type = NV_MEM_TYPE_STOLEN; + } + + dev_priv->vram_size = nv_rd32(dev, 0x10020c) & 0xff000000; + return 0; +} + int nv40_fb_init(struct drm_device *dev) { From 1072856a1c604726be6a8adfb6b2c86033e6a314 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 13 Dec 2011 09:45:25 +1000 Subject: [PATCH 05/57] drm/nv50: add memory type detection DDR1/DDR[23] confirmed on NVA8 (see note about DDR3 in source) by changing the value and watching the binary driver's behaviour. GDDR3/4 values confirmed on a NV96 via the same method above. That GDDR4 is present is interesting, as far as I can see no boards using it were ever released. GDDR5 value is based on VBIOS images of known GDDR5 boards. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nv50_vram.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/gpu/drm/nouveau/nv50_vram.c b/drivers/gpu/drm/nouveau/nv50_vram.c index 2e45e57fd869..840696694611 100644 --- a/drivers/gpu/drm/nouveau/nv50_vram.c +++ b/drivers/gpu/drm/nouveau/nv50_vram.c @@ -189,8 +189,24 @@ nv50_vram_init(struct drm_device *dev) struct nouveau_vram_engine *vram = &dev_priv->engine.vram; const u32 rsvd_head = ( 256 * 1024) >> 12; /* vga memory */ const u32 rsvd_tail = (1024 * 1024) >> 12; /* vbios etc */ + u32 pfb714 = nv_rd32(dev, 0x100714); u32 rblock, length; + switch (pfb714 & 0x00000007) { + case 0: dev_priv->vram_type = NV_MEM_TYPE_DDR1; break; + case 1: + if (0 /* some currently unknown condition */) + dev_priv->vram_type = NV_MEM_TYPE_DDR2; + else + dev_priv->vram_type = NV_MEM_TYPE_DDR3; + break; + case 2: dev_priv->vram_type = NV_MEM_TYPE_GDDR3; break; + case 3: dev_priv->vram_type = NV_MEM_TYPE_GDDR4; break; + case 4: dev_priv->vram_type = NV_MEM_TYPE_GDDR5; break; + default: + break; + } + dev_priv->vram_size = nv_rd32(dev, 0x10020c); dev_priv->vram_size |= (dev_priv->vram_size & 0xff) << 32; dev_priv->vram_size &= 0xffffffff00ULL; From c70c41e89f0481f26749d8264aebc594566c2a6d Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 13 Dec 2011 11:57:55 +1000 Subject: [PATCH 06/57] drm/nv50: hopefully handle the DDR2/DDR3 memtype detection somewhat better M version 2 appears to have a table with some form of memory type info available. NVIDIA appear to ignore the table information except for this DDR2/DDR3 case (which has the same value in 0x100714). My guess is this is due to some of the supported memory types not being represented in the table. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_drv.h | 1 + drivers/gpu/drm/nouveau/nouveau_mem.c | 23 +++++++++++++++++++++++ drivers/gpu/drm/nouveau/nv50_vram.c | 6 +++--- 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 59031d308fcc..5a4947919f1c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -909,6 +909,7 @@ 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_vbios_type(struct drm_device *); extern struct nouveau_tile_reg *nv10_mem_set_tiling( struct drm_device *dev, uint32_t addr, uint32_t size, uint32_t pitch, uint32_t flags); diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c index 4a658310c29a..c5ba6c2eab88 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mem.c +++ b/drivers/gpu/drm/nouveau/nouveau_mem.c @@ -683,6 +683,29 @@ nouveau_mem_timing_fini(struct drm_device *dev) } } +int +nouveau_mem_vbios_type(struct drm_device *dev) +{ + struct bit_entry M; + u8 ramcfg = (nv_rd32(dev, 0x101000) & 0x0000003c) >> 2; + if (!bit_table(dev, 'M', &M) || M.version != 2 || M.length < 5) { + u8 *table = ROMPTR(dev, M.data[3]); + if (table && table[0] == 0x10 && ramcfg < table[3]) { + u8 *entry = table + table[1] + (ramcfg * table[2]); + switch (entry[0] & 0x0f) { + case 0: return NV_MEM_TYPE_DDR2; + case 1: return NV_MEM_TYPE_DDR3; + case 2: return NV_MEM_TYPE_GDDR3; + case 3: return NV_MEM_TYPE_GDDR5; + default: + break; + } + + } + } + return NV_MEM_TYPE_UNKNOWN; +} + static int nouveau_vram_manager_init(struct ttm_mem_type_manager *man, unsigned long psize) { diff --git a/drivers/gpu/drm/nouveau/nv50_vram.c b/drivers/gpu/drm/nouveau/nv50_vram.c index 840696694611..1467efc6d61d 100644 --- a/drivers/gpu/drm/nouveau/nv50_vram.c +++ b/drivers/gpu/drm/nouveau/nv50_vram.c @@ -195,10 +195,10 @@ nv50_vram_init(struct drm_device *dev) switch (pfb714 & 0x00000007) { case 0: dev_priv->vram_type = NV_MEM_TYPE_DDR1; break; case 1: - if (0 /* some currently unknown condition */) - dev_priv->vram_type = NV_MEM_TYPE_DDR2; - else + if (nouveau_mem_vbios_type(dev) == NV_MEM_TYPE_DDR3) dev_priv->vram_type = NV_MEM_TYPE_DDR3; + else + dev_priv->vram_type = NV_MEM_TYPE_DDR2; break; case 2: dev_priv->vram_type = NV_MEM_TYPE_GDDR3; break; case 3: dev_priv->vram_type = NV_MEM_TYPE_GDDR4; break; From f3298532f71f163877b9003009d6e1eefe988258 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 13 Dec 2011 12:46:18 +1000 Subject: [PATCH 07/57] drm/nvc0: add initial memory type detection Uses only the VBIOS tables, from what I can tell this is what NVIDIA do too, I was able to change the detected memory type by modifying this table on a NVC1 chipset. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvc0_vram.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/nouveau/nvc0_vram.c b/drivers/gpu/drm/nouveau/nvc0_vram.c index ce984d573a51..fd687ee888de 100644 --- a/drivers/gpu/drm/nouveau/nvc0_vram.c +++ b/drivers/gpu/drm/nouveau/nvc0_vram.c @@ -115,6 +115,8 @@ nvc0_vram_init(struct drm_device *dev) NV_DEBUG(dev, "0x100800: 0x%08x\n", nv_rd32(dev, 0x100800)); NV_DEBUG(dev, "parts 0x%08x bcast_mem_amount 0x%08x\n", parts, bsize); + dev_priv->vram_type = nouveau_mem_vbios_type(dev); + /* read amount of vram attached to each memory controller */ part = 0; while (parts) { From 668b6c097dbaf190f5d3974798fef37aee025385 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 15 Dec 2011 10:43:03 +1000 Subject: [PATCH 08/57] drm/nouveau: rework the init/takedown ordering Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_state.c | 83 +++++++++++++------------ 1 file changed, 42 insertions(+), 41 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index a30d7af58eb3..a36386c00d9b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -592,16 +592,34 @@ nouveau_card_init(struct drm_device *dev) nv_mask(dev, 0x00088080, 0x00000800, 0x00000000); } - nouveau_pm_init(dev); - - ret = engine->vram.init(dev); + /* PMC */ + ret = engine->mc.init(dev); if (ret) goto out_bios; - ret = nouveau_gpuobj_init(dev); + /* PTIMER */ + ret = engine->timer.init(dev); + if (ret) + goto out_mc; + + /* PFB */ + ret = engine->fb.init(dev); + if (ret) + goto out_timer; + + ret = engine->vram.init(dev); + if (ret) + goto out_fb; + + /* PGPIO */ + ret = nouveau_gpio_create(dev); if (ret) goto out_vram; + ret = nouveau_gpuobj_init(dev); + if (ret) + goto out_gpio; + ret = engine->instmem.init(dev); if (ret) goto out_gpuobj; @@ -614,25 +632,7 @@ nouveau_card_init(struct drm_device *dev) if (ret) goto out_ttmvram; - /* PMC */ - ret = engine->mc.init(dev); - if (ret) - goto out_gart; - - /* PGPIO */ - ret = nouveau_gpio_create(dev); - if (ret) - goto out_mc; - - /* PTIMER */ - ret = engine->timer.init(dev); - if (ret) - goto out_gpio; - - /* PFB */ - ret = engine->fb.init(dev); - if (ret) - goto out_timer; + nouveau_pm_init(dev); if (!dev_priv->noaccel) { switch (dev_priv->card_type) { @@ -783,15 +783,7 @@ out_engine: dev_priv->eng[e]->destroy(dev,e ); } } - - engine->fb.takedown(dev); -out_timer: - engine->timer.takedown(dev); -out_gpio: - nouveau_gpio_destroy(dev); -out_mc: - engine->mc.takedown(dev); -out_gart: + nouveau_pm_fini(dev); nouveau_mem_gart_fini(dev); out_ttmvram: nouveau_mem_vram_fini(dev); @@ -799,10 +791,17 @@ out_instmem: engine->instmem.takedown(dev); out_gpuobj: nouveau_gpuobj_takedown(dev); +out_gpio: + nouveau_gpio_destroy(dev); out_vram: engine->vram.takedown(dev); +out_fb: + engine->fb.takedown(dev); +out_timer: + engine->timer.takedown(dev); +out_mc: + engine->mc.takedown(dev); out_bios: - nouveau_pm_fini(dev); nouveau_bios_takedown(dev); out_display_early: engine->display.late_takedown(dev); @@ -839,11 +838,6 @@ static void nouveau_card_takedown(struct drm_device *dev) } } } - engine->fb.takedown(dev); - engine->timer.takedown(dev); - nouveau_gpio_destroy(dev); - engine->mc.takedown(dev); - engine->display.late_takedown(dev); if (dev_priv->vga_ram) { nouveau_bo_unpin(dev_priv->vga_ram); @@ -859,12 +853,19 @@ static void nouveau_card_takedown(struct drm_device *dev) engine->instmem.takedown(dev); nouveau_gpuobj_takedown(dev); - engine->vram.takedown(dev); - - nouveau_irq_fini(dev); nouveau_pm_fini(dev); + + nouveau_gpio_destroy(dev); + engine->vram.takedown(dev); + engine->fb.takedown(dev); + engine->timer.takedown(dev); + engine->mc.takedown(dev); + nouveau_bios_takedown(dev); + engine->display.late_takedown(dev); + + nouveau_irq_fini(dev); vga_client_register(dev->pdev, NULL, NULL, NULL); } From ddb2005516949dc50d117cb8381d7a3f8f0614b0 Mon Sep 17 00:00:00 2001 From: Martin Peres Date: Sat, 17 Dec 2011 12:24:59 +0100 Subject: [PATCH 09/57] drm/nouveau/pm: style fixes Signed-off-by: Martin Peres Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_drv.h | 13 ++- drivers/gpu/drm/nouveau/nouveau_mem.c | 129 ++++++++++++++++--------- drivers/gpu/drm/nouveau/nouveau_perf.c | 18 ++-- drivers/gpu/drm/nouveau/nouveau_pm.c | 20 ++-- 4 files changed, 110 insertions(+), 70 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 5a4947919f1c..760f85c85ccd 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -448,14 +448,14 @@ struct nouveau_pm_memtiming { u8 WR; }; -struct nouveau_pm_tbl_header{ +struct nouveau_pm_tbl_header { u8 version; u8 header_len; u8 entry_cnt; u8 entry_len; }; -struct nouveau_pm_tbl_entry{ +struct nouveau_pm_tbl_entry { u8 tWR; u8 tUNK_1; u8 tCL; @@ -471,11 +471,6 @@ struct nouveau_pm_tbl_entry{ u8 tUNK_18, tUNK_19, tUNK_20, tUNK_21; }; -/* nouveau_mem.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); - #define NOUVEAU_PM_MAX_LEVEL 8 struct nouveau_pm_level { struct device_attribute dev_attr; @@ -918,6 +913,10 @@ 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 c5ba6c2eab88..5eb9e3df0fec 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mem.c +++ b/drivers/gpu/drm/nouveau/nouveau_mem.c @@ -473,16 +473,21 @@ nouveau_mem_gart_init(struct drm_device *dev) /* 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) { +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"); + NV_DEBUG(dev, "Timing entry format unknown, " + "please contact nouveau developers"); } -void nv40_mem_timing_entry(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr, - struct nouveau_pm_tbl_entry *e, uint8_t magic_number, - struct nouveau_pm_memtiming *timing) { +void nv40_mem_timing_entry(struct drm_device *dev, + struct nouveau_pm_tbl_header *hdr, + struct nouveau_pm_tbl_entry *e, uint8_t magic_number, + struct nouveau_pm_memtiming *timing) +{ timing->reg_0 = (e->tRC << 24 | e->tRFC << 16 | e->tRAS << 8 | e->tRP); @@ -492,15 +497,19 @@ void nv40_mem_timing_entry(struct drm_device *dev, struct nouveau_pm_tbl_header 1 << 16 | (e->tUNK_1 + 2 + magic_number) << 8 | (e->tCL + 2 - magic_number); - timing->reg_2 = (magic_number << 24 | e->tUNK_12 << 16 | e->tUNK_11 << 8 | e->tUNK_10); + timing->reg_2 = magic_number << 24 | e->tUNK_12 << 16 | + e->tUNK_11 << 8 | e->tUNK_10; timing->reg_2 |= 0x20200000; NV_DEBUG(dev, "Entry %d: 220: %08x %08x %08x\n", timing->id, - timing->reg_0, timing->reg_1,timing->reg_2); + timing->reg_0, timing->reg_1, timing->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, uint8_t magic_number,struct nouveau_pm_memtiming *timing) { +void nv50_mem_timing_entry(struct drm_device *dev, struct bit_entry *P, + struct nouveau_pm_tbl_header *hdr, + struct nouveau_pm_tbl_entry *e, uint8_t magic_number, + struct nouveau_pm_memtiming *timing) +{ struct drm_nouveau_private *dev_priv = dev->dev_private; uint8_t unk18 = 1, @@ -527,11 +536,11 @@ void nv50_mem_timing_entry(struct drm_device *dev, struct bit_entry *P, struct n timing->reg_1 = (e->tWR + unk19 + 1 + magic_number) << 24 | max(unk18, (u8) 1) << 16 | (e->tUNK_1 + unk19 + 1 + magic_number) << 8; - if (dev_priv->chipset == 0xa8) { + if (dev_priv->chipset == 0xa8) timing->reg_1 |= (e->tCL - 1); - } else { + else timing->reg_1 |= (e->tCL + 2 - magic_number); - } + timing->reg_2 = (e->tUNK_12 << 16 | e->tUNK_11 << 8 | e->tUNK_10); timing->reg_5 = (e->tRAS << 24 | e->tRC); @@ -539,19 +548,24 @@ void nv50_mem_timing_entry(struct drm_device *dev, struct bit_entry *P, struct n if (P->version == 1) { timing->reg_2 |= magic_number << 24; + timing->reg_3 = (0x14 + e->tCL) << 24 | - 0x16 << 16 | - (e->tCL - 1) << 8 | - (e->tCL - 1); - timing->reg_4 = (nv_rd32(dev,0x10022c) & 0xffff0000) | e->tUNK_13 << 8 | e->tUNK_13; + 0x16 << 16 | + (e->tCL - 1) << 8 | + (e->tCL - 1); + + timing->reg_4 = (nv_rd32(dev, 0x10022c) & 0xffff0000) | + e->tUNK_13 << 8 | e->tUNK_13; + timing->reg_5 |= (e->tCL + 2) << 8; + timing->reg_7 = 0x4000202 | (e->tCL - 1) << 16; } else { timing->reg_2 |= (unk19 - 1) << 24; /* XXX: reg_10022c for recentish cards pretty much unknown*/ timing->reg_3 = e->tCL - 1; timing->reg_4 = (unk20 << 24 | unk21 << 16 | - e->tUNK_13 << 8 | e->tUNK_13); + e->tUNK_13 << 8 | e->tUNK_13); /* XXX: +6? */ timing->reg_5 |= (unk19 + 6) << 8; @@ -569,13 +583,26 @@ void nv50_mem_timing_entry(struct drm_device *dev, struct bit_entry *P, struct n NV_DEBUG(dev, " 240: %08x\n", timing->reg_8); } -void nvc0_mem_timing_entry(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr, - struct nouveau_pm_tbl_entry *e, struct nouveau_pm_memtiming *timing) { - timing->reg_0 = (e->tRC << 24 | (e->tRFC & 0x7f) << 17 | e->tRAS << 8 | e->tRP); - timing->reg_1 = (nv_rd32(dev,0x10f294) & 0xff000000) | (e->tUNK_11&0x0f) << 20 | (e->tUNK_19 << 7) | (e->tCL & 0x0f); - timing->reg_2 = (nv_rd32(dev,0x10f298) & 0xff0000ff) | e->tWR << 16 | e->tUNK_1 << 8; +void nvc0_mem_timing_entry(struct drm_device *dev, + struct nouveau_pm_tbl_header *hdr, + struct nouveau_pm_tbl_entry *e, + struct nouveau_pm_memtiming *timing) +{ + timing->reg_0 = (e->tRC << 24 | (e->tRFC & 0x7f) << 17 | + e->tRAS << 8 | e->tRP); + + timing->reg_1 = (nv_rd32(dev, 0x10f294) & 0xff000000) | + (e->tUNK_11&0x0f) << 20 | (e->tUNK_19 << 7) | + (e->tCL & 0x0f); + + timing->reg_2 = (nv_rd32(dev, 0x10f298) & 0xff0000ff) | + e->tWR << 16 | e->tUNK_1 << 8; + timing->reg_3 = e->tUNK_20 << 9 | e->tUNK_13; - timing->reg_4 = (nv_rd32(dev,0x10f2a0) & 0xfff000ff) | e->tUNK_12 << 15; + + timing->reg_4 = (nv_rd32(dev, 0x10f2a0) & 0xfff000ff) | + e->tUNK_12 << 15; + NV_DEBUG(dev, "Entry %d: 290: %08x %08x %08x %08x\n", timing->id, timing->reg_0, timing->reg_1, timing->reg_2, timing->reg_3); @@ -607,13 +634,13 @@ nouveau_mem_timing_init(struct drm_device *dev) return; if (P.version == 1) - hdr = (struct nouveau_pm_tbl_header *) ROMPTR(dev, P.data[4]); + 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 - 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; @@ -625,32 +652,35 @@ nouveau_mem_timing_init(struct drm_device *dev) } if (hdr->version != 0x10) { - NV_WARN(dev, "memory timing table 0x%02x unknown\n", hdr->version); + 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); + 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); + memtimings->timing = kcalloc(hdr->entry_cnt, + sizeof(*memtimings->timing), GFP_KERNEL); if (!memtimings->timing) return; /* Get "some number" from the timing reg for NV_40 and NV_50 * Used in calculations later... source unknown */ magic_number = 0; - if (P.version == 1) { + if (P.version == 1) magic_number = (nv_rd32(dev, 0x100228) & 0x0f000000) >> 24; - } - entry = (u8*) hdr + hdr->header_len; + 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; @@ -658,17 +688,22 @@ nouveau_mem_timing_init(struct drm_device *dev) timing->WR = entry[0]; timing->CL = entry[2]; - if(dev_priv->card_type <= NV_40) { - nv40_mem_timing_entry(dev,hdr,(struct nouveau_pm_tbl_entry*) entry,magic_number,&pm->memtimings.timing[i]); - } else if(dev_priv->card_type == NV_50){ - nv50_mem_timing_entry(dev,&P,hdr,(struct nouveau_pm_tbl_entry*) entry,magic_number,&pm->memtimings.timing[i]); - } else if(dev_priv->card_type == NV_C0) { - nvc0_mem_timing_entry(dev,hdr,(struct nouveau_pm_tbl_entry*) entry,&pm->memtimings.timing[i]); + if (dev_priv->card_type <= NV_40) { + nv40_mem_timing_entry(dev, hdr, entry_struct, + magic_number, + &pm->memtimings.timing[i]); + } else if (dev_priv->card_type == NV_50) { + nv50_mem_timing_entry(dev, &P, hdr, entry_struct, + magic_number, + &pm->memtimings.timing[i]); + } else if (dev_priv->card_type == NV_C0) { + nvc0_mem_timing_entry(dev, hdr, entry_struct, + &pm->memtimings.timing[i]); } } memtimings->nr_timing = hdr->entry_cnt; - memtimings->supported = P.version == 1; + memtimings->supported = (P.version == 1); } void @@ -677,10 +712,8 @@ 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; - if(mem->timing) { - kfree(mem->timing); - mem->timing = NULL; - } + kfree(mem->timing); + mem->timing = NULL; } int diff --git a/drivers/gpu/drm/nouveau/nouveau_perf.c b/drivers/gpu/drm/nouveau/nouveau_perf.c index 58f497343cec..54895cc60d2a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_perf.c +++ b/drivers/gpu/drm/nouveau/nouveau_perf.c @@ -230,18 +230,20 @@ nouveau_perf_init(struct drm_device *dev) } if (entries > NOUVEAU_PM_MAX_LEVEL) { - NV_DEBUG(dev, "perf table has too many entries - buggy vbios?\n"); + NV_DEBUG(dev, + "perf table has too many entries - buggy vbios?\n"); entries = NOUVEAU_PM_MAX_LEVEL; } entry = perf + headerlen; /* For version 0x15, initialize memtiming table */ - if(version == 0x15) { - memtimings->timing = - kcalloc(entries, sizeof(*memtimings->timing), GFP_KERNEL); + if (version == 0x15) { + memtimings->timing = kcalloc(entries, + sizeof(*memtimings->timing), + GFP_KERNEL); if (!memtimings->timing) { - NV_WARN(dev,"Could not allocate memtiming table\n"); + NV_WARN(dev, "Could not allocate memtiming table\n"); return; } @@ -304,7 +306,7 @@ nouveau_perf_init(struct drm_device *dev) perflvl->dom6 = ROM16(entry[20]) * 1000; break; case 0x40: -#define subent(n) (ROM16(entry[perf[2] + ((n) * perf[3])]) & 0xfff) * 1000 +#define subent(n) ((ROM16(entry[perf[2] + ((n) * perf[3])]) & 0xfff) * 1000) perflvl->fanspeed = 0; /*XXX*/ perflvl->volt_min = entry[2]; if (dev_priv->card_type == NV_50) { @@ -342,7 +344,9 @@ nouveau_perf_init(struct drm_device *dev) /* get the corresponding memory timings */ 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]); + 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 */ 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); } From bc6389e4fa1a6535021ffc4d3b37d48f9a0542e2 Mon Sep 17 00:00:00 2001 From: Martin Peres Date: Fri, 7 Oct 2011 01:33:12 +0200 Subject: [PATCH 10/57] 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(-) 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); } From b1aa5531cc74ea023ad35e9cf5872112a15b4f70 Mon Sep 17 00:00:00 2001 From: Martin Peres Date: Sat, 20 Aug 2011 16:37:06 +0200 Subject: [PATCH 11/57] 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(-) 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; From b0103747094b62231fc951dfaca4897f67670874 Mon Sep 17 00:00:00 2001 From: Martin Peres Date: Thu, 3 Nov 2011 00:03:06 +0100 Subject: [PATCH 12/57] 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(-) 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 From bfb314652430ceca302bae0981d00903f055eee4 Mon Sep 17 00:00:00 2001 From: Roy Spliet Date: Fri, 25 Nov 2011 15:52:22 +0100 Subject: [PATCH 13/57] drm/nouveau/pm: improve memory timing generation - Rename several VBIOS entries to closer match the real world - Add the missing 0x100238 and 0x100240 register values - Parse bit 14 of the VBIOS timing table - "Magic value" -> tCWL, fixing some minor bugs in the process - Also name a few more by their name rather than their number. - Some values seem to be dependent on the memory type. Fix Edits by Martin Peres : - this is a squash commit - reworked for fixing some style issues Signed-off-by: Roy Spliet Signed-off-by: Martin Peres Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_drv.h | 29 +++-- drivers/gpu/drm/nouveau/nouveau_mem.c | 160 ++++++++++++++++---------- 2 files changed, 122 insertions(+), 67 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 78302cccf6fd..c5105326db7f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -446,6 +446,11 @@ struct nouveau_pm_memtiming { /* To be written to 0x1002c0 */ u8 CL; u8 WR; + u8 tCWL; + + bool odt; + bool dll_disable; + bool ron_pull; }; struct nouveau_pm_tbl_header { @@ -457,18 +462,26 @@ struct nouveau_pm_tbl_header { struct nouveau_pm_tbl_entry { u8 tWR; - u8 tUNK_1; + u8 tWTR; u8 tCL; - u8 tRP; /* Byte 3 */ + u8 tRC; u8 empty_4; - u8 tRAS; /* Byte 5 */ + u8 tRFC; /* Byte 5 */ u8 empty_6; - u8 tRFC; /* Byte 7 */ + u8 tRAS; /* Byte 7 */ u8 empty_8; - u8 tRC; /* Byte 9 */ - u8 tUNK_10, tUNK_11, tUNK_12, tUNK_13, tUNK_14; - u8 empty_15,empty_16,empty_17; - u8 tUNK_18, tUNK_19, tUNK_20, tUNK_21; + u8 tRP; /* Byte 9 */ + u8 tRCDRD; + u8 tRCDWR; + u8 tRRD; + u8 tUNK_13; + u8 RAM_FT1; /* 14, a bitmask of random RAM features */ + u8 empty_15; + u8 tUNK_16; + u8 empty_17; + u8 tUNK_18; + u8 tCWL; + u8 tUNK_20, tUNK_21; }; #define NOUVEAU_PM_MAX_LEVEL 8 diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c index 5eb9e3df0fec..3337f2cfb5f6 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mem.c +++ b/drivers/gpu/drm/nouveau/nouveau_mem.c @@ -485,21 +485,21 @@ void nv30_mem_timing_entry(struct drm_device *dev, void nv40_mem_timing_entry(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr, - struct nouveau_pm_tbl_entry *e, uint8_t magic_number, + struct nouveau_pm_tbl_entry *e, struct nouveau_pm_memtiming *timing) { - timing->reg_0 = (e->tRC << 24 | e->tRFC << 16 | e->tRAS << 8 | e->tRP); + timing->reg_0 = (e->tRP << 24 | e->tRAS << 16 | e->tRFC << 8 | e->tRC); /* XXX: I don't trust the -1's and +1's... they must come * from somewhere! */ - timing->reg_1 = (e->tWR + 2 + magic_number) << 24 | - 1 << 16 | - (e->tUNK_1 + 2 + magic_number) << 8 | - (e->tCL + 2 - magic_number); - timing->reg_2 = magic_number << 24 | e->tUNK_12 << 16 | - e->tUNK_11 << 8 | e->tUNK_10; - timing->reg_2 |= 0x20200000; + 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); @@ -507,15 +507,14 @@ void nv40_mem_timing_entry(struct drm_device *dev, void nv50_mem_timing_entry(struct drm_device *dev, struct bit_entry *P, struct nouveau_pm_tbl_header *hdr, - struct nouveau_pm_tbl_entry *e, uint8_t magic_number, + struct nouveau_pm_tbl_entry *e, struct nouveau_pm_memtiming *timing) { struct drm_nouveau_private *dev_priv = dev->dev_private; - uint8_t unk18 = 1, - unk19 = 1, unk20 = 0, - unk21 = 0; + unk21 = 0, + tmp7_3; switch (min(hdr->entry_len, (u8) 22)) { case 22: @@ -523,55 +522,77 @@ void nv50_mem_timing_entry(struct drm_device *dev, struct bit_entry *P, case 21: unk20 = e->tUNK_20; case 20: - unk19 = e->tUNK_19; + if (e->tCWL > 0) + timing->tCWL = e->tCWL; case 19: unk18 = e->tUNK_18; break; } - timing->reg_0 = (e->tRC << 24 | e->tRFC << 16 | e->tRAS << 8 | e->tRP); + timing->reg_0 = (e->tRP << 24 | e->tRAS << 16 | e->tRFC << 8 | e->tRC); - /* XXX: I don't trust the -1's and +1's... they must come - * from somewhere! */ - timing->reg_1 = (e->tWR + unk19 + 1 + magic_number) << 24 | - max(unk18, (u8) 1) << 16 | - (e->tUNK_1 + unk19 + 1 + magic_number) << 8; - if (dev_priv->chipset == 0xa8) - timing->reg_1 |= (e->tCL - 1); - else - timing->reg_1 |= (e->tCL + 2 - magic_number); + timing->reg_1 = (e->tWR + 2 + (timing->tCWL - 1)) << 24 | + max(unk18, (u8) 1) << 16 | + (e->tWTR + 2 + (timing->tCWL - 1)) << 8; - timing->reg_2 = (e->tUNK_12 << 16 | e->tUNK_11 << 8 | e->tUNK_10); + timing->reg_2 = ((timing->tCWL - 1) << 24 | e->tRRD << 16 | + e->tRCDWR << 8 | e->tRCDRD); - timing->reg_5 = (e->tRAS << 24 | e->tRC); - timing->reg_5 += max(e->tUNK_10, e->tUNK_11) << 16; + timing->reg_4 = e->tUNK_13 << 8 | e->tUNK_13; + + timing->reg_5 = (e->tRFC << 24 | max(e->tRCDRD, e->tRCDWR) << 16 | + e->tRP); + + timing->reg_8 = (nv_rd32(dev, 0x100240) & 0xffffff00); if (P->version == 1) { - timing->reg_2 |= magic_number << 24; + timing->reg_1 |= (e->tCL + 2 - (timing->tCWL - 1)); timing->reg_3 = (0x14 + e->tCL) << 24 | 0x16 << 16 | (e->tCL - 1) << 8 | (e->tCL - 1); - timing->reg_4 = (nv_rd32(dev, 0x10022c) & 0xffff0000) | - e->tUNK_13 << 8 | e->tUNK_13; + timing->reg_4 |= (nv_rd32(dev, 0x100230) & 0xffff0000); - timing->reg_5 |= (e->tCL + 2) << 8; + timing->reg_6 = (0x33 - timing->tCWL) << 16 | + timing->tCWL << 8 | + (0x2E + e->tCL - timing->tCWL); timing->reg_7 = 0x4000202 | (e->tCL - 1) << 16; - } else { - timing->reg_2 |= (unk19 - 1) << 24; - /* XXX: reg_10022c for recentish cards pretty much unknown*/ - timing->reg_3 = e->tCL - 1; - timing->reg_4 = (unk20 << 24 | unk21 << 16 | - e->tUNK_13 << 8 | e->tUNK_13); - /* XXX: +6? */ - timing->reg_5 |= (unk19 + 6) << 8; - /* XXX: reg_10023c currently unknown - * 10023c seen as 06xxxxxx, 0bxxxxxx or 0fxxxxxx */ - timing->reg_7 = 0x202; + /* 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); + } else { + timing->reg_5 |= (e->tCL + 2) << 8; + timing->reg_6 |= timing->tCWL << 8; + timing->reg_8 |= (e->tCL - 2); + } + } else { + timing->reg_1 |= (5 + e->tCL - (timing->tCWL)); + + /* XXX: 0xb? 0x30? */ + timing->reg_3 = (0x30 + e->tCL) << 24 | + (nv_rd32(dev, 0x10022c) & 0x00ff0000) | + (0xB + e->tCL) << 8 | + (e->tCL - 1); + + timing->reg_4 |= (unk20 << 24 | unk21 << 16); + + /* XXX: +6? */ + timing->reg_5 |= (timing->tCWL + 6) << 8; + + timing->reg_6 = (0x5A + e->tCL) << 16 | + (6 - e->tCL + timing->tCWL) << 8 | + (0x50 + e->tCL - timing->tCWL); + + tmp7_3 = (nv_rd32(dev, 0x10023c) & 0xff000000) >> 24; + timing->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, @@ -588,27 +609,46 @@ void nvc0_mem_timing_entry(struct drm_device *dev, struct nouveau_pm_tbl_entry *e, struct nouveau_pm_memtiming *timing) { - timing->reg_0 = (e->tRC << 24 | (e->tRFC & 0x7f) << 17 | - e->tRAS << 8 | e->tRP); + timing->tCWL = e->tCWL; + + timing->reg_0 = (e->tRP << 24 | (e->tRAS & 0x7f) << 17 | + e->tRFC << 8 | e->tRC); timing->reg_1 = (nv_rd32(dev, 0x10f294) & 0xff000000) | - (e->tUNK_11&0x0f) << 20 | (e->tUNK_19 << 7) | + (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->tUNK_1 << 8; + e->tWR << 16 | e->tWTR << 8; - timing->reg_3 = e->tUNK_20 << 9 | e->tUNK_13; + timing->reg_3 = (e->tUNK_20&0xf) << 9 | + (e->tUNK_21 & 0xf) << 5 | + (e->tUNK_13 & 0x1f); - timing->reg_4 = (nv_rd32(dev, 0x10f2a0) & 0xfff000ff) | - e->tUNK_12 << 15; + timing->reg_4 = (nv_rd32(dev, 0x10f2a0) & 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 %08x %08x %08x\n", - timing->reg_4, timing->reg_5, - timing->reg_6, timing->reg_7); + NV_DEBUG(dev, " 2a0: %08x\n", + timing->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) +{ + 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; + } } /** @@ -625,7 +665,7 @@ 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 magic_number; + uint8_t tCWL; u8 *entry; int i; @@ -670,11 +710,11 @@ nouveau_mem_timing_init(struct drm_device *dev) if (!memtimings->timing) return; - /* Get "some number" from the timing reg for NV_40 and NV_50 + /* Get tCWL from the timing reg for NV_40 and NV_50 * Used in calculations later... source unknown */ - magic_number = 0; - if (P.version == 1) - magic_number = (nv_rd32(dev, 0x100228) & 0x0f000000) >> 24; + 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) { @@ -687,14 +727,16 @@ nouveau_mem_timing_init(struct drm_device *dev) 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]); if (dev_priv->card_type <= NV_40) { nv40_mem_timing_entry(dev, hdr, entry_struct, - magic_number, &pm->memtimings.timing[i]); } else if (dev_priv->card_type == NV_50) { nv50_mem_timing_entry(dev, &P, hdr, entry_struct, - magic_number, &pm->memtimings.timing[i]); } else if (dev_priv->card_type == NV_C0) { nvc0_mem_timing_entry(dev, hdr, entry_struct, From 1a5f985c17c31367d1cd89db2278e0420c81d635 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 30 Nov 2011 17:23:55 +0100 Subject: [PATCH 14/57] drm/nouveau: Fix module parameter description formats Module parameter descriptions don't take a trailing \n, otherwise it breaks formatting of modinfo's output. Also remove trailing space. Signed-off-by: Jean Delvare Cc: David Airlie Cc: Ben Skeggs Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_drv.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.c b/drivers/gpu/drm/nouveau/nouveau_drv.c index 4567e8ca0497..4f2030bd5676 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.c +++ b/drivers/gpu/drm/nouveau/nouveau_drv.c @@ -93,7 +93,7 @@ MODULE_PARM_DESC(override_conntype, "Ignore DCB connector type"); int nouveau_override_conntype = 0; module_param_named(override_conntype, nouveau_override_conntype, int, 0400); -MODULE_PARM_DESC(tv_disable, "Disable TV-out detection\n"); +MODULE_PARM_DESC(tv_disable, "Disable TV-out detection"); int nouveau_tv_disable = 0; module_param_named(tv_disable, nouveau_tv_disable, int, 0400); @@ -108,27 +108,27 @@ module_param_named(tv_norm, nouveau_tv_norm, charp, 0400); MODULE_PARM_DESC(reg_debug, "Register access debug bitmask:\n" "\t\t0x1 mc, 0x2 video, 0x4 fb, 0x8 extdev,\n" "\t\t0x10 crtc, 0x20 ramdac, 0x40 vgacrtc, 0x80 rmvio,\n" - "\t\t0x100 vgaattr, 0x200 EVO (G80+). "); + "\t\t0x100 vgaattr, 0x200 EVO (G80+)"); int nouveau_reg_debug; module_param_named(reg_debug, nouveau_reg_debug, int, 0600); -MODULE_PARM_DESC(perflvl, "Performance level (default: boot)\n"); +MODULE_PARM_DESC(perflvl, "Performance level (default: boot)"); char *nouveau_perflvl; module_param_named(perflvl, nouveau_perflvl, charp, 0400); -MODULE_PARM_DESC(perflvl_wr, "Allow perflvl changes (warning: dangerous!)\n"); +MODULE_PARM_DESC(perflvl_wr, "Allow perflvl changes (warning: dangerous!)"); int nouveau_perflvl_wr; module_param_named(perflvl_wr, nouveau_perflvl_wr, int, 0400); -MODULE_PARM_DESC(msi, "Enable MSI (default: off)\n"); +MODULE_PARM_DESC(msi, "Enable MSI (default: off)"); int nouveau_msi; module_param_named(msi, nouveau_msi, int, 0400); -MODULE_PARM_DESC(ctxfw, "Use external HUB/GPC ucode (fermi)\n"); +MODULE_PARM_DESC(ctxfw, "Use external HUB/GPC ucode (fermi)"); int nouveau_ctxfw; module_param_named(ctxfw, nouveau_ctxfw, int, 0400); -MODULE_PARM_DESC(mxmdcb, "Santise DCB table according to MXM-SIS\n"); +MODULE_PARM_DESC(mxmdcb, "Santise DCB table according to MXM-SIS"); int nouveau_mxmdcb = 1; module_param_named(mxmdcb, nouveau_mxmdcb, int, 0400); From 3d8a408c43828d586629204fe22134ae8f39b8be Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 23 Jan 2012 12:58:33 +1000 Subject: [PATCH 15/57] 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(-) 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; From 03ddf04bdb9cc4cdf8edb231b78f031647498314 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 16 Jan 2012 22:34:03 +1000 Subject: [PATCH 16/57] drm/nouveau/pm: restructure bios table parsing It turns out we need access to some additional information in various VBIOS tables to handle PFB memory timings correctly. Rather than hack in parsing of the new stuff in some kludgy way, I've restructured the VBIOS parsing to be more primitive, so we can use them in more flexible ways in the future. The perflvl->timing association code is disabled for the moment until it can be reworked. We don't use this stuff yet anyway, so no harm done. Signed-off-by: Ben Skeggs Signed-off-by: Martin Peres --- drivers/gpu/drm/nouveau/nouveau_perf.c | 390 ++++++++++++++----------- 1 file changed, 217 insertions(+), 173 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_perf.c b/drivers/gpu/drm/nouveau/nouveau_perf.c index 2ca26f39292a..ad990553d115 100644 --- a/drivers/gpu/drm/nouveau/nouveau_perf.c +++ b/drivers/gpu/drm/nouveau/nouveau_perf.c @@ -27,6 +27,178 @@ #include "nouveau_drv.h" #include "nouveau_pm.h" +static u8 * +nouveau_perf_table(struct drm_device *dev, u8 *ver) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nvbios *bios = &dev_priv->vbios; + struct bit_entry P; + + if (!bit_table(dev, 'P', &P) && P.version && P.version <= 2) { + u8 *perf = ROMPTR(dev, P.data[0]); + if (perf) { + *ver = perf[0]; + return perf; + } + } + + if (bios->type == NVBIOS_BMP) { + if (bios->data[bios->offset + 6] >= 0x25) { + u8 *perf = ROMPTR(dev, bios->data[bios->offset + 0x94]); + if (perf) { + *ver = perf[1]; + return perf; + } + } + } + + return NULL; +} + +static u8 * +nouveau_perf_entry(struct drm_device *dev, int idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len) +{ + u8 *perf = nouveau_perf_table(dev, ver); + if (perf) { + if (*ver >= 0x12 && *ver < 0x20 && idx < perf[2]) { + *hdr = perf[3]; + *cnt = 0; + *len = 0; + return perf + perf[0] + idx * perf[3]; + } else + if (*ver >= 0x20 && *ver < 0x40 && idx < perf[2]) { + *hdr = perf[3]; + *cnt = perf[4]; + *len = perf[5]; + return perf + perf[1] + idx * (*hdr + (*cnt * *len)); + } else + if (*ver >= 0x40 && *ver < 0x41 && idx < perf[5]) { + *hdr = perf[2]; + *cnt = perf[4]; + *len = perf[3]; + return perf + perf[1] + idx * (*hdr + (*cnt * *len)); + } + } + return NULL; +} + +static u8 * +nouveau_perf_rammap(struct drm_device *dev, u32 freq, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct bit_entry P; + u8 *perf, i; + + if (!bit_table(dev, 'P', &P) && P.version == 2) { + u8 *rammap = ROMPTR(dev, P.data[4]); + if (rammap) { + u8 *ramcfg = rammap + rammap[1]; + + *ver = rammap[0]; + *hdr = rammap[2]; + *cnt = rammap[4]; + *len = rammap[3]; + + freq /= 1000; + for (i = 0; i < rammap[5]; i++) { + if (freq >= ROM16(ramcfg[0]) && + freq <= ROM16(ramcfg[2])) + return ramcfg; + + ramcfg += *hdr + (*cnt * *len); + } + } + + return NULL; + } + + if (dev_priv->chipset == 0x49 || + dev_priv->chipset == 0x4b) + freq /= 2; + + while ((perf = nouveau_perf_entry(dev, i++, ver, hdr, cnt, len))) { + if (*ver >= 0x20 && *ver < 0x25) { + if (perf[0] != 0xff && freq <= ROM16(perf[11]) * 1000) + break; + } else + if (*ver >= 0x25 && *ver < 0x40) { + if (perf[0] != 0xff && freq <= ROM16(perf[12]) * 1000) + break; + } + } + + if (perf) { + u8 *ramcfg = perf + *hdr; + *ver = 0x00; + *hdr = 0; + return ramcfg; + } + + return NULL; +} + +static u8 * +nouveau_perf_ramcfg(struct drm_device *dev, u32 freq, u8 *ver, u8 *len) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nvbios *bios = &dev_priv->vbios; + u8 strap, hdr, cnt; + u8 *rammap; + + strap = (nv_rd32(dev, 0x101000) & 0x0000003c) >> 2; + if (bios->ram_restrict_tbl_ptr) + strap = bios->data[bios->ram_restrict_tbl_ptr + strap]; + + rammap = nouveau_perf_rammap(dev, freq, ver, &hdr, &cnt, len); + if (rammap && strap < cnt) + return rammap + hdr + (strap * *len); + + return NULL; +} + +static u8 * +nouveau_perf_timing(struct drm_device *dev, u32 freq, u8 *ver, u8 *len) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nvbios *bios = &dev_priv->vbios; + struct bit_entry P; + u8 *perf, *timing = NULL; + u8 i = 0, hdr, cnt; + + if (bios->type == NVBIOS_BMP) { + while ((perf = nouveau_perf_entry(dev, i++, ver, &hdr, &cnt, + len)) && *ver == 0x15) { + if (freq <= ROM32(perf[5]) * 20) { + *ver = 0x00; + *len = 14; + return perf + 41; + } + } + return NULL; + } + + if (!bit_table(dev, 'P', &P)) { + if (P.version == 1) + timing = ROMPTR(dev, P.data[4]); + else + if (P.version == 2) + timing = ROMPTR(dev, P.data[8]); + } + + if (timing && timing[0] == 0x10) { + u8 *ramcfg = nouveau_perf_ramcfg(dev, freq, ver, len); + if (ramcfg && ramcfg[1] < timing[2]) { + *ver = timing[0]; + *len = timing[3]; + return timing + timing[1] + (ramcfg[1] * timing[3]); + } + } + + return NULL; +} + static void legacy_perf_init(struct drm_device *dev) { @@ -72,74 +244,11 @@ legacy_perf_init(struct drm_device *dev) pm->nr_perflvl = 1; } -static struct nouveau_pm_memtiming * -nouveau_perf_timing(struct drm_device *dev, struct bit_entry *P, - u16 memclk, u8 *entry, u8 recordlen, u8 entries) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_pm_engine *pm = &dev_priv->engine.pm; - struct nvbios *bios = &dev_priv->vbios; - u8 ramcfg; - int i; - - /* perf v2 has a separate "timing map" table, we have to match - * the target memory clock to a specific entry, *then* use - * ramcfg to select the correct subentry - */ - if (P->version == 2) { - u8 *tmap = ROMPTR(dev, P->data[4]); - if (!tmap) { - NV_DEBUG(dev, "no timing map pointer\n"); - return NULL; - } - - if (tmap[0] != 0x10) { - NV_WARN(dev, "timing map 0x%02x unknown\n", tmap[0]); - return NULL; - } - - entry = tmap + tmap[1]; - recordlen = tmap[2] + (tmap[4] * tmap[3]); - for (i = 0; i < tmap[5]; i++, entry += recordlen) { - if (memclk >= ROM16(entry[0]) && - memclk <= ROM16(entry[2])) - break; - } - - if (i == tmap[5]) { - NV_WARN(dev, "no match in timing map table\n"); - return NULL; - } - - entry += tmap[2]; - recordlen = tmap[3]; - entries = tmap[4]; - } - - ramcfg = (nv_rd32(dev, NV_PEXTDEV_BOOT_0) & 0x0000003c) >> 2; - if (bios->ram_restrict_tbl_ptr) - ramcfg = bios->data[bios->ram_restrict_tbl_ptr + ramcfg]; - - if (ramcfg >= entries) { - NV_WARN(dev, "ramcfg strap out of bounds!\n"); - return NULL; - } - - entry += ramcfg * recordlen; - if (entry[1] >= pm->memtimings.nr_timing) { - if (entry[1] != 0xff) - NV_WARN(dev, "timingset %d does not exist\n", entry[1]); - return NULL; - } - - return &pm->memtimings.timing[entry[1]]; -} - static void -nouveau_perf_voltage(struct drm_device *dev, struct bit_entry *P, - struct nouveau_pm_level *perflvl) +nouveau_perf_voltage(struct drm_device *dev, struct nouveau_pm_level *perflvl) { struct drm_nouveau_private *dev_priv = dev->dev_private; + struct bit_entry P; u8 *vmap; int id; @@ -158,13 +267,13 @@ nouveau_perf_voltage(struct drm_device *dev, struct bit_entry *P, /* on newer ones, the perflvl stores an index into yet another * vbios table containing a min/max voltage value for the perflvl */ - if (P->version != 2 || P->length < 34) { + if (bit_table(dev, 'P', &P) || P.version != 2 || P.length < 34) { NV_DEBUG(dev, "where's our volt map table ptr? %d %d\n", - P->version, P->length); + P.version, P.length); return; } - vmap = ROMPTR(dev, P->data[32]); + vmap = ROMPTR(dev, P.data[32]); if (!vmap) { NV_DEBUG(dev, "volt map table pointer invalid\n"); return; @@ -183,132 +292,66 @@ nouveau_perf_init(struct drm_device *dev) struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_pm_engine *pm = &dev_priv->engine.pm; struct nvbios *bios = &dev_priv->vbios; - struct bit_entry P; - struct nouveau_pm_memtimings *memtimings = &pm->memtimings; - struct nouveau_pm_tbl_header mt_hdr; - u8 version, headerlen, recordlen, entries; - u8 *perf, *entry; - int vid, i; + u8 *perf, ver, hdr, cnt, len; + int vid, i = -1; - if (bios->type == NVBIOS_BIT) { - if (bit_table(dev, 'P', &P)) - return; - - if (P.version != 1 && P.version != 2) { - NV_WARN(dev, "unknown perf for BIT P %d\n", P.version); - return; - } - - perf = ROMPTR(dev, P.data[0]); - version = perf[0]; - headerlen = perf[1]; - if (version < 0x40) { - recordlen = perf[3] + (perf[4] * perf[5]); - entries = perf[2]; - - pm->fan.pwm_divisor = ROM16(perf[6]); - } else { - recordlen = perf[2] + (perf[3] * perf[4]); - entries = perf[5]; - } - } else { - if (bios->data[bios->offset + 6] < 0x25) { - legacy_perf_init(dev); - return; - } - - perf = ROMPTR(dev, bios->data[bios->offset + 0x94]); - if (!perf) { - NV_DEBUG(dev, "perf table pointer invalid\n"); - return; - } - - version = perf[1]; - headerlen = perf[0]; - recordlen = perf[3]; - entries = perf[2]; + if (bios->type == NVBIOS_BMP && bios->data[bios->offset + 6] < 0x25) { + legacy_perf_init(dev); + return; } - if (entries > NOUVEAU_PM_MAX_LEVEL) { - NV_DEBUG(dev, - "perf table has too many entries - buggy vbios?\n"); - entries = NOUVEAU_PM_MAX_LEVEL; - } - - entry = perf + headerlen; - - /* For version 0x15, initialize memtiming table */ - if (version == 0x15) { - memtimings->timing = kcalloc(entries, - sizeof(*memtimings->timing), - GFP_KERNEL); - if (!memtimings->timing) { - NV_WARN(dev, "Could not allocate memtiming table\n"); - return; - } - - mt_hdr.entry_cnt = entries; - mt_hdr.entry_len = 14; - mt_hdr.version = version; - mt_hdr.header_len = 4; - } - - for (i = 0; i < entries; i++) { + while ((perf = nouveau_perf_entry(dev, ++i, &ver, &hdr, &cnt, &len))) { struct nouveau_pm_level *perflvl = &pm->perflvl[pm->nr_perflvl]; - perflvl->timing = NULL; - - if (entry[0] == 0xff) { - entry += recordlen; + if (perf[0] == 0xff) continue; - } - switch (version) { + switch (ver) { case 0x12: case 0x13: case 0x15: - perflvl->fanspeed = entry[55]; - if (recordlen > 56) - perflvl->volt_min = entry[56]; - perflvl->core = ROM32(entry[1]) * 10; - perflvl->memory = ROM32(entry[5]) * 20; + perflvl->fanspeed = perf[55]; + if (hdr > 56) + perflvl->volt_min = perf[56]; + perflvl->core = ROM32(perf[1]) * 10; + perflvl->memory = ROM32(perf[5]) * 20; break; case 0x21: case 0x23: case 0x24: - perflvl->fanspeed = entry[4]; - perflvl->volt_min = entry[5]; - perflvl->shader = ROM16(entry[6]) * 1000; + perflvl->fanspeed = perf[4]; + perflvl->volt_min = perf[5]; + perflvl->shader = ROM16(perf[6]) * 1000; perflvl->core = perflvl->shader; - perflvl->core += (signed char)entry[8] * 1000; + perflvl->core += (signed char)perf[8] * 1000; if (dev_priv->chipset == 0x49 || dev_priv->chipset == 0x4b) - perflvl->memory = ROM16(entry[11]) * 1000; + perflvl->memory = ROM16(perf[11]) * 1000; else - perflvl->memory = ROM16(entry[11]) * 2000; + perflvl->memory = ROM16(perf[11]) * 2000; break; case 0x25: - perflvl->fanspeed = entry[4]; - perflvl->volt_min = entry[5]; - perflvl->core = ROM16(entry[6]) * 1000; - perflvl->shader = ROM16(entry[10]) * 1000; - perflvl->memory = ROM16(entry[12]) * 1000; + perflvl->fanspeed = perf[4]; + perflvl->volt_min = perf[5]; + perflvl->core = ROM16(perf[6]) * 1000; + perflvl->shader = ROM16(perf[10]) * 1000; + perflvl->memory = ROM16(perf[12]) * 1000; break; case 0x30: - perflvl->memscript = ROM16(entry[2]); + perflvl->memscript = ROM16(perf[2]); case 0x35: - perflvl->fanspeed = entry[6]; - perflvl->volt_min = entry[7]; - perflvl->core = ROM16(entry[8]) * 1000; - perflvl->shader = ROM16(entry[10]) * 1000; - perflvl->memory = ROM16(entry[12]) * 1000; - perflvl->vdec = ROM16(entry[16]) * 1000; - perflvl->dom6 = ROM16(entry[20]) * 1000; + perflvl->fanspeed = perf[6]; + perflvl->volt_min = perf[7]; + perflvl->core = ROM16(perf[8]) * 1000; + perflvl->shader = ROM16(perf[10]) * 1000; + perflvl->memory = ROM16(perf[12]) * 1000; + perflvl->vdec = ROM16(perf[16]) * 1000; + perflvl->dom6 = ROM16(perf[20]) * 1000; break; case 0x40: -#define subent(n) ((ROM16(entry[perf[2] + ((n) * perf[3])]) & 0xfff) * 1000) +#define subent(n) ((ROM16(perf[hdr + (n) * len]) & 0xfff) * 1000) perflvl->fanspeed = 0; /*XXX*/ - perflvl->volt_min = entry[2]; + perflvl->volt_min = perf[2]; if (dev_priv->card_type == NV_50) { perflvl->core = subent(0); perflvl->shader = subent(1); @@ -331,17 +374,17 @@ nouveau_perf_init(struct drm_device *dev) } /* make sure vid is valid */ - nouveau_perf_voltage(dev, &P, perflvl); + nouveau_perf_voltage(dev, perflvl); if (pm->voltage.supported && perflvl->volt_min) { vid = nouveau_volt_vid_lookup(dev, perflvl->volt_min); if (vid < 0) { - NV_DEBUG(dev, "drop perflvl %d, bad vid\n", i); - entry += recordlen; + NV_DEBUG(dev, "perflvl %d, bad vid\n", i); continue; } } /* get the corresponding memory timings */ +#if 0 if (version == 0x15) { memtimings->timing[i].id = i; nv30_mem_timing_entry(dev, &mt_hdr, @@ -356,13 +399,14 @@ nouveau_perf_init(struct drm_device *dev) entry + perf[3], perf[5], perf[4]); } +#else + perflvl->timing = NULL; +#endif snprintf(perflvl->name, sizeof(perflvl->name), "performance_level_%d", i); perflvl->id = i; pm->nr_perflvl++; - - entry += recordlen; } } From c7c039fd31be82ecb8d48477955e76badd38141a Mon Sep 17 00:00:00 2001 From: Roy Spliet Date: Mon, 9 Jan 2012 15:23:07 +1000 Subject: [PATCH 17/57] 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 | 584 ++++++++++++++++++++------ drivers/gpu/drm/nouveau/nouveau_pm.c | 1 + drivers/gpu/drm/nouveau/nv50_vram.c | 1 + 4 files changed, 474 insertions(+), 150 deletions(-) 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) +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) { - 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) -{ - - 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)); + 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)); - timing->reg_2 = 0x20200000 | ((timing->tCWL - 1) << 24 | - e->tRRD << 16 | e->tRCDWR << 8 | e->tRCDRD); + 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", timing->id, - timing->reg_0, timing->reg_1, timing->reg_2); + 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) { - 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; + 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 (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; + timing->tCWL = memtimings->boot.tCWL; - nouveau_mem_features_entry(P.version, hdr, entry_struct, - &pm->memtimings.timing[i]); - - 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; From 68a64cad07057c3395b3b18c282df884e87ff3d6 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 23 Jan 2012 13:47:02 +1000 Subject: [PATCH 18/57] 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(-) 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) { From fd99fd6100d3b7aaa8dc76888a38bbb15e8041bc Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 17 Jan 2012 21:10:58 +1000 Subject: [PATCH 19/57] 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(-) 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 *); From 085028ce3bf7136c5ab2eeb8bf012024d88905c8 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Wed, 18 Jan 2012 09:02:28 +1000 Subject: [PATCH 20/57] 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 | 84 ++++++++++++-------------- drivers/gpu/drm/nouveau/nouveau_perf.c | 9 ++- drivers/gpu/drm/nouveau/nouveau_pm.c | 8 +-- 4 files changed, 55 insertions(+), 59 deletions(-) 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; + 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; - } + 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; } - return t; + 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 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 From 2d85bc8855d2734f5585861b261c5fe7a7ed92c1 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 23 Jan 2012 13:12:09 +1000 Subject: [PATCH 21/57] drm/nouveau/pm: introduce ram reclocking helper This will probably result in more lines of code, however, we're going to have at least 3 slightly different implementations of this very soon and I'd rather keep the ram reclocking logic separate from the hw specifics. DDR2/DDR3/GDDR3 implemented thus far, others will be added as necessary. Signed-off-by: Ben Skeggs Signed-off-by: Martin Peres --- drivers/gpu/drm/nouveau/nouveau_mem.c | 96 +++++++++++++++++++++++++++ drivers/gpu/drm/nouveau/nouveau_pm.h | 18 +++++ 2 files changed, 114 insertions(+) diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c index cc46811e2d37..cf82f17cf03a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mem.c +++ b/drivers/gpu/drm/nouveau/nouveau_mem.c @@ -933,6 +933,102 @@ nouveau_mem_timing_read(struct drm_device *dev, struct nouveau_pm_memtiming *t) } } +int +nouveau_mem_exec(struct nouveau_mem_exec_func *exec, + struct nouveau_pm_level *perflvl) +{ + struct drm_nouveau_private *dev_priv = exec->dev->dev_private; + struct nouveau_pm_memtiming *info = &perflvl->timing; + u32 tMRD = 1000, tCKSRE = 0, tCKSRX = 0, tXS = 0, tDLLK = 0; + u32 mr[3] = { info->mr[0], info->mr[1], info->mr[2] }; + u32 mr1_dlloff; + + switch (dev_priv->vram_type) { + case NV_MEM_TYPE_DDR2: + tDLLK = 2000; + mr1_dlloff = 0x00000001; + break; + case NV_MEM_TYPE_DDR3: + tDLLK = 12000; + mr1_dlloff = 0x00000001; + break; + case NV_MEM_TYPE_GDDR3: + tDLLK = 40000; + mr1_dlloff = 0x00000040; + break; + default: + NV_ERROR(exec->dev, "cannot reclock unsupported memtype\n"); + return -ENODEV; + } + + /* fetch current MRs */ + switch (dev_priv->vram_type) { + case NV_MEM_TYPE_DDR3: + mr[2] = exec->mrg(exec, 2); + default: + mr[1] = exec->mrg(exec, 1); + mr[0] = exec->mrg(exec, 0); + break; + } + + /* DLL 'on' -> DLL 'off' mode, disable before entering self-refresh */ + if (!(mr[1] & mr1_dlloff) && (info->mr[1] & mr1_dlloff)) { + exec->precharge(exec); + exec->mrs (exec, 1, mr[1] | mr1_dlloff); + exec->wait(exec, tMRD); + } + + /* enter self-refresh mode */ + exec->precharge(exec); + exec->refresh(exec); + exec->refresh(exec); + exec->refresh_auto(exec, false); + exec->refresh_self(exec, true); + exec->wait(exec, tCKSRE); + + /* modify input clock frequency */ + exec->clock_set(exec); + + /* exit self-refresh mode */ + exec->wait(exec, tCKSRX); + exec->precharge(exec); + exec->refresh_self(exec, false); + exec->refresh_auto(exec, true); + exec->wait(exec, tXS); + + /* update MRs */ + if (mr[2] != info->mr[2]) { + exec->mrs (exec, 2, info->mr[2]); + exec->wait(exec, tMRD); + } + + if (mr[1] != info->mr[1]) { + exec->mrs (exec, 1, info->mr[1]); + exec->wait(exec, tMRD); + } + + if (mr[0] != info->mr[0]) { + exec->mrs (exec, 0, info->mr[0]); + exec->wait(exec, tMRD); + } + + /* update PFB timing registers */ + exec->timing_set(exec); + + /* DLL reset */ + if (!(info->mr[1] & mr1_dlloff)) { + exec->mrs (exec, 0, info->mr[0] | 0x00000100); + exec->wait(exec, tMRD); + exec->mrs (exec, 0, info->mr[0] | 0x00000000); + exec->wait(exec, tMRD); + exec->wait(exec, tDLLK); + if (dev_priv->vram_type == NV_MEM_TYPE_GDDR3) + exec->precharge(exec); + } + + return 0; +} + int nouveau_mem_vbios_type(struct drm_device *dev) { diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.h b/drivers/gpu/drm/nouveau/nouveau_pm.h index 9e7ad33aa091..5a61f05f49da 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.h +++ b/drivers/gpu/drm/nouveau/nouveau_pm.h @@ -25,6 +25,24 @@ #ifndef __NOUVEAU_PM_H__ #define __NOUVEAU_PM_H__ +struct nouveau_mem_exec_func { + struct drm_device *dev; + void (*precharge)(struct nouveau_mem_exec_func *); + void (*refresh)(struct nouveau_mem_exec_func *); + void (*refresh_auto)(struct nouveau_mem_exec_func *, bool); + void (*refresh_self)(struct nouveau_mem_exec_func *, bool); + void (*wait)(struct nouveau_mem_exec_func *, u32 nsec); + u32 (*mrg)(struct nouveau_mem_exec_func *, int mr); + void (*mrs)(struct nouveau_mem_exec_func *, int mr, u32 data); + void (*clock_set)(struct nouveau_mem_exec_func *); + void (*timing_set)(struct nouveau_mem_exec_func *); + void *priv; +}; + +/* nouveau_mem.c */ +int nouveau_mem_exec(struct nouveau_mem_exec_func *, + struct nouveau_pm_level *); + /* nouveau_pm.c */ int nouveau_pm_init(struct drm_device *dev); void nouveau_pm_fini(struct drm_device *dev); From 6bdf68c9a427220692ad7607858e96caa2cd3147 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 23 Jan 2012 13:17:11 +1000 Subject: [PATCH 22/57] drm/nv50/pm: initial work towards proper memory reclocking, with timings Signed-off-by: Ben Skeggs Signed-off-by: Martin Peres --- drivers/gpu/drm/nouveau/nv50_pm.c | 198 +++++++++++++++++++++++------- 1 file changed, 153 insertions(+), 45 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c index ec5481dfcd82..17eb3277b1a0 100644 --- a/drivers/gpu/drm/nouveau/nv50_pm.c +++ b/drivers/gpu/drm/nouveau/nv50_pm.c @@ -352,8 +352,13 @@ nv50_pm_clocks_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) } struct nv50_pm_state { + struct nouveau_pm_level *perflvl; + struct hwsq_ucode mclk_hwsq; u32 mscript; + u32 mmast; + u32 mctrl; + u32 mcoef; u32 emast; u32 nctrl; @@ -415,36 +420,155 @@ clk_same(u32 a, u32 b) return ((a / 1000) == (b / 1000)); } +static void +mclk_precharge(struct nouveau_mem_exec_func *exec) +{ + struct nv50_pm_state *info = exec->priv; + struct hwsq_ucode *hwsq = &info->mclk_hwsq; + + hwsq_wr32(hwsq, 0x1002d4, 0x00000001); +} + +static void +mclk_refresh(struct nouveau_mem_exec_func *exec) +{ + struct nv50_pm_state *info = exec->priv; + struct hwsq_ucode *hwsq = &info->mclk_hwsq; + + hwsq_wr32(hwsq, 0x1002d0, 0x00000001); +} + +static void +mclk_refresh_auto(struct nouveau_mem_exec_func *exec, bool enable) +{ + struct nv50_pm_state *info = exec->priv; + struct hwsq_ucode *hwsq = &info->mclk_hwsq; + + hwsq_wr32(hwsq, 0x100210, enable ? 0x80000000 : 0x00000000); +} + +static void +mclk_refresh_self(struct nouveau_mem_exec_func *exec, bool enable) +{ + struct nv50_pm_state *info = exec->priv; + struct hwsq_ucode *hwsq = &info->mclk_hwsq; + + hwsq_wr32(hwsq, 0x1002dc, enable ? 0x00000001 : 0x00000000); +} + +static void +mclk_wait(struct nouveau_mem_exec_func *exec, u32 nsec) +{ + struct nv50_pm_state *info = exec->priv; + struct hwsq_ucode *hwsq = &info->mclk_hwsq; + + if (nsec > 1000) + hwsq_usec(hwsq, (nsec + 500) / 1000); +} + +static u32 +mclk_mrg(struct nouveau_mem_exec_func *exec, int mr) +{ + if (mr <= 1) + return nv_rd32(exec->dev, 0x1002c0 + ((mr - 0) * 4)); + if (mr <= 3) + return nv_rd32(exec->dev, 0x1002e0 + ((mr - 2) * 4)); + return 0; +} + +static void +mclk_mrs(struct nouveau_mem_exec_func *exec, int mr, u32 data) +{ + struct drm_nouveau_private *dev_priv = exec->dev->dev_private; + struct nv50_pm_state *info = exec->priv; + struct hwsq_ucode *hwsq = &info->mclk_hwsq; + + if (mr <= 1) { + if (dev_priv->vram_rank_B) + hwsq_wr32(hwsq, 0x1002c8 + ((mr - 0) * 4), data); + hwsq_wr32(hwsq, 0x1002c0 + ((mr - 0) * 4), data); + } else + if (mr <= 3) { + if (dev_priv->vram_rank_B) + hwsq_wr32(hwsq, 0x1002e8 + ((mr - 2) * 4), data); + hwsq_wr32(hwsq, 0x1002e0 + ((mr - 2) * 4), data); + } +} + +static void +mclk_clock_set(struct nouveau_mem_exec_func *exec) +{ + struct nv50_pm_state *info = exec->priv; + struct hwsq_ucode *hwsq = &info->mclk_hwsq; + u32 ctrl = nv_rd32(exec->dev, 0x004008); + + info->mmast = nv_rd32(exec->dev, 0x00c040); + info->mmast &= ~0xc0000000; /* get MCLK_2 from HREF */ + info->mmast |= 0x0000c000; /* use MCLK_2 as MPLL_BYPASS clock */ + + hwsq_wr32(hwsq, 0xc040, info->mmast); + hwsq_wr32(hwsq, 0x4008, ctrl | 0x00000200); /* bypass MPLL */ + if (info->mctrl & 0x80000000) + hwsq_wr32(hwsq, 0x400c, info->mcoef); + hwsq_wr32(hwsq, 0x4008, info->mctrl); +} + +static void +mclk_timing_set(struct nouveau_mem_exec_func *exec) +{ + struct drm_device *dev = exec->dev; + struct nv50_pm_state *info = exec->priv; + struct nouveau_pm_level *perflvl = info->perflvl; + struct hwsq_ucode *hwsq = &info->mclk_hwsq; + int i; + + for (i = 0; i < 9; i++) { + u32 reg = 0x100220 + (i * 4); + u32 val = nv_rd32(dev, reg); + if (val != perflvl->timing.reg[i]) + hwsq_wr32(hwsq, reg, perflvl->timing.reg[i]); + } +} + static int -calc_mclk(struct drm_device *dev, u32 freq, struct hwsq_ucode *hwsq) +calc_mclk(struct drm_device *dev, struct nouveau_pm_level *perflvl, + struct nv50_pm_state *info) { struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_mem_exec_func exec = { + .dev = dev, + .precharge = mclk_precharge, + .refresh = mclk_refresh, + .refresh_auto = mclk_refresh_auto, + .refresh_self = mclk_refresh_self, + .wait = mclk_wait, + .mrg = mclk_mrg, + .mrs = mclk_mrs, + .clock_set = mclk_clock_set, + .timing_set = mclk_timing_set, + .priv = info + }; + struct hwsq_ucode *hwsq = &info->mclk_hwsq; struct pll_lims pll; - u32 mast = nv_rd32(dev, 0x00c040); - u32 ctrl = nv_rd32(dev, 0x004008); - u32 coef = nv_rd32(dev, 0x00400c); - u32 orig = ctrl; u32 crtc_mask = 0; int N, M, P; int ret, i; /* use pcie refclock if possible, otherwise use mpll */ - ctrl &= ~0x81ff0200; - if (clk_same(freq, read_clk(dev, clk_src_href))) { - ctrl |= 0x00000200 | (pll.log2p_bias << 19); + info->mctrl = nv_rd32(dev, 0x004008); + info->mctrl &= ~0x81ff0200; + if (clk_same(perflvl->memory, read_clk(dev, clk_src_href))) { + info->mctrl |= 0x00000200 | (pll.log2p_bias << 19); } else { - ret = calc_pll(dev, 0x4008, &pll, freq, &N, &M, &P); + ret = calc_pll(dev, 0x4008, &pll, perflvl->memory, &N, &M, &P); if (ret == 0) return -EINVAL; - ctrl |= 0x80000000 | (P << 22) | (P << 16); - ctrl |= pll.log2p_bias << 19; - coef = (N << 8) | M; + info->mctrl |= 0x80000000 | (P << 22) | (P << 16); + info->mctrl |= pll.log2p_bias << 19; + info->mcoef = (N << 8) | M; } - mast &= ~0xc0000000; /* get MCLK_2 from HREF */ - mast |= 0x0000c000; /* use MCLK_2 as MPLL_BYPASS clock */ - /* determine active crtcs */ for (i = 0; i < 2; i++) { if (nv_rd32(dev, NV50_PDISPLAY_CRTC_C(i, CLOCK))) @@ -462,25 +586,10 @@ calc_mclk(struct drm_device *dev, u32 freq, struct hwsq_ucode *hwsq) hwsq_setf(hwsq, 0x10, 0); /* disable bus access */ hwsq_op5f(hwsq, 0x00, 0x01); /* no idea :s */ - /* prepare memory controller */ - hwsq_wr32(hwsq, 0x1002d4, 0x00000001); /* precharge banks and idle */ - hwsq_wr32(hwsq, 0x1002d0, 0x00000001); /* force refresh */ - hwsq_wr32(hwsq, 0x100210, 0x00000000); /* stop the automatic refresh */ - hwsq_wr32(hwsq, 0x1002dc, 0x00000001); /* start self refresh mode */ + ret = nouveau_mem_exec(&exec, perflvl); + if (ret) + return ret; - /* reclock memory */ - hwsq_wr32(hwsq, 0xc040, mast); - hwsq_wr32(hwsq, 0x4008, orig | 0x00000200); /* bypass MPLL */ - hwsq_wr32(hwsq, 0x400c, coef); - hwsq_wr32(hwsq, 0x4008, ctrl); - - /* restart memory controller */ - hwsq_wr32(hwsq, 0x1002d4, 0x00000001); /* precharge banks and idle */ - hwsq_wr32(hwsq, 0x1002dc, 0x00000000); /* stop self refresh mode */ - hwsq_wr32(hwsq, 0x100210, 0x80000000); /* restart automatic refresh */ - hwsq_usec(hwsq, 12); /* wait for the PLL to stabilize */ - - hwsq_usec(hwsq, 48); /* may be unnecessary: causes flickering */ hwsq_setf(hwsq, 0x10, 1); /* enable bus access */ hwsq_op5f(hwsq, 0x00, 0x00); /* no idea, reverse of 0x00, 0x01? */ if (dev_priv->chipset >= 0x92) @@ -506,6 +615,17 @@ nv50_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl) info = kmalloc(sizeof(*info), GFP_KERNEL); if (!info) return ERR_PTR(-ENOMEM); + info->perflvl = perflvl; + + /* memory: build hwsq ucode which we'll use to reclock memory. + * use pcie refclock if possible, otherwise use mpll */ + info->mclk_hwsq.len = 0; + if (perflvl->memory) { + ret = calc_mclk(dev, perflvl, info); + if (ret) + goto error; + info->mscript = perflvl->memscript; + } /* core: for the moment at least, always use nvpll */ clk = calc_pll(dev, 0x4028, &pll, perflvl->core, &N, &M, &P1); @@ -536,18 +656,6 @@ nv50_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl) info->scoef = (N << 8) | M; } - /* memory: build hwsq ucode which we'll use to reclock memory */ - info->mclk_hwsq.len = 0; - if (perflvl->memory) { - clk = calc_mclk(dev, perflvl->memory, &info->mclk_hwsq); - if (clk < 0) { - ret = clk; - goto error; - } - - info->mscript = perflvl->memscript; - } - /* vdec: avoid modifying xpll until we know exactly how the other * clock domains work, i suspect at least some of them can also be * tied to xpll... From e495d0d7e36298f76336fdc58685ac4cacd454ba Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 23 Jan 2012 13:22:58 +1000 Subject: [PATCH 23/57] drm/nv50/disp: more accurate function to determine active crtcs Signed-off-by: Ben Skeggs Signed-off-by: Martin Peres --- drivers/gpu/drm/nouveau/nv50_display.c | 23 +++++++++++++++++++++++ drivers/gpu/drm/nouveau/nv50_display.h | 2 ++ drivers/gpu/drm/nouveau/nv50_pm.c | 11 +++-------- 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index 7ba28e08ee31..ce440e2e58ca 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -50,6 +50,29 @@ nv50_sor_nr(struct drm_device *dev) return 4; } +u32 +nv50_display_active_crtcs(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + u32 mask = 0; + int i; + + if (dev_priv->chipset < 0x90 || + dev_priv->chipset == 0x92 || + dev_priv->chipset == 0xa0) { + for (i = 0; i < 2; i++) + mask |= nv_rd32(dev, NV50_PDISPLAY_SOR_MODE_CTRL_C(i)); + } else { + for (i = 0; i < 4; i++) + mask |= nv_rd32(dev, NV90_PDISPLAY_SOR_MODE_CTRL_C(i)); + } + + for (i = 0; i < 3; i++) + mask |= nv_rd32(dev, NV50_PDISPLAY_DAC_MODE_CTRL_C(i)); + + return mask & 3; +} + static int evo_icmd(struct drm_device *dev, int ch, u32 mthd, u32 data) { diff --git a/drivers/gpu/drm/nouveau/nv50_display.h b/drivers/gpu/drm/nouveau/nv50_display.h index 95874f7c043c..5d3dd14d2837 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.h +++ b/drivers/gpu/drm/nouveau/nv50_display.h @@ -74,6 +74,8 @@ void nv50_display_destroy(struct drm_device *dev); int nv50_crtc_blank(struct nouveau_crtc *, bool blank); int nv50_crtc_set_clock(struct drm_device *, int head, int pclk); +u32 nv50_display_active_crtcs(struct drm_device *); + int nv50_display_sync(struct drm_device *); int nv50_display_flip_next(struct drm_crtc *, struct drm_framebuffer *, struct nouveau_channel *chan); diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c index 17eb3277b1a0..109e473fd5f8 100644 --- a/drivers/gpu/drm/nouveau/nv50_pm.c +++ b/drivers/gpu/drm/nouveau/nv50_pm.c @@ -28,6 +28,7 @@ #include "nouveau_hw.h" #include "nouveau_pm.h" #include "nouveau_hwsq.h" +#include "nv50_display.h" enum clk_src { clk_src_crystal, @@ -535,6 +536,7 @@ calc_mclk(struct drm_device *dev, struct nouveau_pm_level *perflvl, struct nv50_pm_state *info) { struct drm_nouveau_private *dev_priv = dev->dev_private; + u32 crtc_mask = nv50_display_active_crtcs(dev); struct nouveau_mem_exec_func exec = { .dev = dev, .precharge = mclk_precharge, @@ -550,9 +552,8 @@ calc_mclk(struct drm_device *dev, struct nouveau_pm_level *perflvl, }; struct hwsq_ucode *hwsq = &info->mclk_hwsq; struct pll_lims pll; - u32 crtc_mask = 0; int N, M, P; - int ret, i; + int ret; /* use pcie refclock if possible, otherwise use mpll */ info->mctrl = nv_rd32(dev, 0x004008); @@ -569,12 +570,6 @@ calc_mclk(struct drm_device *dev, struct nouveau_pm_level *perflvl, info->mcoef = (N << 8) | M; } - /* determine active crtcs */ - for (i = 0; i < 2; i++) { - if (nv_rd32(dev, NV50_PDISPLAY_CRTC_C(i, CLOCK))) - crtc_mask |= (1 << i); - } - /* build the ucode which will reclock the memory for us */ hwsq_init(hwsq); if (crtc_mask) { From 496a73bbecb81e6753715995e4519d152f814667 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 24 Jan 2012 09:47:04 +1000 Subject: [PATCH 24/57] drm/nv50/pm: use hwsq for engine reclocking too Idea from Martin Peres, different implementation by me. v2: Martin Peres: - fix mast calculation Signed-off-by: Ben Skeggs Signed-off-by: Martin Peres --- drivers/gpu/drm/nouveau/nv50_pm.c | 187 ++++++++++++++++-------------- 1 file changed, 99 insertions(+), 88 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c index 109e473fd5f8..0f2632638c92 100644 --- a/drivers/gpu/drm/nouveau/nv50_pm.c +++ b/drivers/gpu/drm/nouveau/nv50_pm.c @@ -354,21 +354,12 @@ nv50_pm_clocks_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) struct nv50_pm_state { struct nouveau_pm_level *perflvl; - + struct hwsq_ucode eclk_hwsq; struct hwsq_ucode mclk_hwsq; u32 mscript; u32 mmast; u32 mctrl; u32 mcoef; - - u32 emast; - u32 nctrl; - u32 ncoef; - u32 sctrl; - u32 scoef; - - u32 amast; - u32 pdivs; }; static u32 @@ -598,10 +589,11 @@ nv50_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nv50_pm_state *info; + struct hwsq_ucode *hwsq; struct pll_lims pll; + u32 out, mast, divs, ctrl; int clk, ret = -EINVAL; int N, M, P1, P2; - u32 out; if (dev_priv->chipset == 0xaa || dev_priv->chipset == 0xac) @@ -622,41 +614,32 @@ nv50_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl) info->mscript = perflvl->memscript; } - /* core: for the moment at least, always use nvpll */ - clk = calc_pll(dev, 0x4028, &pll, perflvl->core, &N, &M, &P1); - if (clk == 0) - goto error; + divs = read_div(dev); + mast = info->mmast; - info->emast = 0x00000003; - info->nctrl = 0x80000000 | (P1 << 19) | (P1 << 16); - info->ncoef = (N << 8) | M; + /* start building HWSQ script for engine reclocking */ + hwsq = &info->eclk_hwsq; + hwsq_init(hwsq); + hwsq_setf(hwsq, 0x10, 0); /* disable bus access */ + hwsq_op5f(hwsq, 0x00, 0x01); /* wait for access disabled? */ - /* shader: tie to nvclk if possible, otherwise use spll. have to be - * very careful that the shader clock is at least twice the core, or - * some chipsets will be very unhappy. i expect most or all of these - * cases will be handled by tying to nvclk, but it's possible there's - * corners - */ - if (P1-- && perflvl->shader == (perflvl->core << 1)) { - info->emast |= 0x00000020; - info->sctrl = 0x00000000 | (P1 << 19) | (P1 << 16); - info->scoef = nv_rd32(dev, 0x004024); - } else { - clk = calc_pll(dev, 0x4020, &pll, perflvl->shader, &N, &M, &P1); - if (clk == 0) - goto error; - - info->emast |= 0x00000030; - info->sctrl = 0x80000000 | (P1 << 19) | (P1 << 16); - info->scoef = (N << 8) | M; + /* vdec/dom6: switch to "safe" clocks temporarily */ + if (perflvl->vdec) { + mast &= ~0x00000c00; + divs &= ~0x00000700; } + if (perflvl->dom6) { + mast &= ~0x0c000000; + divs &= ~0x00000007; + } + + hwsq_wr32(hwsq, 0x00c040, mast); + /* vdec: avoid modifying xpll until we know exactly how the other * clock domains work, i suspect at least some of them can also be * tied to xpll... */ - info->amast = nv_rd32(dev, 0x00c040); - info->pdivs = read_div(dev); if (perflvl->vdec) { /* see how close we can get using nvclk as a source */ clk = calc_div(perflvl->core, perflvl->vdec, &P1); @@ -669,16 +652,14 @@ nv50_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl) out = calc_div(out, perflvl->vdec, &P2); /* select whichever gets us closest */ - info->amast &= ~0x00000c00; - info->pdivs &= ~0x00000700; if (abs((int)perflvl->vdec - clk) <= abs((int)perflvl->vdec - out)) { if (dev_priv->chipset != 0x98) - info->amast |= 0x00000c00; - info->pdivs |= P1 << 8; + mast |= 0x00000c00; + divs |= P1 << 8; } else { - info->amast |= 0x00000800; - info->pdivs |= P2 << 8; + mast |= 0x00000800; + divs |= P2 << 8; } } @@ -686,21 +667,82 @@ nv50_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl) * of the host clock frequency */ if (perflvl->dom6) { - info->amast &= ~0x0c000000; if (clk_same(perflvl->dom6, read_clk(dev, clk_src_href))) { - info->amast |= 0x00000000; + mast |= 0x00000000; } else if (clk_same(perflvl->dom6, read_clk(dev, clk_src_hclk))) { - info->amast |= 0x08000000; + mast |= 0x08000000; } else { clk = read_clk(dev, clk_src_hclk) * 3; clk = calc_div(clk, perflvl->dom6, &P1); - info->amast |= 0x0c000000; - info->pdivs = (info->pdivs & ~0x00000007) | P1; + mast |= 0x0c000000; + divs |= P1; } } + /* vdec/dom6: complete switch to new clocks */ + switch (dev_priv->chipset) { + case 0x92: + case 0x94: + case 0x96: + hwsq_wr32(hwsq, 0x004800, divs); + break; + default: + hwsq_wr32(hwsq, 0x004700, divs); + break; + } + + hwsq_wr32(hwsq, 0x00c040, mast); + + /* core/shader: make sure sclk/nvclk are disconnected from their + * PLLs (nvclk to dom6, sclk to hclk) + */ + if (dev_priv->chipset < 0x92) + mast = (mast & ~0x001000b0) | 0x00100080; + else + mast = (mast & ~0x000000b3) | 0x00000081; + + hwsq_wr32(hwsq, 0x00c040, mast); + + /* core: for the moment at least, always use nvpll */ + clk = calc_pll(dev, 0x4028, &pll, perflvl->core, &N, &M, &P1); + if (clk == 0) + goto error; + + ctrl = nv_rd32(dev, 0x004028) & ~0xc03f0100; + mast &= ~0x00100000; + mast |= 3; + + hwsq_wr32(hwsq, 0x004028, 0x80000000 | (P1 << 19) | (P1 << 16) | ctrl); + hwsq_wr32(hwsq, 0x00402c, (N << 8) | M); + + /* shader: tie to nvclk if possible, otherwise use spll. have to be + * very careful that the shader clock is at least twice the core, or + * some chipsets will be very unhappy. i expect most or all of these + * cases will be handled by tying to nvclk, but it's possible there's + * corners + */ + ctrl = nv_rd32(dev, 0x004020) & ~0xc03f0100; + + if (P1-- && perflvl->shader == (perflvl->core << 1)) { + hwsq_wr32(hwsq, 0x004020, (P1 << 19) | (P1 << 16) | ctrl); + hwsq_wr32(hwsq, 0x00c040, 0x00000020 | mast); + } else { + clk = calc_pll(dev, 0x4020, &pll, perflvl->shader, &N, &M, &P1); + if (clk == 0) + goto error; + ctrl |= 0x80000000; + + hwsq_wr32(hwsq, 0x004020, (P1 << 19) | (P1 << 16) | ctrl); + hwsq_wr32(hwsq, 0x004024, (N << 8) | M); + hwsq_wr32(hwsq, 0x00c040, 0x00000030 | mast); + } + + hwsq_setf(hwsq, 0x10, 1); /* enable bus access */ + hwsq_op5f(hwsq, 0x00, 0x00); /* wait for access enabled? */ + hwsq_fini(hwsq); + return info; error: kfree(info); @@ -708,7 +750,7 @@ error: } static int -prog_mclk(struct drm_device *dev, struct hwsq_ucode *hwsq) +prog_hwsq(struct drm_device *dev, struct hwsq_ucode *hwsq) { struct drm_nouveau_private *dev_priv = dev->dev_private; u32 hwsq_data, hwsq_kick; @@ -748,20 +790,17 @@ prog_mclk(struct drm_device *dev, struct hwsq_ucode *hwsq) int nv50_pm_clocks_set(struct drm_device *dev, void *data) { - struct drm_nouveau_private *dev_priv = dev->dev_private; struct nv50_pm_state *info = data; struct bit_entry M; - int ret = 0; + int ret = -EBUSY; /* halt and idle execution engines */ nv_mask(dev, 0x002504, 0x00000001, 0x00000001); if (!nv_wait(dev, 0x002504, 0x00000010, 0x00000010)) - goto error; + goto resume; - /* memory: it is *very* important we change this first, the ucode - * we build in pre() now has hardcoded 0xc040 values, which can't - * change before we execute it or the engine clocks may end up - * messed up. + /* program memory clock, if necessary - must come before engine clock + * reprogramming due to how we construct the hwsq scripts in pre() */ if (info->mclk_hwsq.len) { /* execute some scripts that do ??? from the vbios.. */ @@ -775,42 +814,14 @@ nv50_pm_clocks_set(struct drm_device *dev, void *data) nouveau_bios_init_exec(dev, info->mscript); } - ret = prog_mclk(dev, &info->mclk_hwsq); + ret = prog_hwsq(dev, &info->mclk_hwsq); if (ret) goto resume; } - /* reclock vdec/dom6 */ - nv_mask(dev, 0x00c040, 0x00000c00, 0x00000000); - switch (dev_priv->chipset) { - case 0x92: - case 0x94: - case 0x96: - nv_mask(dev, 0x004800, 0x00000707, info->pdivs); - break; - default: - nv_mask(dev, 0x004700, 0x00000707, info->pdivs); - break; - } - nv_mask(dev, 0x00c040, 0x0c000c00, info->amast); + /* program engine clocks */ + ret = prog_hwsq(dev, &info->eclk_hwsq); - /* core/shader: make sure sclk/nvclk are disconnected from their - * plls (nvclk to dom6, sclk to hclk), modify the plls, and - * reconnect sclk/nvclk to their new clock source - */ - if (dev_priv->chipset < 0x92) - nv_mask(dev, 0x00c040, 0x001000b0, 0x00100080); /* grrr! */ - else - nv_mask(dev, 0x00c040, 0x000000b3, 0x00000081); - nv_mask(dev, 0x004020, 0xc03f0100, info->sctrl); - nv_wr32(dev, 0x004024, info->scoef); - nv_mask(dev, 0x004028, 0xc03f0100, info->nctrl); - nv_wr32(dev, 0x00402c, info->ncoef); - nv_mask(dev, 0x00c040, 0x00100033, info->emast); - - goto resume; -error: - ret = -EBUSY; resume: nv_mask(dev, 0x002504, 0x00000001, 0x00000000); kfree(info); From c57ebf5ef3588d21031f12e39131d79071269845 Mon Sep 17 00:00:00 2001 From: Martin Peres Date: Mon, 9 Jan 2012 15:23:10 +1000 Subject: [PATCH 25/57] drm/nv50/pm: wait for all fifo-connected engines to idle before reclocking Signed-off-by: Martin Peres Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nv50_pm.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c index 0f2632638c92..e677310c8f57 100644 --- a/drivers/gpu/drm/nouveau/nv50_pm.c +++ b/drivers/gpu/drm/nouveau/nv50_pm.c @@ -798,6 +798,8 @@ nv50_pm_clocks_set(struct drm_device *dev, void *data) nv_mask(dev, 0x002504, 0x00000001, 0x00000001); if (!nv_wait(dev, 0x002504, 0x00000010, 0x00000010)) goto resume; + if (!nv_wait(dev, 0x00251c, 0x0000003f, 0x0000003f)) + goto resume; /* program memory clock, if necessary - must come before engine clock * reprogramming due to how we construct the hwsq scripts in pre() From 1a7287ea6f37c930a092123f6584b0b24cfe2d13 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 24 Jan 2012 10:24:05 +1000 Subject: [PATCH 26/57] drm/nouveau/pm: track mr2 for gddr3 There's some "extended" GDDR3 chipsets out there with EMRS2 settings that change the layout of MRS/EMRS1 bitmaps.. Sigh.. Still need to track down how exactly we're supposed to handle this. Signed-off-by: Ben Skeggs Signed-off-by: Martin Peres --- drivers/gpu/drm/nouveau/nouveau_mem.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c index cf82f17cf03a..3d43098de25b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mem.c +++ b/drivers/gpu/drm/nouveau/nouveau_mem.c @@ -766,8 +766,10 @@ nouveau_mem_gddr3_mr(struct drm_device *dev, u32 freq, t->mr[1] = (boot->mr[1] & 0x100f40) | t->drive_strength | (t->odt << 2) | (nv_mem_wr_lut_gddr3[e->tWR] & 0xf) << 4; + t->mr[2] = boot->mr[2]; - NV_DEBUG(dev, "(%u) MR: %08x %08x", t->id, t->mr[0], t->mr[1]); + NV_DEBUG(dev, "(%u) MR: %08x %08x %08x", t->id, + t->mr[0], t->mr[1], t->mr[2]); return 0; } @@ -963,6 +965,7 @@ nouveau_mem_exec(struct nouveau_mem_exec_func *exec, /* fetch current MRs */ switch (dev_priv->vram_type) { + case NV_MEM_TYPE_GDDR3: case NV_MEM_TYPE_DDR3: mr[2] = exec->mrg(exec, 2); default: From 0ce71415587b1fd56f67294ac46f860b8b679b95 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 24 Jan 2012 11:03:14 +1000 Subject: [PATCH 27/57] drm/nv50: fix detection of second vram rank Goes a long way to correcting NVS295 memory reclocking issues. Signed-off-by: Ben Skeggs Signed-off-by: Martin Peres --- drivers/gpu/drm/nouveau/nv50_vram.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/nouveau/nv50_vram.c b/drivers/gpu/drm/nouveau/nv50_vram.c index a52b9a51e010..9ed9ae397d75 100644 --- a/drivers/gpu/drm/nouveau/nv50_vram.c +++ b/drivers/gpu/drm/nouveau/nv50_vram.c @@ -207,7 +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_rank_B = !!(nv_rd32(dev, 0x100200) & 0x4); dev_priv->vram_size = nv_rd32(dev, 0x10020c); dev_priv->vram_size |= (dev_priv->vram_size & 0xff) << 32; dev_priv->vram_size &= 0xffffffff00ULL; From a9bc247cbbc6310d2aefe4db0a6e92defde4426b Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 24 Jan 2012 11:26:40 +1000 Subject: [PATCH 28/57] drm/nouveau/pm: detect when we need dll disabled for gddr3 Fixes minor flickering on NVS295 when at perflvl 0. Signed-off-by: Ben Skeggs Signed-off-by: Martin Peres --- drivers/gpu/drm/nouveau/nouveau_mem.c | 10 ++++++++++ drivers/gpu/drm/nouveau/nouveau_perf.c | 2 +- drivers/gpu/drm/nouveau/nouveau_pm.h | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c index 3d43098de25b..f0e2a439f26a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mem.c +++ b/drivers/gpu/drm/nouveau/nouveau_mem.c @@ -735,6 +735,8 @@ nouveau_mem_gddr3_mr(struct drm_device *dev, u32 freq, struct nouveau_pm_memtiming *boot, struct nouveau_pm_memtiming *t) { + u8 rver, rlen, *ramcfg = nouveau_perf_ramcfg(dev, freq, &rver, &rlen); + if (len < 15) { t->drive_strength = boot->drive_strength; t->odt = boot->odt; @@ -763,9 +765,17 @@ nouveau_mem_gddr3_mr(struct drm_device *dev, u32 freq, /* 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; + if (ramcfg && rver == 0x00) { + /* DLL enable/disable */ + t->mr[1] &= ~0x00000040; + if (ramcfg[3] & 0x08) + t->mr[1] |= 0x00000040; + } + t->mr[2] = boot->mr[2]; NV_DEBUG(dev, "(%u) MR: %08x %08x %08x", t->id, diff --git a/drivers/gpu/drm/nouveau/nouveau_perf.c b/drivers/gpu/drm/nouveau/nouveau_perf.c index bd6071f10008..cc8beb8e2f06 100644 --- a/drivers/gpu/drm/nouveau/nouveau_perf.c +++ b/drivers/gpu/drm/nouveau/nouveau_perf.c @@ -139,7 +139,7 @@ nouveau_perf_rammap(struct drm_device *dev, u32 freq, return NULL; } -static u8 * +u8 * nouveau_perf_ramcfg(struct drm_device *dev, u32 freq, u8 *ver, u8 *len) { struct drm_nouveau_private *dev_priv = dev->dev_private; diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.h b/drivers/gpu/drm/nouveau/nouveau_pm.h index 5a61f05f49da..1a8ae15803a0 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.h +++ b/drivers/gpu/drm/nouveau/nouveau_pm.h @@ -60,6 +60,7 @@ int nouveau_voltage_gpio_set(struct drm_device *, int voltage); 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); +u8 *nouveau_perf_ramcfg(struct drm_device *, u32 freq, u8 *ver, u8 *len); /* nouveau_mem.c */ void nouveau_mem_timing_init(struct drm_device *); From b830973b68895813b911fb04626d907744e7d7a2 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 24 Jan 2012 13:39:56 +1000 Subject: [PATCH 29/57] drm/nouveau/pm: fix dll off -> dll on transitions Signed-off-by: Ben Skeggs Signed-off-by: Martin Peres --- drivers/gpu/drm/nouveau/nouveau_mem.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c index f0e2a439f26a..e4ba4abca31a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mem.c +++ b/drivers/gpu/drm/nouveau/nouveau_mem.c @@ -1016,7 +1016,8 @@ nouveau_mem_exec(struct nouveau_mem_exec_func *exec, } if (mr[1] != info->mr[1]) { - exec->mrs (exec, 1, info->mr[1]); + /* need to keep DLL off until later, at least on GDDR3 */ + exec->mrs (exec, 1, info->mr[1] | (mr[1] & mr1_dlloff)); exec->wait(exec, tMRD); } @@ -1028,8 +1029,12 @@ nouveau_mem_exec(struct nouveau_mem_exec_func *exec, /* update PFB timing registers */ exec->timing_set(exec); - /* DLL reset */ + /* DLL (enable + ) reset */ if (!(info->mr[1] & mr1_dlloff)) { + if (mr[1] & mr1_dlloff) { + exec->mrs (exec, 1, info->mr[1]); + exec->wait(exec, tMRD); + } exec->mrs (exec, 0, info->mr[0] | 0x00000100); exec->wait(exec, tMRD); exec->mrs (exec, 0, info->mr[0] | 0x00000000); From 8d7bb400638906075c38cb07891993cf95076aa7 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 24 Jan 2012 15:59:07 +1000 Subject: [PATCH 30/57] 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 | 137 +++++++++++++++++-------- drivers/gpu/drm/nouveau/nouveau_pm.h | 2 + 4 files changed, 119 insertions(+), 45 deletions(-) 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; + strncpy(string, profile, sizeof(string)); + if ((ptr = strchr(string, '\n'))) + *ptr = '\0'; - for (i = 0; i < pm->nr_perflvl; i++) { - if (pm->perflvl[i].id == pl) { - perflvl = &pm->perflvl[i]; - break; - } - } + ptr = strsep(&cur, ","); + if (ptr) + ac = profile_find(dev, ptr); - if (!perflvl) - return -EINVAL; - } + ptr = strsep(&cur, ","); + if (ptr) + dc = profile_find(dev, ptr); + else + dc = ac; - 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"); - } + if (ac == NULL || dc == NULL) + return -EINVAL; - return ret; + 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 *); From 25c53c1068a804c6b51f86e937cebab6274dc056 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 24 Jan 2012 18:03:25 +1000 Subject: [PATCH 31/57] 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(+) 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); From 070be296b65d120c2fef58a5435f159ae32c4995 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 24 Jan 2012 18:30:10 +1000 Subject: [PATCH 32/57] drm/nouveau/mem: handle dll_off for ddr2/ddr3 Signed-off-by: Ben Skeggs Signed-off-by: Martin Peres --- drivers/gpu/drm/nouveau/nouveau_mem.c | 34 ++++++++++++++++++--------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c index e4ba4abca31a..8ff0027302a6 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mem.c +++ b/drivers/gpu/drm/nouveau/nouveau_mem.c @@ -735,8 +735,6 @@ nouveau_mem_gddr3_mr(struct drm_device *dev, u32 freq, struct nouveau_pm_memtiming *boot, struct nouveau_pm_memtiming *t) { - u8 rver, rlen, *ramcfg = nouveau_perf_ramcfg(dev, freq, &rver, &rlen); - if (len < 15) { t->drive_strength = boot->drive_strength; t->odt = boot->odt; @@ -765,17 +763,9 @@ nouveau_mem_gddr3_mr(struct drm_device *dev, u32 freq, /* 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; - if (ramcfg && rver == 0x00) { - /* DLL enable/disable */ - t->mr[1] &= ~0x00000040; - if (ramcfg[3] & 0x08) - t->mr[1] |= 0x00000040; - } - t->mr[2] = boot->mr[2]; NV_DEBUG(dev, "(%u) MR: %08x %08x %08x", t->id, @@ -832,7 +822,7 @@ nouveau_mem_timing_calc(struct drm_device *dev, u32 freq, struct nouveau_pm_engine *pm = &dev_priv->engine.pm; struct nouveau_pm_memtiming *boot = &pm->boot.timing; struct nouveau_pm_tbl_entry *e; - u8 ver, len, *ptr; + u8 ver, len, *ptr, *ramcfg; int ret; ptr = nouveau_perf_timing(dev, freq, &ver, &len); @@ -874,6 +864,28 @@ nouveau_mem_timing_calc(struct drm_device *dev, u32 freq, break; default: ret = -EINVAL; + break; + } + + ramcfg = nouveau_perf_ramcfg(dev, freq, &ver, &len); + if (ramcfg) { + int dll_off; + + if (ver == 0x00) + dll_off = !!(ramcfg[3] & 0x04); + else + dll_off = !!(ramcfg[2] & 0x40); + + switch (dev_priv->vram_type) { + case NV_MEM_TYPE_GDDR3: + t->mr[1] &= ~0x00000040; + t->mr[1] |= 0x00000040 * dll_off; + break; + default: + t->mr[1] &= ~0x00000001; + t->mr[1] |= 0x00000001 * dll_off; + break; + } } return ret; From 4abb410a13eec3f49863be2e84ad062fef00dac0 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 12 Jan 2012 16:17:16 +1000 Subject: [PATCH 33/57] drm/nouveau: recognise DCB connector type for DP+DVI+VGA DMS-59 Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_bios.h | 2 ++ drivers/gpu/drm/nouveau/nouveau_connector.c | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.h b/drivers/gpu/drm/nouveau/nouveau_bios.h index a37c31e358aa..0fc4e21c748b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.h +++ b/drivers/gpu/drm/nouveau/nouveau_bios.h @@ -75,6 +75,8 @@ enum dcb_connector_type { DCB_CONNECTOR_eDP = 0x47, DCB_CONNECTOR_HDMI_0 = 0x60, DCB_CONNECTOR_HDMI_1 = 0x61, + DCB_CONNECTOR_DMS59_DP0 = 0x64, + DCB_CONNECTOR_DMS59_DP1 = 0x65, DCB_CONNECTOR_NONE = 0xff }; diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index f3ce34be082a..cdae563be5bd 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -858,6 +858,8 @@ drm_conntype_from_dcb(enum dcb_connector_type dcb) case DCB_CONNECTOR_DVI_D : return DRM_MODE_CONNECTOR_DVID; case DCB_CONNECTOR_LVDS : case DCB_CONNECTOR_LVDS_SPWG: return DRM_MODE_CONNECTOR_LVDS; + case DCB_CONNECTOR_DMS59_DP0: + case DCB_CONNECTOR_DMS59_DP1: case DCB_CONNECTOR_DP : return DRM_MODE_CONNECTOR_DisplayPort; case DCB_CONNECTOR_eDP : return DRM_MODE_CONNECTOR_eDP; case DCB_CONNECTOR_HDMI_0 : @@ -1002,7 +1004,9 @@ nouveau_connector_create(struct drm_device *dev, int index) nv_connector->type == DCB_CONNECTOR_DVI_I || nv_connector->type == DCB_CONNECTOR_HDMI_0 || nv_connector->type == DCB_CONNECTOR_HDMI_1 || - nv_connector->type == DCB_CONNECTOR_DP)) { + nv_connector->type == DCB_CONNECTOR_DP || + nv_connector->type == DCB_CONNECTOR_DMS59_DP0 || + nv_connector->type == DCB_CONNECTOR_DMS59_DP1)) { drm_connector_attach_property(connector, disp->underscan_property, UNDERSCAN_OFF); From 990449c77cafb77e7468722262c049675ab03e30 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 12 Jan 2012 15:34:54 +1000 Subject: [PATCH 34/57] drm/nv50-nvc0/vm: support unsnooped system memory v2 (Emil Velikov ): - Fixed a regression on certain nv50 IGP due to not passing the correct target type to nv50_vm_addr() Signed-off-by: Ben Skeggs Signed-off-by: Emil Velikov Tested-by: Johannes Obermayr --- drivers/gpu/drm/nouveau/nouveau_drv.h | 1 + drivers/gpu/drm/nouveau/nv50_vm.c | 29 ++++++++++++--------------- drivers/gpu/drm/nouveau/nvc0_vm.c | 4 +++- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 63e77fc282f2..3a69a4aabd35 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -1759,6 +1759,7 @@ nv44_graph_class(struct drm_device *dev) #define NV_MEM_ACCESS_RW (NV_MEM_ACCESS_RO | NV_MEM_ACCESS_WO) #define NV_MEM_ACCESS_SYS 4 #define NV_MEM_ACCESS_VM 8 +#define NV_MEM_ACCESS_NOSNOOP 16 #define NV_MEM_TARGET_VRAM 0 #define NV_MEM_TARGET_PCI 1 diff --git a/drivers/gpu/drm/nouveau/nv50_vm.c b/drivers/gpu/drm/nouveau/nv50_vm.c index 6f38ceae3aa4..44fbac9c7d93 100644 --- a/drivers/gpu/drm/nouveau/nv50_vm.c +++ b/drivers/gpu/drm/nouveau/nv50_vm.c @@ -57,27 +57,15 @@ nv50_vm_map_pgt(struct nouveau_gpuobj *pgd, u32 pde, } static inline u64 -nv50_vm_addr(struct nouveau_vma *vma, u64 phys, u32 memtype, u32 target) +vm_addr(struct nouveau_vma *vma, u64 phys, u32 memtype, u32 target) { - struct drm_nouveau_private *dev_priv = vma->vm->dev->dev_private; - phys |= 1; /* present */ phys |= (u64)memtype << 40; - - /* IGPs don't have real VRAM, re-target to stolen system memory */ - if (target == 0 && dev_priv->vram_sys_base) { - phys += dev_priv->vram_sys_base; - target = 3; - } - phys |= target << 4; - if (vma->access & NV_MEM_ACCESS_SYS) phys |= (1 << 6); - if (!(vma->access & NV_MEM_ACCESS_WO)) phys |= (1 << 3); - return phys; } @@ -85,11 +73,19 @@ void nv50_vm_map(struct nouveau_vma *vma, struct nouveau_gpuobj *pgt, struct nouveau_mem *mem, u32 pte, u32 cnt, u64 phys, u64 delta) { + struct drm_nouveau_private *dev_priv = vma->vm->dev->dev_private; u32 comp = (mem->memtype & 0x180) >> 7; - u32 block; + u32 block, target; int i; - phys = nv50_vm_addr(vma, phys, mem->memtype, 0); + /* IGPs don't have real VRAM, re-target to stolen system memory */ + target = 0; + if (dev_priv->vram_sys_base) { + phys += dev_priv->vram_sys_base; + target = 3; + } + + phys = vm_addr(vma, phys, mem->memtype, target); pte <<= 3; cnt <<= 3; @@ -125,9 +121,10 @@ void nv50_vm_map_sg(struct nouveau_vma *vma, struct nouveau_gpuobj *pgt, struct nouveau_mem *mem, u32 pte, u32 cnt, dma_addr_t *list) { + u32 target = (vma->access & NV_MEM_ACCESS_NOSNOOP) ? 3 : 2; pte <<= 3; while (cnt--) { - u64 phys = nv50_vm_addr(vma, (u64)*list++, mem->memtype, 2); + u64 phys = vm_addr(vma, (u64)*list++, mem->memtype, target); nv_wo32(pgt, pte + 0, lower_32_bits(phys)); nv_wo32(pgt, pte + 4, upper_32_bits(phys)); pte += 8; diff --git a/drivers/gpu/drm/nouveau/nvc0_vm.c b/drivers/gpu/drm/nouveau/nvc0_vm.c index 9e352944a35a..30d2bd58828f 100644 --- a/drivers/gpu/drm/nouveau/nvc0_vm.c +++ b/drivers/gpu/drm/nouveau/nvc0_vm.c @@ -77,9 +77,11 @@ void nvc0_vm_map_sg(struct nouveau_vma *vma, struct nouveau_gpuobj *pgt, struct nouveau_mem *mem, u32 pte, u32 cnt, dma_addr_t *list) { + u32 target = (vma->access & NV_MEM_ACCESS_NOSNOOP) ? 7 : 5; + pte <<= 3; while (cnt--) { - u64 phys = nvc0_vm_addr(vma, *list++, mem->memtype, 5); + u64 phys = nvc0_vm_addr(vma, *list++, mem->memtype, target); nv_wo32(pgt, pte + 0, lower_32_bits(phys)); nv_wo32(pgt, pte + 4, upper_32_bits(phys)); pte += 8; From df26bc9c320602539b1b5b3d85786e4c8de7bf43 Mon Sep 17 00:00:00 2001 From: Christoph Bumiller Date: Sat, 21 Jan 2012 23:13:26 +0100 Subject: [PATCH 35/57] drm/nv50/display: expose color vibrance control Signed-off-by: Christoph Bumiller Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_connector.c | 23 +++++++++++ drivers/gpu/drm/nouveau/nouveau_crtc.h | 3 ++ drivers/gpu/drm/nouveau/nouveau_display.c | 14 +++++++ drivers/gpu/drm/nouveau/nouveau_drv.h | 3 ++ drivers/gpu/drm/nouveau/nv50_crtc.c | 42 ++++++++++++++++++++- drivers/gpu/drm/nouveau/nv50_evo.h | 3 +- 6 files changed, 85 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index cdae563be5bd..9f9d50dbca7f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -519,6 +519,19 @@ nouveau_connector_set_property(struct drm_connector *connector, return nv_crtc->set_dither(nv_crtc, true); } + if (nv_crtc && nv_crtc->set_color_vibrance) { + /* Hue */ + if (property == disp->vibrant_hue_property) { + nv_crtc->vibrant_hue = value - 90; + return nv_crtc->set_color_vibrance(nv_crtc, true); + } + /* Saturation */ + if (property == disp->color_vibrance_property) { + nv_crtc->color_vibrance = value - 100; + return nv_crtc->set_color_vibrance(nv_crtc, true); + } + } + if (nv_encoder && nv_encoder->dcb->type == OUTPUT_TV) return get_slave_funcs(encoder)->set_property( encoder, connector, property, value); @@ -1018,6 +1031,16 @@ nouveau_connector_create(struct drm_device *dev, int index) 0); } + /* Add hue and saturation options */ + if (disp->vibrant_hue_property) + drm_connector_attach_property(connector, + disp->vibrant_hue_property, + 90); + if (disp->color_vibrance_property) + drm_connector_attach_property(connector, + disp->color_vibrance_property, + 150); + switch (nv_connector->type) { case DCB_CONNECTOR_VGA: if (dev_priv->card_type >= NV_50) { diff --git a/drivers/gpu/drm/nouveau/nouveau_crtc.h b/drivers/gpu/drm/nouveau/nouveau_crtc.h index 686f6b4a1da3..e6d0d1eb0133 100644 --- a/drivers/gpu/drm/nouveau/nouveau_crtc.h +++ b/drivers/gpu/drm/nouveau/nouveau_crtc.h @@ -35,6 +35,8 @@ struct nouveau_crtc { uint32_t dpms_saved_fp_control; uint32_t fp_users; int saturation; + int color_vibrance; + int vibrant_hue; int sharpness; int last_dpms; @@ -67,6 +69,7 @@ struct nouveau_crtc { int (*set_dither)(struct nouveau_crtc *crtc, bool update); int (*set_scale)(struct nouveau_crtc *crtc, bool update); + int (*set_color_vibrance)(struct nouveau_crtc *crtc, bool update); }; static inline struct nouveau_crtc *nouveau_crtc(struct drm_crtc *crtc) diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 5565e5056ba1..35acc92f647b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -286,6 +286,20 @@ nouveau_display_create(struct drm_device *dev) disp->underscan_vborder_property = drm_property_create_range(dev, 0, "underscan vborder", 0, 128); + if (gen == 1) { + disp->vibrant_hue_property = + drm_property_create(dev, DRM_MODE_PROP_RANGE, + "vibrant hue", 2); + disp->vibrant_hue_property->values[0] = 0; + disp->vibrant_hue_property->values[1] = 180; /* -90..+90 */ + + disp->color_vibrance_property = + drm_property_create(dev, DRM_MODE_PROP_RANGE, + "color vibrance", 2); + disp->color_vibrance_property->values[0] = 0; + disp->color_vibrance_property->values[1] = 200; /* -100..+100 */ + } + dev->mode_config.funcs = (void *)&nouveau_mode_config_funcs; dev->mode_config.fb_base = pci_resource_start(dev->pdev, 1); diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 3a69a4aabd35..4bb6aeec91f6 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -406,6 +406,9 @@ struct nouveau_display_engine { struct drm_property *underscan_property; struct drm_property *underscan_hborder_property; struct drm_property *underscan_vborder_property; + /* not really hue and saturation: */ + struct drm_property *vibrant_hue_property; + struct drm_property *color_vibrance_property; }; struct nouveau_gpio_engine { diff --git a/drivers/gpu/drm/nouveau/nv50_crtc.c b/drivers/gpu/drm/nouveau/nv50_crtc.c index 8f6c2ace3adf..701b927998bf 100644 --- a/drivers/gpu/drm/nouveau/nv50_crtc.c +++ b/drivers/gpu/drm/nouveau/nv50_crtc.c @@ -170,6 +170,41 @@ nv50_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool update) return ret; } +static int +nv50_crtc_set_color_vibrance(struct nouveau_crtc *nv_crtc, bool update) +{ + struct drm_device *dev = nv_crtc->base.dev; + struct nouveau_channel *evo = nv50_display(dev)->master; + int ret; + int adj; + u32 hue, vib; + + NV_DEBUG_KMS(dev, "vibrance = %i, hue = %i\n", + nv_crtc->color_vibrance, nv_crtc->vibrant_hue); + + ret = RING_SPACE(evo, 2 + (update ? 2 : 0)); + if (ret) { + NV_ERROR(dev, "no space while setting color vibrance\n"); + return ret; + } + + adj = (nv_crtc->color_vibrance > 0) ? 50 : 0; + vib = ((nv_crtc->color_vibrance * 2047 + adj) / 100) & 0xfff; + + hue = ((nv_crtc->vibrant_hue * 2047) / 100) & 0xfff; + + BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, COLOR_CTRL), 1); + OUT_RING (evo, (hue << 20) | (vib << 8)); + + if (update) { + BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1); + OUT_RING (evo, 0); + FIRE_RING (evo); + } + + return 0; +} + struct nouveau_connector * nouveau_crtc_connector_get(struct nouveau_crtc *nv_crtc) { @@ -577,8 +612,6 @@ nv50_crtc_do_mode_set_base(struct drm_crtc *crtc, OUT_RING (evo, fb->base.depth == 8 ? NV50_EVO_CRTC_CLUT_MODE_OFF : NV50_EVO_CRTC_CLUT_MODE_ON); - BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, COLOR_CTRL), 1); - OUT_RING (evo, NV50_EVO_CRTC_COLOR_CTRL_COLOR); BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, FB_POS), 1); OUT_RING (evo, (y << 16) | x); @@ -661,6 +694,7 @@ nv50_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *umode, nv_crtc->set_dither(nv_crtc, false); nv_crtc->set_scale(nv_crtc, false); + nv_crtc->set_color_vibrance(nv_crtc, false); return nv50_crtc_do_mode_set_base(crtc, old_fb, x, y, false); } @@ -721,6 +755,9 @@ nv50_crtc_create(struct drm_device *dev, int index) if (!nv_crtc) return -ENOMEM; + nv_crtc->color_vibrance = 50; + nv_crtc->vibrant_hue = 0; + /* Default CLUT parameters, will be activated on the hw upon * first mode set. */ @@ -751,6 +788,7 @@ nv50_crtc_create(struct drm_device *dev, int index) /* set function pointers */ nv_crtc->set_dither = nv50_crtc_set_dither; nv_crtc->set_scale = nv50_crtc_set_scale; + nv_crtc->set_color_vibrance = nv50_crtc_set_color_vibrance; drm_crtc_init(dev, &nv_crtc->base, &nv50_crtc_funcs); drm_crtc_helper_add(&nv_crtc->base, &nv50_crtc_helper_funcs); diff --git a/drivers/gpu/drm/nouveau/nv50_evo.h b/drivers/gpu/drm/nouveau/nv50_evo.h index 3860ca62cb19..771d879bc834 100644 --- a/drivers/gpu/drm/nouveau/nv50_evo.h +++ b/drivers/gpu/drm/nouveau/nv50_evo.h @@ -104,7 +104,8 @@ #define NV50_EVO_CRTC_SCALE_CTRL_INACTIVE 0x00000000 #define NV50_EVO_CRTC_SCALE_CTRL_ACTIVE 0x00000009 #define NV50_EVO_CRTC_COLOR_CTRL 0x000008a8 -#define NV50_EVO_CRTC_COLOR_CTRL_COLOR 0x00040000 +#define NV50_EVO_CRTC_COLOR_CTRL_VIBRANCE 0x000fff00 +#define NV50_EVO_CRTC_COLOR_CTRL_HUE 0xfff00000 #define NV50_EVO_CRTC_FB_POS 0x000008c0 #define NV50_EVO_CRTC_REAL_RES 0x000008c8 #define NV50_EVO_CRTC_SCALE_CENTER_OFFSET 0x000008d4 From 950c44b6ddc0dfb44e59fdb031e09567a1b391c0 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Wed, 1 Feb 2012 09:05:42 +1000 Subject: [PATCH 36/57] drm/nvc0/fb: detect presense of second rank Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvc0_vram.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/nouveau/nvc0_vram.c b/drivers/gpu/drm/nouveau/nvc0_vram.c index fd687ee888de..82b51b2506ab 100644 --- a/drivers/gpu/drm/nouveau/nvc0_vram.c +++ b/drivers/gpu/drm/nouveau/nvc0_vram.c @@ -116,6 +116,7 @@ nvc0_vram_init(struct drm_device *dev) NV_DEBUG(dev, "parts 0x%08x bcast_mem_amount 0x%08x\n", parts, bsize); dev_priv->vram_type = nouveau_mem_vbios_type(dev); + dev_priv->vram_rank_B = !!(nv_rd32(dev, 0x10f200) & 0x00000004); /* read amount of vram attached to each memory controller */ part = 0; From 7d3a766b6aa4e293e72bfd6add477f05ac7fdf5a Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Wed, 1 Feb 2012 15:17:07 +1000 Subject: [PATCH 37/57] drm/nouveau/pm: init only after display subsystem has been created This patch fixes an oops cause by pm_trigger accessing the (uninitialised) crtc list. Reported-by: Roy Spliet Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_state.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index a36386c00d9b..9d6c3ff22e81 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -632,8 +632,6 @@ nouveau_card_init(struct drm_device *dev) if (ret) goto out_ttmvram; - nouveau_pm_init(dev); - if (!dev_priv->noaccel) { switch (dev_priv->card_type) { case NV_04: @@ -738,11 +736,12 @@ nouveau_card_init(struct drm_device *dev) goto out_irq; nouveau_backlight_init(dev); + nouveau_pm_init(dev); if (dev_priv->eng[NVOBJ_ENGINE_GR]) { ret = nouveau_fence_init(dev); if (ret) - goto out_disp; + goto out_pm; ret = nouveau_channel_alloc(dev, &dev_priv->channel, NULL, NvDmaFB, NvDmaTT); @@ -766,7 +765,8 @@ out_chan: nouveau_channel_put_unlocked(&dev_priv->channel); out_fence: nouveau_fence_fini(dev); -out_disp: +out_pm: + nouveau_pm_fini(dev); nouveau_backlight_exit(dev); nouveau_display_destroy(dev); out_irq: @@ -783,7 +783,6 @@ out_engine: dev_priv->eng[e]->destroy(dev,e ); } } - nouveau_pm_fini(dev); nouveau_mem_gart_fini(dev); out_ttmvram: nouveau_mem_vram_fini(dev); @@ -826,6 +825,7 @@ static void nouveau_card_takedown(struct drm_device *dev) nouveau_fence_fini(dev); } + nouveau_pm_fini(dev); nouveau_backlight_exit(dev); nouveau_display_destroy(dev); @@ -854,8 +854,6 @@ static void nouveau_card_takedown(struct drm_device *dev) engine->instmem.takedown(dev); nouveau_gpuobj_takedown(dev); - nouveau_pm_fini(dev); - nouveau_gpio_destroy(dev); engine->vram.takedown(dev); engine->fb.takedown(dev); From 84ddfda6d4765e410981edeef81c9b5a5a1ce87b Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 2 Feb 2012 09:33:05 +1000 Subject: [PATCH 38/57] drm/nouveau/mxm: call mxmi to determine revision before calling mxms There's a HP laptop out there where the MXM version in the VBIOS doesn't match what the ACPI implementation is expecting. These tables will accept 0x00 to MXMS to return latest version, but *only* if MXMI has been called first.. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_mxm.c | 39 ++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_mxm.c b/drivers/gpu/drm/nouveau/nouveau_mxm.c index e5a64f0f4cb7..07d0d1e03690 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mxm.c +++ b/drivers/gpu/drm/nouveau/nouveau_mxm.c @@ -582,6 +582,35 @@ mxm_shadow_dsm(struct drm_device *dev, u8 version) #define WMI_WMMX_GUID "F6CB5C3C-9CAE-4EBD-B577-931EA32A2CC0" +static u8 +wmi_wmmx_mxmi(struct drm_device *dev, u8 version) +{ + u32 mxmi_args[] = { 0x494D584D /* MXMI */, version, 0 }; + struct acpi_buffer args = { sizeof(mxmi_args), mxmi_args }; + struct acpi_buffer retn = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + acpi_status status; + + status = wmi_evaluate_method(WMI_WMMX_GUID, 0, 0, &args, &retn); + if (ACPI_FAILURE(status)) { + MXM_DBG(dev, "WMMX MXMI returned %d\n", status); + return 0x00; + } + + obj = retn.pointer; + if (obj->type == ACPI_TYPE_INTEGER) { + version = obj->integer.value; + MXM_DBG(dev, "WMMX MXMI version %d.%d\n", + (version >> 4), version & 0x0f); + } else { + version = 0; + MXM_DBG(dev, "WMMX MXMI returned non-integer\n"); + } + + kfree(obj); + return version; +} + static bool mxm_shadow_wmi(struct drm_device *dev, u8 version) { @@ -592,7 +621,15 @@ mxm_shadow_wmi(struct drm_device *dev, u8 version) union acpi_object *obj; acpi_status status; - if (!wmi_has_guid(WMI_WMMX_GUID)) + if (!wmi_has_guid(WMI_WMMX_GUID)) { + MXM_DBG(dev, "WMMX GUID not found\n"); + return false; + } + + mxms_args[1] = wmi_wmmx_mxmi(dev, 0x00); + if (!mxms_args[1]) + mxms_args[1] = wmi_wmmx_mxmi(dev, version); + if (!mxms_args[1]) return false; status = wmi_evaluate_method(WMI_WMMX_GUID, 0, 0, &args, &retn); From 44ab8cc56c45ca781371a4a77f35da19cf5db028 Mon Sep 17 00:00:00 2001 From: Xi Wang Date: Fri, 3 Feb 2012 11:13:55 -0500 Subject: [PATCH 39/57] drm/nouveau/dp: fix bad comparison in dp_link_train_commit() The comparison (lpre == DP_TRAIN_PRE_EMPHASIS_9_5) is always false: lpre is initialized as (lane & 0x0c) >> 2, which is at most 3, while DP_TRAIN_PRE_EMPHASIS_9_5 is defined as (3 << 3). Signed-off-by: Xi Wang Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_dp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 9b93b703ceab..c51c68b045a8 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -432,7 +432,7 @@ dp_link_train_commit(struct drm_device *dev, struct dp_state *dp) dp->conf[i] = (lpre << 3) | lvsw; if (lvsw == DP_TRAIN_VOLTAGE_SWING_1200) dp->conf[i] |= DP_TRAIN_MAX_SWING_REACHED; - if (lpre == DP_TRAIN_PRE_EMPHASIS_9_5) + if ((lpre << 3) == DP_TRAIN_PRE_EMPHASIS_9_5) dp->conf[i] |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED; NV_DEBUG_KMS(dev, "config lane %d %02x\n", i, dp->conf[i]); From 1ae73f2f16f1a905ada71e2a190d5760b4f17ed8 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 7 Feb 2012 09:59:54 +1000 Subject: [PATCH 40/57] drm/nvc0/pm: restrict pll mode to clocks that can actually use it Fixes reclocking failure on some chips where we attempted to set PDAEMON to PLL mode. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvc0_pm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/nouveau/nvc0_pm.c b/drivers/gpu/drm/nouveau/nvc0_pm.c index e9992f62c1c0..ce65f81bb871 100644 --- a/drivers/gpu/drm/nouveau/nvc0_pm.c +++ b/drivers/gpu/drm/nouveau/nvc0_pm.c @@ -269,7 +269,7 @@ calc_clk(struct drm_device *dev, int clk, struct nvc0_pm_clock *info, u32 freq) clk0 = calc_div(dev, clk, clk0, freq, &div1D); /* see if we can get any closer using PLLs */ - if (clk0 != freq) { + if (clk0 != freq && (0x00004387 & (1 << clk))) { if (clk < 7) clk1 = calc_pll(dev, clk, freq, &info->coef); else From e6084257d07fffc394ac6e4cf4496c946fd904ed Mon Sep 17 00:00:00 2001 From: Roy Spliet Date: Tue, 7 Feb 2012 00:29:06 +0100 Subject: [PATCH 41/57] drm/nouveau/pm: several fixes for nvc0 memory timings This patch fixes two small issues in timing generation as spotted on several NVCx cards. In addition, the header of the file is updated to also contain (some of) the current developers of this code. Signed-off-by: Roy Spliet Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_mem.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c index 8ff0027302a6..b08065f981df 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mem.c +++ b/drivers/gpu/drm/nouveau/nouveau_mem.c @@ -26,7 +26,8 @@ * DEALINGS IN THE SOFTWARE. * * Authors: - * Keith Whitwell + * Ben Skeggs + * Roy Spliet */ @@ -613,13 +614,13 @@ nvc0_mem_timing_calc(struct drm_device *dev, u32 freq, t->reg[1] = (boot->reg[1] & 0xff000000) | (e->tRCDWR & 0x0f) << 20 | (e->tRCDRD & 0x0f) << 14 | - (e->tCWL << 7) | + (t->tCWL << 7) | (e->tCL & 0x0f); t->reg[2] = (boot->reg[2] & 0xff0000ff) | e->tWR << 16 | e->tWTR << 8; - t->reg[3] = (e->tUNK_20 & 0xf) << 9 | + t->reg[3] = (e->tUNK_20 & 0x1f) << 9 | (e->tUNK_21 & 0xf) << 5 | (e->tUNK_13 & 0x1f); @@ -930,6 +931,8 @@ nouveau_mem_timing_read(struct drm_device *dev, struct nouveau_pm_memtiming *t) t->tCWL = 0; if (dev_priv->card_type < NV_C0) { t->tCWL = ((nv_rd32(dev, 0x100228) & 0x0f000000) >> 24) + 1; + } else if (dev_priv->card_type <= NV_D0) { + t->tCWL = ((nv_rd32(dev, 0x10f294) & 0x00000f80) >> 7); } t->mr[0] = nv_rd32(dev, mr_base); From 8b83d67c2e2b7e704e6923d00209b6e67f7bdb35 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 9 Feb 2012 15:25:25 +1000 Subject: [PATCH 42/57] drm/nv40/pm: fix fanspeed regression Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_perf.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/nouveau/nouveau_perf.c b/drivers/gpu/drm/nouveau/nouveau_perf.c index e64901509f90..69a528d106e6 100644 --- a/drivers/gpu/drm/nouveau/nouveau_perf.c +++ b/drivers/gpu/drm/nouveau/nouveau_perf.c @@ -300,6 +300,10 @@ nouveau_perf_init(struct drm_device *dev) return; } + perf = nouveau_perf_table(dev, &ver); + if (ver >= 0x20 && ver < 0x40) + pm->fan.pwm_divisor = ROM16(perf[6]); + while ((perf = nouveau_perf_entry(dev, ++i, &ver, &hdr, &cnt, &len))) { struct nouveau_pm_level *perflvl = &pm->perflvl[pm->nr_perflvl]; From 29181d2f7bec0f51273ceab8c565410d52ef1eb0 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Sun, 4 Mar 2012 15:44:59 +1000 Subject: [PATCH 43/57] drm/nvc0/vram: get part count from PUNITS Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvc0_vram.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nvc0_vram.c b/drivers/gpu/drm/nouveau/nvc0_vram.c index 82b51b2506ab..a7eef8934c07 100644 --- a/drivers/gpu/drm/nouveau/nvc0_vram.c +++ b/drivers/gpu/drm/nouveau/nvc0_vram.c @@ -106,34 +106,32 @@ nvc0_vram_init(struct drm_device *dev) struct nouveau_vram_engine *vram = &dev_priv->engine.vram; const u32 rsvd_head = ( 256 * 1024) >> 12; /* vga memory */ const u32 rsvd_tail = (1024 * 1024) >> 12; /* vbios etc */ - u32 parts = nv_rd32(dev, 0x121c74); + u32 parts = nv_rd32(dev, 0x022438); + u32 pmask = nv_rd32(dev, 0x022554); u32 bsize = nv_rd32(dev, 0x10f20c); u32 offset, length; bool uniform = true; int ret, part; NV_DEBUG(dev, "0x100800: 0x%08x\n", nv_rd32(dev, 0x100800)); - NV_DEBUG(dev, "parts 0x%08x bcast_mem_amount 0x%08x\n", parts, bsize); + NV_DEBUG(dev, "parts 0x%08x mask 0x%08x\n", parts, pmask); dev_priv->vram_type = nouveau_mem_vbios_type(dev); dev_priv->vram_rank_B = !!(nv_rd32(dev, 0x10f200) & 0x00000004); /* read amount of vram attached to each memory controller */ - part = 0; - while (parts) { - u32 psize = nv_rd32(dev, 0x11020c + (part++ * 0x1000)); - if (psize == 0) - continue; - parts--; + for (part = 0; part < parts; part++) { + if (!(pmask & (1 << part))) { + u32 psize = nv_rd32(dev, 0x11020c + (part * 0x1000)); + if (psize != bsize) { + if (psize < bsize) + bsize = psize; + uniform = false; + } - if (psize != bsize) { - if (psize < bsize) - bsize = psize; - uniform = false; + NV_DEBUG(dev, "%d: mem_amount 0x%08x\n", part, psize); + dev_priv->vram_size += (u64)psize << 20; } - - NV_DEBUG(dev, "%d: mem_amount 0x%08x\n", part, psize); - dev_priv->vram_size += (u64)psize << 20; } /* if all controllers have the same amount attached, there's no holes */ From 7c5f6a87b2337cc81fb6d16e848501e2dbe00a3d Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Sun, 4 Mar 2012 16:25:59 +1000 Subject: [PATCH 44/57] drm/nvd0/disp: attempt to handle more than 2 crtcs if possible Theoretically handles CRTC2/CRTC3, should any GF119 out there actually have them enabled. The room is there for the regs etc, so why not :) Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvd0_display.c | 33 +++++++++++--------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index d2ba2f07400b..296f205249fc 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -1548,7 +1548,7 @@ nvd0_display_bh(unsigned long data) { struct drm_device *dev = (struct drm_device *)data; struct nvd0_display *disp = nvd0_display(dev); - u32 mask, crtc; + u32 mask = 0, crtc = ~0; int i; if (drm_debug & (DRM_UT_DRIVER | DRM_UT_KMS)) { @@ -1564,12 +1564,8 @@ nvd0_display_bh(unsigned long data) } } - mask = nv_rd32(dev, 0x6101d4); - crtc = 0; - if (!mask) { - mask = nv_rd32(dev, 0x6109d4); - crtc = 1; - } + while (!mask && ++crtc < dev->mode_config.num_crtc) + mask = nv_rd32(dev, 0x6101d4 + (crtc * 0x800)); if (disp->modeset & 0x00000001) nvd0_display_unk1_handler(dev, crtc, mask); @@ -1584,6 +1580,7 @@ nvd0_display_intr(struct drm_device *dev) { struct nvd0_display *disp = nvd0_display(dev); u32 intr = nv_rd32(dev, 0x610088); + int i; if (intr & 0x00000001) { u32 stat = nv_rd32(dev, 0x61008c); @@ -1628,16 +1625,13 @@ nvd0_display_intr(struct drm_device *dev) intr &= ~0x00100000; } - if (intr & 0x01000000) { - u32 stat = nv_rd32(dev, 0x6100bc); - nv_wr32(dev, 0x6100bc, stat); - intr &= ~0x01000000; - } - - if (intr & 0x02000000) { - u32 stat = nv_rd32(dev, 0x6108bc); - nv_wr32(dev, 0x6108bc, stat); - intr &= ~0x02000000; + for (i = 0; i < dev->mode_config.num_crtc; i++) { + u32 mask = 0x01000000 << i; + if (intr & mask) { + u32 stat = nv_rd32(dev, 0x6100bc + (i * 0x800)); + nv_wr32(dev, 0x6100bc + (i * 0x800), stat); + intr &= ~mask; + } } if (intr) @@ -1774,7 +1768,7 @@ nvd0_display_create(struct drm_device *dev) struct pci_dev *pdev = dev->pdev; struct nvd0_display *disp; struct dcb_entry *dcbe; - int ret, i; + int crtcs, ret, i; disp = kzalloc(sizeof(*disp), GFP_KERNEL); if (!disp) @@ -1782,7 +1776,8 @@ nvd0_display_create(struct drm_device *dev) dev_priv->engine.display.priv = disp; /* create crtc objects to represent the hw heads */ - for (i = 0; i < 2; i++) { + crtcs = nv_rd32(dev, 0x022448); + for (i = 0; i < crtcs; i++) { ret = nvd0_crtc_create(dev, i); if (ret) goto out; From 05a7c15d48ff53b054bc36fc8a2be8bc9b60d9e7 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Sun, 4 Mar 2012 17:05:54 +1000 Subject: [PATCH 45/57] drm/nouveau/bios: attempt acpi rom fetch before pcirom There's cards out there with completely messed up PCIROM images that have a perfectly valid signature.. Sigh! Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_bios.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index e5cbead85e50..773d9cbe19df 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -204,8 +204,8 @@ struct methods { static struct methods shadow_methods[] = { { "PRAMIN", load_vbios_pramin, true }, { "PROM", load_vbios_prom, false }, - { "PCIROM", load_vbios_pci, true }, { "ACPI", load_vbios_acpi, true }, + { "PCIROM", load_vbios_pci, true }, }; #define NUM_SHADOW_METHODS ARRAY_SIZE(shadow_methods) From 4489b9835a0867617a9ec29cc978a9c2c9eca363 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Wed, 7 Mar 2012 13:22:50 +1000 Subject: [PATCH 46/57] drm/nouveau/bios: rework vbios shadowing Refactored to allow shadowing of VBIOS images longer than 64KiB, which allows us to pass the VBIOS checksum test on certain boards. There's also a workaround for reading the PROM VBIOS on some chipsets. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_bios.c | 281 ++++++++++++++----------- drivers/gpu/drm/nouveau/nouveau_bios.h | 4 +- 2 files changed, 161 insertions(+), 124 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index 773d9cbe19df..8dbeeea91872 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -65,195 +65,232 @@ static bool nv_cksum(const uint8_t *data, unsigned int length) } static int -score_vbios(struct drm_device *dev, const uint8_t *data, const bool writeable) +score_vbios(struct nvbios *bios, const bool writeable) { - if (!(data[0] == 0x55 && data[1] == 0xAA)) { - NV_TRACEWARN(dev, "... BIOS signature not found\n"); + if (!bios->data || bios->data[0] != 0x55 || bios->data[1] != 0xAA) { + NV_TRACEWARN(bios->dev, "... BIOS signature not found\n"); return 0; } - if (nv_cksum(data, data[2] * 512)) { - NV_TRACEWARN(dev, "... BIOS checksum invalid\n"); + if (nv_cksum(bios->data, bios->data[2] * 512)) { + NV_TRACEWARN(bios->dev, "... BIOS checksum invalid\n"); /* if a ro image is somewhat bad, it's probably all rubbish */ return writeable ? 2 : 1; - } else - NV_TRACE(dev, "... appears to be valid\n"); + } + NV_TRACE(bios->dev, "... appears to be valid\n"); return 3; } -static void load_vbios_prom(struct drm_device *dev, uint8_t *data) +static void +bios_shadow_prom(struct nvbios *bios) { + struct drm_device *dev = bios->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; - uint32_t pci_nv_20, save_pci_nv_20; - int pcir_ptr; + u32 pcireg, access; + u16 pcir; int i; + /* enable access to rom */ if (dev_priv->card_type >= NV_50) - pci_nv_20 = 0x88050; + pcireg = 0x088050; else - pci_nv_20 = NV_PBUS_PCI_NV_20; + pcireg = NV_PBUS_PCI_NV_20; + access = nv_mask(dev, pcireg, 0x00000001, 0x00000000); - /* enable ROM access */ - save_pci_nv_20 = nvReadMC(dev, pci_nv_20); - nvWriteMC(dev, pci_nv_20, - save_pci_nv_20 & ~NV_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED); + /* bail if no rom signature, with a workaround for a PROM reading + * issue on some chipsets. the first read after a period of + * inactivity returns the wrong result, so retry the first header + * byte a few times before giving up as a workaround + */ + i = 16; + do { + if (nv_rd08(dev, NV_PROM_OFFSET + 0) == 0x55) + break; + } while (i--); - /* bail if no rom signature */ - if (nv_rd08(dev, NV_PROM_OFFSET) != 0x55 || - nv_rd08(dev, NV_PROM_OFFSET + 1) != 0xaa) + if (!i || nv_rd08(dev, NV_PROM_OFFSET + 1) != 0xaa) goto out; /* additional check (see note below) - read PCI record header */ - pcir_ptr = nv_rd08(dev, NV_PROM_OFFSET + 0x18) | - nv_rd08(dev, NV_PROM_OFFSET + 0x19) << 8; - if (nv_rd08(dev, NV_PROM_OFFSET + pcir_ptr) != 'P' || - nv_rd08(dev, NV_PROM_OFFSET + pcir_ptr + 1) != 'C' || - nv_rd08(dev, NV_PROM_OFFSET + pcir_ptr + 2) != 'I' || - nv_rd08(dev, NV_PROM_OFFSET + pcir_ptr + 3) != 'R') + pcir = nv_rd08(dev, NV_PROM_OFFSET + 0x18) | + nv_rd08(dev, NV_PROM_OFFSET + 0x19) << 8; + if (nv_rd08(dev, NV_PROM_OFFSET + pcir + 0) != 'P' || + nv_rd08(dev, NV_PROM_OFFSET + pcir + 1) != 'C' || + nv_rd08(dev, NV_PROM_OFFSET + pcir + 2) != 'I' || + nv_rd08(dev, NV_PROM_OFFSET + pcir + 3) != 'R') goto out; - /* on some 6600GT/6800LE prom reads are messed up. nvclock alleges a - * a good read may be obtained by waiting or re-reading (cargocult: 5x) - * each byte. we'll hope pramin has something usable instead - */ - for (i = 0; i < NV_PROM_SIZE; i++) - data[i] = nv_rd08(dev, NV_PROM_OFFSET + i); + /* read entire bios image to system memory */ + bios->length = nv_rd08(dev, NV_PROM_OFFSET + 2) * 512; + bios->data = kmalloc(bios->length, GFP_KERNEL); + if (bios->data) { + for (i = 0; i < bios->length; i++) + bios->data[i] = nv_rd08(dev, NV_PROM_OFFSET + i); + } out: - /* disable ROM access */ - nvWriteMC(dev, pci_nv_20, - save_pci_nv_20 | NV_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED); + /* disable access to rom */ + nv_wr32(dev, pcireg, access); } -static void load_vbios_pramin(struct drm_device *dev, uint8_t *data) +static void +bios_shadow_pramin(struct nvbios *bios) { + struct drm_device *dev = bios->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; - uint32_t old_bar0_pramin = 0; + u32 bar0 = 0; int i; if (dev_priv->card_type >= NV_50) { u64 addr = (u64)(nv_rd32(dev, 0x619f04) & 0xffffff00) << 8; if (!addr) { - addr = (u64)nv_rd32(dev, 0x1700) << 16; + addr = (u64)nv_rd32(dev, 0x001700) << 16; addr += 0xf0000; } - old_bar0_pramin = nv_rd32(dev, 0x1700); - nv_wr32(dev, 0x1700, addr >> 16); + bar0 = nv_mask(dev, 0x001700, 0xffffffff, addr >> 16); } /* bail if no rom signature */ - if (nv_rd08(dev, NV_PRAMIN_OFFSET) != 0x55 || + if (nv_rd08(dev, NV_PRAMIN_OFFSET + 0) != 0x55 || nv_rd08(dev, NV_PRAMIN_OFFSET + 1) != 0xaa) goto out; - for (i = 0; i < NV_PROM_SIZE; i++) - data[i] = nv_rd08(dev, NV_PRAMIN_OFFSET + i); + bios->length = nv_rd08(dev, NV_PRAMIN_OFFSET + 2) * 512; + bios->data = kmalloc(bios->length, GFP_KERNEL); + if (bios->data) { + for (i = 0; i < bios->length; i++) + bios->data[i] = nv_rd08(dev, NV_PRAMIN_OFFSET + i); + } out: if (dev_priv->card_type >= NV_50) - nv_wr32(dev, 0x1700, old_bar0_pramin); + nv_wr32(dev, 0x001700, bar0); } -static void load_vbios_pci(struct drm_device *dev, uint8_t *data) +static void +bios_shadow_pci(struct nvbios *bios) { - void __iomem *rom = NULL; - size_t rom_len; - int ret; + struct pci_dev *pdev = bios->dev->pdev; + size_t length; - ret = pci_enable_rom(dev->pdev); - if (ret) - return; + if (!pci_enable_rom(pdev)) { + void __iomem *rom = pci_map_rom(pdev, &length); + if (rom) { + bios->data = kmalloc(length, GFP_KERNEL); + if (bios->data) { + memcpy_fromio(bios->data, rom, length); + bios->length = length; + } + pci_unmap_rom(pdev, rom); + } - rom = pci_map_rom(dev->pdev, &rom_len); - if (!rom) - goto out; - memcpy_fromio(data, rom, rom_len); - pci_unmap_rom(dev->pdev, rom); - -out: - pci_disable_rom(dev->pdev); -} - -static void load_vbios_acpi(struct drm_device *dev, uint8_t *data) -{ - int i; - int ret; - int size = 64 * 1024; - - if (!nouveau_acpi_rom_supported(dev->pdev)) - return; - - for (i = 0; i < (size / ROM_BIOS_PAGE); i++) { - ret = nouveau_acpi_get_bios_chunk(data, - (i * ROM_BIOS_PAGE), - ROM_BIOS_PAGE); - if (ret <= 0) - break; + pci_disable_rom(pdev); + } +} + +static void +bios_shadow_acpi(struct nvbios *bios) +{ + struct pci_dev *pdev = bios->dev->pdev; + int ptr, len, ret; + u8 data[3]; + + if (!nouveau_acpi_rom_supported(pdev)) + return; + + ret = nouveau_acpi_get_bios_chunk(data, 0, sizeof(data)); + if (ret != sizeof(data)) + return; + + bios->length = min(data[2] * 512, 65536); + bios->data = kmalloc(bios->length, GFP_KERNEL); + if (!bios->data) + return; + + len = bios->length; + ptr = 0; + while (len) { + int size = (len > ROM_BIOS_PAGE) ? ROM_BIOS_PAGE : len; + + ret = nouveau_acpi_get_bios_chunk(bios->data, ptr, size); + if (ret != size) { + kfree(bios->data); + bios->data = NULL; + return; + } + + len -= size; + ptr += size; } - return; } struct methods { const char desc[8]; - void (*loadbios)(struct drm_device *, uint8_t *); + void (*shadow)(struct nvbios *); const bool rw; + int score; + u32 size; + u8 *data; }; -static struct methods shadow_methods[] = { - { "PRAMIN", load_vbios_pramin, true }, - { "PROM", load_vbios_prom, false }, - { "ACPI", load_vbios_acpi, true }, - { "PCIROM", load_vbios_pci, true }, -}; -#define NUM_SHADOW_METHODS ARRAY_SIZE(shadow_methods) - -static bool NVShadowVBIOS(struct drm_device *dev, uint8_t *data) +static bool +bios_shadow(struct drm_device *dev) { - struct methods *methods = shadow_methods; - int testscore = 3; - int scores[NUM_SHADOW_METHODS], i; + struct methods shadow_methods[] = { + { "PRAMIN", bios_shadow_pramin, true, 0, 0, NULL }, + { "PROM", bios_shadow_prom, false, 0, 0, NULL }, + { "ACPI", bios_shadow_acpi, true, 0, 0, NULL }, + { "PCIROM", bios_shadow_pci, true, 0, 0, NULL }, + {} + }; + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nvbios *bios = &dev_priv->vbios; + struct methods *mthd, *best; if (nouveau_vbios) { - for (i = 0; i < NUM_SHADOW_METHODS; i++) - if (!strcasecmp(nouveau_vbios, methods[i].desc)) - break; + mthd = shadow_methods; + do { + if (strcasecmp(nouveau_vbios, mthd->desc)) + continue; + NV_INFO(dev, "VBIOS source: %s\n", mthd->desc); - if (i < NUM_SHADOW_METHODS) { - NV_INFO(dev, "Attempting to use BIOS image from %s\n", - methods[i].desc); - - methods[i].loadbios(dev, data); - if (score_vbios(dev, data, methods[i].rw)) + mthd->shadow(bios); + mthd->score = score_vbios(bios, mthd->rw); + if (mthd->score) return true; - } + } while ((++mthd)->shadow); NV_ERROR(dev, "VBIOS source \'%s\' invalid\n", nouveau_vbios); } - for (i = 0; i < NUM_SHADOW_METHODS; i++) { - NV_TRACE(dev, "Attempting to load BIOS image from %s\n", - methods[i].desc); - data[0] = data[1] = 0; /* avoid reuse of previous image */ - methods[i].loadbios(dev, data); - scores[i] = score_vbios(dev, data, methods[i].rw); - if (scores[i] == testscore) - return true; - } + mthd = shadow_methods; + do { + NV_TRACE(dev, "Checking %s for VBIOS\n", mthd->desc); + mthd->shadow(bios); + mthd->score = score_vbios(bios, mthd->rw); + mthd->size = bios->length; + mthd->data = bios->data; + } while (mthd->score != 3 && (++mthd)->shadow); - while (--testscore > 0) { - for (i = 0; i < NUM_SHADOW_METHODS; i++) { - if (scores[i] == testscore) { - NV_TRACE(dev, "Using BIOS image from %s\n", - methods[i].desc); - methods[i].loadbios(dev, data); - return true; - } + mthd = shadow_methods; + best = mthd; + do { + if (mthd->score > best->score) { + kfree(best->data); + best = mthd; } + } while ((++mthd)->shadow); + + if (best->score) { + NV_TRACE(dev, "Using VBIOS from %s\n", best->desc); + bios->length = best->size; + bios->data = best->data; + return true; } - NV_ERROR(dev, "No valid BIOS image found\n"); + NV_ERROR(dev, "No valid VBIOS image found\n"); return false; } @@ -6334,11 +6371,7 @@ static bool NVInitVBIOS(struct drm_device *dev) spin_lock_init(&bios->lock); bios->dev = dev; - if (!NVShadowVBIOS(dev, bios->data)) - return false; - - bios->length = NV_PROM_SIZE; - return true; + return bios_shadow(dev); } static int nouveau_parse_vbios_struct(struct drm_device *dev) @@ -6498,6 +6531,10 @@ nouveau_bios_init(struct drm_device *dev) void nouveau_bios_takedown(struct drm_device *dev) { + struct drm_nouveau_private *dev_priv = dev->dev_private; + nouveau_mxm_fini(dev); nouveau_i2c_fini(dev); + + kfree(dev_priv->vbios.data); } diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.h b/drivers/gpu/drm/nouveau/nouveau_bios.h index 0fc4e21c748b..1f3233df00e6 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.h +++ b/drivers/gpu/drm/nouveau/nouveau_bios.h @@ -211,6 +211,8 @@ struct nvbios { NVBIOS_BIT } type; uint16_t offset; + uint32_t length; + uint8_t *data; uint8_t chip_version; @@ -221,8 +223,6 @@ struct nvbios { spinlock_t lock; - uint8_t data[NV_PROM_SIZE]; - unsigned int length; bool execute; uint8_t major_version; From c11dd0da5277596d0ccdccb745b273d69a94f2d7 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Wed, 7 Mar 2012 14:18:49 +1000 Subject: [PATCH 47/57] 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(-) 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); From 8c1dcb6573ae71ffae392edf5f458543d310607e Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 9 Mar 2012 15:22:19 +1000 Subject: [PATCH 48/57] drm/nouveau/dp: make functions for executing various bios tables More code to do the same thing, but will make it easier to handle various changes that could possibly happen the the VBIOS tables. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_dp.c | 55 +++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index c51c68b045a8..1a613ab4e08f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -554,6 +554,49 @@ dp_link_train_eq(struct drm_device *dev, struct dp_state *dp) return eq_done ? 0 : -1; } +static void +dp_set_downspread(struct drm_device *dev, struct dp_state *dp, bool enable) +{ + u16 script = 0x0000; + u8 *entry, *table = nouveau_dp_bios_data(dev, dp->dcb, &entry); + if (table) { + if (table[0] >= 0x20 && table[0] <= 0x30) { + if (enable) + script = ROM16(entry[12]); + else + script = ROM16(entry[14]); + } + } + + nouveau_bios_run_init_table(dev, script, dp->dcb, dp->crtc); +} + +static void +dp_link_train_init(struct drm_device *dev, struct dp_state *dp) +{ + u16 script = 0x0000; + u8 *entry, *table = nouveau_dp_bios_data(dev, dp->dcb, &entry); + if (table) { + if (table[0] >= 0x20 && table[0] <= 0x30) + script = ROM16(entry[6]); + } + + nouveau_bios_run_init_table(dev, script, dp->dcb, dp->crtc); +} + +static void +dp_link_train_fini(struct drm_device *dev, struct dp_state *dp) +{ + u16 script = 0x0000; + u8 *entry, *table = nouveau_dp_bios_data(dev, dp->dcb, &entry); + if (table) { + if (table[0] >= 0x20 && table[0] <= 0x30) + script = ROM16(entry[8]); + } + + nouveau_bios_run_init_table(dev, script, dp->dcb, dp->crtc); +} + bool nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate) { @@ -589,16 +632,10 @@ nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate) nouveau_gpio_irq(dev, 0, nv_connector->hpd, 0xff, false); /* enable down-spreading, if possible */ - if (dp.table[1] >= 16) { - u16 script = ROM16(dp.entry[14]); - if (nv_encoder->dp.dpcd[3] & 1) - script = ROM16(dp.entry[12]); - - nouveau_bios_run_init_table(dev, script, dp.dcb, dp.crtc); - } + dp_set_downspread(dev, &dp, nv_encoder->dp.dpcd[3] & 1); /* execute pre-train script from vbios */ - nouveau_bios_run_init_table(dev, ROM16(dp.entry[6]), dp.dcb, dp.crtc); + dp_link_train_init(dev, &dp); /* start off at highest link rate supported by encoder and display */ while (*link_bw > nv_encoder->dp.link_bw) @@ -632,7 +669,7 @@ nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate) dp_set_training_pattern(dev, &dp, DP_TRAINING_PATTERN_DISABLE); /* execute post-train script from vbios */ - nouveau_bios_run_init_table(dev, ROM16(dp.entry[8]), dp.dcb, dp.crtc); + dp_link_train_fini(dev, &dp); /* re-enable hotplug detect */ nouveau_gpio_irq(dev, 0, nv_connector->hpd, 0xff, true); From 8663bc7cde00c8e832d985354f8a6d57a52f7d92 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 9 Mar 2012 16:22:56 +1000 Subject: [PATCH 49/57] drm/nouveau/dp: move all nv50/sor-specific code out of nouveau_dp.c Off-chip encoders (which we don't support yet anyway), and newer chipsets (such as NVD9...), will need their own code for this. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_dp.c | 237 ++-------------------- drivers/gpu/drm/nouveau/nouveau_drv.h | 8 - drivers/gpu/drm/nouveau/nouveau_encoder.h | 18 ++ drivers/gpu/drm/nouveau/nv50_display.c | 4 +- drivers/gpu/drm/nouveau/nv50_sor.c | 196 +++++++++++++++++- 5 files changed, 233 insertions(+), 230 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 1a613ab4e08f..f38f561a0058 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -161,116 +161,6 @@ out: return ret; } -static u32 -dp_link_bw_get(struct drm_device *dev, int or, int link) -{ - u32 ctrl = nv_rd32(dev, 0x614300 + (or * 0x800)); - if (!(ctrl & 0x000c0000)) - return 162000; - return 270000; -} - -static int -dp_lane_count_get(struct drm_device *dev, int or, int link) -{ - u32 ctrl = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link)); - switch (ctrl & 0x000f0000) { - case 0x00010000: return 1; - case 0x00030000: return 2; - default: - return 4; - } -} - -void -nouveau_dp_tu_update(struct drm_device *dev, int or, int link, u32 clk, u32 bpp) -{ - const u32 symbol = 100000; - int bestTU = 0, bestVTUi = 0, bestVTUf = 0, bestVTUa = 0; - int TU, VTUi, VTUf, VTUa; - u64 link_data_rate, link_ratio, unk; - u32 best_diff = 64 * symbol; - u32 link_nr, link_bw, r; - - /* calculate packed data rate for each lane */ - link_nr = dp_lane_count_get(dev, or, link); - link_data_rate = (clk * bpp / 8) / link_nr; - - /* calculate ratio of packed data rate to link symbol rate */ - link_bw = dp_link_bw_get(dev, or, link); - link_ratio = link_data_rate * symbol; - r = do_div(link_ratio, link_bw); - - for (TU = 64; TU >= 32; TU--) { - /* calculate average number of valid symbols in each TU */ - u32 tu_valid = link_ratio * TU; - u32 calc, diff; - - /* find a hw representation for the fraction.. */ - VTUi = tu_valid / symbol; - calc = VTUi * symbol; - diff = tu_valid - calc; - if (diff) { - if (diff >= (symbol / 2)) { - VTUf = symbol / (symbol - diff); - if (symbol - (VTUf * diff)) - VTUf++; - - if (VTUf <= 15) { - VTUa = 1; - calc += symbol - (symbol / VTUf); - } else { - VTUa = 0; - VTUf = 1; - calc += symbol; - } - } else { - VTUa = 0; - VTUf = min((int)(symbol / diff), 15); - calc += symbol / VTUf; - } - - diff = calc - tu_valid; - } else { - /* no remainder, but the hw doesn't like the fractional - * part to be zero. decrement the integer part and - * have the fraction add a whole symbol back - */ - VTUa = 0; - VTUf = 1; - VTUi--; - } - - if (diff < best_diff) { - best_diff = diff; - bestTU = TU; - bestVTUa = VTUa; - bestVTUf = VTUf; - bestVTUi = VTUi; - if (diff == 0) - break; - } - } - - if (!bestTU) { - NV_ERROR(dev, "DP: unable to find suitable config\n"); - return; - } - - /* XXX close to vbios numbers, but not right */ - unk = (symbol - link_ratio) * bestTU; - unk *= link_ratio; - r = do_div(unk, symbol); - r = do_div(unk, symbol); - unk += 6; - - nv_mask(dev, NV50_SOR_DP_CTRL(or, link), 0x000001fc, bestTU << 2); - nv_mask(dev, NV50_SOR_DP_SCFG(or, link), 0x010f7f3f, bestVTUa << 24 | - bestVTUf << 16 | - bestVTUi << 8 | - unk); -} - u8 * nouveau_dp_bios_data(struct drm_device *dev, struct dcb_entry *dcb, u8 **entry) { @@ -318,13 +208,10 @@ nouveau_dp_bios_data(struct drm_device *dev, struct dcb_entry *dcb, u8 **entry) * link training *****************************************************************************/ struct dp_state { + struct dp_train_func *func; struct dcb_entry *dcb; - u8 *table; - u8 *entry; int auxch; int crtc; - int or; - int link; u8 *dpcd; int link_nr; u32 link_bw; @@ -335,100 +222,48 @@ struct dp_state { static void dp_set_link_config(struct drm_device *dev, struct dp_state *dp) { - int or = dp->or, link = dp->link; - u8 *entry, sink[2]; - u32 dp_ctrl; - u16 script; + u8 sink[2]; NV_DEBUG_KMS(dev, "%d lanes at %d KB/s\n", dp->link_nr, dp->link_bw); - /* set selected link rate on source */ - switch (dp->link_bw) { - case 270000: - nv_mask(dev, 0x614300 + (or * 0x800), 0x000c0000, 0x00040000); - sink[0] = DP_LINK_BW_2_7; - break; - default: - nv_mask(dev, 0x614300 + (or * 0x800), 0x000c0000, 0x00000000); - sink[0] = DP_LINK_BW_1_62; - break; - } - - /* offset +0x0a of each dp encoder table entry is a pointer to another - * table, that has (among other things) pointers to more scripts that - * need to be executed, this time depending on link speed. - */ - entry = ROMPTR(dev, dp->entry[10]); - if (entry) { - if (dp->table[0] < 0x30) { - while (dp->link_bw < (ROM16(entry[0]) * 10)) - entry += 4; - script = ROM16(entry[2]); - } else { - while (dp->link_bw < (entry[0] * 27000)) - entry += 3; - script = ROM16(entry[1]); - } - - nouveau_bios_run_init_table(dev, script, dp->dcb, dp->crtc); - } - - /* configure lane count on the source */ - dp_ctrl = ((1 << dp->link_nr) - 1) << 16; - sink[1] = dp->link_nr; - if (dp->dpcd[2] & DP_ENHANCED_FRAME_CAP) { - dp_ctrl |= 0x00004000; - sink[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; - } - - nv_mask(dev, NV50_SOR_DP_CTRL(or, link), 0x001f4000, dp_ctrl); + /* set desired link configuration on the source */ + dp->func->link_set(dev, dp->dcb, dp->crtc, dp->link_nr, dp->link_bw, + dp->dpcd[2] & DP_ENHANCED_FRAME_CAP); /* inform the sink of the new configuration */ + sink[0] = dp->link_bw / 27000; + sink[1] = dp->link_nr; + if (dp->dpcd[2] & DP_ENHANCED_FRAME_CAP) + sink[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; + auxch_tx(dev, dp->auxch, 8, DP_LINK_BW_SET, sink, 2); } static void -dp_set_training_pattern(struct drm_device *dev, struct dp_state *dp, u8 tp) +dp_set_training_pattern(struct drm_device *dev, struct dp_state *dp, u8 pattern) { u8 sink_tp; - NV_DEBUG_KMS(dev, "training pattern %d\n", tp); + NV_DEBUG_KMS(dev, "training pattern %d\n", pattern); - nv_mask(dev, NV50_SOR_DP_CTRL(dp->or, dp->link), 0x0f000000, tp << 24); + dp->func->train_set(dev, dp->dcb, pattern); auxch_tx(dev, dp->auxch, 9, DP_TRAINING_PATTERN_SET, &sink_tp, 1); sink_tp &= ~DP_TRAINING_PATTERN_MASK; - sink_tp |= tp; + sink_tp |= pattern; auxch_tx(dev, dp->auxch, 8, DP_TRAINING_PATTERN_SET, &sink_tp, 1); } -static const u8 nv50_lane_map[] = { 16, 8, 0, 24 }; -static const u8 nvaf_lane_map[] = { 24, 16, 8, 0 }; - static int dp_link_train_commit(struct drm_device *dev, struct dp_state *dp) { - struct drm_nouveau_private *dev_priv = dev->dev_private; - u32 mask = 0, drv = 0, pre = 0, unk = 0; - const u8 *shifts; - int link = dp->link; - int or = dp->or; int i; - if (dev_priv->chipset != 0xaf) - shifts = nv50_lane_map; - else - shifts = nvaf_lane_map; - for (i = 0; i < dp->link_nr; i++) { - u8 *conf = dp->entry + dp->table[4]; u8 lane = (dp->stat[4 + (i >> 1)] >> ((i & 1) * 4)) & 0xf; u8 lpre = (lane & 0x0c) >> 2; u8 lvsw = (lane & 0x03) >> 0; - mask |= 0xff << shifts[i]; - unk |= 1 << (shifts[i] >> 3); - dp->conf[i] = (lpre << 3) | lvsw; if (lvsw == DP_TRAIN_VOLTAGE_SWING_1200) dp->conf[i] |= DP_TRAIN_MAX_SWING_REACHED; @@ -436,41 +271,9 @@ dp_link_train_commit(struct drm_device *dev, struct dp_state *dp) dp->conf[i] |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED; NV_DEBUG_KMS(dev, "config lane %d %02x\n", i, dp->conf[i]); - - if (dp->table[0] < 0x30) { - u8 *last = conf + (dp->entry[4] * dp->table[5]); - while (lvsw != conf[0] || lpre != conf[1]) { - conf += dp->table[5]; - if (conf >= last) - return -EINVAL; - } - - conf += 2; - } else { - /* no lookup table anymore, set entries for each - * combination of voltage swing and pre-emphasis - * level allowed by the DP spec. - */ - switch (lvsw) { - case 0: lpre += 0; break; - case 1: lpre += 4; break; - case 2: lpre += 7; break; - case 3: lpre += 9; break; - } - - conf = conf + (lpre * dp->table[5]); - conf++; - } - - drv |= conf[0] << shifts[i]; - pre |= conf[1] << shifts[i]; - unk = (unk & ~0x0000ff00) | (conf[2] << 8); + dp->func->train_adj(dev, dp->dcb, i, lvsw, lpre); } - nv_mask(dev, NV50_SOR_DP_UNK118(or, link), mask, drv); - nv_mask(dev, NV50_SOR_DP_UNK120(or, link), mask, pre); - nv_mask(dev, NV50_SOR_DP_UNK130(or, link), 0x0000ff0f, unk); - return auxch_tx(dev, dp->auxch, 8, DP_TRAINING_LANE0_SET, dp->conf, 4); } @@ -598,7 +401,8 @@ dp_link_train_fini(struct drm_device *dev, struct dp_state *dp) } bool -nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate) +nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate, + struct dp_train_func *func) { struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); @@ -614,15 +418,10 @@ nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate) if (!auxch) return false; - dp.table = nouveau_dp_bios_data(dev, nv_encoder->dcb, &dp.entry); - if (!dp.table) - return -EINVAL; - + dp.func = func; dp.dcb = nv_encoder->dcb; dp.crtc = nv_crtc->index; dp.auxch = auxch->drive; - dp.or = nv_encoder->or; - dp.link = !(nv_encoder->dcb->sorconf.link & 1); dp.dpcd = nv_encoder->dp.dpcd; /* some sinks toggle hotplug in response to some of the actions diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 4bb6aeec91f6..cf0e74a2c667 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -1162,14 +1162,6 @@ int nouveau_ttm_mmap(struct file *, struct vm_area_struct *); /* nouveau_hdmi.c */ void nouveau_hdmi_mode_set(struct drm_encoder *, struct drm_display_mode *); -/* nouveau_dp.c */ -int nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr, - uint8_t *data, int data_nr); -bool nouveau_dp_detect(struct drm_encoder *); -bool nouveau_dp_link_train(struct drm_encoder *, u32 datarate); -void nouveau_dp_tu_update(struct drm_device *, int, int, u32, u32); -u8 *nouveau_dp_bios_data(struct drm_device *, struct dcb_entry *, u8 **); - /* nv04_fb.c */ extern int nv04_fb_vram_init(struct drm_device *); extern int nv04_fb_init(struct drm_device *); diff --git a/drivers/gpu/drm/nouveau/nouveau_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h index e5d6e3faff3d..fa431742271e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_encoder.h +++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h @@ -32,6 +32,14 @@ #define NV_DPMS_CLEARED 0x80 +struct dp_train_func { + void (*link_set)(struct drm_device *, struct dcb_entry *, int crtc, + int nr, u32 bw, bool enhframe); + void (*train_set)(struct drm_device *, struct dcb_entry *, u8 pattern); + void (*train_adj)(struct drm_device *, struct dcb_entry *, + u8 lane, u8 swing, u8 preem); +}; + struct nouveau_encoder { struct drm_encoder_slave base; @@ -78,9 +86,19 @@ get_slave_funcs(struct drm_encoder *enc) return to_encoder_slave(enc)->slave_funcs; } +/* nouveau_dp.c */ +int nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr, + uint8_t *data, int data_nr); +bool nouveau_dp_detect(struct drm_encoder *); +bool nouveau_dp_link_train(struct drm_encoder *, u32 datarate, + struct dp_train_func *); +u8 *nouveau_dp_bios_data(struct drm_device *, struct dcb_entry *, u8 **); + struct nouveau_connector * nouveau_encoder_connector_get(struct nouveau_encoder *encoder); int nv50_sor_create(struct drm_connector *, struct dcb_entry *); +void nv50_sor_dp_calc_tu(struct drm_device *, int, int, u32, u32); int nv50_dac_create(struct drm_connector *, struct dcb_entry *); + #endif /* __NOUVEAU_ENCODER_H__ */ diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index ce440e2e58ca..0e47a898f415 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -863,9 +863,9 @@ nv50_display_unk20_handler(struct drm_device *dev) if (type == OUTPUT_DP) { int link = !(dcb->dpconf.sor.link & 1); if ((mc & 0x000f0000) == 0x00020000) - nouveau_dp_tu_update(dev, or, link, pclk, 18); + nv50_sor_dp_calc_tu(dev, or, link, pclk, 18); else - nouveau_dp_tu_update(dev, or, link, pclk, 24); + nv50_sor_dp_calc_tu(dev, or, link, pclk, 24); } if (dcb->type != OUTPUT_ANALOG) { diff --git a/drivers/gpu/drm/nouveau/nv50_sor.c b/drivers/gpu/drm/nouveau/nv50_sor.c index c4423ba9c9bf..ba1b8cc03545 100644 --- a/drivers/gpu/drm/nouveau/nv50_sor.c +++ b/drivers/gpu/drm/nouveau/nv50_sor.c @@ -36,6 +36,193 @@ #include "nouveau_crtc.h" #include "nv50_display.h" +static u32 +nv50_sor_dp_lane_map(struct drm_device *dev, struct dcb_entry *dcb, u8 lane) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + static const u8 nvaf[] = { 24, 16, 8, 0 }; /* thanks, apple.. */ + static const u8 nv50[] = { 16, 8, 0, 24 }; + if (dev_priv->card_type == 0xaf) + return nvaf[lane]; + return nv50[lane]; +} + +static void +nv50_sor_dp_train_set(struct drm_device *dev, struct dcb_entry *dcb, u8 pattern) +{ + u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1); + nv_mask(dev, NV50_SOR_DP_CTRL(or, link), 0x0f000000, pattern << 24); +} + +static void +nv50_sor_dp_train_adj(struct drm_device *dev, struct dcb_entry *dcb, + u8 lane, u8 swing, u8 preem) +{ + u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1); + u32 shift = nv50_sor_dp_lane_map(dev, dcb, lane); + u32 mask = 0x000000ff << shift; + u8 *table, *entry, *config; + + table = nouveau_dp_bios_data(dev, dcb, &entry); + if (!table || (table[0] != 0x20 && table[0] != 0x21)) { + NV_ERROR(dev, "PDISP: unsupported DP table for chipset\n"); + return; + } + + config = entry + table[4]; + while (config[0] != swing || config[1] != preem) { + config += table[5]; + if (config >= entry + table[4] + entry[4] * table[5]) + return; + } + + nv_mask(dev, NV50_SOR_DP_UNK118(or, link), mask, config[2] << shift); + nv_mask(dev, NV50_SOR_DP_UNK120(or, link), mask, config[3] << shift); + nv_mask(dev, NV50_SOR_DP_UNK130(or, link), 0x0000ff00, config[4] << 8); +} + +static void +nv50_sor_dp_link_set(struct drm_device *dev, struct dcb_entry *dcb, int crtc, + int link_nr, u32 link_bw, bool enhframe) +{ + u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1); + u32 dpctrl = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link)) & ~0x001f4000; + u32 clksor = nv_rd32(dev, 0x614300 + (or * 0x800)) & ~0x000c0000; + u8 *table, *entry, mask; + int i; + + table = nouveau_dp_bios_data(dev, dcb, &entry); + if (!table || (table[0] != 0x20 && table[0] != 0x21)) { + NV_ERROR(dev, "PDISP: unsupported DP table for chipset\n"); + return; + } + + entry = ROMPTR(dev, entry[10]); + if (entry) { + while (link_bw < ROM16(entry[0]) * 10) + entry += 4; + + nouveau_bios_run_init_table(dev, ROM16(entry[2]), dcb, crtc); + } + + dpctrl |= ((1 << link_nr) - 1) << 16; + if (enhframe) + dpctrl |= 0x00004000; + + if (link_bw > 162000) + clksor |= 0x00040000; + + nv_wr32(dev, 0x614300 + (or * 0x800), clksor); + nv_wr32(dev, NV50_SOR_DP_CTRL(or, link), dpctrl); + + mask = 0; + for (i = 0; i < link_nr; i++) + mask |= 1 << (nv50_sor_dp_lane_map(dev, dcb, i) >> 3); + nv_mask(dev, NV50_SOR_DP_UNK130(or, link), 0x0000000f, mask); +} + +static void +nv50_sor_dp_link_get(struct drm_device *dev, u32 or, u32 link, u32 *nr, u32 *bw) +{ + u32 dpctrl = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link)) & 0x000f0000; + u32 clksor = nv_rd32(dev, 0x614300 + (or * 0x800)); + if (clksor & 0x000c0000) + *bw = 270000; + else + *bw = 162000; + + if (dpctrl > 0x00030000) *nr = 4; + else if (dpctrl > 0x00010000) *nr = 2; + else *nr = 1; +} + +void +nv50_sor_dp_calc_tu(struct drm_device *dev, int or, int link, u32 clk, u32 bpp) +{ + const u32 symbol = 100000; + int bestTU = 0, bestVTUi = 0, bestVTUf = 0, bestVTUa = 0; + int TU, VTUi, VTUf, VTUa; + u64 link_data_rate, link_ratio, unk; + u32 best_diff = 64 * symbol; + u32 link_nr, link_bw, r; + + /* calculate packed data rate for each lane */ + nv50_sor_dp_link_get(dev, or, link, &link_nr, &link_bw); + link_data_rate = (clk * bpp / 8) / link_nr; + + /* calculate ratio of packed data rate to link symbol rate */ + link_ratio = link_data_rate * symbol; + r = do_div(link_ratio, link_bw); + + for (TU = 64; TU >= 32; TU--) { + /* calculate average number of valid symbols in each TU */ + u32 tu_valid = link_ratio * TU; + u32 calc, diff; + + /* find a hw representation for the fraction.. */ + VTUi = tu_valid / symbol; + calc = VTUi * symbol; + diff = tu_valid - calc; + if (diff) { + if (diff >= (symbol / 2)) { + VTUf = symbol / (symbol - diff); + if (symbol - (VTUf * diff)) + VTUf++; + + if (VTUf <= 15) { + VTUa = 1; + calc += symbol - (symbol / VTUf); + } else { + VTUa = 0; + VTUf = 1; + calc += symbol; + } + } else { + VTUa = 0; + VTUf = min((int)(symbol / diff), 15); + calc += symbol / VTUf; + } + + diff = calc - tu_valid; + } else { + /* no remainder, but the hw doesn't like the fractional + * part to be zero. decrement the integer part and + * have the fraction add a whole symbol back + */ + VTUa = 0; + VTUf = 1; + VTUi--; + } + + if (diff < best_diff) { + best_diff = diff; + bestTU = TU; + bestVTUa = VTUa; + bestVTUf = VTUf; + bestVTUi = VTUi; + if (diff == 0) + break; + } + } + + if (!bestTU) { + NV_ERROR(dev, "DP: unable to find suitable config\n"); + return; + } + + /* XXX close to vbios numbers, but not right */ + unk = (symbol - link_ratio) * bestTU; + unk *= link_ratio; + r = do_div(unk, symbol); + r = do_div(unk, symbol); + unk += 6; + + nv_mask(dev, NV50_SOR_DP_CTRL(or, link), 0x000001fc, bestTU << 2); + nv_mask(dev, NV50_SOR_DP_SCFG(or, link), 0x010f7f3f, bestVTUa << 24 | + bestVTUf << 16 | + bestVTUi << 8 | + unk); +} static void nv50_sor_disconnect(struct drm_encoder *encoder) { @@ -124,9 +311,16 @@ nv50_sor_dpms(struct drm_encoder *encoder, int mode) return; if (mode == DRM_MODE_DPMS_ON) { + struct dp_train_func func = { + .link_set = nv50_sor_dp_link_set, + .train_set = nv50_sor_dp_train_set, + .train_adj = nv50_sor_dp_train_adj + }; + u32 rate = nv_encoder->dp.datarate; u8 status = DP_SET_POWER_D0; + nouveau_dp_auxch(auxch, 8, DP_SET_POWER, &status, 1); - nouveau_dp_link_train(encoder, nv_encoder->dp.datarate); + nouveau_dp_link_train(encoder, rate, &func); } else { u8 status = DP_SET_POWER_D3; nouveau_dp_auxch(auxch, 8, DP_SET_POWER, &status, 1); From e436d1bb0a3e5ff27b190d0bf9173f4f6ad21d3f Mon Sep 17 00:00:00 2001 From: Martin Peres Date: Fri, 9 Mar 2012 00:15:01 +0100 Subject: [PATCH 50/57] drm/nv50/hwsq: some nv92 fixes The shift from hwsq_data = 0x1400 to 0x080000 actually happened in nv94, not nv92 This fixes some reclocking issues on my newly acquired nv92 Signed-off-by: Martin Peres Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nv50_pm.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c index e677310c8f57..d020ed4979b4 100644 --- a/drivers/gpu/drm/nouveau/nv50_pm.c +++ b/drivers/gpu/drm/nouveau/nv50_pm.c @@ -756,17 +756,18 @@ prog_hwsq(struct drm_device *dev, struct hwsq_ucode *hwsq) u32 hwsq_data, hwsq_kick; int i; - if (dev_priv->chipset < 0x90) { + if (dev_priv->chipset < 0x94) { hwsq_data = 0x001400; hwsq_kick = 0x00000003; } else { hwsq_data = 0x080000; hwsq_kick = 0x00000001; } - /* upload hwsq ucode */ nv_mask(dev, 0x001098, 0x00000008, 0x00000000); nv_wr32(dev, 0x001304, 0x00000000); + if (dev_priv->chipset >= 0x92) + nv_wr32(dev, 0x001318, 0x00000000); for (i = 0; i < hwsq->len / 4; i++) nv_wr32(dev, hwsq_data + (i * 4), hwsq->ptr.u32[i]); nv_mask(dev, 0x001098, 0x00000018, 0x00000018); From f14d9a4dda65439d74326694db727c6d2a5df0ce Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Sun, 11 Mar 2012 01:20:54 +1000 Subject: [PATCH 51/57] drm/nouveau/dp: make dp dpms function common, call from sor code instead GF119 will use this too. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_dp.c | 23 ++++++++++++++++++++ drivers/gpu/drm/nouveau/nouveau_encoder.h | 4 ++-- drivers/gpu/drm/nouveau/nv50_sor.c | 26 ++++++----------------- 3 files changed, 31 insertions(+), 22 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index f38f561a0058..7be4e06e8438 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -475,6 +475,29 @@ nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate, return true; } +void +nouveau_dp_dpms(struct drm_encoder *encoder, int mode, u32 datarate, + struct dp_train_func *func) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nouveau_i2c_chan *auxch; + u8 status; + + auxch = nouveau_i2c_find(encoder->dev, nv_encoder->dcb->i2c_index); + if (!auxch) + return; + + if (mode == DRM_MODE_DPMS_ON) + status = DP_SET_POWER_D0; + else + status = DP_SET_POWER_D3; + + nouveau_dp_auxch(auxch, 8, DP_SET_POWER, &status, 1); + + if (mode == DRM_MODE_DPMS_ON) + nouveau_dp_link_train(encoder, datarate, func); +} + bool nouveau_dp_detect(struct drm_encoder *encoder) { diff --git a/drivers/gpu/drm/nouveau/nouveau_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h index fa431742271e..3dc14a3dcc4c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_encoder.h +++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h @@ -90,8 +90,8 @@ get_slave_funcs(struct drm_encoder *enc) int nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr, uint8_t *data, int data_nr); bool nouveau_dp_detect(struct drm_encoder *); -bool nouveau_dp_link_train(struct drm_encoder *, u32 datarate, - struct dp_train_func *); +void nouveau_dp_dpms(struct drm_encoder *, int mode, u32 datarate, + struct dp_train_func *); u8 *nouveau_dp_bios_data(struct drm_device *, struct dcb_entry *, u8 **); struct nouveau_connector * diff --git a/drivers/gpu/drm/nouveau/nv50_sor.c b/drivers/gpu/drm/nouveau/nv50_sor.c index ba1b8cc03545..9a5c2c98658c 100644 --- a/drivers/gpu/drm/nouveau/nv50_sor.c +++ b/drivers/gpu/drm/nouveau/nv50_sor.c @@ -304,27 +304,13 @@ nv50_sor_dpms(struct drm_encoder *encoder, int mode) } if (nv_encoder->dcb->type == OUTPUT_DP) { - struct nouveau_i2c_chan *auxch; + struct dp_train_func func = { + .link_set = nv50_sor_dp_link_set, + .train_set = nv50_sor_dp_train_set, + .train_adj = nv50_sor_dp_train_adj + }; - auxch = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index); - if (!auxch) - return; - - if (mode == DRM_MODE_DPMS_ON) { - struct dp_train_func func = { - .link_set = nv50_sor_dp_link_set, - .train_set = nv50_sor_dp_train_set, - .train_adj = nv50_sor_dp_train_adj - }; - u32 rate = nv_encoder->dp.datarate; - u8 status = DP_SET_POWER_D0; - - nouveau_dp_auxch(auxch, 8, DP_SET_POWER, &status, 1); - nouveau_dp_link_train(encoder, rate, &func); - } else { - u8 status = DP_SET_POWER_D3; - nouveau_dp_auxch(auxch, 8, DP_SET_POWER, &status, 1); - } + nouveau_dp_dpms(encoder, mode, nv_encoder->dp.datarate, &func); } } From 6e83fda2c055f17780b2feef404f06803a49a261 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Sun, 11 Mar 2012 01:28:48 +1000 Subject: [PATCH 52/57] drm/nvd0/disp: initial implementation of displayport Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_dp.c | 6 +- drivers/gpu/drm/nouveau/nvd0_display.c | 169 +++++++++++++++++++++++++ 2 files changed, 171 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 7be4e06e8438..b3644651d89b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -364,10 +364,8 @@ dp_set_downspread(struct drm_device *dev, struct dp_state *dp, bool enable) u8 *entry, *table = nouveau_dp_bios_data(dev, dp->dcb, &entry); if (table) { if (table[0] >= 0x20 && table[0] <= 0x30) { - if (enable) - script = ROM16(entry[12]); - else - script = ROM16(entry[14]); + if (enable) script = ROM16(entry[12]); + else script = ROM16(entry[14]); } } diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index 296f205249fc..1723733ad9fa 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -1183,6 +1183,143 @@ nvd0_hdmi_disconnect(struct drm_encoder *encoder) /****************************************************************************** * SOR *****************************************************************************/ +static inline u32 +nvd0_sor_dp_lane_map(struct drm_device *dev, struct dcb_entry *dcb, u8 lane) +{ + static const u8 nvd0[] = { 16, 8, 0, 24 }; + return nvd0[lane]; +} + +static void +nvd0_sor_dp_train_set(struct drm_device *dev, struct dcb_entry *dcb, u8 pattern) +{ + const u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1); + const u32 loff = (or * 0x800) + (link * 0x80); + nv_mask(dev, 0x61c110 + loff, 0x0f0f0f0f, 0x01010101 * pattern); +} + +static void +nvd0_sor_dp_train_adj(struct drm_device *dev, struct dcb_entry *dcb, + u8 lane, u8 swing, u8 preem) +{ + const u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1); + const u32 loff = (or * 0x800) + (link * 0x80); + u32 shift = nvd0_sor_dp_lane_map(dev, dcb, lane); + u32 mask = 0x000000ff << shift; + u8 *table, *entry, *config = NULL; + + switch (swing) { + case 0: preem += 0; break; + case 1: preem += 4; break; + case 2: preem += 7; break; + case 3: preem += 9; break; + } + + table = nouveau_dp_bios_data(dev, dcb, &entry); + if (table) { + if (table[0] == 0x30) { + config = entry + table[4]; + config += table[5] * preem; + } + } + + if (!config) { + NV_ERROR(dev, "PDISP: unsupported DP table for chipset\n"); + return; + } + + nv_mask(dev, 0x61c118 + loff, mask, config[1] << shift); + nv_mask(dev, 0x61c120 + loff, mask, config[2] << shift); + nv_mask(dev, 0x61c130 + loff, 0x0000ff00, config[3] << 8); + nv_mask(dev, 0x61c13c + loff, 0x00000000, 0x00000000); +} + +static void +nvd0_sor_dp_link_set(struct drm_device *dev, struct dcb_entry *dcb, int crtc, + int link_nr, u32 link_bw, bool enhframe) +{ + const u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1); + const u32 loff = (or * 0x800) + (link * 0x80); + const u32 soff = (or * 0x800); + u32 dpctrl = nv_rd32(dev, 0x61c10c + loff) & ~0x001f4000; + u32 clksor = nv_rd32(dev, 0x612300 + soff) & ~0x007c0000; + u32 script = 0x0000, lane_mask = 0; + u8 *table, *entry; + int i; + + link_bw /= 27000; + + table = nouveau_dp_bios_data(dev, dcb, &entry); + if (table) { + if (table[0] == 0x30) entry = ROMPTR(dev, entry[10]); + else entry = NULL; + + while (entry) { + if (entry[0] >= link_bw) + break; + entry += 3; + } + + nouveau_bios_run_init_table(dev, script, dcb, crtc); + } + + clksor |= link_bw << 18; + dpctrl |= ((1 << link_nr) - 1) << 16; + if (enhframe) + dpctrl |= 0x00004000; + + for (i = 0; i < link_nr; i++) + lane_mask |= 1 << (nvd0_sor_dp_lane_map(dev, dcb, i) >> 3); + + nv_wr32(dev, 0x612300 + soff, clksor); + nv_wr32(dev, 0x61c10c + loff, dpctrl); + nv_mask(dev, 0x61c130 + loff, 0x0000000f, lane_mask); +} + +static void +nvd0_sor_dp_link_get(struct drm_device *dev, struct dcb_entry *dcb, + u32 *link_nr, u32 *link_bw) +{ + const u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1); + const u32 loff = (or * 0x800) + (link * 0x80); + const u32 soff = (or * 0x800); + u32 dpctrl = nv_rd32(dev, 0x61c10c + loff) & 0x000f0000; + u32 clksor = nv_rd32(dev, 0x612300 + soff); + + if (dpctrl > 0x00030000) *link_nr = 4; + else if (dpctrl > 0x00010000) *link_nr = 2; + else *link_nr = 1; + + *link_bw = (clksor & 0x007c0000) >> 18; + *link_bw *= 27000; +} + +static void +nvd0_sor_dp_calc_tu(struct drm_device *dev, struct dcb_entry *dcb, + u32 crtc, u32 datarate) +{ + const u32 symbol = 100000; + const u32 TU = 64; + u32 link_nr, link_bw; + u64 ratio, value; + + nvd0_sor_dp_link_get(dev, dcb, &link_nr, &link_bw); + + ratio = datarate; + ratio *= symbol; + do_div(ratio, link_nr * link_bw); + + value = (symbol - ratio) * TU; + value *= ratio; + do_div(value, symbol); + do_div(value, symbol); + + value += 5; + value |= 0x08000000; + + nv_wr32(dev, 0x616610 + (crtc * 0x800), value); +} + static void nvd0_sor_dpms(struct drm_encoder *encoder, int mode) { @@ -1215,6 +1352,16 @@ nvd0_sor_dpms(struct drm_encoder *encoder, int mode) nv_mask(dev, 0x61c004 + (or * 0x0800), 0x80000001, dpms_ctrl); nv_wait(dev, 0x61c004 + (or * 0x0800), 0x80000000, 0x00000000); nv_wait(dev, 0x61c030 + (or * 0x0800), 0x10000000, 0x00000000); + + if (nv_encoder->dcb->type == OUTPUT_DP) { + struct dp_train_func func = { + .link_set = nvd0_sor_dp_link_set, + .train_set = nvd0_sor_dp_train_set, + .train_adj = nvd0_sor_dp_train_adj + }; + + nouveau_dp_dpms(encoder, mode, nv_encoder->dp.datarate, &func); + } } static bool @@ -1306,6 +1453,19 @@ nvd0_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode, } break; + case OUTPUT_DP: + if (nv_connector->base.display_info.bpc == 6) + nv_encoder->dp.datarate = mode->clock * 18 / 8; + else + nv_encoder->dp.datarate = mode->clock * 24 / 8; + + if (nv_encoder->dcb->sorconf.link & 1) + mode_ctrl |= 0x00000800; + else + mode_ctrl |= 0x00000900; + + or_config = (mode_ctrl & 0x00000f00) >> 8; + break; default: BUG_ON(1); break; @@ -1313,6 +1473,11 @@ nvd0_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode, nvd0_sor_dpms(encoder, DRM_MODE_DPMS_ON); + if (nv_encoder->dcb->type == OUTPUT_DP) { + nvd0_sor_dp_calc_tu(dev, nv_encoder->dcb, nv_crtc->index, + nv_encoder->dp.datarate); + } + push = evo_wait(dev, EVO_MASTER, 4); if (push) { evo_mthd(push, 0x0200 + (nv_encoder->or * 0x20), 2); @@ -1413,6 +1578,8 @@ lookup_dcb(struct drm_device *dev, int id, u32 mc) case 0x00000100: type = OUTPUT_TMDS; break; case 0x00000200: type = OUTPUT_TMDS; break; case 0x00000500: type = OUTPUT_TMDS; break; + case 0x00000800: type = OUTPUT_DP; break; + case 0x00000900: type = OUTPUT_DP; break; default: NV_ERROR(dev, "PDISP: unknown SOR mc 0x%08x\n", mc); return NULL; @@ -1498,6 +1665,7 @@ nvd0_display_unk2_handler(struct drm_device *dev, u32 crtc, u32 mask) break; case OUTPUT_TMDS: case OUTPUT_LVDS: + case OUTPUT_DP: if (cfg & 0x00000100) tmp = 0x00000101; else @@ -1798,6 +1966,7 @@ nvd0_display_create(struct drm_device *dev) switch (dcbe->type) { case OUTPUT_TMDS: case OUTPUT_LVDS: + case OUTPUT_DP: nvd0_sor_create(connector, dcbe); break; case OUTPUT_ANALOG: From c674844bab6df084ef1c93d65925af6e1bfb06a0 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Sun, 11 Mar 2012 16:13:49 +1000 Subject: [PATCH 53/57] drm/nvd0/disp: fix dcb sor link matching in supervisor handler Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvd0_display.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index 1723733ad9fa..3c2543190bb8 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -1567,19 +1567,19 @@ static struct dcb_entry * lookup_dcb(struct drm_device *dev, int id, u32 mc) { struct drm_nouveau_private *dev_priv = dev->dev_private; - int type, or, i; + int type, or, i, link = -1; if (id < 4) { type = OUTPUT_ANALOG; or = id; } else { switch (mc & 0x00000f00) { - case 0x00000000: type = OUTPUT_LVDS; break; - case 0x00000100: type = OUTPUT_TMDS; break; - case 0x00000200: type = OUTPUT_TMDS; break; - case 0x00000500: type = OUTPUT_TMDS; break; - case 0x00000800: type = OUTPUT_DP; break; - case 0x00000900: type = OUTPUT_DP; break; + case 0x00000000: link = 0; type = OUTPUT_LVDS; break; + case 0x00000100: link = 0; type = OUTPUT_TMDS; break; + case 0x00000200: link = 1; type = OUTPUT_TMDS; break; + case 0x00000500: link = 0; type = OUTPUT_TMDS; break; + case 0x00000800: link = 0; type = OUTPUT_DP; break; + case 0x00000900: link = 1; type = OUTPUT_DP; break; default: NV_ERROR(dev, "PDISP: unknown SOR mc 0x%08x\n", mc); return NULL; @@ -1590,7 +1590,8 @@ lookup_dcb(struct drm_device *dev, int id, u32 mc) for (i = 0; i < dev_priv->vbios.dcb.entries; i++) { struct dcb_entry *dcb = &dev_priv->vbios.dcb.entry[i]; - if (dcb->type == type && (dcb->or & (1 << or))) + if (dcb->type == type && (dcb->or & (1 << or)) && + (link < 0 || link == !(dcb->sorconf.link & 1))) return dcb; } From 6860dc8251eacd1672fa660b85bb59a45350aa70 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 12 Mar 2012 11:16:55 +1000 Subject: [PATCH 54/57] drm/nouveau/dp: account for channel coding overhead in link training Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_dp.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index b3644651d89b..302b2f7d0678 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -422,6 +422,9 @@ nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate, dp.auxch = auxch->drive; dp.dpcd = nv_encoder->dp.dpcd; + /* adjust required bandwidth for 8B/10B coding overhead */ + datarate = (datarate / 8) * 10; + /* some sinks toggle hotplug in response to some of the actions * we take during link training (DP_SET_POWER is one), we need * to ignore them for the moment to avoid races. From 3488c57b983546e6bf4c9e0bfd0f7f2a1292267a Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 12 Mar 2012 11:42:20 +1000 Subject: [PATCH 55/57] drm/nvd0/disp: move syncs/magic setup to or mode_set NVIDIA appear to do these around the same place they do the MODE_CTRL methods, and for DP at least we need to bash some extra bits in "syncs" to keep EVO happy. It's a bit of a guess as to the 6/8bpc, but i have no better idea yet. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvd0_display.c | 59 +++++++++++++++++--------- 1 file changed, 39 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index 3c2543190bb8..ae6d80fff4bd 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -634,8 +634,7 @@ nvd0_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *umode, u32 hactive, hsynce, hbackp, hfrontp, hblanke, hblanks; u32 vactive, vsynce, vbackp, vfrontp, vblanke, vblanks; u32 vblan2e = 0, vblan2s = 1; - u32 magic = 0x31ec6000; - u32 syncs, *push; + u32 *push; int ret; hactive = mode->htotal; @@ -655,15 +654,8 @@ nvd0_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *umode, vblan2e = vactive + vsynce + vbackp; vblan2s = vblan2e + (mode->vdisplay * vscan / ilace); vactive = (vactive * 2) + 1; - magic |= 0x00000001; } - syncs = 0x00000001; - if (mode->flags & DRM_MODE_FLAG_NHSYNC) - syncs |= 0x00000008; - if (mode->flags & DRM_MODE_FLAG_NVSYNC) - syncs |= 0x00000010; - ret = nvd0_crtc_swap_fbs(crtc, old_fb); if (ret) return ret; @@ -683,9 +675,6 @@ nvd0_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *umode, evo_data(push, mode->clock * 1000); evo_data(push, 0x00200000); /* ??? */ evo_data(push, mode->clock * 1000); - evo_mthd(push, 0x0404 + (nv_crtc->index * 0x300), 2); - evo_data(push, syncs); - evo_data(push, magic); evo_mthd(push, 0x04d0 + (nv_crtc->index * 0x300), 2); evo_data(push, 0x00000311); evo_data(push, 0x00000100); @@ -974,13 +963,26 @@ nvd0_dac_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, { struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); - u32 *push; + u32 syncs, magic, *push; + + syncs = 0x00000001; + if (mode->flags & DRM_MODE_FLAG_NHSYNC) + syncs |= 0x00000008; + if (mode->flags & DRM_MODE_FLAG_NVSYNC) + syncs |= 0x00000010; + + magic = 0x31ec6000 | (nv_crtc->index << 25); + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + magic |= 0x00000001; nvd0_dac_dpms(encoder, DRM_MODE_DPMS_ON); - push = evo_wait(encoder->dev, EVO_MASTER, 4); + push = evo_wait(encoder->dev, EVO_MASTER, 8); if (push) { - evo_mthd(push, 0x0180 + (nv_encoder->or * 0x20), 2); + evo_mthd(push, 0x0404 + (nv_crtc->index * 0x300), 2); + evo_data(push, syncs); + evo_data(push, magic); + evo_mthd(push, 0x0180 + (nv_encoder->or * 0x020), 2); evo_data(push, 1 << nv_crtc->index); evo_data(push, 0x00ff); evo_kick(push, encoder->dev, EVO_MASTER); @@ -1404,7 +1406,18 @@ nvd0_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode, struct nouveau_connector *nv_connector; struct nvbios *bios = &dev_priv->vbios; u32 mode_ctrl = (1 << nv_crtc->index); - u32 *push, or_config; + u32 syncs, magic, *push; + u32 or_config; + + syncs = 0x00000001; + if (mode->flags & DRM_MODE_FLAG_NHSYNC) + syncs |= 0x00000008; + if (mode->flags & DRM_MODE_FLAG_NVSYNC) + syncs |= 0x00000010; + + magic = 0x31ec6000 | (nv_crtc->index << 25); + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + magic |= 0x00000001; nv_connector = nouveau_encoder_connector_get(nv_encoder); switch (nv_encoder->dcb->type) { @@ -1454,10 +1467,13 @@ nvd0_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode, } break; case OUTPUT_DP: - if (nv_connector->base.display_info.bpc == 6) + if (nv_connector->base.display_info.bpc == 6) { nv_encoder->dp.datarate = mode->clock * 18 / 8; - else + syncs |= 0x00000140; + } else { nv_encoder->dp.datarate = mode->clock * 24 / 8; + syncs |= 0x00000180; + } if (nv_encoder->dcb->sorconf.link & 1) mode_ctrl |= 0x00000800; @@ -1478,9 +1494,12 @@ nvd0_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode, nv_encoder->dp.datarate); } - push = evo_wait(dev, EVO_MASTER, 4); + push = evo_wait(dev, EVO_MASTER, 8); if (push) { - evo_mthd(push, 0x0200 + (nv_encoder->or * 0x20), 2); + evo_mthd(push, 0x0404 + (nv_crtc->index * 0x300), 2); + evo_data(push, syncs); + evo_data(push, magic); + evo_mthd(push, 0x0200 + (nv_encoder->or * 0x020), 2); evo_data(push, mode_ctrl); evo_data(push, or_config); evo_kick(push, dev, EVO_MASTER); From 4cbb0f8d2b06c72aae3552ff1a0a57814c6ce7d2 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 12 Mar 2012 15:23:44 +1000 Subject: [PATCH 56/57] drm/nvd0/disp: disconnect encoders before reprogramming them Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvd0_display.c | 66 +++++++++++++------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index ae6d80fff4bd..dfb8a951cbbe 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -284,6 +284,8 @@ nvd0_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb, u32 *push; int ret; + evo_sync(crtc->dev, EVO_MASTER); + swap_interval <<= 4; if (swap_interval == 0) swap_interval |= 0x100; @@ -593,7 +595,7 @@ nvd0_crtc_commit(struct drm_crtc *crtc) evo_kick(push, crtc->dev, EVO_MASTER); } - nvd0_crtc_cursor_show(nv_crtc, nv_crtc->cursor.visible, false); + nvd0_crtc_cursor_show(nv_crtc, nv_crtc->cursor.visible, true); nvd0_display_flip_next(crtc, crtc->fb, NULL, 1); } @@ -947,11 +949,6 @@ nvd0_dac_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, return true; } -static void -nvd0_dac_prepare(struct drm_encoder *encoder) -{ -} - static void nvd0_dac_commit(struct drm_encoder *encoder) { @@ -1045,7 +1042,7 @@ nvd0_dac_destroy(struct drm_encoder *encoder) static const struct drm_encoder_helper_funcs nvd0_dac_hfunc = { .dpms = nvd0_dac_dpms, .mode_fixup = nvd0_dac_mode_fixup, - .prepare = nvd0_dac_prepare, + .prepare = nvd0_dac_disconnect, .commit = nvd0_dac_commit, .mode_set = nvd0_dac_mode_set, .disable = nvd0_dac_disconnect, @@ -1385,9 +1382,38 @@ nvd0_sor_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, return true; } +static void +nvd0_sor_disconnect(struct drm_encoder *encoder) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct drm_device *dev = encoder->dev; + u32 *push; + + if (nv_encoder->crtc) { + nvd0_crtc_prepare(nv_encoder->crtc); + + push = evo_wait(dev, EVO_MASTER, 4); + if (push) { + evo_mthd(push, 0x0200 + (nv_encoder->or * 0x20), 1); + evo_data(push, 0x00000000); + evo_mthd(push, 0x0080, 1); + evo_data(push, 0x00000000); + evo_kick(push, dev, EVO_MASTER); + } + + nvd0_hdmi_disconnect(encoder); + + nv_encoder->crtc = NULL; + nv_encoder->last_dpms = DRM_MODE_DPMS_OFF; + } +} + static void nvd0_sor_prepare(struct drm_encoder *encoder) { + nvd0_sor_disconnect(encoder); + if (nouveau_encoder(encoder)->dcb->type == OUTPUT_DP) + evo_sync(encoder->dev, EVO_MASTER); } static void @@ -1508,32 +1534,6 @@ nvd0_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode, nv_encoder->crtc = encoder->crtc; } -static void -nvd0_sor_disconnect(struct drm_encoder *encoder) -{ - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct drm_device *dev = encoder->dev; - u32 *push; - - if (nv_encoder->crtc) { - nvd0_crtc_prepare(nv_encoder->crtc); - - push = evo_wait(dev, EVO_MASTER, 4); - if (push) { - evo_mthd(push, 0x0200 + (nv_encoder->or * 0x20), 1); - evo_data(push, 0x00000000); - evo_mthd(push, 0x0080, 1); - evo_data(push, 0x00000000); - evo_kick(push, dev, EVO_MASTER); - } - - nvd0_hdmi_disconnect(encoder); - - nv_encoder->crtc = NULL; - nv_encoder->last_dpms = DRM_MODE_DPMS_OFF; - } -} - static void nvd0_sor_destroy(struct drm_encoder *encoder) { From 2f5394c3ed573de2ab18cdac503b8045cd16ac5e Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 12 Mar 2012 15:55:43 +1000 Subject: [PATCH 57/57] drm/nouveau: map first page of mmio early and determine chipset earlier Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_drv.h | 4 +- drivers/gpu/drm/nouveau/nouveau_state.c | 121 ++++++++++++------------ 2 files changed, 62 insertions(+), 63 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index cf0e74a2c667..a184ba331273 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -695,14 +695,14 @@ struct nv04_mode_state { }; enum nouveau_card_type { - NV_04 = 0x00, + NV_04 = 0x04, NV_10 = 0x10, NV_20 = 0x20, NV_30 = 0x30, NV_40 = 0x40, NV_50 = 0x50, NV_C0 = 0xc0, - NV_D0 = 0xd0 + NV_D0 = 0xd0, }; struct drm_nouveau_private { diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index 9d6c3ff22e81..9c144fb8bbba 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -993,7 +993,7 @@ static int nouveau_remove_conflicting_drivers(struct drm_device *dev) int nouveau_load(struct drm_device *dev, unsigned long flags) { struct drm_nouveau_private *dev_priv; - uint32_t reg0, strap; + uint32_t reg0 = ~0, strap; resource_size_t mmio_start_offs; int ret; @@ -1012,10 +1012,65 @@ int nouveau_load(struct drm_device *dev, unsigned long flags) NV_DEBUG(dev, "vendor: 0x%X device: 0x%X class: 0x%X\n", dev->pci_vendor, dev->pci_device, dev->pdev->class); - /* resource 0 is mmio regs */ - /* resource 1 is linear FB */ - /* resource 2 is RAMIN (mmio regs + 0x1000000) */ - /* resource 6 is bios */ + /* first up, map the start of mmio and determine the chipset */ + dev_priv->mmio = ioremap(pci_resource_start(dev->pdev, 0), PAGE_SIZE); + if (dev_priv->mmio) { +#ifdef __BIG_ENDIAN + /* put the card into big-endian mode if it's not */ + if (nv_rd32(dev, NV03_PMC_BOOT_1) != 0x01000001) + nv_wr32(dev, NV03_PMC_BOOT_1, 0x01000001); + DRM_MEMORYBARRIER(); +#endif + + /* determine chipset and derive architecture from it */ + reg0 = nv_rd32(dev, NV03_PMC_BOOT_0); + if ((reg0 & 0x0f000000) > 0) { + dev_priv->chipset = (reg0 & 0xff00000) >> 20; + switch (dev_priv->chipset & 0xf0) { + case 0x10: + case 0x20: + case 0x30: + dev_priv->card_type = dev_priv->chipset & 0xf0; + break; + case 0x40: + case 0x60: + dev_priv->card_type = NV_40; + break; + case 0x50: + case 0x80: + case 0x90: + case 0xa0: + dev_priv->card_type = NV_50; + break; + case 0xc0: + dev_priv->card_type = NV_C0; + break; + case 0xd0: + dev_priv->card_type = NV_D0; + break; + default: + break; + } + } else + if ((reg0 & 0xff00fff0) == 0x20004000) { + if (reg0 & 0x00f00000) + dev_priv->chipset = 0x05; + else + dev_priv->chipset = 0x04; + dev_priv->card_type = NV_04; + } + + iounmap(dev_priv->mmio); + } + + if (!dev_priv->card_type) { + NV_ERROR(dev, "unsupported chipset 0x%08x\n", reg0); + ret = -EINVAL; + goto err_priv; + } + + NV_INFO(dev, "Detected an NV%2x generation card (0x%08x)\n", + dev_priv->card_type, reg0); /* map the mmio regs */ mmio_start_offs = pci_resource_start(dev->pdev, 0); @@ -1029,62 +1084,6 @@ int nouveau_load(struct drm_device *dev, unsigned long flags) NV_DEBUG(dev, "regs mapped ok at 0x%llx\n", (unsigned long long)mmio_start_offs); -#ifdef __BIG_ENDIAN - /* Put the card in BE mode if it's not */ - if (nv_rd32(dev, NV03_PMC_BOOT_1) != 0x01000001) - nv_wr32(dev, NV03_PMC_BOOT_1, 0x01000001); - - DRM_MEMORYBARRIER(); -#endif - - /* Time to determine the card architecture */ - reg0 = nv_rd32(dev, NV03_PMC_BOOT_0); - - /* We're dealing with >=NV10 */ - if ((reg0 & 0x0f000000) > 0) { - /* Bit 27-20 contain the architecture in hex */ - dev_priv->chipset = (reg0 & 0xff00000) >> 20; - /* NV04 or NV05 */ - } else if ((reg0 & 0xff00fff0) == 0x20004000) { - if (reg0 & 0x00f00000) - dev_priv->chipset = 0x05; - else - dev_priv->chipset = 0x04; - } else - dev_priv->chipset = 0xff; - - switch (dev_priv->chipset & 0xf0) { - case 0x00: - case 0x10: - case 0x20: - case 0x30: - dev_priv->card_type = dev_priv->chipset & 0xf0; - break; - case 0x40: - case 0x60: - dev_priv->card_type = NV_40; - break; - case 0x50: - case 0x80: - case 0x90: - case 0xa0: - dev_priv->card_type = NV_50; - break; - case 0xc0: - dev_priv->card_type = NV_C0; - break; - case 0xd0: - dev_priv->card_type = NV_D0; - break; - default: - NV_INFO(dev, "Unsupported chipset 0x%08x\n", reg0); - ret = -EINVAL; - goto err_mmio; - } - - NV_INFO(dev, "Detected an NV%2x generation card (0x%08x)\n", - dev_priv->card_type, reg0); - /* determine frequency of timing crystal */ strap = nv_rd32(dev, 0x101000); if ( dev_priv->chipset < 0x17 ||