From ffbc559b0699891c6deb9fd2b4750671eab94999 Mon Sep 17 00:00:00 2001 From: Emil Velikov Date: Sun, 21 Aug 2011 22:48:12 +0100 Subject: drm/nv50/crtc: Bail out if FB is not bound to crtc Fixes possbile NULL pointer dereference Resolves 'kernel crash in nv50_crtc_do_mode_set_base during shutdown' https://bugs.freedesktop.org/show_bug.cgi?id=40005 Signed-off-by: Emil Velikov Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nv50_crtc.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nv50_crtc.c b/drivers/gpu/drm/nouveau/nv50_crtc.c index 46ad59ea218..5d989073ba6 100644 --- a/drivers/gpu/drm/nouveau/nv50_crtc.c +++ b/drivers/gpu/drm/nouveau/nv50_crtc.c @@ -519,12 +519,18 @@ nv50_crtc_do_mode_set_base(struct drm_crtc *crtc, struct drm_device *dev = nv_crtc->base.dev; struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_channel *evo = nv50_display(dev)->master; - struct drm_framebuffer *drm_fb = nv_crtc->base.fb; - struct nouveau_framebuffer *fb = nouveau_framebuffer(drm_fb); + struct drm_framebuffer *drm_fb; + struct nouveau_framebuffer *fb; int ret; NV_DEBUG_KMS(dev, "index %d\n", nv_crtc->index); + /* no fb bound */ + if (!atomic && !crtc->fb) { + NV_DEBUG_KMS(dev, "No FB bound\n"); + return 0; + } + /* If atomic, we want to switch to the fb we were passed, so * now we update pointers to do that. (We don't pin; just * assume we're already pinned and update the base address.) @@ -533,6 +539,8 @@ nv50_crtc_do_mode_set_base(struct drm_crtc *crtc, drm_fb = passed_fb; fb = nouveau_framebuffer(passed_fb); } else { + drm_fb = crtc->fb; + fb = nouveau_framebuffer(crtc->fb); /* If not atomic, we can go ahead and pin, and unpin the * old fb we were passed. */ -- cgit v1.2.3-70-g09d2 From cfd8be088e97a762902a4820f501fb13102984e9 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 23 Aug 2011 10:23:11 +1000 Subject: drm/nouveau: fix oops on pre-semaphore hardware Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_fence.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c index 8d02d875376..c919cfc8f2f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fence.c +++ b/drivers/gpu/drm/nouveau/nouveau_fence.c @@ -530,7 +530,8 @@ nouveau_fence_channel_init(struct nouveau_channel *chan) nouveau_gpuobj_ref(NULL, &obj); if (ret) return ret; - } else { + } else + if (USE_SEMA(dev)) { /* map fence bo into channel's vm */ ret = nouveau_bo_vma_add(dev_priv->fence.bo, chan->vm, &chan->fence.vma); -- cgit v1.2.3-70-g09d2 From 17c8b960930da3599e47801a54ac0ea1070545d2 Mon Sep 17 00:00:00 2001 From: Marcin Slusarz Date: Mon, 22 Aug 2011 23:14:05 +0200 Subject: drm/nouveau: properly handle allocation failure in nouveau_sgdma_populate Not cleaning after alloc failure would result in crash on destroy, because nouveau_sgdma_clear assumes "ttm_alloced" to be not null when "pages" is not null. Signed-off-by: Marcin Slusarz Cc: stable@kernel.org Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_sgdma.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_sgdma.c b/drivers/gpu/drm/nouveau/nouveau_sgdma.c index c444cadbf84..88062de26b0 100644 --- a/drivers/gpu/drm/nouveau/nouveau_sgdma.c +++ b/drivers/gpu/drm/nouveau/nouveau_sgdma.c @@ -37,8 +37,11 @@ nouveau_sgdma_populate(struct ttm_backend *be, unsigned long num_pages, return -ENOMEM; nvbe->ttm_alloced = kmalloc(sizeof(bool) * num_pages, GFP_KERNEL); - if (!nvbe->ttm_alloced) + if (!nvbe->ttm_alloced) { + kfree(nvbe->pages); + nvbe->pages = NULL; return -ENOMEM; + } nvbe->nr_pages = 0; while (num_pages--) { -- cgit v1.2.3-70-g09d2 From 1bf27066017c820b8ab2a1ac8430ea470c2de0c3 Mon Sep 17 00:00:00 2001 From: Marcin Slusarz Date: Mon, 22 Aug 2011 23:22:13 +0200 Subject: drm/nouveau: fix nv04_sgdma_bind on non-"4kB pages" archs nv04_sgdma_bind binds the same page multiple times on architectures where PAGE_SIZE != 4096. Let's fix it. Signed-off-by: Marcin Slusarz Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_sgdma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_sgdma.c b/drivers/gpu/drm/nouveau/nouveau_sgdma.c index 88062de26b0..2706cb3d871 100644 --- a/drivers/gpu/drm/nouveau/nouveau_sgdma.c +++ b/drivers/gpu/drm/nouveau/nouveau_sgdma.c @@ -129,7 +129,7 @@ nv04_sgdma_bind(struct ttm_backend *be, struct ttm_mem_reg *mem) for (j = 0; j < PAGE_SIZE / NV_CTXDMA_PAGE_SIZE; j++, pte++) { nv_wo32(gpuobj, (pte * 4) + 0, offset_l | 3); - dma_offset += NV_CTXDMA_PAGE_SIZE; + offset_l += NV_CTXDMA_PAGE_SIZE; } } -- cgit v1.2.3-70-g09d2 From 0e83bb4eee1c504ab98367b4f7d1bc337ab213d2 Mon Sep 17 00:00:00 2001 From: Emil Velikov Date: Thu, 25 Aug 2011 21:36:51 +0100 Subject: drm/nv04/crtc: Bail out if FB is not bound to crtc This commit resolves a possible 'NULL pointer dereference' It uses the same approach as radeon, intel and nouveau/nv50 Fixes bug 'Nouveau: Kernel oops when unplugging external monitor' https://bugs.freedesktop.org/show_bug.cgi?id=40336 Signed-off-by: Emil Velikov Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nv04_crtc.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nv04_crtc.c b/drivers/gpu/drm/nouveau/nv04_crtc.c index 118261d4927..5e45398a9e2 100644 --- a/drivers/gpu/drm/nouveau/nv04_crtc.c +++ b/drivers/gpu/drm/nouveau/nv04_crtc.c @@ -781,11 +781,20 @@ nv04_crtc_do_mode_set_base(struct drm_crtc *crtc, struct drm_device *dev = crtc->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; struct nv04_crtc_reg *regp = &dev_priv->mode_reg.crtc_reg[nv_crtc->index]; - struct drm_framebuffer *drm_fb = nv_crtc->base.fb; - struct nouveau_framebuffer *fb = nouveau_framebuffer(drm_fb); + struct drm_framebuffer *drm_fb; + struct nouveau_framebuffer *fb; int arb_burst, arb_lwm; int ret; + NV_DEBUG_KMS(dev, "index %d\n", nv_crtc->index); + + /* no fb bound */ + if (!atomic && !crtc->fb) { + NV_DEBUG_KMS(dev, "No FB bound\n"); + return 0; + } + + /* If atomic, we want to switch to the fb we were passed, so * now we update pointers to do that. (We don't pin; just * assume we're already pinned and update the base address.) @@ -794,6 +803,8 @@ nv04_crtc_do_mode_set_base(struct drm_crtc *crtc, drm_fb = passed_fb; fb = nouveau_framebuffer(passed_fb); } else { + drm_fb = crtc->fb; + fb = nouveau_framebuffer(crtc->fb); /* If not atomic, we can go ahead and pin, and unpin the * old fb we were passed. */ -- cgit v1.2.3-70-g09d2 From c3450239c78a4ef6c10da13dfc18831f43dbe0c5 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 9 Jun 2011 13:45:31 +1000 Subject: drm/nouveau/pm: store voltage in microvolts Instead of 10s of millivolts, to match fermi vbios. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_drv.h | 8 ++++---- drivers/gpu/drm/nouveau/nouveau_perf.c | 11 ++++++----- drivers/gpu/drm/nouveau/nouveau_pm.c | 2 +- drivers/gpu/drm/nouveau/nouveau_volt.c | 2 +- 4 files changed, 12 insertions(+), 11 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index d7d51deb34b..39d6bb313ba 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -414,8 +414,8 @@ struct nouveau_gpio_engine { }; struct nouveau_pm_voltage_level { - u8 voltage; - u8 vid; + u32 voltage; /* microvolts */ + u8 vid; }; struct nouveau_pm_voltage { @@ -451,8 +451,8 @@ struct nouveau_pm_level { u32 unk05; u32 unk0a; - u8 voltage; - u8 fanspeed; + u32 voltage; /* microvolts */ + u8 fanspeed; u16 memscript; struct nouveau_pm_memtiming *timing; diff --git a/drivers/gpu/drm/nouveau/nouveau_perf.c b/drivers/gpu/drm/nouveau/nouveau_perf.c index ef9dec0e6f8..117ce16f358 100644 --- a/drivers/gpu/drm/nouveau/nouveau_perf.c +++ b/drivers/gpu/drm/nouveau/nouveau_perf.c @@ -203,7 +203,8 @@ nouveau_perf_init(struct drm_device *dev) case 0x13: case 0x15: perflvl->fanspeed = entry[55]; - perflvl->voltage = (recordlen > 56) ? entry[56] : 0; + if (recordlen > 56) + perflvl->voltage = entry[56] * 10000; perflvl->core = ROM32(entry[1]) * 10; perflvl->memory = ROM32(entry[5]) * 20; break; @@ -211,7 +212,7 @@ nouveau_perf_init(struct drm_device *dev) case 0x23: case 0x24: perflvl->fanspeed = entry[4]; - perflvl->voltage = entry[5]; + perflvl->voltage = entry[5] * 10000; perflvl->core = ROM16(entry[6]) * 1000; if (dev_priv->chipset == 0x49 || @@ -223,7 +224,7 @@ nouveau_perf_init(struct drm_device *dev) break; case 0x25: perflvl->fanspeed = entry[4]; - perflvl->voltage = entry[5]; + perflvl->voltage = entry[5] * 10000; perflvl->core = ROM16(entry[6]) * 1000; perflvl->shader = ROM16(entry[10]) * 1000; perflvl->memory = ROM16(entry[12]) * 1000; @@ -232,7 +233,7 @@ nouveau_perf_init(struct drm_device *dev) perflvl->memscript = ROM16(entry[2]); case 0x35: perflvl->fanspeed = entry[6]; - perflvl->voltage = entry[7]; + perflvl->voltage = entry[7] * 10000; perflvl->core = ROM16(entry[8]) * 1000; perflvl->shader = ROM16(entry[10]) * 1000; perflvl->memory = ROM16(entry[12]) * 1000; @@ -242,7 +243,7 @@ nouveau_perf_init(struct drm_device *dev) case 0x40: #define subent(n) entry[perf[2] + ((n) * perf[3])] perflvl->fanspeed = 0; /*XXX*/ - perflvl->voltage = entry[2]; + perflvl->voltage = entry[2] * 10000; if (dev_priv->card_type == NV_50) { perflvl->core = ROM16(subent(0)) & 0xfff; perflvl->shader = ROM16(subent(1)) & 0xfff; diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index da8d994d5e8..f5703ef6851 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -168,7 +168,7 @@ nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len) v[0] = '\0'; if (perflvl->voltage) - snprintf(v, sizeof(v), " voltage %dmV", perflvl->voltage * 10); + snprintf(v, sizeof(v), " voltage %dmV", perflvl->voltage / 1000); f[0] = '\0'; if (perflvl->fanspeed) diff --git a/drivers/gpu/drm/nouveau/nouveau_volt.c b/drivers/gpu/drm/nouveau/nouveau_volt.c index 75e872741d9..9eec27581b1 100644 --- a/drivers/gpu/drm/nouveau/nouveau_volt.c +++ b/drivers/gpu/drm/nouveau/nouveau_volt.c @@ -203,7 +203,7 @@ nouveau_volt_init(struct drm_device *dev) entry = volt + headerlen; for (i = 0; i < entries; i++, entry += recordlen) { - voltage->level[i].voltage = entry[0]; + voltage->level[i].voltage = entry[0] * 10000; voltage->level[i].vid = entry[1] >> vidshift; } voltage->nr_level = entries; -- cgit v1.2.3-70-g09d2 From f60dfb996c510d9f197d67983a7f61eaf1c8ad67 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 9 Jun 2011 16:16:38 +1000 Subject: drm/nouveau/pm: initial attempt at parsing volt 0x40 Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_volt.c | 43 +++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 8 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_volt.c b/drivers/gpu/drm/nouveau/nouveau_volt.c index 9eec27581b1..471daec933f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_volt.c +++ b/drivers/gpu/drm/nouveau/nouveau_volt.c @@ -170,6 +170,13 @@ nouveau_volt_init(struct drm_device *dev) */ vidshift = 2; break; + case 0x40: + headerlen = volt[1]; + recordlen = volt[2]; + entries = volt[3]; /* not a clue what the entries are for.. */ + vidmask = volt[11]; /* guess.. */ + vidshift = 0; + break; default: NV_WARN(dev, "voltage table 0x%02x unknown\n", volt[0]); return; @@ -197,16 +204,36 @@ nouveau_volt_init(struct drm_device *dev) } /* parse vbios entries into common format */ - voltage->level = kcalloc(entries, sizeof(*voltage->level), GFP_KERNEL); - if (!voltage->level) - return; + if (volt[0] < 0x40) { + voltage->nr_level = entries; + voltage->level = + kcalloc(entries, sizeof(*voltage->level), GFP_KERNEL); + if (!voltage->level) + return; - entry = volt + headerlen; - for (i = 0; i < entries; i++, entry += recordlen) { - voltage->level[i].voltage = entry[0] * 10000; - voltage->level[i].vid = entry[1] >> vidshift; + entry = volt + headerlen; + for (i = 0; i < entries; i++, entry += recordlen) { + voltage->level[i].voltage = entry[0] * 10000; + voltage->level[i].vid = entry[1] >> vidshift; + } + } else { + u32 volt_uv = ROM32(volt[4]); + s16 step_uv = ROM16(volt[8]); + u8 vid; + + voltage->nr_level = voltage->vid_mask + 1; + voltage->level = kcalloc(voltage->nr_level, + sizeof(*voltage->level), GFP_KERNEL); + if (!voltage->level) + return; + + for (vid = 0; vid <= voltage->vid_mask; vid++) { + voltage->level[vid].voltage = volt_uv; + voltage->level[vid].vid = vid; + volt_uv += step_uv; + } } - voltage->nr_level = entries; + voltage->supported = true; } -- cgit v1.2.3-70-g09d2 From a31214ef3e6cf427afe76b54c67e11c92d2aaeb8 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 9 Jun 2011 16:17:48 +1000 Subject: drm/nouveau/pm: add yet another vid gpio tag Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_volt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_volt.c b/drivers/gpu/drm/nouveau/nouveau_volt.c index 471daec933f..5389aba46d6 100644 --- a/drivers/gpu/drm/nouveau/nouveau_volt.c +++ b/drivers/gpu/drm/nouveau/nouveau_volt.c @@ -27,7 +27,7 @@ #include "nouveau_drv.h" #include "nouveau_pm.h" -static const enum dcb_gpio_tag vidtag[] = { 0x04, 0x05, 0x06, 0x1a }; +static const enum dcb_gpio_tag vidtag[] = { 0x04, 0x05, 0x06, 0x1a, 0x73 }; static int nr_vidtag = sizeof(vidtag) / sizeof(vidtag[0]); int -- cgit v1.2.3-70-g09d2 From 3b5565ddfd8fe71f6470a5d240a6bb50ba90d4ff Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 9 Jun 2011 16:57:07 +1000 Subject: drm/nouveau/pm: add support for parsing perflvl voltage on fermi chips Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_drv.h | 3 +- drivers/gpu/drm/nouveau/nouveau_perf.c | 58 ++++++++++++++++++++++++++++++---- drivers/gpu/drm/nouveau/nouveau_pm.c | 24 +++++++++----- 3 files changed, 69 insertions(+), 16 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 39d6bb313ba..84a19a5fc55 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -451,7 +451,8 @@ struct nouveau_pm_level { u32 unk05; u32 unk0a; - u32 voltage; /* microvolts */ + u32 volt_min; /* microvolts */ + u32 volt_max; u8 fanspeed; u16 memscript; diff --git a/drivers/gpu/drm/nouveau/nouveau_perf.c b/drivers/gpu/drm/nouveau/nouveau_perf.c index 117ce16f358..18d1d995b53 100644 --- a/drivers/gpu/drm/nouveau/nouveau_perf.c +++ b/drivers/gpu/drm/nouveau/nouveau_perf.c @@ -134,6 +134,49 @@ nouveau_perf_timing(struct drm_device *dev, struct bit_entry *P, return &pm->memtimings.timing[entry[1]]; } +static void +nouveau_perf_voltage(struct drm_device *dev, struct bit_entry *P, + struct nouveau_pm_level *perflvl) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nvbios *bios = &dev_priv->vbios; + u8 *vmap; + int id; + + id = perflvl->volt_min; + perflvl->volt_min = 0; + + /* pre-fermi vbios stores the voltage level directly in the + * perflvl entry as a multiple of 10mV + */ + if (dev_priv->card_type < NV_C0) { + perflvl->volt_min = id * 10000; + perflvl->volt_max = perflvl->volt_min; + return; + } + + /* from fermi onwards, 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) { + NV_DEBUG(dev, "where's our volt map table ptr? %d %d\n", + P->version, P->length); + return; + } + + vmap = ROMPTR(bios, P->data[32]); + if (!vmap) { + NV_DEBUG(dev, "volt map table pointer invalid\n"); + return; + } + + if (id < vmap[3]) { + vmap += vmap[1] + (vmap[2] * id); + perflvl->volt_min = ROM32(vmap[0]); + perflvl->volt_max = ROM32(vmap[4]); + } +} + void nouveau_perf_init(struct drm_device *dev) { @@ -204,7 +247,7 @@ nouveau_perf_init(struct drm_device *dev) case 0x15: perflvl->fanspeed = entry[55]; if (recordlen > 56) - perflvl->voltage = entry[56] * 10000; + perflvl->volt_min = entry[56]; perflvl->core = ROM32(entry[1]) * 10; perflvl->memory = ROM32(entry[5]) * 20; break; @@ -212,7 +255,7 @@ nouveau_perf_init(struct drm_device *dev) case 0x23: case 0x24: perflvl->fanspeed = entry[4]; - perflvl->voltage = entry[5] * 10000; + perflvl->volt_min = entry[5]; perflvl->core = ROM16(entry[6]) * 1000; if (dev_priv->chipset == 0x49 || @@ -224,7 +267,7 @@ nouveau_perf_init(struct drm_device *dev) break; case 0x25: perflvl->fanspeed = entry[4]; - perflvl->voltage = entry[5] * 10000; + perflvl->volt_min = entry[5]; perflvl->core = ROM16(entry[6]) * 1000; perflvl->shader = ROM16(entry[10]) * 1000; perflvl->memory = ROM16(entry[12]) * 1000; @@ -233,7 +276,7 @@ nouveau_perf_init(struct drm_device *dev) perflvl->memscript = ROM16(entry[2]); case 0x35: perflvl->fanspeed = entry[6]; - perflvl->voltage = entry[7] * 10000; + perflvl->volt_min = entry[7]; perflvl->core = ROM16(entry[8]) * 1000; perflvl->shader = ROM16(entry[10]) * 1000; perflvl->memory = ROM16(entry[12]) * 1000; @@ -243,7 +286,7 @@ nouveau_perf_init(struct drm_device *dev) case 0x40: #define subent(n) entry[perf[2] + ((n) * perf[3])] perflvl->fanspeed = 0; /*XXX*/ - perflvl->voltage = entry[2] * 10000; + perflvl->volt_min = entry[2]; if (dev_priv->card_type == NV_50) { perflvl->core = ROM16(subent(0)) & 0xfff; perflvl->shader = ROM16(subent(1)) & 0xfff; @@ -263,8 +306,9 @@ nouveau_perf_init(struct drm_device *dev) } /* make sure vid is valid */ - if (pm->voltage.supported && perflvl->voltage) { - vid = nouveau_volt_vid_lookup(dev, perflvl->voltage); + nouveau_perf_voltage(dev, &P, 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; diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index f5703ef6851..cab576b2f15 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -64,11 +64,11 @@ nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl) if (perflvl == pm->cur) return 0; - if (pm->voltage.supported && pm->voltage_set && perflvl->voltage) { - ret = pm->voltage_set(dev, perflvl->voltage); + if (pm->voltage.supported && pm->voltage_set && perflvl->volt_min) { + ret = pm->voltage_set(dev, perflvl->volt_min); if (ret) { NV_ERROR(dev, "voltage_set %d failed: %d\n", - perflvl->voltage, ret); + perflvl->volt_min, ret); } } @@ -146,8 +146,10 @@ nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) if (pm->voltage.supported && pm->voltage_get) { ret = pm->voltage_get(dev); - if (ret > 0) - perflvl->voltage = ret; + if (ret > 0) { + perflvl->volt_min = ret; + perflvl->volt_max = ret; + } } return 0; @@ -156,7 +158,7 @@ nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) static void nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len) { - char c[16], s[16], v[16], f[16], t[16]; + char c[16], s[16], v[32], f[16], t[16]; c[0] = '\0'; if (perflvl->core) @@ -167,8 +169,14 @@ nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len) snprintf(s, sizeof(s), " shader %dMHz", perflvl->shader / 1000); v[0] = '\0'; - if (perflvl->voltage) - snprintf(v, sizeof(v), " voltage %dmV", perflvl->voltage / 1000); + if (perflvl->volt_min && perflvl->volt_min != perflvl->volt_max) { + snprintf(v, sizeof(v), " voltage %dmV-%dmV", + perflvl->volt_min / 1000, perflvl->volt_max / 1000); + } else + if (perflvl->volt_min) { + snprintf(v, sizeof(v), " voltage %dmV", + perflvl->volt_min / 1000); + } f[0] = '\0'; if (perflvl->fanspeed) -- cgit v1.2.3-70-g09d2 From 93dccbedeb2280ca2c234530236b950b232afa65 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 9 Jun 2011 17:27:47 +1000 Subject: drm/nouveau/pm: show any info we can manage to glean on current perflvl Previously wouldn't show detected voltage if we couldn't figure out the clock frequencies.. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_pm.c | 48 +++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 23 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index cab576b2f15..53d2ad8a049 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -123,26 +123,25 @@ nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) struct nouveau_pm_engine *pm = &dev_priv->engine.pm; int ret; - if (!pm->clock_get) - return -EINVAL; - memset(perflvl, 0, sizeof(*perflvl)); - ret = pm->clock_get(dev, PLL_CORE); - if (ret > 0) - perflvl->core = ret; + if (pm->clock_get) { + ret = pm->clock_get(dev, PLL_CORE); + if (ret > 0) + perflvl->core = ret; - ret = pm->clock_get(dev, PLL_MEMORY); - if (ret > 0) - perflvl->memory = ret; + ret = pm->clock_get(dev, PLL_MEMORY); + if (ret > 0) + perflvl->memory = ret; - ret = pm->clock_get(dev, PLL_SHADER); - if (ret > 0) - perflvl->shader = ret; + ret = pm->clock_get(dev, PLL_SHADER); + if (ret > 0) + perflvl->shader = ret; - ret = pm->clock_get(dev, PLL_UNK05); - if (ret > 0) - perflvl->unk05 = ret; + ret = pm->clock_get(dev, PLL_UNK05); + if (ret > 0) + perflvl->unk05 = ret; + } if (pm->voltage.supported && pm->voltage_get) { ret = pm->voltage_get(dev); @@ -158,7 +157,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]; + char c[16], s[16], v[32], f[16], t[16], m[16]; c[0] = '\0'; if (perflvl->core) @@ -168,6 +167,10 @@ nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len) if (perflvl->shader) snprintf(s, sizeof(s), " shader %dMHz", perflvl->shader / 1000); + m[0] = '\0'; + if (perflvl->memory) + snprintf(m, sizeof(m), " memory %dMHz", perflvl->memory / 1000); + v[0] = '\0'; if (perflvl->volt_min && perflvl->volt_min != perflvl->volt_max) { snprintf(v, sizeof(v), " voltage %dmV-%dmV", @@ -186,8 +189,7 @@ nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len) if (perflvl->timing) snprintf(t, sizeof(t), " timing %d", perflvl->timing->id); - snprintf(ptr, len, "memory %dMHz%s%s%s%s%s\n", perflvl->memory / 1000, - c, s, v, f, t); + snprintf(ptr, len, "%s%s%s%s%s%s\n", c, s, m, t, v, f); } static ssize_t @@ -198,7 +200,7 @@ nouveau_pm_get_perflvl_info(struct device *d, char *ptr = buf; int len = PAGE_SIZE; - snprintf(ptr, len, "%d: ", perflvl->id); + snprintf(ptr, len, "%d:", perflvl->id); ptr += strlen(buf); len -= strlen(buf); @@ -219,9 +221,9 @@ nouveau_pm_get_perflvl(struct device *d, struct device_attribute *a, char *buf) if (!pm->cur) snprintf(ptr, len, "setting: boot\n"); else if (pm->cur == &pm->boot) - snprintf(ptr, len, "setting: boot\nc: "); + snprintf(ptr, len, "setting: boot\nc:"); else - snprintf(ptr, len, "setting: static %d\nc: ", pm->cur->id); + snprintf(ptr, len, "setting: static %d\nc:", pm->cur->id); ptr += strlen(buf); len -= strlen(buf); @@ -496,7 +498,7 @@ nouveau_pm_init(struct drm_device *dev) 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); + NV_INFO(dev, "%d:%s", pm->perflvl[i].id, info); } /* determine current ("boot") performance level */ @@ -506,7 +508,7 @@ nouveau_pm_init(struct drm_device *dev) pm->cur = &pm->boot; nouveau_pm_perflvl_info(&pm->boot, info, sizeof(info)); - NV_INFO(dev, "c: %s", info); + NV_INFO(dev, "c:%s", info); } /* switch performance levels now if requested */ -- cgit v1.2.3-70-g09d2 From 3c71c2330b56ca4304b6cd0273ba861657a60a53 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 9 Jun 2011 17:34:02 +1000 Subject: drm/nvc0/pm: enable voltage_get I don't have a terribly good reason for not enabling voltage_set too, but, lets wait and see. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_state.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index 10656e430b4..48bddd5b350 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -422,6 +422,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->vram.put = nv50_vram_del; engine->vram.flags_valid = nvc0_vram_flags_valid; engine->pm.temp_get = nv84_temp_get; + engine->pm.voltage_get = nouveau_voltage_gpio_get; break; default: NV_ERROR(dev, "NV%02x unsupported\n", dev_priv->chipset); -- cgit v1.2.3-70-g09d2 From da1dc4cfecdf314241cc5e0c5df1f66b4cc80cc7 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 10 Jun 2011 12:07:09 +1000 Subject: drm/nouveau/pm: allow voltage-only perflvl set, enable nvc0 Okay, my card didn't blow up. Lets turn it on! Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_pm.c | 13 ++++++------- drivers/gpu/drm/nouveau/nouveau_state.c | 1 + 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index 53d2ad8a049..179067a1d26 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -72,10 +72,12 @@ nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl) } } - nouveau_pm_clock_set(dev, perflvl, PLL_CORE, perflvl->core); - nouveau_pm_clock_set(dev, perflvl, PLL_SHADER, perflvl->shader); - nouveau_pm_clock_set(dev, perflvl, PLL_MEMORY, perflvl->memory); - nouveau_pm_clock_set(dev, perflvl, PLL_UNK05, perflvl->unk05); + if (pm->clock_set) { + nouveau_pm_clock_set(dev, perflvl, PLL_CORE, perflvl->core); + nouveau_pm_clock_set(dev, perflvl, PLL_SHADER, perflvl->shader); + nouveau_pm_clock_set(dev, perflvl, PLL_MEMORY, perflvl->memory); + nouveau_pm_clock_set(dev, perflvl, PLL_UNK05, perflvl->unk05); + } pm->cur = perflvl; return 0; @@ -92,9 +94,6 @@ nouveau_pm_profile_set(struct drm_device *dev, const char *profile) if (nouveau_perflvl_wr != 7777) return -EPERM; - if (!pm->clock_set) - return -EINVAL; - if (!strncmp(profile, "boot", 4)) perflvl = &pm->boot; else { diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index 48bddd5b350..bd4c8f56b5d 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -423,6 +423,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->vram.flags_valid = nvc0_vram_flags_valid; engine->pm.temp_get = nv84_temp_get; engine->pm.voltage_get = nouveau_voltage_gpio_get; + engine->pm.voltage_set = nouveau_voltage_gpio_set; break; default: NV_ERROR(dev, "NV%02x unsupported\n", dev_priv->chipset); -- cgit v1.2.3-70-g09d2 From 03ce8d9e63199fd5983129941a6694123b885753 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 10 Jun 2011 15:33:11 +1000 Subject: drm/nouveau/pm: some fermi chipsets still use volt 0x30 Fun, fun. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_drv.h | 1 + drivers/gpu/drm/nouveau/nouveau_perf.c | 8 ++++---- drivers/gpu/drm/nouveau/nouveau_volt.c | 3 ++- 3 files changed, 7 insertions(+), 5 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 84a19a5fc55..491158d7304 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -420,6 +420,7 @@ struct nouveau_pm_voltage_level { struct nouveau_pm_voltage { bool supported; + u8 version; u8 vid_mask; struct nouveau_pm_voltage_level *level; diff --git a/drivers/gpu/drm/nouveau/nouveau_perf.c b/drivers/gpu/drm/nouveau/nouveau_perf.c index 18d1d995b53..b0e995fdcba 100644 --- a/drivers/gpu/drm/nouveau/nouveau_perf.c +++ b/drivers/gpu/drm/nouveau/nouveau_perf.c @@ -146,16 +146,16 @@ nouveau_perf_voltage(struct drm_device *dev, struct bit_entry *P, id = perflvl->volt_min; perflvl->volt_min = 0; - /* pre-fermi vbios stores the voltage level directly in the - * perflvl entry as a multiple of 10mV + /* boards using voltage table version <0x40 store the voltage + * level directly in the perflvl entry as a multiple of 10mV */ - if (dev_priv->card_type < NV_C0) { + if (dev_priv->engine.pm.voltage.version < 0x40) { perflvl->volt_min = id * 10000; perflvl->volt_max = perflvl->volt_min; return; } - /* from fermi onwards, the perflvl stores an index into yet another + /* 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) { diff --git a/drivers/gpu/drm/nouveau/nouveau_volt.c b/drivers/gpu/drm/nouveau/nouveau_volt.c index 5389aba46d6..86d03e15735 100644 --- a/drivers/gpu/drm/nouveau/nouveau_volt.c +++ b/drivers/gpu/drm/nouveau/nouveau_volt.c @@ -204,7 +204,8 @@ nouveau_volt_init(struct drm_device *dev) } /* parse vbios entries into common format */ - if (volt[0] < 0x40) { + voltage->version = volt[0]; + if (voltage->version < 0x40) { voltage->nr_level = entries; voltage->level = kcalloc(entries, sizeof(*voltage->level), GFP_KERNEL); -- cgit v1.2.3-70-g09d2 From 3b0582d31d11faad1b40377d5adb28f0aa545fce Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 17 Jun 2011 11:09:40 +1000 Subject: drm/nva3/pm: rewrite clock readback functions, far more correct now Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nva3_pm.c | 95 +++++++++++++++++++++++++-------------- 1 file changed, 62 insertions(+), 33 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nva3_pm.c b/drivers/gpu/drm/nouveau/nva3_pm.c index e4b2b9e934b..35fc57a9369 100644 --- a/drivers/gpu/drm/nouveau/nva3_pm.c +++ b/drivers/gpu/drm/nouveau/nva3_pm.c @@ -27,11 +27,59 @@ #include "nouveau_bios.h" #include "nouveau_pm.h" -/* This is actually a lot more complex than it appears here, but hopefully - * this should be able to deal with what the VBIOS leaves for us.. - * - * If not, well, I'll jump off that bridge when I come to it. - */ +static u32 read_pll(struct drm_device *dev, u32 pll, int clk); +static u32 read_clk(struct drm_device *dev, int clk); + +static u32 +read_clk(struct drm_device *dev, int clk) +{ + u32 sctl, sdiv, sclk; + + if (clk >= 0x40) + return 27000; + + sctl = nv_rd32(dev, 0x4120 + (clk * 4)); + switch (sctl & 0x00003100) { + case 0x00000100: + return 27000; + case 0x00002100: + if (sctl & 0x00000040) + return 108000; + return 100000; + case 0x00003100: + sdiv = ((sctl & 0x003f0000) >> 16) + 2; + if ((sctl & 0x00000030) != 0x00000030) + sclk = read_pll(dev, 0x00e820, 0x41); + else + sclk = read_pll(dev, 0x00e8a0, 0x42); + + return (sclk * 2) / sdiv; + default: + return 0; + } +} + +static u32 +read_pll(struct drm_device *dev, u32 pll, int clk) +{ + u32 ctrl = nv_rd32(dev, pll + 0); + u32 sclk, P = 1, N = 1, M = 1; + + if (!(ctrl & 0x00000008)) { + u32 coef = nv_rd32(dev, pll + 4); + M = (coef & 0x000000ff) >> 0; + N = (coef & 0x0000ff00) >> 8; + P = (coef & 0x003f0000) >> 16; + if ((pll & 0x00ff00) == 0x00e800) + P = 1; + + sclk = read_clk(dev, 0x00 + clk); + } else { + sclk = read_clk(dev, 0x10 + clk); + } + + return sclk * N / (M * P); +} struct nva3_pm_state { enum pll_types type; @@ -67,35 +115,16 @@ nva3_pm_pll_offset(u32 id) int nva3_pm_clock_get(struct drm_device *dev, u32 id) { - u32 src0, src1, ctrl, coef; - struct pll_lims pll; - int ret, off; - int P, N, M; - - ret = get_pll_limits(dev, id, &pll); - if (ret) - return ret; - - off = nva3_pm_pll_offset(id); - if (off < 0) - return off; - - src0 = nv_rd32(dev, 0x4120 + (off * 4)); - src1 = nv_rd32(dev, 0x4160 + (off * 4)); - ctrl = nv_rd32(dev, pll.reg + 0); - coef = nv_rd32(dev, pll.reg + 4); - NV_DEBUG(dev, "PLL %02x: 0x%08x 0x%08x 0x%08x 0x%08x\n", - id, src0, src1, ctrl, coef); - - if (ctrl & 0x00000008) { - u32 div = ((src1 & 0x003c0000) >> 18) + 1; - return (pll.refclk * 2) / div; + switch (id) { + case PLL_CORE: + return read_pll(dev, 0x4200, 0); + case PLL_SHADER: + return read_pll(dev, 0x4220, 1); + case PLL_MEMORY: + return read_pll(dev, 0x4000, 2); + default: + return -ENOENT; } - - P = (coef & 0x003f0000) >> 16; - N = (coef & 0x0000ff00) >> 8; - M = (coef & 0x000000ff); - return pll.refclk * N / M / P; } void * -- cgit v1.2.3-70-g09d2 From 77e7da6814623927cc4435d992bef9c84075594c Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 17 Jun 2011 11:25:57 +1000 Subject: drm/nouveau/pm: add hooks to get/set *all* clocks at once This is probably better than having to tell the common code about all the clocks that exist on every chipset. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_drv.h | 5 +++++ drivers/gpu/drm/nouveau/nouveau_pm.c | 11 +++++++++++ 2 files changed, 16 insertions(+) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 491158d7304..7a88d9cb2ac 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -498,6 +498,11 @@ struct nouveau_pm_engine { void *(*clock_pre)(struct drm_device *, struct nouveau_pm_level *, u32 id, int khz); void (*clock_set)(struct drm_device *, void *); + + int (*clocks_get)(struct drm_device *, struct nouveau_pm_level *); + void *(*clocks_pre)(struct drm_device *, struct nouveau_pm_level *); + void (*clocks_set)(struct drm_device *, void *); + int (*voltage_get)(struct drm_device *); int (*voltage_set)(struct drm_device *, int voltage); int (*fanspeed_get)(struct drm_device *); diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index 179067a1d26..db68531b811 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -72,6 +72,12 @@ nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl) } } + if (pm->clocks_pre) { + void *state = pm->clocks_pre(dev, perflvl); + if (IS_ERR(state)) + return PTR_ERR(state); + pm->clocks_set(dev, state); + } else if (pm->clock_set) { nouveau_pm_clock_set(dev, perflvl, PLL_CORE, perflvl->core); nouveau_pm_clock_set(dev, perflvl, PLL_SHADER, perflvl->shader); @@ -124,6 +130,11 @@ nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) memset(perflvl, 0, sizeof(*perflvl)); + if (pm->clocks_get) { + ret = pm->clocks_get(dev, perflvl); + if (ret) + return ret; + } else if (pm->clock_get) { ret = pm->clock_get(dev, PLL_CORE); if (ret > 0) -- cgit v1.2.3-70-g09d2 From ca94a71fc4d99c99871dfca528a88aab1557641c Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 17 Jun 2011 15:38:48 +1000 Subject: drm/nva3/pm: rewrite clock_set, and switch to new interfaces Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_pm.h | 7 +- drivers/gpu/drm/nouveau/nouveau_state.c | 6 +- drivers/gpu/drm/nouveau/nva3_pm.c | 277 ++++++++++++++++---------------- 3 files changed, 147 insertions(+), 143 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.h b/drivers/gpu/drm/nouveau/nouveau_pm.h index 4a9838ddace..884bb7f90a1 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.h +++ b/drivers/gpu/drm/nouveau/nouveau_pm.h @@ -59,10 +59,9 @@ void *nv50_pm_clock_pre(struct drm_device *, struct nouveau_pm_level *, void nv50_pm_clock_set(struct drm_device *, void *); /* nva3_pm.c */ -int nva3_pm_clock_get(struct drm_device *, u32 id); -void *nva3_pm_clock_pre(struct drm_device *, struct nouveau_pm_level *, - u32 id, int khz); -void nva3_pm_clock_set(struct drm_device *, void *); +int nva3_pm_clocks_get(struct drm_device *, struct nouveau_pm_level *); +void *nva3_pm_clocks_pre(struct drm_device *, struct nouveau_pm_level *); +void nva3_pm_clocks_set(struct drm_device *, void *); /* nouveau_temp.c */ void nouveau_temp_init(struct drm_device *dev); diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index bd4c8f56b5d..3e7f3812bfc 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -359,9 +359,9 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->pm.clock_set = nv50_pm_clock_set; break; default: - engine->pm.clock_get = nva3_pm_clock_get; - engine->pm.clock_pre = nva3_pm_clock_pre; - engine->pm.clock_set = nva3_pm_clock_set; + engine->pm.clocks_get = nva3_pm_clocks_get; + engine->pm.clocks_pre = nva3_pm_clocks_pre; + engine->pm.clocks_set = nva3_pm_clocks_set; break; } engine->pm.voltage_get = nouveau_voltage_gpio_get; diff --git a/drivers/gpu/drm/nouveau/nva3_pm.c b/drivers/gpu/drm/nouveau/nva3_pm.c index 35fc57a9369..562e781c4f5 100644 --- a/drivers/gpu/drm/nouveau/nva3_pm.c +++ b/drivers/gpu/drm/nouveau/nva3_pm.c @@ -27,11 +27,20 @@ #include "nouveau_bios.h" #include "nouveau_pm.h" -static u32 read_pll(struct drm_device *dev, u32 pll, int clk); -static u32 read_clk(struct drm_device *dev, int clk); +static u32 read_clk(struct drm_device *, int, bool); +static u32 read_pll(struct drm_device *, u32, int); static u32 -read_clk(struct drm_device *dev, int clk) +read_vco(struct drm_device *dev, int clk) +{ + u32 sctl = nv_rd32(dev, 0x4120 + (clk * 4)); + if ((sctl & 0x00000030) != 0x00000030) + return read_pll(dev, 0x00e820, 0x41); + return read_pll(dev, 0x00e8a0, 0x42); +} + +static u32 +read_clk(struct drm_device *dev, int clk, bool ignore_en) { u32 sctl, sdiv, sclk; @@ -39,20 +48,19 @@ read_clk(struct drm_device *dev, int clk) return 27000; sctl = nv_rd32(dev, 0x4120 + (clk * 4)); - switch (sctl & 0x00003100) { - case 0x00000100: + if (!ignore_en && !(sctl & 0x00000100)) + return 0; + + switch (sctl & 0x00003000) { + case 0x00000000: return 27000; - case 0x00002100: + case 0x00002000: if (sctl & 0x00000040) return 108000; return 100000; - case 0x00003100: + case 0x00003000: + sclk = read_vco(dev, clk); sdiv = ((sctl & 0x003f0000) >> 16) + 2; - if ((sctl & 0x00000030) != 0x00000030) - sclk = read_pll(dev, 0x00e820, 0x41); - else - sclk = read_pll(dev, 0x00e8a0, 0x42); - return (sclk * 2) / sdiv; default: return 0; @@ -73,161 +81,158 @@ read_pll(struct drm_device *dev, u32 pll, int clk) if ((pll & 0x00ff00) == 0x00e800) P = 1; - sclk = read_clk(dev, 0x00 + clk); + sclk = read_clk(dev, 0x00 + clk, false); } else { - sclk = read_clk(dev, 0x10 + clk); + sclk = read_clk(dev, 0x10 + clk, false); } return sclk * N / (M * P); } -struct nva3_pm_state { - enum pll_types type; - u32 src0; - u32 src1; - u32 ctrl; - u32 coef; - u32 old_pnm; - u32 new_pnm; - u32 new_div; +struct creg { + u32 clk; + u32 pll; }; static int -nva3_pm_pll_offset(u32 id) +calc_clk(struct drm_device *dev, u32 pll, int clk, u32 khz, struct creg *reg) { - static const u32 pll_map[] = { - 0x00, PLL_CORE, - 0x01, PLL_SHADER, - 0x02, PLL_MEMORY, - 0x00, 0x00 - }; - const u32 *map = pll_map; - - while (map[1]) { - if (id == map[1]) - return map[0]; - map += 2; + struct pll_lims limits; + u32 oclk, sclk, sdiv; + int P, N, M, diff; + int ret; + + reg->pll = 0; + reg->clk = 0; + + switch (khz) { + case 27000: + reg->clk = 0x00000100; + return khz; + case 100000: + reg->clk = 0x00002100; + return khz; + case 108000: + reg->clk = 0x00002140; + return khz; + default: + sclk = read_vco(dev, clk); + sdiv = min((sclk * 2) / (khz - 2999), (u32)65); + if (sdiv > 4) { + oclk = (sclk * 2) / sdiv; + diff = khz - oclk; + if (!pll || (diff >= -2000 && diff < 3000)) { + reg->clk = (((sdiv - 2) << 16) | 0x00003100); + return oclk; + } + } + break; } - return -ENOENT; + ret = get_pll_limits(dev, pll, &limits); + if (ret) + return ret; + + limits.refclk = read_clk(dev, clk - 0x10, true); + if (!limits.refclk) + return -EINVAL; + + ret = nva3_calc_pll(dev, &limits, khz, &N, NULL, &M, &P); + if (ret >= 0) { + reg->clk = nv_rd32(dev, 0x4120 + (clk * 4)); + reg->pll = (P << 16) | (N << 8) | M; + } + return ret; } int -nva3_pm_clock_get(struct drm_device *dev, u32 id) +nva3_pm_clocks_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) { - switch (id) { - case PLL_CORE: - return read_pll(dev, 0x4200, 0); - case PLL_SHADER: - return read_pll(dev, 0x4220, 1); - case PLL_MEMORY: - return read_pll(dev, 0x4000, 2); - default: - return -ENOENT; - } + perflvl->core = read_pll(dev, 0x4200, 0); + perflvl->shader = read_pll(dev, 0x4220, 1); + perflvl->memory = read_pll(dev, 0x4000, 2); + return 0; } +struct nva3_pm_state { + struct creg nclk; + struct creg sclk; + struct creg mclk; +}; + void * -nva3_pm_clock_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl, - u32 id, int khz) +nva3_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl) { - struct nva3_pm_state *pll; - struct pll_lims limits; - int N, M, P, diff; - int ret, off; + struct nva3_pm_state *info; + int ret; - ret = get_pll_limits(dev, id, &limits); + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return ERR_PTR(-ENOMEM); + + ret = calc_clk(dev, 0x4200, 0x10, perflvl->core, &info->nclk); if (ret < 0) - return (ret == -ENOENT) ? NULL : ERR_PTR(ret); + goto out; - off = nva3_pm_pll_offset(id); - if (id < 0) - return ERR_PTR(-EINVAL); + ret = calc_clk(dev, 0x4220, 0x11, perflvl->shader, &info->sclk); + if (ret < 0) + goto out; + ret = calc_clk(dev, 0x4000, 0x12, perflvl->memory, &info->mclk); + if (ret < 0) + goto out; - pll = kzalloc(sizeof(*pll), GFP_KERNEL); - if (!pll) - return ERR_PTR(-ENOMEM); - pll->type = id; - pll->src0 = 0x004120 + (off * 4); - pll->src1 = 0x004160 + (off * 4); - pll->ctrl = limits.reg + 0; - pll->coef = limits.reg + 4; - - /* If target clock is within [-2, 3) MHz of a divisor, we'll - * use that instead of calculating MNP values - */ - pll->new_div = min((limits.refclk * 2) / (khz - 2999), 16); - if (pll->new_div) { - diff = khz - ((limits.refclk * 2) / pll->new_div); - if (diff < -2000 || diff >= 3000) - pll->new_div = 0; +out: + if (ret < 0) { + kfree(info); + info = ERR_PTR(ret); } + return info; +} - if (!pll->new_div) { - ret = nva3_calc_pll(dev, &limits, khz, &N, NULL, &M, &P); - if (ret < 0) - return ERR_PTR(ret); - - pll->new_pnm = (P << 16) | (N << 8) | M; - pll->new_div = 2 - 1; +static void +prog_pll(struct drm_device *dev, u32 pll, int clk, struct creg *reg) +{ + const u32 src0 = 0x004120 + (clk * 4); + const u32 src1 = 0x004160 + (clk * 4); + const u32 ctrl = pll + 0; + const u32 coef = pll + 4; + u32 cntl; + + cntl = nv_rd32(dev, ctrl) & 0xfffffff2; + if (reg->pll) { + nv_mask(dev, src0, 0x00000101, 0x00000101); + nv_wr32(dev, coef, reg->pll); + nv_wr32(dev, ctrl, cntl | 0x00000015); + nv_mask(dev, src1, 0x00000100, 0x00000000); + nv_mask(dev, src1, 0x00000001, 0x00000000); } else { - pll->new_pnm = 0; - pll->new_div--; + nv_mask(dev, src1, 0x003f3141, 0x00000101 | reg->clk); + nv_wr32(dev, ctrl, cntl | 0x0000001d); + nv_mask(dev, ctrl, 0x00000001, 0x00000000); + nv_mask(dev, src0, 0x00000100, 0x00000000); + nv_mask(dev, src0, 0x00000001, 0x00000000); } - - if ((nv_rd32(dev, pll->src1) & 0x00000101) != 0x00000101) - pll->old_pnm = nv_rd32(dev, pll->coef); - return pll; } void -nva3_pm_clock_set(struct drm_device *dev, void *pre_state) +nva3_pm_clocks_set(struct drm_device *dev, void *pre_state) { - struct nva3_pm_state *pll = pre_state; - u32 ctrl = 0; - - /* For the memory clock, NVIDIA will build a "script" describing - * the reclocking process and ask PDAEMON to execute it. - */ - if (pll->type == PLL_MEMORY) { - nv_wr32(dev, 0x100210, 0); - nv_wr32(dev, 0x1002dc, 1); - nv_wr32(dev, 0x004018, 0x00001000); - ctrl = 0x18000100; - } - - if (pll->old_pnm || !pll->new_pnm) { - nv_mask(dev, pll->src1, 0x003c0101, 0x00000101 | - (pll->new_div << 18)); - nv_wr32(dev, pll->ctrl, 0x0001001d | ctrl); - nv_mask(dev, pll->ctrl, 0x00000001, 0x00000000); - } - - if (pll->new_pnm) { - nv_mask(dev, pll->src0, 0x00000101, 0x00000101); - nv_wr32(dev, pll->coef, pll->new_pnm); - nv_wr32(dev, pll->ctrl, 0x0001001d | ctrl); - nv_mask(dev, pll->ctrl, 0x00000010, 0x00000000); - nv_mask(dev, pll->ctrl, 0x00020010, 0x00020010); - nv_wr32(dev, pll->ctrl, 0x00010015 | ctrl); - nv_mask(dev, pll->src1, 0x00000100, 0x00000000); - nv_mask(dev, pll->src1, 0x00000001, 0x00000000); - if (pll->type == PLL_MEMORY) - nv_wr32(dev, 0x4018, 0x10005000); - } else { - nv_mask(dev, pll->ctrl, 0x00000001, 0x00000000); - nv_mask(dev, pll->src0, 0x00000100, 0x00000000); - nv_mask(dev, pll->src0, 0x00000001, 0x00000000); - if (pll->type == PLL_MEMORY) - nv_wr32(dev, 0x4018, 0x1000d000); - } - - if (pll->type == PLL_MEMORY) { - nv_wr32(dev, 0x1002dc, 0); - nv_wr32(dev, 0x100210, 0x80000000); - } - - kfree(pll); + struct nva3_pm_state *info = pre_state; + + prog_pll(dev, 0x004200, 0, &info->nclk); + prog_pll(dev, 0x004220, 1, &info->sclk); + + nv_wr32(dev, 0x100210, 0); + nv_wr32(dev, 0x1002dc, 1); + nv_wr32(dev, 0x004018, 0x00001000); + prog_pll(dev, 0x004000, 2, &info->mclk); + if (nv_rd32(dev, 0x4000) & 0x00000008) + nv_wr32(dev, 0x004018, 0x1000d000); + else + nv_wr32(dev, 0x004018, 0x10005000); + nv_wr32(dev, 0x1002dc, 0); + nv_wr32(dev, 0x100210, 0x80000000); + + kfree(info); } - -- cgit v1.2.3-70-g09d2 From 4fd2847e9bfa592ef8f76d5ec8a5c809682c323d Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 17 Jun 2011 16:11:31 +1000 Subject: drm/nva3/pm: parse/reclock vdec/41a0 clocks Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_drv.h | 2 ++ drivers/gpu/drm/nouveau/nouveau_perf.c | 4 ++++ drivers/gpu/drm/nouveau/nva3_pm.c | 20 ++++++++++++++++++++ 3 files changed, 26 insertions(+) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 7a88d9cb2ac..06867055181 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -449,8 +449,10 @@ struct nouveau_pm_level { u32 core; u32 memory; u32 shader; + u32 vdec; u32 unk05; u32 unk0a; + u32 unka0; u32 volt_min; /* microvolts */ u32 volt_max; diff --git a/drivers/gpu/drm/nouveau/nouveau_perf.c b/drivers/gpu/drm/nouveau/nouveau_perf.c index b0e995fdcba..b00cf4b5046 100644 --- a/drivers/gpu/drm/nouveau/nouveau_perf.c +++ b/drivers/gpu/drm/nouveau/nouveau_perf.c @@ -291,6 +291,8 @@ nouveau_perf_init(struct drm_device *dev) perflvl->core = ROM16(subent(0)) & 0xfff; perflvl->shader = ROM16(subent(1)) & 0xfff; perflvl->memory = ROM16(subent(2)) & 0xfff; + perflvl->vdec = ROM16(subent(3)) & 0xfff; + perflvl->unka0 = ROM16(subent(4)) & 0xfff; } else { perflvl->shader = ROM16(subent(3)) & 0xfff; perflvl->core = perflvl->shader / 2; @@ -302,6 +304,8 @@ nouveau_perf_init(struct drm_device *dev) perflvl->shader *= 1000; perflvl->memory *= 1000; perflvl->unk0a *= 1000; + perflvl->vdec *= 1000; + perflvl->unka0 *= 1000; break; } diff --git a/drivers/gpu/drm/nouveau/nva3_pm.c b/drivers/gpu/drm/nouveau/nva3_pm.c index 562e781c4f5..a9e3de4a952 100644 --- a/drivers/gpu/drm/nouveau/nva3_pm.c +++ b/drivers/gpu/drm/nouveau/nva3_pm.c @@ -151,6 +151,8 @@ nva3_pm_clocks_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) perflvl->core = read_pll(dev, 0x4200, 0); perflvl->shader = read_pll(dev, 0x4220, 1); perflvl->memory = read_pll(dev, 0x4000, 2); + perflvl->unka0 = read_clk(dev, 0x20, false); + perflvl->vdec = read_clk(dev, 0x21, false); return 0; } @@ -158,6 +160,8 @@ struct nva3_pm_state { struct creg nclk; struct creg sclk; struct creg mclk; + struct creg vdec; + struct creg unka0; }; void * @@ -182,6 +186,14 @@ nva3_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl) if (ret < 0) goto out; + ret = calc_clk(dev, 0x0000, 0x20, perflvl->unka0, &info->unka0); + if (ret < 0) + goto out; + + ret = calc_clk(dev, 0x0000, 0x21, perflvl->vdec, &info->vdec); + if (ret < 0) + goto out; + out: if (ret < 0) { kfree(info); @@ -215,6 +227,12 @@ prog_pll(struct drm_device *dev, u32 pll, int clk, struct creg *reg) } } +static void +prog_clk(struct drm_device *dev, int clk, struct creg *reg) +{ + nv_mask(dev, 0x004120 + (clk * 4), 0x003f3141, 0x00000101 | reg->clk); +} + void nva3_pm_clocks_set(struct drm_device *dev, void *pre_state) { @@ -222,6 +240,8 @@ nva3_pm_clocks_set(struct drm_device *dev, void *pre_state) prog_pll(dev, 0x004200, 0, &info->nclk); prog_pll(dev, 0x004220, 1, &info->sclk); + prog_clk(dev, 0x20, &info->unka0); + prog_clk(dev, 0x21, &info->vdec); nv_wr32(dev, 0x100210, 0); nv_wr32(dev, 0x1002dc, 1); -- cgit v1.2.3-70-g09d2 From cec2a270dbaafba7e2340e9489a5658c67960962 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 17 Jun 2011 16:33:13 +1000 Subject: drm/nva3/pm: tidy and add some comments here and there Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nva3_pm.c | 125 ++++++++++++++++++++++++-------------- 1 file changed, 78 insertions(+), 47 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nva3_pm.c b/drivers/gpu/drm/nouveau/nva3_pm.c index a9e3de4a952..98ea3aa0bb6 100644 --- a/drivers/gpu/drm/nouveau/nva3_pm.c +++ b/drivers/gpu/drm/nouveau/nva3_pm.c @@ -28,15 +28,15 @@ #include "nouveau_pm.h" static u32 read_clk(struct drm_device *, int, bool); -static u32 read_pll(struct drm_device *, u32, int); +static u32 read_pll(struct drm_device *, int, u32); static u32 read_vco(struct drm_device *dev, int clk) { u32 sctl = nv_rd32(dev, 0x4120 + (clk * 4)); if ((sctl & 0x00000030) != 0x00000030) - return read_pll(dev, 0x00e820, 0x41); - return read_pll(dev, 0x00e8a0, 0x42); + return read_pll(dev, 0x41, 0x00e820); + return read_pll(dev, 0x42, 0x00e8a0); } static u32 @@ -44,6 +44,7 @@ read_clk(struct drm_device *dev, int clk, bool ignore_en) { u32 sctl, sdiv, sclk; + /* refclk for the 0xe8xx plls always 27KHz */ if (clk >= 0x40) return 27000; @@ -68,7 +69,7 @@ read_clk(struct drm_device *dev, int clk, bool ignore_en) } static u32 -read_pll(struct drm_device *dev, u32 pll, int clk) +read_pll(struct drm_device *dev, int clk, u32 pll) { u32 ctrl = nv_rd32(dev, pll + 0); u32 sclk, P = 1, N = 1, M = 1; @@ -78,6 +79,8 @@ read_pll(struct drm_device *dev, u32 pll, int clk) M = (coef & 0x000000ff) >> 0; N = (coef & 0x0000ff00) >> 8; P = (coef & 0x003f0000) >> 16; + + /* not post-divider on these.. */ if ((pll & 0x00ff00) == 0x00e800) P = 1; @@ -95,7 +98,7 @@ struct creg { }; static int -calc_clk(struct drm_device *dev, u32 pll, int clk, u32 khz, struct creg *reg) +calc_clk(struct drm_device *dev, int clk, u32 pll, u32 khz, struct creg *reg) { struct pll_lims limits; u32 oclk, sclk, sdiv; @@ -104,6 +107,10 @@ calc_clk(struct drm_device *dev, u32 pll, int clk, u32 khz, struct creg *reg) reg->pll = 0; reg->clk = 0; + if (!khz) { + NV_DEBUG(dev, "no clock for 0x%04x/0x%02x\n", pll, clk); + return 0; + } switch (khz) { case 27000: @@ -118,6 +125,14 @@ calc_clk(struct drm_device *dev, u32 pll, int clk, u32 khz, struct creg *reg) default: sclk = read_vco(dev, clk); sdiv = min((sclk * 2) / (khz - 2999), (u32)65); + /* if the clock has a PLL attached, and we can get a within + * [-2, 3) MHz of a divider, we'll disable the PLL and use + * the divider instead. + * + * divider can go as low as 2, limited here because NVIDIA + * and the VBIOS on my NVA8 seem to prefer using the PLL + * for 810MHz - is there a good reason? + */ if (sdiv > 4) { oclk = (sclk * 2) / sdiv; diff = khz - oclk; @@ -126,6 +141,12 @@ calc_clk(struct drm_device *dev, u32 pll, int clk, u32 khz, struct creg *reg) return oclk; } } + + if (!pll) { + NV_ERROR(dev, "bad freq %02x: %d %d\n", clk, khz, sclk); + return -ERANGE; + } + break; } @@ -145,12 +166,53 @@ calc_clk(struct drm_device *dev, u32 pll, int clk, u32 khz, struct creg *reg) return ret; } +static void +prog_pll(struct drm_device *dev, int clk, u32 pll, struct creg *reg) +{ + const u32 src0 = 0x004120 + (clk * 4); + const u32 src1 = 0x004160 + (clk * 4); + const u32 ctrl = pll + 0; + const u32 coef = pll + 4; + u32 cntl; + + if (!reg->clk && !reg->pll) { + NV_DEBUG(dev, "no clock for %02x\n", clk); + return; + } + + cntl = nv_rd32(dev, ctrl) & 0xfffffff2; + if (reg->pll) { + nv_mask(dev, src0, 0x00000101, 0x00000101); + nv_wr32(dev, coef, reg->pll); + nv_wr32(dev, ctrl, cntl | 0x00000015); + nv_mask(dev, src1, 0x00000100, 0x00000000); + nv_mask(dev, src1, 0x00000001, 0x00000000); + } else { + nv_mask(dev, src1, 0x003f3141, 0x00000101 | reg->clk); + nv_wr32(dev, ctrl, cntl | 0x0000001d); + nv_mask(dev, ctrl, 0x00000001, 0x00000000); + nv_mask(dev, src0, 0x00000100, 0x00000000); + nv_mask(dev, src0, 0x00000001, 0x00000000); + } +} + +static void +prog_clk(struct drm_device *dev, int clk, struct creg *reg) +{ + if (!reg->clk) { + NV_DEBUG(dev, "no clock for %02x\n", clk); + return; + } + + nv_mask(dev, 0x004120 + (clk * 4), 0x003f3141, 0x00000101 | reg->clk); +} + int nva3_pm_clocks_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) { - perflvl->core = read_pll(dev, 0x4200, 0); - perflvl->shader = read_pll(dev, 0x4220, 1); - perflvl->memory = read_pll(dev, 0x4000, 2); + perflvl->core = read_pll(dev, 0x00, 0x4200); + perflvl->shader = read_pll(dev, 0x01, 0x4220); + perflvl->memory = read_pll(dev, 0x02, 0x4000); perflvl->unka0 = read_clk(dev, 0x20, false); perflvl->vdec = read_clk(dev, 0x21, false); return 0; @@ -174,23 +236,23 @@ nva3_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl) if (!info) return ERR_PTR(-ENOMEM); - ret = calc_clk(dev, 0x4200, 0x10, perflvl->core, &info->nclk); + ret = calc_clk(dev, 0x10, 0x4200, perflvl->core, &info->nclk); if (ret < 0) goto out; - ret = calc_clk(dev, 0x4220, 0x11, perflvl->shader, &info->sclk); + ret = calc_clk(dev, 0x11, 0x4220, perflvl->shader, &info->sclk); if (ret < 0) goto out; - ret = calc_clk(dev, 0x4000, 0x12, perflvl->memory, &info->mclk); + ret = calc_clk(dev, 0x12, 0x4000, perflvl->memory, &info->mclk); if (ret < 0) goto out; - ret = calc_clk(dev, 0x0000, 0x20, perflvl->unka0, &info->unka0); + ret = calc_clk(dev, 0x20, 0x0000, perflvl->unka0, &info->unka0); if (ret < 0) goto out; - ret = calc_clk(dev, 0x0000, 0x21, perflvl->vdec, &info->vdec); + ret = calc_clk(dev, 0x21, 0x0000, perflvl->vdec, &info->vdec); if (ret < 0) goto out; @@ -202,51 +264,20 @@ out: return info; } -static void -prog_pll(struct drm_device *dev, u32 pll, int clk, struct creg *reg) -{ - const u32 src0 = 0x004120 + (clk * 4); - const u32 src1 = 0x004160 + (clk * 4); - const u32 ctrl = pll + 0; - const u32 coef = pll + 4; - u32 cntl; - - cntl = nv_rd32(dev, ctrl) & 0xfffffff2; - if (reg->pll) { - nv_mask(dev, src0, 0x00000101, 0x00000101); - nv_wr32(dev, coef, reg->pll); - nv_wr32(dev, ctrl, cntl | 0x00000015); - nv_mask(dev, src1, 0x00000100, 0x00000000); - nv_mask(dev, src1, 0x00000001, 0x00000000); - } else { - nv_mask(dev, src1, 0x003f3141, 0x00000101 | reg->clk); - nv_wr32(dev, ctrl, cntl | 0x0000001d); - nv_mask(dev, ctrl, 0x00000001, 0x00000000); - nv_mask(dev, src0, 0x00000100, 0x00000000); - nv_mask(dev, src0, 0x00000001, 0x00000000); - } -} - -static void -prog_clk(struct drm_device *dev, int clk, struct creg *reg) -{ - nv_mask(dev, 0x004120 + (clk * 4), 0x003f3141, 0x00000101 | reg->clk); -} - void nva3_pm_clocks_set(struct drm_device *dev, void *pre_state) { struct nva3_pm_state *info = pre_state; - prog_pll(dev, 0x004200, 0, &info->nclk); - prog_pll(dev, 0x004220, 1, &info->sclk); + prog_pll(dev, 0x00, 0x004200, &info->nclk); + prog_pll(dev, 0x01, 0x004220, &info->sclk); prog_clk(dev, 0x20, &info->unka0); prog_clk(dev, 0x21, &info->vdec); nv_wr32(dev, 0x100210, 0); nv_wr32(dev, 0x1002dc, 1); nv_wr32(dev, 0x004018, 0x00001000); - prog_pll(dev, 0x004000, 2, &info->mclk); + prog_pll(dev, 0x02, 0x004000, &info->mclk); if (nv_rd32(dev, 0x4000) & 0x00000008) nv_wr32(dev, 0x004018, 0x1000d000); else -- cgit v1.2.3-70-g09d2 From 95f0de3a0ae52bbe11f285ae46b5319bb2a2360d Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Sat, 18 Jun 2011 16:08:22 +1000 Subject: drm/nv50/gr: insert set/clr of a ctxprog flag at start/end of ctxprog The set will be replaced with a wait on the same flag by a subsequent commit in order to halt a ctxprog's execution temporarily. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nv50_grctx.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nv50_grctx.c b/drivers/gpu/drm/nouveau/nv50_grctx.c index de9abff12b9..e04fb4483c5 100644 --- a/drivers/gpu/drm/nouveau/nv50_grctx.c +++ b/drivers/gpu/drm/nouveau/nv50_grctx.c @@ -40,6 +40,9 @@ #define CP_FLAG_UNK0B ((0 * 32) + 0xb) #define CP_FLAG_UNK0B_CLEAR 0 #define CP_FLAG_UNK0B_SET 1 +#define CP_FLAG_STATE ((0 * 32) + 0x1c) +#define CP_FLAG_STATE_STOPPED 0 +#define CP_FLAG_STATE_RUNNING 1 #define CP_FLAG_UNK1D ((0 * 32) + 0x1d) #define CP_FLAG_UNK1D_CLEAR 0 #define CP_FLAG_UNK1D_SET 1 @@ -194,6 +197,8 @@ nv50_grctx_init(struct nouveau_grctx *ctx) "the devs.\n"); return -ENOSYS; } + + cp_set (ctx, STATE, RUNNING); /* decide whether we're loading/unloading the context */ cp_bra (ctx, AUTO_SAVE, PENDING, cp_setup_save); cp_bra (ctx, USER_SAVE, PENDING, cp_setup_save); @@ -260,6 +265,7 @@ nv50_grctx_init(struct nouveau_grctx *ctx) cp_name(ctx, cp_exit); cp_set (ctx, USER_SAVE, NOT_PENDING); cp_set (ctx, USER_LOAD, NOT_PENDING); + cp_set (ctx, STATE, STOPPED); cp_out (ctx, CP_END); ctx->ctxvals_pos += 0x400; /* padding... no idea why you need it */ -- cgit v1.2.3-70-g09d2 From 78e2933d07124ea28593a1bdadc546294f77a504 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Sat, 18 Jun 2011 16:27:24 +1000 Subject: drm/nouveau: add function to wait until a callback returns true Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_drv.h | 4 ++++ drivers/gpu/drm/nouveau/nouveau_state.c | 17 +++++++++++++++++ 2 files changed, 21 insertions(+) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 06867055181..0f0c5e59535 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -833,6 +833,8 @@ extern bool nouveau_wait_eq(struct drm_device *, uint64_t timeout, uint32_t reg, uint32_t mask, uint32_t val); extern bool nouveau_wait_ne(struct drm_device *, uint64_t timeout, uint32_t reg, uint32_t mask, uint32_t val); +extern bool nouveau_wait_cb(struct drm_device *, u64 timeout, + bool (*cond)(void *), void *); extern bool nouveau_wait_for_idle(struct drm_device *); extern int nouveau_card_init(struct drm_device *); @@ -1457,6 +1459,8 @@ static inline void nv_wr08(struct drm_device *dev, unsigned reg, u8 val) nouveau_wait_eq(dev, 2000000000ULL, (reg), (mask), (val)) #define nv_wait_ne(dev, reg, mask, val) \ nouveau_wait_ne(dev, 2000000000ULL, (reg), (mask), (val)) +#define nv_wait_cb(dev, func, data) \ + nouveau_wait_cb(dev, 2000000000ULL, (func), (data)) /* PRAMIN access */ static inline u32 nv_ri32(struct drm_device *dev, unsigned offset) diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index 3e7f3812bfc..8dc73b6b813 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -1200,6 +1200,23 @@ nouveau_wait_ne(struct drm_device *dev, uint64_t timeout, return false; } +/* Wait until cond(data) == true, up until timeout has hit */ +bool +nouveau_wait_cb(struct drm_device *dev, u64 timeout, + bool (*cond)(void *), void *data) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer; + u64 start = ptimer->read(dev); + + do { + if (cond(data) == true) + return true; + } while (ptimer->read(dev) - start < timeout); + + return false; +} + /* Waits for PGRAPH to go completely idle */ bool nouveau_wait_for_idle(struct drm_device *dev) { -- cgit v1.2.3-70-g09d2 From d0f67a48f47a1874622418ba6bc2c45935b01b36 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Sat, 18 Jun 2011 16:28:00 +1000 Subject: drm/nva3/pm: idle graphics engine before changing clocks Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nva3_pm.c | 40 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nva3_pm.c b/drivers/gpu/drm/nouveau/nva3_pm.c index 98ea3aa0bb6..8541d5215e0 100644 --- a/drivers/gpu/drm/nouveau/nva3_pm.c +++ b/drivers/gpu/drm/nouveau/nva3_pm.c @@ -264,10 +264,40 @@ out: return info; } +static bool +nva3_pm_grcp_idle(void *data) +{ + struct drm_device *dev = data; + + if (!(nv_rd32(dev, 0x400304) & 0x00000001)) + return true; + if (nv_rd32(dev, 0x400308) == 0x0050001c) + return true; + return false; +} + void nva3_pm_clocks_set(struct drm_device *dev, void *pre_state) { + struct drm_nouveau_private *dev_priv = dev->dev_private; struct nva3_pm_state *info = pre_state; + unsigned long flags; + + /* prevent any new grctx switches from starting */ + spin_lock_irqsave(&dev_priv->context_switch_lock, flags); + nv_wr32(dev, 0x400324, 0x00000000); + nv_wr32(dev, 0x400328, 0x0050001c); /* wait flag 0x1c */ + /* wait for any pending grctx switches to complete */ + if (!nv_wait_cb(dev, nva3_pm_grcp_idle, dev)) { + NV_ERROR(dev, "pm: ctxprog didn't go idle\n"); + goto cleanup; + } + /* freeze PFIFO */ + nv_mask(dev, 0x002504, 0x00000001, 0x00000001); + if (!nv_wait(dev, 0x002504, 0x00000010, 0x00000010)) { + NV_ERROR(dev, "pm: fifo didn't go idle\n"); + goto cleanup; + } prog_pll(dev, 0x00, 0x004200, &info->nclk); prog_pll(dev, 0x01, 0x004220, &info->sclk); @@ -285,5 +315,15 @@ nva3_pm_clocks_set(struct drm_device *dev, void *pre_state) nv_wr32(dev, 0x1002dc, 0); nv_wr32(dev, 0x100210, 0x80000000); +cleanup: + /* unfreeze PFIFO */ + nv_mask(dev, 0x002504, 0x00000001, 0x00000000); + /* restore ctxprog to normal */ + nv_wr32(dev, 0x400324, 0x00000000); + nv_wr32(dev, 0x400328, 0x0070009c); /* set flag 0x1c */ + /* unblock it if necessary */ + if (nv_rd32(dev, 0x400308) == 0x0050001c) + nv_mask(dev, 0x400824, 0x10000000, 0x10000000); + spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); kfree(info); } -- cgit v1.2.3-70-g09d2 From 354d0781e5cef1f227ee3064cb65053365177d3b Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Sun, 19 Jun 2011 01:44:36 +1000 Subject: drm/nvc0/pm: initial implementation of clocks_get() Not too certain on memory clock yet, but it gets the right numbers for each perflvl on my NVC0. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/Makefile | 2 +- drivers/gpu/drm/nouveau/nouveau_pm.h | 3 + drivers/gpu/drm/nouveau/nouveau_state.c | 1 + drivers/gpu/drm/nouveau/nvc0_pm.c | 145 ++++++++++++++++++++++++++++++++ 4 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/nouveau/nvc0_pm.c (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile index 0583677e458..88213b532bd 100644 --- a/drivers/gpu/drm/nouveau/Makefile +++ b/drivers/gpu/drm/nouveau/Makefile @@ -30,7 +30,7 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \ nv04_fbcon.o nv50_fbcon.o nvc0_fbcon.o \ nv10_gpio.o nv50_gpio.o \ nv50_calc.o \ - nv04_pm.o nv50_pm.o nva3_pm.o \ + nv04_pm.o nv50_pm.o nva3_pm.o nvc0_pm.o \ nv50_vram.o nvc0_vram.o \ nv50_vm.o nvc0_vm.o diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.h b/drivers/gpu/drm/nouveau/nouveau_pm.h index 884bb7f90a1..f519883d9a4 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.h +++ b/drivers/gpu/drm/nouveau/nouveau_pm.h @@ -63,6 +63,9 @@ int nva3_pm_clocks_get(struct drm_device *, struct nouveau_pm_level *); void *nva3_pm_clocks_pre(struct drm_device *, struct nouveau_pm_level *); void nva3_pm_clocks_set(struct drm_device *, void *); +/* nvc0_pm.c */ +int nvc0_pm_clocks_get(struct drm_device *, struct nouveau_pm_level *); + /* nouveau_temp.c */ void nouveau_temp_init(struct drm_device *dev); void nouveau_temp_fini(struct drm_device *dev); diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index 8dc73b6b813..109f0d98461 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -422,6 +422,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->vram.put = nv50_vram_del; engine->vram.flags_valid = nvc0_vram_flags_valid; engine->pm.temp_get = nv84_temp_get; + engine->pm.clocks_get = nvc0_pm_clocks_get; engine->pm.voltage_get = nouveau_voltage_gpio_get; engine->pm.voltage_set = nouveau_voltage_gpio_set; break; diff --git a/drivers/gpu/drm/nouveau/nvc0_pm.c b/drivers/gpu/drm/nouveau/nvc0_pm.c new file mode 100644 index 00000000000..aff4426b240 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvc0_pm.c @@ -0,0 +1,145 @@ +/* + * Copyright 2011 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ + +#include "drmP.h" +#include "nouveau_drv.h" +#include "nouveau_bios.h" +#include "nouveau_pm.h" + +static u32 read_div(struct drm_device *, int, u32, u32); +static u32 read_pll(struct drm_device *, u32); + +static u32 +read_vco(struct drm_device *dev, u32 dsrc) +{ + u32 ssrc = nv_rd32(dev, dsrc); + if (!(ssrc & 0x00000100)) + return read_pll(dev, 0x00e800); + return read_pll(dev, 0x00e820); +} + +static u32 +read_pll(struct drm_device *dev, u32 pll) +{ + u32 coef = nv_rd32(dev, pll + 4); + u32 P = (coef & 0x003f0000) >> 16; + u32 N = (coef & 0x0000ff00) >> 8; + u32 M = (coef & 0x000000ff) >> 0; + u32 sclk, doff; + + switch (pll & 0xfff000) { + case 0x00e000: + sclk = 27000; + P = 1; + break; + case 0x137000: + doff = (pll - 0x137000) / 0x20; + sclk = read_div(dev, doff, 0x137120, 0x137140); + break; + case 0x132000: + switch (pll) { + case 0x132000: + sclk = read_pll(dev, 0x132020); + break; + case 0x132020: + sclk = read_div(dev, 0, 0x137320, 0x137330); + break; + default: + return 0; + } + break; + default: + return 0; + } + + return sclk * N / M / P; +} + +static u32 +read_div(struct drm_device *dev, int doff, u32 dsrc, u32 dctl) +{ + u32 ssrc = nv_rd32(dev, dsrc + (doff * 4)); + u32 sctl = nv_rd32(dev, dctl + (doff * 4)); + + switch (ssrc & 0x00000003) { + case 0: + if ((ssrc & 0x00030000) != 0x00030000) + return 27000; + return 108000; + case 2: + return 100000; + case 3: + if (sctl & 0x80000000) { + u32 sclk = read_vco(dev, dsrc); + u32 sdiv = (sctl & 0x0000003f) + 2; + return (sclk * 2) / sdiv; + } + + return read_vco(dev, dsrc); + default: + return 0; + } +} + +static u32 +read_mem(struct drm_device *dev) +{ + u32 ssel = nv_rd32(dev, 0x1373f0); + if (ssel & 0x00000001) + return read_div(dev, 0, 0x137300, 0x137310); + return read_pll(dev, 0x132000); +} + +static u32 +read_clk(struct drm_device *dev, int clk) +{ + u32 sctl = nv_rd32(dev, 0x137250 + (clk * 4)); + u32 ssel = nv_rd32(dev, 0x137100); + u32 sclk, sdiv; + + if (ssel & (1 << clk)) { + if (clk < 7) + sclk = read_pll(dev, 0x137000 + (clk * 0x20)); + else + sclk = read_pll(dev, 0x1370e0); + sdiv = ((sctl & 0x00003f00) >> 8) + 2; + } else { + sclk = read_div(dev, clk, 0x137160, 0x1371d0); + sdiv = ((sctl & 0x0000003f) >> 0) + 2; + } + + if (sctl & 0x80000000) + return (sclk * 2) / sdiv; + return sclk; +} + +int +nvc0_pm_clocks_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) +{ + perflvl->shader = read_clk(dev, 0x00); + perflvl->core = perflvl->shader / 2; + perflvl->memory = read_mem(dev); + perflvl->vdec = read_clk(dev, 0x0e); + return 0; +} -- cgit v1.2.3-70-g09d2 From 9698b9a680e9aee124b1cd752abf1c672ea24c03 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 21 Jun 2011 15:12:26 +1000 Subject: drm/nvc0/pm: more complete parsing of clock domains Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_drv.h | 11 ++++++++--- drivers/gpu/drm/nouveau/nouveau_perf.c | 31 +++++++++++++++---------------- drivers/gpu/drm/nouveau/nva3_pm.c | 2 ++ drivers/gpu/drm/nouveau/nvc0_pm.c | 6 ++++++ 4 files changed, 31 insertions(+), 19 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 0f0c5e59535..f31fbe28e86 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -449,10 +449,15 @@ struct nouveau_pm_level { u32 core; u32 memory; u32 shader; + u32 rop; + u32 copy; + u32 daemon; u32 vdec; - u32 unk05; - u32 unk0a; - u32 unka0; + u32 unk05; /* nv50:nva3, roughly.. */ + u32 unka0; /* nva3:nvc0 */ + u32 hub01; /* nvc0- */ + u32 hub06; /* nvc0- */ + u32 hub07; /* nvc0- */ u32 volt_min; /* microvolts */ u32 volt_max; diff --git a/drivers/gpu/drm/nouveau/nouveau_perf.c b/drivers/gpu/drm/nouveau/nouveau_perf.c index b00cf4b5046..bb50f249023 100644 --- a/drivers/gpu/drm/nouveau/nouveau_perf.c +++ b/drivers/gpu/drm/nouveau/nouveau_perf.c @@ -284,28 +284,27 @@ nouveau_perf_init(struct drm_device *dev) perflvl->unk05 = ROM16(entry[16]) * 1000; break; case 0x40: -#define subent(n) entry[perf[2] + ((n) * perf[3])] +#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) { - perflvl->core = ROM16(subent(0)) & 0xfff; - perflvl->shader = ROM16(subent(1)) & 0xfff; - perflvl->memory = ROM16(subent(2)) & 0xfff; - perflvl->vdec = ROM16(subent(3)) & 0xfff; - perflvl->unka0 = ROM16(subent(4)) & 0xfff; + perflvl->core = subent(0); + perflvl->shader = subent(1); + perflvl->memory = subent(2); + perflvl->vdec = subent(3); + perflvl->unka0 = subent(4); } else { - perflvl->shader = ROM16(subent(3)) & 0xfff; + perflvl->hub06 = subent(0); + perflvl->hub01 = subent(1); + perflvl->copy = subent(2); + perflvl->shader = subent(3); + perflvl->rop = subent(4); + perflvl->memory = subent(5); + perflvl->vdec = subent(6); + perflvl->daemon = subent(10); + perflvl->hub07 = subent(11); perflvl->core = perflvl->shader / 2; - perflvl->unk0a = ROM16(subent(4)) & 0xfff; - perflvl->memory = ROM16(subent(5)) & 0xfff; } - - perflvl->core *= 1000; - perflvl->shader *= 1000; - perflvl->memory *= 1000; - perflvl->unk0a *= 1000; - perflvl->vdec *= 1000; - perflvl->unka0 *= 1000; break; } diff --git a/drivers/gpu/drm/nouveau/nva3_pm.c b/drivers/gpu/drm/nouveau/nva3_pm.c index 8541d5215e0..864a15bd612 100644 --- a/drivers/gpu/drm/nouveau/nva3_pm.c +++ b/drivers/gpu/drm/nouveau/nva3_pm.c @@ -215,6 +215,8 @@ nva3_pm_clocks_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) perflvl->memory = read_pll(dev, 0x02, 0x4000); perflvl->unka0 = read_clk(dev, 0x20, false); perflvl->vdec = read_clk(dev, 0x21, false); + perflvl->daemon = read_clk(dev, 0x25, false); + perflvl->copy = perflvl->core; return 0; } diff --git a/drivers/gpu/drm/nouveau/nvc0_pm.c b/drivers/gpu/drm/nouveau/nvc0_pm.c index aff4426b240..6dc1a974b57 100644 --- a/drivers/gpu/drm/nouveau/nvc0_pm.c +++ b/drivers/gpu/drm/nouveau/nvc0_pm.c @@ -140,6 +140,12 @@ nvc0_pm_clocks_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) perflvl->shader = read_clk(dev, 0x00); perflvl->core = perflvl->shader / 2; perflvl->memory = read_mem(dev); + perflvl->rop = read_clk(dev, 0x01); + perflvl->hub07 = read_clk(dev, 0x02); + perflvl->hub06 = read_clk(dev, 0x07); + perflvl->hub01 = read_clk(dev, 0x08); + perflvl->copy = read_clk(dev, 0x09); + perflvl->daemon = read_clk(dev, 0x0c); perflvl->vdec = read_clk(dev, 0x0e); return 0; } -- cgit v1.2.3-70-g09d2 From 323dcac552b39884cdeff26a38d5dd80854795a1 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 23 Jun 2011 16:21:21 +1000 Subject: drm/nouveau: rename nv40_mpeg to nv31_mpeg Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/Makefile | 2 +- drivers/gpu/drm/nouveau/nouveau_drv.h | 4 +- drivers/gpu/drm/nouveau/nouveau_state.c | 2 +- drivers/gpu/drm/nouveau/nv31_mpeg.c | 311 ++++++++++++++++++++++++++++++++ drivers/gpu/drm/nouveau/nv40_mpeg.c | 311 -------------------------------- 5 files changed, 315 insertions(+), 315 deletions(-) create mode 100644 drivers/gpu/drm/nouveau/nv31_mpeg.c delete mode 100644 drivers/gpu/drm/nouveau/nv40_mpeg.c (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile index 88213b532bd..f65ade6ba45 100644 --- a/drivers/gpu/drm/nouveau/Makefile +++ b/drivers/gpu/drm/nouveau/Makefile @@ -21,7 +21,7 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \ nv40_grctx.o nv50_grctx.o nvc0_grctx.o \ nv84_crypt.o \ nva3_copy.o nvc0_copy.o \ - nv40_mpeg.o nv50_mpeg.o \ + nv31_mpeg.o nv50_mpeg.o \ nv04_instmem.o nv50_instmem.o nvc0_instmem.o \ nv50_evo.o nv50_crtc.o nv50_dac.o nv50_sor.o \ nv50_cursor.o nv50_display.o \ diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index f31fbe28e86..ba258e39f6c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -1195,8 +1195,8 @@ extern int nva3_copy_create(struct drm_device *dev); /* nvc0_copy.c */ extern int nvc0_copy_create(struct drm_device *dev, int engine); -/* nv40_mpeg.c */ -extern int nv40_mpeg_create(struct drm_device *dev); +/* nv31_mpeg.c */ +extern int nv31_mpeg_create(struct drm_device *dev); /* nv50_mpeg.c */ extern int nv50_mpeg_create(struct drm_device *dev); diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index 109f0d98461..93a2e83d024 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -634,7 +634,7 @@ nouveau_card_init(struct drm_device *dev) } if (dev_priv->card_type == NV_40) - nv40_mpeg_create(dev); + nv31_mpeg_create(dev); else if (dev_priv->card_type == NV_50 && (dev_priv->chipset < 0x98 || dev_priv->chipset == 0xa0)) diff --git a/drivers/gpu/drm/nouveau/nv31_mpeg.c b/drivers/gpu/drm/nouveau/nv31_mpeg.c new file mode 100644 index 00000000000..72e86660258 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv31_mpeg.c @@ -0,0 +1,311 @@ +/* + * Copyright 2011 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ + +#include "drmP.h" +#include "nouveau_drv.h" +#include "nouveau_ramht.h" + +struct nv31_mpeg_engine { + struct nouveau_exec_engine base; +}; + +static int +nv40_mpeg_context_new(struct nouveau_channel *chan, int engine) +{ + struct drm_device *dev = chan->dev; + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_gpuobj *ctx = NULL; + unsigned long flags; + int ret; + + NV_DEBUG(dev, "ch%d\n", chan->id); + + ret = nouveau_gpuobj_new(dev, NULL, 264 * 4, 16, NVOBJ_FLAG_ZERO_ALLOC | + NVOBJ_FLAG_ZERO_FREE, &ctx); + if (ret) + return ret; + + nv_wo32(ctx, 0x78, 0x02001ec1); + + spin_lock_irqsave(&dev_priv->context_switch_lock, flags); + nv_mask(dev, 0x002500, 0x00000001, 0x00000000); + if ((nv_rd32(dev, 0x003204) & 0x1f) == chan->id) + nv_wr32(dev, 0x00330c, ctx->pinst >> 4); + nv_wo32(chan->ramfc, 0x54, ctx->pinst >> 4); + nv_mask(dev, 0x002500, 0x00000001, 0x00000001); + spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); + + chan->engctx[engine] = ctx; + return 0; +} + +static void +nv40_mpeg_context_del(struct nouveau_channel *chan, int engine) +{ + struct drm_nouveau_private *dev_priv = chan->dev->dev_private; + struct nouveau_gpuobj *ctx = chan->engctx[engine]; + struct drm_device *dev = chan->dev; + unsigned long flags; + u32 inst = 0x80000000 | (ctx->pinst >> 4); + + spin_lock_irqsave(&dev_priv->context_switch_lock, flags); + nv_mask(dev, 0x00b32c, 0x00000001, 0x00000000); + if (nv_rd32(dev, 0x00b318) == inst) + nv_mask(dev, 0x00b318, 0x80000000, 0x00000000); + nv_mask(dev, 0x00b32c, 0x00000001, 0x00000001); + spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); + + nouveau_gpuobj_ref(NULL, &ctx); + chan->engctx[engine] = NULL; +} + +static int +nv31_mpeg_object_new(struct nouveau_channel *chan, int engine, + u32 handle, u16 class) +{ + struct drm_device *dev = chan->dev; + struct nouveau_gpuobj *obj = NULL; + int ret; + + ret = nouveau_gpuobj_new(dev, chan, 20, 16, NVOBJ_FLAG_ZERO_ALLOC | + NVOBJ_FLAG_ZERO_FREE, &obj); + if (ret) + return ret; + obj->engine = 2; + obj->class = class; + + nv_wo32(obj, 0x00, class); + + ret = nouveau_ramht_insert(chan, handle, obj); + nouveau_gpuobj_ref(NULL, &obj); + return ret; +} + +static int +nv31_mpeg_init(struct drm_device *dev, int engine) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nv31_mpeg_engine *pmpeg = nv_engine(dev, engine); + int i; + + /* VPE init */ + nv_mask(dev, 0x000200, 0x00000002, 0x00000000); + nv_mask(dev, 0x000200, 0x00000002, 0x00000002); + nv_wr32(dev, 0x00b0e0, 0x00000020); /* nvidia: rd 0x01, wr 0x20 */ + nv_wr32(dev, 0x00b0e8, 0x00000020); /* nvidia: rd 0x01, wr 0x20 */ + + for (i = 0; i < dev_priv->engine.fb.num_tiles; i++) + pmpeg->base.set_tile_region(dev, i); + + /* PMPEG init */ + nv_wr32(dev, 0x00b32c, 0x00000000); + nv_wr32(dev, 0x00b314, 0x00000100); + nv_wr32(dev, 0x00b220, 0x00000044); + nv_wr32(dev, 0x00b300, 0x02001ec1); + nv_mask(dev, 0x00b32c, 0x00000001, 0x00000001); + + nv_wr32(dev, 0x00b100, 0xffffffff); + nv_wr32(dev, 0x00b140, 0xffffffff); + + if (!nv_wait(dev, 0x00b200, 0x00000001, 0x00000000)) { + NV_ERROR(dev, "PMPEG init: 0x%08x\n", nv_rd32(dev, 0x00b200)); + return -EBUSY; + } + + return 0; +} + +static int +nv31_mpeg_fini(struct drm_device *dev, int engine, bool suspend) +{ + /*XXX: context save? */ + nv_mask(dev, 0x00b32c, 0x00000001, 0x00000000); + nv_wr32(dev, 0x00b140, 0x00000000); + return 0; +} + +static int +nv31_mpeg_mthd_dma(struct nouveau_channel *chan, u32 class, u32 mthd, u32 data) +{ + struct drm_device *dev = chan->dev; + u32 inst = data << 4; + u32 dma0 = nv_ri32(dev, inst + 0); + u32 dma1 = nv_ri32(dev, inst + 4); + u32 dma2 = nv_ri32(dev, inst + 8); + u32 base = (dma2 & 0xfffff000) | (dma0 >> 20); + u32 size = dma1 + 1; + + /* only allow linear DMA objects */ + if (!(dma0 & 0x00002000)) + return -EINVAL; + + if (mthd == 0x0190) { + /* DMA_CMD */ + nv_mask(dev, 0x00b300, 0x00030000, (dma0 & 0x00030000)); + nv_wr32(dev, 0x00b334, base); + nv_wr32(dev, 0x00b324, size); + } else + if (mthd == 0x01a0) { + /* DMA_DATA */ + nv_mask(dev, 0x00b300, 0x000c0000, (dma0 & 0x00030000) << 2); + nv_wr32(dev, 0x00b360, base); + nv_wr32(dev, 0x00b364, size); + } else { + /* DMA_IMAGE, VRAM only */ + if (dma0 & 0x000c0000) + return -EINVAL; + + nv_wr32(dev, 0x00b370, base); + nv_wr32(dev, 0x00b374, size); + } + + return 0; +} + +static int +nv31_mpeg_isr_chid(struct drm_device *dev, u32 inst) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_gpuobj *ctx; + unsigned long flags; + int i; + + spin_lock_irqsave(&dev_priv->channels.lock, flags); + for (i = 0; i < dev_priv->engine.fifo.channels; i++) { + if (!dev_priv->channels.ptr[i]) + continue; + + ctx = dev_priv->channels.ptr[i]->engctx[NVOBJ_ENGINE_MPEG]; + if (ctx && ctx->pinst == inst) + break; + } + spin_unlock_irqrestore(&dev_priv->channels.lock, flags); + return i; +} + +static void +nv31_vpe_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, 0x00b008 + (i * 0x10), tile->pitch); + nv_wr32(dev, 0x00b004 + (i * 0x10), tile->limit); + nv_wr32(dev, 0x00b000 + (i * 0x10), tile->addr); +} + +static void +nv31_mpeg_isr(struct drm_device *dev) +{ + u32 inst = (nv_rd32(dev, 0x00b318) & 0x000fffff) << 4; + u32 chid = nv31_mpeg_isr_chid(dev, inst); + u32 stat = nv_rd32(dev, 0x00b100); + u32 type = nv_rd32(dev, 0x00b230); + u32 mthd = nv_rd32(dev, 0x00b234); + u32 data = nv_rd32(dev, 0x00b238); + u32 show = stat; + + if (stat & 0x01000000) { + /* happens on initial binding of the object */ + if (type == 0x00000020 && mthd == 0x0000) { + nv_mask(dev, 0x00b308, 0x00000000, 0x00000000); + show &= ~0x01000000; + } + + if (type == 0x00000010) { + if (!nouveau_gpuobj_mthd_call2(dev, chid, 0x3174, mthd, data)) + show &= ~0x01000000; + } + } + + nv_wr32(dev, 0x00b100, stat); + nv_wr32(dev, 0x00b230, 0x00000001); + + if (show && nouveau_ratelimit()) { + NV_INFO(dev, "PMPEG: Ch %d [0x%08x] 0x%08x 0x%08x 0x%08x 0x%08x\n", + chid, inst, stat, type, mthd, data); + } +} + +static void +nv31_vpe_isr(struct drm_device *dev) +{ + if (nv_rd32(dev, 0x00b100)) + nv31_mpeg_isr(dev); + + if (nv_rd32(dev, 0x00b800)) { + u32 stat = nv_rd32(dev, 0x00b800); + NV_INFO(dev, "PMSRCH: 0x%08x\n", stat); + nv_wr32(dev, 0xb800, stat); + } +} + +static void +nv31_mpeg_destroy(struct drm_device *dev, int engine) +{ + struct nv31_mpeg_engine *pmpeg = nv_engine(dev, engine); + + nouveau_irq_unregister(dev, 0); + + NVOBJ_ENGINE_DEL(dev, MPEG); + kfree(pmpeg); +} + +int +nv31_mpeg_create(struct drm_device *dev) +{ + struct nv31_mpeg_engine *pmpeg; + + pmpeg = kzalloc(sizeof(*pmpeg), GFP_KERNEL); + if (!pmpeg) + return -ENOMEM; + + pmpeg->base.destroy = nv31_mpeg_destroy; + pmpeg->base.init = nv31_mpeg_init; + pmpeg->base.fini = nv31_mpeg_fini; + pmpeg->base.context_new = nv40_mpeg_context_new; + pmpeg->base.context_del = nv40_mpeg_context_del; + pmpeg->base.object_new = nv31_mpeg_object_new; + + /* ISR vector, PMC_ENABLE bit, and TILE regs are shared between + * all VPE engines, for this driver's purposes the PMPEG engine + * will be treated as the "master" and handle the global VPE + * bits too + */ + pmpeg->base.set_tile_region = nv31_vpe_set_tile_region; + nouveau_irq_register(dev, 0, nv31_vpe_isr); + + NVOBJ_ENGINE_ADD(dev, MPEG, &pmpeg->base); + NVOBJ_CLASS(dev, 0x3174, MPEG); + NVOBJ_MTHD (dev, 0x3174, 0x0190, nv31_mpeg_mthd_dma); + NVOBJ_MTHD (dev, 0x3174, 0x01a0, nv31_mpeg_mthd_dma); + NVOBJ_MTHD (dev, 0x3174, 0x01b0, nv31_mpeg_mthd_dma); + +#if 0 + NVOBJ_ENGINE_ADD(dev, ME, &pme->base); + NVOBJ_CLASS(dev, 0x4075, ME); +#endif + return 0; + +} diff --git a/drivers/gpu/drm/nouveau/nv40_mpeg.c b/drivers/gpu/drm/nouveau/nv40_mpeg.c deleted file mode 100644 index ad03a0e1fc7..00000000000 --- a/drivers/gpu/drm/nouveau/nv40_mpeg.c +++ /dev/null @@ -1,311 +0,0 @@ -/* - * Copyright 2011 Red Hat Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - * - * Authors: Ben Skeggs - */ - -#include "drmP.h" -#include "nouveau_drv.h" -#include "nouveau_ramht.h" - -struct nv40_mpeg_engine { - struct nouveau_exec_engine base; -}; - -static int -nv40_mpeg_context_new(struct nouveau_channel *chan, int engine) -{ - struct drm_device *dev = chan->dev; - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_gpuobj *ctx = NULL; - unsigned long flags; - int ret; - - NV_DEBUG(dev, "ch%d\n", chan->id); - - ret = nouveau_gpuobj_new(dev, NULL, 264 * 4, 16, NVOBJ_FLAG_ZERO_ALLOC | - NVOBJ_FLAG_ZERO_FREE, &ctx); - if (ret) - return ret; - - nv_wo32(ctx, 0x78, 0x02001ec1); - - spin_lock_irqsave(&dev_priv->context_switch_lock, flags); - nv_mask(dev, 0x002500, 0x00000001, 0x00000000); - if ((nv_rd32(dev, 0x003204) & 0x1f) == chan->id) - nv_wr32(dev, 0x00330c, ctx->pinst >> 4); - nv_wo32(chan->ramfc, 0x54, ctx->pinst >> 4); - nv_mask(dev, 0x002500, 0x00000001, 0x00000001); - spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); - - chan->engctx[engine] = ctx; - return 0; -} - -static void -nv40_mpeg_context_del(struct nouveau_channel *chan, int engine) -{ - struct drm_nouveau_private *dev_priv = chan->dev->dev_private; - struct nouveau_gpuobj *ctx = chan->engctx[engine]; - struct drm_device *dev = chan->dev; - unsigned long flags; - u32 inst = 0x80000000 | (ctx->pinst >> 4); - - spin_lock_irqsave(&dev_priv->context_switch_lock, flags); - nv_mask(dev, 0x00b32c, 0x00000001, 0x00000000); - if (nv_rd32(dev, 0x00b318) == inst) - nv_mask(dev, 0x00b318, 0x80000000, 0x00000000); - nv_mask(dev, 0x00b32c, 0x00000001, 0x00000001); - spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); - - nouveau_gpuobj_ref(NULL, &ctx); - chan->engctx[engine] = NULL; -} - -static int -nv40_mpeg_object_new(struct nouveau_channel *chan, int engine, - u32 handle, u16 class) -{ - struct drm_device *dev = chan->dev; - struct nouveau_gpuobj *obj = NULL; - int ret; - - ret = nouveau_gpuobj_new(dev, chan, 20, 16, NVOBJ_FLAG_ZERO_ALLOC | - NVOBJ_FLAG_ZERO_FREE, &obj); - if (ret) - return ret; - obj->engine = 2; - obj->class = class; - - nv_wo32(obj, 0x00, class); - - ret = nouveau_ramht_insert(chan, handle, obj); - nouveau_gpuobj_ref(NULL, &obj); - return ret; -} - -static int -nv40_mpeg_init(struct drm_device *dev, int engine) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nv40_mpeg_engine *pmpeg = nv_engine(dev, engine); - int i; - - /* VPE init */ - nv_mask(dev, 0x000200, 0x00000002, 0x00000000); - nv_mask(dev, 0x000200, 0x00000002, 0x00000002); - nv_wr32(dev, 0x00b0e0, 0x00000020); /* nvidia: rd 0x01, wr 0x20 */ - nv_wr32(dev, 0x00b0e8, 0x00000020); /* nvidia: rd 0x01, wr 0x20 */ - - for (i = 0; i < dev_priv->engine.fb.num_tiles; i++) - pmpeg->base.set_tile_region(dev, i); - - /* PMPEG init */ - nv_wr32(dev, 0x00b32c, 0x00000000); - nv_wr32(dev, 0x00b314, 0x00000100); - nv_wr32(dev, 0x00b220, 0x00000044); - nv_wr32(dev, 0x00b300, 0x02001ec1); - nv_mask(dev, 0x00b32c, 0x00000001, 0x00000001); - - nv_wr32(dev, 0x00b100, 0xffffffff); - nv_wr32(dev, 0x00b140, 0xffffffff); - - if (!nv_wait(dev, 0x00b200, 0x00000001, 0x00000000)) { - NV_ERROR(dev, "PMPEG init: 0x%08x\n", nv_rd32(dev, 0x00b200)); - return -EBUSY; - } - - return 0; -} - -static int -nv40_mpeg_fini(struct drm_device *dev, int engine, bool suspend) -{ - /*XXX: context save? */ - nv_mask(dev, 0x00b32c, 0x00000001, 0x00000000); - nv_wr32(dev, 0x00b140, 0x00000000); - return 0; -} - -static int -nv40_mpeg_mthd_dma(struct nouveau_channel *chan, u32 class, u32 mthd, u32 data) -{ - struct drm_device *dev = chan->dev; - u32 inst = data << 4; - u32 dma0 = nv_ri32(dev, inst + 0); - u32 dma1 = nv_ri32(dev, inst + 4); - u32 dma2 = nv_ri32(dev, inst + 8); - u32 base = (dma2 & 0xfffff000) | (dma0 >> 20); - u32 size = dma1 + 1; - - /* only allow linear DMA objects */ - if (!(dma0 & 0x00002000)) - return -EINVAL; - - if (mthd == 0x0190) { - /* DMA_CMD */ - nv_mask(dev, 0x00b300, 0x00030000, (dma0 & 0x00030000)); - nv_wr32(dev, 0x00b334, base); - nv_wr32(dev, 0x00b324, size); - } else - if (mthd == 0x01a0) { - /* DMA_DATA */ - nv_mask(dev, 0x00b300, 0x000c0000, (dma0 & 0x00030000) << 2); - nv_wr32(dev, 0x00b360, base); - nv_wr32(dev, 0x00b364, size); - } else { - /* DMA_IMAGE, VRAM only */ - if (dma0 & 0x000c0000) - return -EINVAL; - - nv_wr32(dev, 0x00b370, base); - nv_wr32(dev, 0x00b374, size); - } - - return 0; -} - -static int -nv40_mpeg_isr_chid(struct drm_device *dev, u32 inst) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_gpuobj *ctx; - unsigned long flags; - int i; - - spin_lock_irqsave(&dev_priv->channels.lock, flags); - for (i = 0; i < dev_priv->engine.fifo.channels; i++) { - if (!dev_priv->channels.ptr[i]) - continue; - - ctx = dev_priv->channels.ptr[i]->engctx[NVOBJ_ENGINE_MPEG]; - if (ctx && ctx->pinst == inst) - break; - } - spin_unlock_irqrestore(&dev_priv->channels.lock, flags); - return i; -} - -static void -nv40_vpe_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, 0x00b008 + (i * 0x10), tile->pitch); - nv_wr32(dev, 0x00b004 + (i * 0x10), tile->limit); - nv_wr32(dev, 0x00b000 + (i * 0x10), tile->addr); -} - -static void -nv40_mpeg_isr(struct drm_device *dev) -{ - u32 inst = (nv_rd32(dev, 0x00b318) & 0x000fffff) << 4; - u32 chid = nv40_mpeg_isr_chid(dev, inst); - u32 stat = nv_rd32(dev, 0x00b100); - u32 type = nv_rd32(dev, 0x00b230); - u32 mthd = nv_rd32(dev, 0x00b234); - u32 data = nv_rd32(dev, 0x00b238); - u32 show = stat; - - if (stat & 0x01000000) { - /* happens on initial binding of the object */ - if (type == 0x00000020 && mthd == 0x0000) { - nv_mask(dev, 0x00b308, 0x00000000, 0x00000000); - show &= ~0x01000000; - } - - if (type == 0x00000010) { - if (!nouveau_gpuobj_mthd_call2(dev, chid, 0x3174, mthd, data)) - show &= ~0x01000000; - } - } - - nv_wr32(dev, 0x00b100, stat); - nv_wr32(dev, 0x00b230, 0x00000001); - - if (show && nouveau_ratelimit()) { - NV_INFO(dev, "PMPEG: Ch %d [0x%08x] 0x%08x 0x%08x 0x%08x 0x%08x\n", - chid, inst, stat, type, mthd, data); - } -} - -static void -nv40_vpe_isr(struct drm_device *dev) -{ - if (nv_rd32(dev, 0x00b100)) - nv40_mpeg_isr(dev); - - if (nv_rd32(dev, 0x00b800)) { - u32 stat = nv_rd32(dev, 0x00b800); - NV_INFO(dev, "PMSRCH: 0x%08x\n", stat); - nv_wr32(dev, 0xb800, stat); - } -} - -static void -nv40_mpeg_destroy(struct drm_device *dev, int engine) -{ - struct nv40_mpeg_engine *pmpeg = nv_engine(dev, engine); - - nouveau_irq_unregister(dev, 0); - - NVOBJ_ENGINE_DEL(dev, MPEG); - kfree(pmpeg); -} - -int -nv40_mpeg_create(struct drm_device *dev) -{ - struct nv40_mpeg_engine *pmpeg; - - pmpeg = kzalloc(sizeof(*pmpeg), GFP_KERNEL); - if (!pmpeg) - return -ENOMEM; - - pmpeg->base.destroy = nv40_mpeg_destroy; - pmpeg->base.init = nv40_mpeg_init; - pmpeg->base.fini = nv40_mpeg_fini; - pmpeg->base.context_new = nv40_mpeg_context_new; - pmpeg->base.context_del = nv40_mpeg_context_del; - pmpeg->base.object_new = nv40_mpeg_object_new; - - /* ISR vector, PMC_ENABLE bit, and TILE regs are shared between - * all VPE engines, for this driver's purposes the PMPEG engine - * will be treated as the "master" and handle the global VPE - * bits too - */ - pmpeg->base.set_tile_region = nv40_vpe_set_tile_region; - nouveau_irq_register(dev, 0, nv40_vpe_isr); - - NVOBJ_ENGINE_ADD(dev, MPEG, &pmpeg->base); - NVOBJ_CLASS(dev, 0x3174, MPEG); - NVOBJ_MTHD (dev, 0x3174, 0x0190, nv40_mpeg_mthd_dma); - NVOBJ_MTHD (dev, 0x3174, 0x01a0, nv40_mpeg_mthd_dma); - NVOBJ_MTHD (dev, 0x3174, 0x01b0, nv40_mpeg_mthd_dma); - -#if 0 - NVOBJ_ENGINE_ADD(dev, ME, &pme->base); - NVOBJ_CLASS(dev, 0x4075, ME); -#endif - return 0; - -} -- cgit v1.2.3-70-g09d2 From 52d073318a4c32865e6439f7f6c247092a6f6af3 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 23 Jun 2011 16:44:05 +1000 Subject: drm/nv31/mpeg: support for a single class3174 user Uncertain if/how the hw does multiple PMPEG channels, supporting one is better than none however. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_state.c | 5 ++++- drivers/gpu/drm/nouveau/nv31_mpeg.c | 39 ++++++++++++++++++++++++++++++--- 2 files changed, 40 insertions(+), 4 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index 93a2e83d024..07691c2ecea 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -633,7 +633,10 @@ nouveau_card_init(struct drm_device *dev) break; } - if (dev_priv->card_type == NV_40) + if (dev_priv->card_type == NV_40 || + dev_priv->chipset == 0x31 || + dev_priv->chipset == 0x34 || + dev_priv->chipset == 0x36) nv31_mpeg_create(dev); else if (dev_priv->card_type == NV_50 && diff --git a/drivers/gpu/drm/nouveau/nv31_mpeg.c b/drivers/gpu/drm/nouveau/nv31_mpeg.c index 72e86660258..6f06a0713f0 100644 --- a/drivers/gpu/drm/nouveau/nv31_mpeg.c +++ b/drivers/gpu/drm/nouveau/nv31_mpeg.c @@ -28,8 +28,30 @@ struct nv31_mpeg_engine { struct nouveau_exec_engine base; + atomic_t refcount; }; + +static int +nv31_mpeg_context_new(struct nouveau_channel *chan, int engine) +{ + struct nv31_mpeg_engine *pmpeg = nv_engine(chan->dev, engine); + + if (!atomic_add_unless(&pmpeg->refcount, 1, 1)) + return -EBUSY; + + chan->engctx[engine] = (void *)0xdeadcafe; + return 0; +} + +static void +nv31_mpeg_context_del(struct nouveau_channel *chan, int engine) +{ + struct nv31_mpeg_engine *pmpeg = nv_engine(chan->dev, engine); + atomic_dec(&pmpeg->refcount); + chan->engctx[engine] = NULL; +} + static int nv40_mpeg_context_new(struct nouveau_channel *chan, int engine) { @@ -121,7 +143,7 @@ nv31_mpeg_init(struct drm_device *dev, int engine) /* PMPEG init */ nv_wr32(dev, 0x00b32c, 0x00000000); nv_wr32(dev, 0x00b314, 0x00000100); - nv_wr32(dev, 0x00b220, 0x00000044); + nv_wr32(dev, 0x00b220, nv44_graph_class(dev) ? 0x00000044 : 0x00000031); nv_wr32(dev, 0x00b300, 0x02001ec1); nv_mask(dev, 0x00b32c, 0x00000001, 0x00000001); @@ -191,6 +213,10 @@ nv31_mpeg_isr_chid(struct drm_device *dev, u32 inst) unsigned long flags; int i; + /* hardcode drm channel id on nv3x, so swmthd lookup works */ + if (dev_priv->card_type < NV_40) + return 0; + spin_lock_irqsave(&dev_priv->channels.lock, flags); for (i = 0; i < dev_priv->engine.fifo.channels; i++) { if (!dev_priv->channels.ptr[i]) @@ -275,17 +301,24 @@ nv31_mpeg_destroy(struct drm_device *dev, int engine) int nv31_mpeg_create(struct drm_device *dev) { + struct drm_nouveau_private *dev_priv = dev->dev_private; struct nv31_mpeg_engine *pmpeg; pmpeg = kzalloc(sizeof(*pmpeg), GFP_KERNEL); if (!pmpeg) return -ENOMEM; + atomic_set(&pmpeg->refcount, 0); pmpeg->base.destroy = nv31_mpeg_destroy; pmpeg->base.init = nv31_mpeg_init; pmpeg->base.fini = nv31_mpeg_fini; - pmpeg->base.context_new = nv40_mpeg_context_new; - pmpeg->base.context_del = nv40_mpeg_context_del; + if (dev_priv->card_type < NV_40) { + pmpeg->base.context_new = nv31_mpeg_context_new; + pmpeg->base.context_del = nv31_mpeg_context_del; + } else { + pmpeg->base.context_new = nv40_mpeg_context_new; + pmpeg->base.context_del = nv40_mpeg_context_del; + } pmpeg->base.object_new = nv31_mpeg_object_new; /* ISR vector, PMC_ENABLE bit, and TILE regs are shared between -- cgit v1.2.3-70-g09d2 From 987eec10dd76624d0edacdc7ecc7e1a6fc877373 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 24 Jun 2011 10:14:07 +1000 Subject: drm/nouveau: embed nouveau_mm Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_drv.h | 2 +- drivers/gpu/drm/nouveau/nouveau_mm.c | 60 +++++++++++++++-------------------- drivers/gpu/drm/nouveau/nouveau_mm.h | 4 +-- drivers/gpu/drm/nouveau/nouveau_vm.c | 38 +++++++++++----------- drivers/gpu/drm/nouveau/nouveau_vm.h | 2 +- drivers/gpu/drm/nouveau/nv50_vram.c | 4 +-- drivers/gpu/drm/nouveau/nvc0_vram.c | 2 +- 7 files changed, 51 insertions(+), 61 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index ba258e39f6c..6629f30598f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -518,7 +518,7 @@ struct nouveau_pm_engine { }; struct nouveau_vram_engine { - struct nouveau_mm *mm; + struct nouveau_mm mm; int (*init)(struct drm_device *); void (*takedown)(struct drm_device *dev); diff --git a/drivers/gpu/drm/nouveau/nouveau_mm.c b/drivers/gpu/drm/nouveau/nouveau_mm.c index 1640dec3b82..75b5dd93a32 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mm.c +++ b/drivers/gpu/drm/nouveau/nouveau_mm.c @@ -27,7 +27,7 @@ #include "nouveau_mm.h" static inline void -region_put(struct nouveau_mm *rmm, struct nouveau_mm_node *a) +region_put(struct nouveau_mm *mm, struct nouveau_mm_node *a) { list_del(&a->nl_entry); list_del(&a->fl_entry); @@ -35,7 +35,7 @@ region_put(struct nouveau_mm *rmm, struct nouveau_mm_node *a) } static struct nouveau_mm_node * -region_split(struct nouveau_mm *rmm, struct nouveau_mm_node *a, u32 size) +region_split(struct nouveau_mm *mm, struct nouveau_mm_node *a, u32 size) { struct nouveau_mm_node *b; @@ -57,33 +57,33 @@ region_split(struct nouveau_mm *rmm, struct nouveau_mm_node *a, u32 size) return b; } -#define node(root, dir) ((root)->nl_entry.dir == &rmm->nodes) ? NULL : \ +#define node(root, dir) ((root)->nl_entry.dir == &mm->nodes) ? NULL : \ list_entry((root)->nl_entry.dir, struct nouveau_mm_node, nl_entry) void -nouveau_mm_put(struct nouveau_mm *rmm, struct nouveau_mm_node *this) +nouveau_mm_put(struct nouveau_mm *mm, struct nouveau_mm_node *this) { struct nouveau_mm_node *prev = node(this, prev); struct nouveau_mm_node *next = node(this, next); - list_add(&this->fl_entry, &rmm->free); + list_add(&this->fl_entry, &mm->free); this->type = 0; if (prev && prev->type == 0) { prev->length += this->length; - region_put(rmm, this); + region_put(mm, this); this = prev; } if (next && next->type == 0) { next->offset = this->offset; next->length += this->length; - region_put(rmm, this); + region_put(mm, this); } } int -nouveau_mm_get(struct nouveau_mm *rmm, int type, u32 size, u32 size_nc, +nouveau_mm_get(struct nouveau_mm *mm, int type, u32 size, u32 size_nc, u32 align, struct nouveau_mm_node **pnode) { struct nouveau_mm_node *prev, *this, *next; @@ -92,17 +92,17 @@ nouveau_mm_get(struct nouveau_mm *rmm, int type, u32 size, u32 size_nc, u32 splitoff; u32 s, e; - list_for_each_entry(this, &rmm->free, fl_entry) { + list_for_each_entry(this, &mm->free, fl_entry) { e = this->offset + this->length; s = this->offset; prev = node(this, prev); if (prev && prev->type != type) - s = roundup(s, rmm->block_size); + s = roundup(s, mm->block_size); next = node(this, next); if (next && next->type != type) - e = rounddown(e, rmm->block_size); + e = rounddown(e, mm->block_size); s = (s + align_mask) & ~align_mask; e &= ~align_mask; @@ -110,10 +110,10 @@ nouveau_mm_get(struct nouveau_mm *rmm, int type, u32 size, u32 size_nc, continue; splitoff = s - this->offset; - if (splitoff && !region_split(rmm, this, splitoff)) + if (splitoff && !region_split(mm, this, splitoff)) return -ENOMEM; - this = region_split(rmm, this, min(size, e - s)); + this = region_split(mm, this, min(size, e - s)); if (!this) return -ENOMEM; @@ -127,9 +127,8 @@ nouveau_mm_get(struct nouveau_mm *rmm, int type, u32 size, u32 size_nc, } int -nouveau_mm_init(struct nouveau_mm **prmm, u32 offset, u32 length, u32 block) +nouveau_mm_init(struct nouveau_mm *mm, u32 offset, u32 length, u32 block) { - struct nouveau_mm *rmm; struct nouveau_mm_node *heap; heap = kzalloc(sizeof(*heap), GFP_KERNEL); @@ -138,32 +137,25 @@ nouveau_mm_init(struct nouveau_mm **prmm, u32 offset, u32 length, u32 block) heap->offset = roundup(offset, block); heap->length = rounddown(offset + length, block) - heap->offset; - rmm = kzalloc(sizeof(*rmm), GFP_KERNEL); - if (!rmm) { - kfree(heap); - return -ENOMEM; - } - rmm->block_size = block; - mutex_init(&rmm->mutex); - INIT_LIST_HEAD(&rmm->nodes); - INIT_LIST_HEAD(&rmm->free); - list_add(&heap->nl_entry, &rmm->nodes); - list_add(&heap->fl_entry, &rmm->free); - - *prmm = rmm; + mutex_init(&mm->mutex); + mm->block_size = block; + INIT_LIST_HEAD(&mm->nodes); + INIT_LIST_HEAD(&mm->free); + + list_add(&heap->nl_entry, &mm->nodes); + list_add(&heap->fl_entry, &mm->free); return 0; } int -nouveau_mm_fini(struct nouveau_mm **prmm) +nouveau_mm_fini(struct nouveau_mm *mm) { - struct nouveau_mm *rmm = *prmm; struct nouveau_mm_node *node, *heap = - list_first_entry(&rmm->nodes, struct nouveau_mm_node, nl_entry); + list_first_entry(&mm->nodes, struct nouveau_mm_node, nl_entry); - if (!list_is_singular(&rmm->nodes)) { + if (!list_is_singular(&mm->nodes)) { printk(KERN_ERR "nouveau_mm not empty at destroy time!\n"); - list_for_each_entry(node, &rmm->nodes, nl_entry) { + list_for_each_entry(node, &mm->nodes, nl_entry) { printk(KERN_ERR "0x%02x: 0x%08x 0x%08x\n", node->type, node->offset, node->length); } @@ -172,7 +164,5 @@ nouveau_mm_fini(struct nouveau_mm **prmm) } kfree(heap); - kfree(rmm); - *prmm = NULL; return 0; } diff --git a/drivers/gpu/drm/nouveau/nouveau_mm.h b/drivers/gpu/drm/nouveau/nouveau_mm.h index b9c016d2155..b8fe9088b9e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mm.h +++ b/drivers/gpu/drm/nouveau/nouveau_mm.h @@ -44,8 +44,8 @@ struct nouveau_mm { u32 block_size; }; -int nouveau_mm_init(struct nouveau_mm **, u32 offset, u32 length, u32 block); -int nouveau_mm_fini(struct nouveau_mm **); +int nouveau_mm_init(struct nouveau_mm *, u32 offset, u32 length, u32 block); +int nouveau_mm_fini(struct nouveau_mm *); int nouveau_mm_pre(struct nouveau_mm *); int nouveau_mm_get(struct nouveau_mm *, int type, u32 size, u32 size_nc, u32 align, struct nouveau_mm_node **); diff --git a/drivers/gpu/drm/nouveau/nouveau_vm.c b/drivers/gpu/drm/nouveau/nouveau_vm.c index 244fd38fdb8..d432a2a791f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_vm.c +++ b/drivers/gpu/drm/nouveau/nouveau_vm.c @@ -172,9 +172,9 @@ nouveau_vm_unmap_pgt(struct nouveau_vm *vm, int big, u32 fpde, u32 lpde) vm->map_pgt(vpgd->obj, pde, vpgt->obj); } - mutex_unlock(&vm->mm->mutex); + mutex_unlock(&vm->mm.mutex); nouveau_gpuobj_ref(NULL, &pgt); - mutex_lock(&vm->mm->mutex); + mutex_lock(&vm->mm.mutex); } } @@ -191,18 +191,18 @@ nouveau_vm_map_pgt(struct nouveau_vm *vm, u32 pde, u32 type) pgt_size = (1 << (vm->pgt_bits + 12)) >> type; pgt_size *= 8; - mutex_unlock(&vm->mm->mutex); + mutex_unlock(&vm->mm.mutex); ret = nouveau_gpuobj_new(vm->dev, NULL, pgt_size, 0x1000, NVOBJ_FLAG_ZERO_ALLOC, &pgt); - mutex_lock(&vm->mm->mutex); + mutex_lock(&vm->mm.mutex); if (unlikely(ret)) return ret; /* someone beat us to filling the PDE while we didn't have the lock */ if (unlikely(vpgt->refcount[big]++)) { - mutex_unlock(&vm->mm->mutex); + mutex_unlock(&vm->mm.mutex); nouveau_gpuobj_ref(NULL, &pgt); - mutex_lock(&vm->mm->mutex); + mutex_lock(&vm->mm.mutex); return 0; } @@ -223,10 +223,10 @@ nouveau_vm_get(struct nouveau_vm *vm, u64 size, u32 page_shift, u32 fpde, lpde, pde; int ret; - mutex_lock(&vm->mm->mutex); - ret = nouveau_mm_get(vm->mm, page_shift, msize, 0, align, &vma->node); + mutex_lock(&vm->mm.mutex); + ret = nouveau_mm_get(&vm->mm, page_shift, msize, 0, align, &vma->node); if (unlikely(ret != 0)) { - mutex_unlock(&vm->mm->mutex); + mutex_unlock(&vm->mm.mutex); return ret; } @@ -245,13 +245,13 @@ nouveau_vm_get(struct nouveau_vm *vm, u64 size, u32 page_shift, if (ret) { if (pde != fpde) nouveau_vm_unmap_pgt(vm, big, fpde, pde - 1); - nouveau_mm_put(vm->mm, vma->node); - mutex_unlock(&vm->mm->mutex); + nouveau_mm_put(&vm->mm, vma->node); + mutex_unlock(&vm->mm.mutex); vma->node = NULL; return ret; } } - mutex_unlock(&vm->mm->mutex); + mutex_unlock(&vm->mm.mutex); vma->vm = vm; vma->offset = (u64)vma->node->offset << 12; @@ -270,11 +270,11 @@ nouveau_vm_put(struct nouveau_vma *vma) fpde = (vma->node->offset >> vm->pgt_bits); lpde = (vma->node->offset + vma->node->length - 1) >> vm->pgt_bits; - mutex_lock(&vm->mm->mutex); + mutex_lock(&vm->mm.mutex); nouveau_vm_unmap_pgt(vm, vma->node->type != vm->spg_shift, fpde, lpde); - nouveau_mm_put(vm->mm, vma->node); + nouveau_mm_put(&vm->mm, vma->node); vma->node = NULL; - mutex_unlock(&vm->mm->mutex); + mutex_unlock(&vm->mm.mutex); } int @@ -360,11 +360,11 @@ nouveau_vm_link(struct nouveau_vm *vm, struct nouveau_gpuobj *pgd) nouveau_gpuobj_ref(pgd, &vpgd->obj); - mutex_lock(&vm->mm->mutex); + mutex_lock(&vm->mm.mutex); for (i = vm->fpde; i <= vm->lpde; i++) vm->map_pgt(pgd, i, vm->pgt[i - vm->fpde].obj); list_add(&vpgd->head, &vm->pgd_list); - mutex_unlock(&vm->mm->mutex); + mutex_unlock(&vm->mm.mutex); return 0; } @@ -377,7 +377,7 @@ nouveau_vm_unlink(struct nouveau_vm *vm, struct nouveau_gpuobj *mpgd) if (!mpgd) return; - mutex_lock(&vm->mm->mutex); + mutex_lock(&vm->mm.mutex); list_for_each_entry_safe(vpgd, tmp, &vm->pgd_list, head) { if (vpgd->obj == mpgd) { pgd = vpgd->obj; @@ -386,7 +386,7 @@ nouveau_vm_unlink(struct nouveau_vm *vm, struct nouveau_gpuobj *mpgd) break; } } - mutex_unlock(&vm->mm->mutex); + mutex_unlock(&vm->mm.mutex); nouveau_gpuobj_ref(NULL, &pgd); } diff --git a/drivers/gpu/drm/nouveau/nouveau_vm.h b/drivers/gpu/drm/nouveau/nouveau_vm.h index 579ca8cc223..6ce995f7797 100644 --- a/drivers/gpu/drm/nouveau/nouveau_vm.h +++ b/drivers/gpu/drm/nouveau/nouveau_vm.h @@ -51,7 +51,7 @@ struct nouveau_vma { struct nouveau_vm { struct drm_device *dev; - struct nouveau_mm *mm; + struct nouveau_mm mm; int refcount; struct list_head pgd_list; diff --git a/drivers/gpu/drm/nouveau/nv50_vram.c b/drivers/gpu/drm/nouveau/nv50_vram.c index af32daecd1e..9da23838e63 100644 --- a/drivers/gpu/drm/nouveau/nv50_vram.c +++ b/drivers/gpu/drm/nouveau/nv50_vram.c @@ -51,7 +51,7 @@ void nv50_vram_del(struct drm_device *dev, struct nouveau_mem **pmem) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_mm *mm = dev_priv->engine.vram.mm; + struct nouveau_mm *mm = &dev_priv->engine.vram.mm; struct nouveau_mm_node *this; struct nouveau_mem *mem; @@ -82,7 +82,7 @@ nv50_vram_new(struct drm_device *dev, u64 size, u32 align, u32 size_nc, u32 memtype, struct nouveau_mem **pmem) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_mm *mm = dev_priv->engine.vram.mm; + struct nouveau_mm *mm = &dev_priv->engine.vram.mm; struct nouveau_mm_node *r; struct nouveau_mem *mem; int comp = (memtype & 0x300) >> 8; diff --git a/drivers/gpu/drm/nouveau/nvc0_vram.c b/drivers/gpu/drm/nouveau/nvc0_vram.c index e45a24d84e9..abed0d3d579 100644 --- a/drivers/gpu/drm/nouveau/nvc0_vram.c +++ b/drivers/gpu/drm/nouveau/nvc0_vram.c @@ -61,7 +61,7 @@ nvc0_vram_new(struct drm_device *dev, u64 size, u32 align, u32 ncmin, u32 type, struct nouveau_mem **pmem) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_mm *mm = dev_priv->engine.vram.mm; + struct nouveau_mm *mm = &dev_priv->engine.vram.mm; struct nouveau_mm_node *r; struct nouveau_mem *mem; int ret; -- cgit v1.2.3-70-g09d2 From a12036ba2c0a190c93e5238c5f32fdb8c023c068 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 24 Jun 2011 10:23:20 +1000 Subject: drm/nouveau: allow a nouveau_mm to be created with holes Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_mm.c | 47 +++++++++++++++++++++--------------- drivers/gpu/drm/nouveau/nouveau_mm.h | 1 + 2 files changed, 28 insertions(+), 20 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_mm.c b/drivers/gpu/drm/nouveau/nouveau_mm.c index 75b5dd93a32..b29ffb3d140 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mm.c +++ b/drivers/gpu/drm/nouveau/nouveau_mm.c @@ -129,21 +129,25 @@ nouveau_mm_get(struct nouveau_mm *mm, int type, u32 size, u32 size_nc, int nouveau_mm_init(struct nouveau_mm *mm, u32 offset, u32 length, u32 block) { - struct nouveau_mm_node *heap; + struct nouveau_mm_node *node; + + if (block) { + mutex_init(&mm->mutex); + INIT_LIST_HEAD(&mm->nodes); + INIT_LIST_HEAD(&mm->free); + mm->block_size = block; + mm->heap_nodes = 0; + } - heap = kzalloc(sizeof(*heap), GFP_KERNEL); - if (!heap) + node = kzalloc(sizeof(*node), GFP_KERNEL); + if (!node) return -ENOMEM; - heap->offset = roundup(offset, block); - heap->length = rounddown(offset + length, block) - heap->offset; - - mutex_init(&mm->mutex); - mm->block_size = block; - INIT_LIST_HEAD(&mm->nodes); - INIT_LIST_HEAD(&mm->free); + node->offset = roundup(offset, mm->block_size); + node->length = rounddown(offset + length, mm->block_size) - node->offset; - list_add(&heap->nl_entry, &mm->nodes); - list_add(&heap->fl_entry, &mm->free); + list_add_tail(&node->nl_entry, &mm->nodes); + list_add_tail(&node->fl_entry, &mm->free); + mm->heap_nodes++; return 0; } @@ -152,15 +156,18 @@ nouveau_mm_fini(struct nouveau_mm *mm) { struct nouveau_mm_node *node, *heap = list_first_entry(&mm->nodes, struct nouveau_mm_node, nl_entry); - - if (!list_is_singular(&mm->nodes)) { - printk(KERN_ERR "nouveau_mm not empty at destroy time!\n"); - list_for_each_entry(node, &mm->nodes, nl_entry) { - printk(KERN_ERR "0x%02x: 0x%08x 0x%08x\n", - node->type, node->offset, node->length); + int nodes = 0; + + list_for_each_entry(node, &mm->nodes, nl_entry) { + if (nodes++ == mm->heap_nodes) { + printk(KERN_ERR "nouveau_mm in use at destroy time!\n"); + list_for_each_entry(node, &mm->nodes, nl_entry) { + printk(KERN_ERR "0x%02x: 0x%08x 0x%08x\n", + node->type, node->offset, node->length); + } + WARN_ON(1); + return -EBUSY; } - WARN_ON(1); - return -EBUSY; } kfree(heap); diff --git a/drivers/gpu/drm/nouveau/nouveau_mm.h b/drivers/gpu/drm/nouveau/nouveau_mm.h index b8fe9088b9e..57a600c35c9 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mm.h +++ b/drivers/gpu/drm/nouveau/nouveau_mm.h @@ -42,6 +42,7 @@ struct nouveau_mm { struct mutex mutex; u32 block_size; + int heap_nodes; }; int nouveau_mm_init(struct nouveau_mm *, u32 offset, u32 length, u32 block); -- cgit v1.2.3-70-g09d2 From 3c23a7b8bc6d78f906bbba5eea80b1f8b1002ef9 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 24 Jun 2011 11:14:00 +1000 Subject: drm/nvc0/gr: add support for nvcf chipset untested, written from a trace, accel disabled by default until it is Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_state.c | 1 + drivers/gpu/drm/nouveau/nvc0_graph.c | 3 +++ drivers/gpu/drm/nouveau/nvc0_graph.h | 1 + drivers/gpu/drm/nouveau/nvc0_grctx.c | 7 +++++-- drivers/gpu/drm/nouveau/nvc0_grgpc.fuc | 8 +++++++- drivers/gpu/drm/nouveau/nvc0_grgpc.fuc.h | 29 ++++++++++++++++------------- drivers/gpu/drm/nouveau/nvc0_grhub.fuc | 3 +++ drivers/gpu/drm/nouveau/nvc0_grhub.fuc.h | 16 ++++++++-------- 8 files changed, 44 insertions(+), 24 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index 07691c2ecea..83d549d5071 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -1011,6 +1011,7 @@ int nouveau_load(struct drm_device *dev, unsigned long flags) switch (dev_priv->chipset) { case 0xc1: /* known broken */ case 0xc8: /* never tested */ + case 0xcf: /* never tested */ NV_INFO(dev, "acceleration disabled by default, pass " "noaccel=0 to force enable\n"); dev_priv->noaccel = true; diff --git a/drivers/gpu/drm/nouveau/nvc0_graph.c b/drivers/gpu/drm/nouveau/nvc0_graph.c index 5b2f6f42046..84321640a07 100644 --- a/drivers/gpu/drm/nouveau/nvc0_graph.c +++ b/drivers/gpu/drm/nouveau/nvc0_graph.c @@ -864,6 +864,9 @@ nvc0_graph_create(struct drm_device *dev) case 0xce: /* 4/4/0/0, 4 */ priv->magic_not_rop_nr = 0x03; break; + case 0xcf: /* 4/0/0/0, 3 */ + priv->magic_not_rop_nr = 0x03; + break; } if (!priv->magic_not_rop_nr) { diff --git a/drivers/gpu/drm/nouveau/nvc0_graph.h b/drivers/gpu/drm/nouveau/nvc0_graph.h index 55689e99728..636fe9812f7 100644 --- a/drivers/gpu/drm/nouveau/nvc0_graph.h +++ b/drivers/gpu/drm/nouveau/nvc0_graph.h @@ -82,6 +82,7 @@ nvc0_graph_class(struct drm_device *dev) case 0xc3: case 0xc4: case 0xce: /* guess, mmio trace shows only 0x9097 state */ + case 0xcf: /* guess, mmio trace shows only 0x9097 state */ return 0x9097; case 0xc1: return 0x9197; diff --git a/drivers/gpu/drm/nouveau/nvc0_grctx.c b/drivers/gpu/drm/nouveau/nvc0_grctx.c index 31018eaf527..0c9737a4914 100644 --- a/drivers/gpu/drm/nouveau/nvc0_grctx.c +++ b/drivers/gpu/drm/nouveau/nvc0_grctx.c @@ -1678,7 +1678,10 @@ nvc0_grctx_generate_tp(struct drm_device *dev) nv_wr32(dev, 0x419c04, 0x00000006); nv_wr32(dev, 0x419c08, 0x00000002); nv_wr32(dev, 0x419c20, 0x00000000); - nv_wr32(dev, 0x419cb0, 0x00060048); //XXX: 0xce 0x00020048 + if (chipset == 0xce || chipset == 0xcf) + nv_wr32(dev, 0x419cb0, 0x00020048); + else + nv_wr32(dev, 0x419cb0, 0x00060048); nv_wr32(dev, 0x419ce8, 0x00000000); nv_wr32(dev, 0x419cf4, 0x00000183); nv_wr32(dev, 0x419d20, chipset != 0xc1 ? 0x02180000 : 0x12180000); @@ -1784,7 +1787,7 @@ nvc0_grctx_generate(struct nouveau_channel *chan) if (1) { const u8 chipset_tp_max[] = { 16, 4, 0, 4, 8, 0, 0, 0, - 16, 0, 0, 0, 0, 0, 8, 0 }; + 16, 0, 0, 0, 0, 0, 8, 4 }; u8 max = chipset_tp_max[dev_priv->chipset & 0x0f]; u8 tpnr[GPC_MAX]; u8 data[TP_MAX]; diff --git a/drivers/gpu/drm/nouveau/nvc0_grgpc.fuc b/drivers/gpu/drm/nouveau/nvc0_grgpc.fuc index 0ec2add72a7..06f5e26d1e0 100644 --- a/drivers/gpu/drm/nouveau/nvc0_grgpc.fuc +++ b/drivers/gpu/drm/nouveau/nvc0_grgpc.fuc @@ -77,6 +77,11 @@ chipsets: .b16 nvc0_gpc_mmio_tail .b16 nvc0_tpc_mmio_head .b16 nvc3_tpc_mmio_tail +.b8 0xcf 0 0 0 +.b16 nvc0_gpc_mmio_head +.b16 nvc0_gpc_mmio_tail +.b16 nvc0_tpc_mmio_head +.b16 nvcf_tpc_mmio_tail .b8 0 0 0 0 // GPC mmio lists @@ -134,8 +139,9 @@ mmctx_data(0x000750, 2) nvc0_tpc_mmio_tail: mmctx_data(0x000758, 1) mmctx_data(0x0002c4, 1) -mmctx_data(0x0004bc, 1) mmctx_data(0x0006e0, 1) +nvcf_tpc_mmio_tail: +mmctx_data(0x0004bc, 1) nvc3_tpc_mmio_tail: mmctx_data(0x000544, 1) nvc1_tpc_mmio_tail: diff --git a/drivers/gpu/drm/nouveau/nvc0_grgpc.fuc.h b/drivers/gpu/drm/nouveau/nvc0_grgpc.fuc.h index 1896c898f5b..6f820324480 100644 --- a/drivers/gpu/drm/nouveau/nvc0_grgpc.fuc.h +++ b/drivers/gpu/drm/nouveau/nvc0_grgpc.fuc.h @@ -25,23 +25,26 @@ uint32_t nvc0_grgpc_data[] = { 0x00000000, 0x00000000, 0x000000c0, - 0x011000b0, - 0x01640114, + 0x011c00bc, + 0x01700120, 0x000000c1, - 0x011400b0, - 0x01780114, + 0x012000bc, + 0x01840120, 0x000000c3, - 0x011000b0, - 0x01740114, + 0x011c00bc, + 0x01800120, 0x000000c4, - 0x011000b0, - 0x01740114, + 0x011c00bc, + 0x01800120, 0x000000c8, - 0x011000b0, - 0x01640114, + 0x011c00bc, + 0x01700120, 0x000000ce, - 0x011000b0, - 0x01740114, + 0x011c00bc, + 0x01800120, + 0x000000cf, + 0x011c00bc, + 0x017c0120, 0x00000000, 0x00000380, 0x14000400, @@ -90,8 +93,8 @@ uint32_t nvc0_grgpc_data[] = { 0x04000750, 0x00000758, 0x000002c4, - 0x000004bc, 0x000006e0, + 0x000004bc, 0x00000544, }; diff --git a/drivers/gpu/drm/nouveau/nvc0_grhub.fuc b/drivers/gpu/drm/nouveau/nvc0_grhub.fuc index a1a599124cf..e4f8c7e89dd 100644 --- a/drivers/gpu/drm/nouveau/nvc0_grhub.fuc +++ b/drivers/gpu/drm/nouveau/nvc0_grhub.fuc @@ -56,6 +56,9 @@ chipsets: .b8 0xce 0 0 0 .b16 nvc0_hub_mmio_head .b16 nvc0_hub_mmio_tail +.b8 0xcf 0 0 0 +.b16 nvc0_hub_mmio_head +.b16 nvc0_hub_mmio_tail .b8 0 0 0 0 nvc0_hub_mmio_head: diff --git a/drivers/gpu/drm/nouveau/nvc0_grhub.fuc.h b/drivers/gpu/drm/nouveau/nvc0_grhub.fuc.h index b3b541b6d04..241d3263f1e 100644 --- a/drivers/gpu/drm/nouveau/nvc0_grhub.fuc.h +++ b/drivers/gpu/drm/nouveau/nvc0_grhub.fuc.h @@ -23,17 +23,19 @@ uint32_t nvc0_grhub_data[] = { 0x00000000, 0x00000000, 0x000000c0, - 0x012c0090, + 0x01340098, 0x000000c1, - 0x01300090, + 0x01380098, 0x000000c3, - 0x012c0090, + 0x01340098, 0x000000c4, - 0x012c0090, + 0x01340098, 0x000000c8, - 0x012c0090, + 0x01340098, 0x000000ce, - 0x012c0090, + 0x01340098, + 0x000000cf, + 0x01340098, 0x00000000, 0x0417e91c, 0x04400204, @@ -190,8 +192,6 @@ uint32_t nvc0_grhub_data[] = { 0x00000000, 0x00000000, 0x00000000, - 0x00000000, - 0x00000000, }; uint32_t nvc0_grhub_code[] = { -- cgit v1.2.3-70-g09d2 From aa6500964c0e5c08e453439a95431ec34b548427 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 24 Jun 2011 12:47:46 +1000 Subject: drm/nvc0/vram: support non-uniform memory size per controller Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvc0_vram.c | 48 +++++++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 5 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nvc0_vram.c b/drivers/gpu/drm/nouveau/nvc0_vram.c index abed0d3d579..edbfe9360ae 100644 --- a/drivers/gpu/drm/nouveau/nvc0_vram.c +++ b/drivers/gpu/drm/nouveau/nvc0_vram.c @@ -106,12 +106,50 @@ 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 length; + u32 parts = nv_rd32(dev, 0x121c74); + u32 bsize = nv_rd32(dev, 0x10f20c); + u32 offset, length; + bool uniform = true; + int ret, i; - dev_priv->vram_size = nv_rd32(dev, 0x10f20c) << 20; - dev_priv->vram_size *= nv_rd32(dev, 0x121c74); + 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); - length = (dev_priv->vram_size >> 12) - rsvd_head - rsvd_tail; + /* read amount of vram attached to each memory controller */ + for (i = 0; i < parts; i++) { + u32 psize = nv_rd32(dev, 0x11020c + (i * 0x1000)); + if (psize != bsize) { + if (psize < bsize) + bsize = psize; + uniform = false; + } + + NV_DEBUG(dev, "%d: mem_amount 0x%08x\n", i, psize); + + dev_priv->vram_size += (u64)psize << 20; + } + + /* if all controllers have the same amount attached, there's no holes */ + if (uniform) { + offset = rsvd_head; + length = (dev_priv->vram_size >> 12) - rsvd_head - rsvd_tail; + return nouveau_mm_init(&vram->mm, offset, length, 1); + } - return nouveau_mm_init(&vram->mm, rsvd_head, length, 1); + /* otherwise, address lowest common amount from 0GiB */ + ret = nouveau_mm_init(&vram->mm, rsvd_head, (bsize << 8) * parts, 1); + if (ret) + return ret; + + /* and the rest starting from (8GiB + common_size) */ + offset = (0x0200000000ULL >> 12) + (bsize << 8); + length = (dev_priv->vram_size >> 12) - (bsize << 8) - rsvd_tail; + + ret = nouveau_mm_init(&vram->mm, offset, length, 0); + if (ret) { + nouveau_mm_fini(&vram->mm); + return ret; + } + + return 0; } -- cgit v1.2.3-70-g09d2 From e425e0b33990575fd1c41671725b36247a325ea9 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Wed, 29 Jun 2011 10:42:14 +1000 Subject: drm/nvc0/gr: copy GPC mpart config from PFFB Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvc0_graph.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nvc0_graph.c b/drivers/gpu/drm/nouveau/nvc0_graph.c index 84321640a07..1a17530efb8 100644 --- a/drivers/gpu/drm/nouveau/nvc0_graph.c +++ b/drivers/gpu/drm/nouveau/nvc0_graph.c @@ -390,7 +390,7 @@ nvc0_graph_init_gpc_0(struct drm_device *dev) } nv_wr32(dev, GPC_BCAST(0x1bd4), magicgpc918); - nv_wr32(dev, GPC_BCAST(0x08ac), priv->rop_nr); + nv_wr32(dev, GPC_BCAST(0x08ac), nv_rd32(dev, 0x100800)); } static void -- cgit v1.2.3-70-g09d2 From 0b3b5579e1e498af2383b0d0d68e84f2e0bc76db Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Wed, 29 Jun 2011 10:45:07 +1000 Subject: drm/nouveau: don't complain for disabled timingset entries Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_perf.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_perf.c b/drivers/gpu/drm/nouveau/nouveau_perf.c index bb50f249023..b4327dad6e5 100644 --- a/drivers/gpu/drm/nouveau/nouveau_perf.c +++ b/drivers/gpu/drm/nouveau/nouveau_perf.c @@ -127,7 +127,8 @@ nouveau_perf_timing(struct drm_device *dev, struct bit_entry *P, entry += ramcfg * recordlen; if (entry[1] >= pm->memtimings.nr_timing) { - NV_WARN(dev, "timingset %d does not exist\n", entry[1]); + if (entry[1] != 0xff) + NV_WARN(dev, "timingset %d does not exist\n", entry[1]); return NULL; } -- cgit v1.2.3-70-g09d2 From 16cd399c65bc68332a860b0b572079d0316df3ca Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Sat, 2 Jul 2011 14:37:36 +1000 Subject: drm/nvc0/gr: unblacklist nvcf acceleration Reported to be working. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_state.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index 83d549d5071..07691c2ecea 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -1011,7 +1011,6 @@ int nouveau_load(struct drm_device *dev, unsigned long flags) switch (dev_priv->chipset) { case 0xc1: /* known broken */ case 0xc8: /* never tested */ - case 0xcf: /* never tested */ NV_INFO(dev, "acceleration disabled by default, pass " "noaccel=0 to force enable\n"); dev_priv->noaccel = true; -- cgit v1.2.3-70-g09d2 From 591b06d73bb8a2da879b1159342b8be192bf1119 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Sun, 3 Jul 2011 21:16:12 +1000 Subject: drm/nouveau/tmr: calibrate for ns timestamps on init We previously assumed (incorrectly a lot of the time) that PTIMER would be programmed at a frequency which'd give its 64-bit timestamps in nanoseconds. By programming PTIMER ourselves, we avoid this problem. Reviewed-by: Martin Peres Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nv04_timer.c | 108 ++++++++++++++++++++++++++--------- 1 file changed, 82 insertions(+), 26 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nv04_timer.c b/drivers/gpu/drm/nouveau/nv04_timer.c index 1d09ddd5739..afb9d4b6a02 100644 --- a/drivers/gpu/drm/nouveau/nv04_timer.c +++ b/drivers/gpu/drm/nouveau/nv04_timer.c @@ -3,46 +3,102 @@ #include "nouveau_drv.h" #include "nouveau_drm.h" +static u32 +nv04_crystal_freq(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + u32 extdev_boot0 = nv_rd32(dev, 0x101000); + int type; + + type = !!(extdev_boot0 & 0x00000040); + if ((dev_priv->chipset >= 0x17 && dev_priv->chipset < 0x20) || + dev_priv->chipset >= 0x25) + type |= (extdev_boot0 & 0x00400000) ? 2 : 0; + + switch (type) { + case 0: return 13500000; + case 1: return 14318180; + case 2: return 27000000; + case 3: return 25000000; + default: + break; + } + + return 0; +} + int nv04_timer_init(struct drm_device *dev) { + struct drm_nouveau_private *dev_priv = dev->dev_private; + u32 m, n, d; + nv_wr32(dev, NV04_PTIMER_INTR_EN_0, 0x00000000); nv_wr32(dev, NV04_PTIMER_INTR_0, 0xFFFFFFFF); - /* Just use the pre-existing values when possible for now; these regs - * are not written in nv (driver writer missed a /4 on the address), and - * writing 8 and 3 to the correct regs breaks the timings on the LVDS - * hardware sequencing microcode. - * A correct solution (involving calculations with the GPU PLL) can - * be done when kernel modesetting lands - */ - if (!nv_rd32(dev, NV04_PTIMER_NUMERATOR) || - !nv_rd32(dev, NV04_PTIMER_DENOMINATOR)) { - nv_wr32(dev, NV04_PTIMER_NUMERATOR, 0x00000008); - nv_wr32(dev, NV04_PTIMER_DENOMINATOR, 0x00000003); + /* aim for 31.25MHz, which gives us nanosecond timestamps */ + d = 1000000000 / 32; + + /* determine base clock for timer source */ + if (dev_priv->chipset < 0x40) { + n = dev_priv->engine.pm.clock_get(dev, PLL_CORE); + } else + if (dev_priv->chipset == 0x40) { + /*XXX: figure this out */ + n = 0; + } else { + n = nv04_crystal_freq(dev); + m = 1; + while (n < (d * 2)) { + n += (n / m); + m++; + } + + nv_wr32(dev, 0x009220, m - 1); } + if (!n) { + NV_WARN(dev, "PTIMER: unknown input clock freq\n"); + if (!nv_rd32(dev, NV04_PTIMER_NUMERATOR) || + !nv_rd32(dev, NV04_PTIMER_DENOMINATOR)) { + nv_wr32(dev, NV04_PTIMER_NUMERATOR, 1); + nv_wr32(dev, NV04_PTIMER_DENOMINATOR, 1); + } + return 0; + } + + /* reduce ratio to acceptable values */ + while (((n % 5) == 0) && ((d % 5) == 0)) { + n /= 5; + d /= 5; + } + + while (((n % 2) == 0) && ((d % 2) == 0)) { + n /= 2; + d /= 2; + } + + while (n > 0xffff || d > 0xffff) { + n >>= 1; + d >>= 1; + } + + nv_wr32(dev, NV04_PTIMER_NUMERATOR, n); + nv_wr32(dev, NV04_PTIMER_DENOMINATOR, d); return 0; } -uint64_t +u64 nv04_timer_read(struct drm_device *dev) { - uint32_t low; - /* From kmmio dumps on nv28 this looks like how the blob does this. - * It reads the high dword twice, before and after. - * The only explanation seems to be that the 64-bit timer counter - * advances between high and low dword reads and may corrupt the - * result. Not confirmed. - */ - uint32_t high2 = nv_rd32(dev, NV04_PTIMER_TIME_1); - uint32_t high1; + u32 hi, lo; + do { - high1 = high2; - low = nv_rd32(dev, NV04_PTIMER_TIME_0); - high2 = nv_rd32(dev, NV04_PTIMER_TIME_1); - } while (high1 != high2); - return (((uint64_t)high2) << 32) | (uint64_t)low; + hi = nv_rd32(dev, NV04_PTIMER_TIME_1); + lo = nv_rd32(dev, NV04_PTIMER_TIME_0); + } while (hi != nv_rd32(dev, NV04_PTIMER_TIME_1)); + + return ((u64)hi << 32 | lo); } void -- cgit v1.2.3-70-g09d2 From f3f2f54e11ff6f3f39a108bfcf7e074b282e3a50 Mon Sep 17 00:00:00 2001 From: Martin Peres Date: Mon, 4 Jul 2011 09:41:34 +1000 Subject: drm/nv04/pm: recalibrate timer on nvclk changes Signed-off-by: Martin Peres Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nv04_pm.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nv04_pm.c b/drivers/gpu/drm/nouveau/nv04_pm.c index eb1c70dd82e..9ae92a87b8c 100644 --- a/drivers/gpu/drm/nouveau/nv04_pm.c +++ b/drivers/gpu/drm/nouveau/nv04_pm.c @@ -68,6 +68,7 @@ void nv04_pm_clock_set(struct drm_device *dev, void *pre_state) { struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer; struct nv04_pm_state *state = pre_state; u32 reg = state->pll.reg; @@ -85,6 +86,9 @@ nv04_pm_clock_set(struct drm_device *dev, void *pre_state) nv_mask(dev, 0x1002c0, 0, 1 << 8); } + if (reg == NV_PRAMDAC_NVPLL_COEFF) + ptimer->init(dev); + kfree(state); } -- cgit v1.2.3-70-g09d2 From 2e9733ff7d4f1c7185bea16041f532d6142ca40a Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Sat, 2 Jul 2011 20:28:49 +1000 Subject: drm/nvd0: add a card_type for 0xdX chipsets These are different enough from 0xcX to justify it, half fermi, half kepler(??).. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_bo.c | 2 +- drivers/gpu/drm/nouveau/nouveau_drv.h | 1 + drivers/gpu/drm/nouveau/nouveau_object.c | 2 +- drivers/gpu/drm/nouveau/nouveau_vm.c | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index 890d50e4d68..7226f419e17 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -956,7 +956,7 @@ nouveau_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem) break; } - if (dev_priv->card_type == NV_C0) + if (dev_priv->card_type >= NV_C0) page_shift = node->page_shift; else page_shift = 12; diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 6629f30598f..842cd7acde8 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -637,6 +637,7 @@ enum nouveau_card_type { NV_40 = 0x40, NV_50 = 0x50, NV_C0 = 0xc0, + NV_D0 = 0xd0 }; struct drm_nouveau_private { diff --git a/drivers/gpu/drm/nouveau/nouveau_object.c b/drivers/gpu/drm/nouveau/nouveau_object.c index 159b7c437d3..363379c226e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_object.c +++ b/drivers/gpu/drm/nouveau/nouveau_object.c @@ -746,7 +746,7 @@ nouveau_gpuobj_channel_init(struct nouveau_channel *chan, int ret, i; NV_DEBUG(dev, "ch%d vram=0x%08x tt=0x%08x\n", chan->id, vram_h, tt_h); - if (dev_priv->card_type == NV_C0) + if (dev_priv->card_type >= NV_C0) return nvc0_gpuobj_channel_init(chan, vm); /* Allocate a chunk of memory for per-channel object storage */ diff --git a/drivers/gpu/drm/nouveau/nouveau_vm.c b/drivers/gpu/drm/nouveau/nouveau_vm.c index d432a2a791f..ef0832b29ad 100644 --- a/drivers/gpu/drm/nouveau/nouveau_vm.c +++ b/drivers/gpu/drm/nouveau/nouveau_vm.c @@ -306,7 +306,7 @@ nouveau_vm_new(struct drm_device *dev, u64 offset, u64 length, u64 mm_offset, block = length; } else - if (dev_priv->card_type == NV_C0) { + if (dev_priv->card_type >= NV_C0) { vm->map_pgt = nvc0_vm_map_pgt; vm->map = nvc0_vm_map; vm->map_sg = nvc0_vm_map_sg; -- cgit v1.2.3-70-g09d2 From 048a88595a66526f68636b51b1cdb5842bc0f28c Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 4 Jul 2011 10:47:19 +1000 Subject: drm/nouveau: make general drm modesetting init common Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_crtc.h | 3 +-- drivers/gpu/drm/nouveau/nouveau_drv.h | 1 - drivers/gpu/drm/nouveau/nouveau_mem.c | 2 -- drivers/gpu/drm/nouveau/nouveau_state.c | 21 +++++++++++++++++++++ drivers/gpu/drm/nouveau/nv04_display.c | 23 ----------------------- drivers/gpu/drm/nouveau/nv50_crtc.c | 2 -- drivers/gpu/drm/nouveau/nv50_cursor.c | 18 ------------------ drivers/gpu/drm/nouveau/nv50_display.c | 29 ++++++++++------------------- 8 files changed, 32 insertions(+), 67 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_crtc.h b/drivers/gpu/drm/nouveau/nouveau_crtc.h index cb1ce2a0916..bf8e1289953 100644 --- a/drivers/gpu/drm/nouveau/nouveau_crtc.h +++ b/drivers/gpu/drm/nouveau/nouveau_crtc.h @@ -82,14 +82,13 @@ static inline struct drm_crtc *to_drm_crtc(struct nouveau_crtc *crtc) } int nv50_crtc_create(struct drm_device *dev, int index); -int nv50_cursor_init(struct nouveau_crtc *); -void nv50_cursor_fini(struct nouveau_crtc *); int nv50_crtc_cursor_set(struct drm_crtc *drm_crtc, struct drm_file *file_priv, uint32_t buffer_handle, uint32_t width, uint32_t height); int nv50_crtc_cursor_move(struct drm_crtc *drm_crtc, int x, int y); int nv04_cursor_init(struct nouveau_crtc *); +int nv50_cursor_init(struct nouveau_crtc *); struct nouveau_connector * nouveau_crtc_connector_get(struct nouveau_crtc *crtc); diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 842cd7acde8..0661bcc94e5 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -736,7 +736,6 @@ struct drm_nouveau_private { uint64_t vram_size; uint64_t vram_sys_base; - uint64_t fb_phys; uint64_t fb_available_size; uint64_t fb_mappable_pages; uint64_t fb_aper_free; diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c index f9ae2fc3d6f..bd3c39f6938 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mem.c +++ b/drivers/gpu/drm/nouveau/nouveau_mem.c @@ -408,8 +408,6 @@ nouveau_mem_vram_init(struct drm_device *dev) if (ret) return ret; - dev_priv->fb_phys = pci_resource_start(dev->pdev, 1); - ret = nouveau_ttm_global_init(dev_priv); if (ret) return ret; diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index 07691c2ecea..2d7a4ed6014 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -657,6 +657,26 @@ nouveau_card_init(struct drm_device *dev) goto out_engine; } + /* initialise general modesetting */ + drm_mode_config_init(dev); + drm_mode_create_scaling_mode_property(dev); + drm_mode_create_dithering_property(dev); + dev->mode_config.funcs = (void *)&nouveau_mode_config_funcs; + dev->mode_config.fb_base = pci_resource_start(dev->pdev, 1); + dev->mode_config.min_width = 0; + dev->mode_config.min_height = 0; + if (dev_priv->card_type < NV_10) { + dev->mode_config.max_width = 2048; + dev->mode_config.max_height = 2048; + } else + if (dev_priv->card_type < NV_50) { + dev->mode_config.max_width = 4096; + dev->mode_config.max_height = 4096; + } else { + dev->mode_config.max_width = 8192; + dev->mode_config.max_height = 8192; + } + ret = engine->display.create(dev); if (ret) goto out_fifo; @@ -747,6 +767,7 @@ static void nouveau_card_takedown(struct drm_device *dev) } engine->display.destroy(dev); + drm_mode_config_cleanup(dev); if (!dev_priv->noaccel) { engine->fifo.takedown(dev); diff --git a/drivers/gpu/drm/nouveau/nv04_display.c b/drivers/gpu/drm/nouveau/nv04_display.c index 1715e1464b7..6bd8518d7b2 100644 --- a/drivers/gpu/drm/nouveau/nv04_display.c +++ b/drivers/gpu/drm/nouveau/nv04_display.c @@ -126,27 +126,6 @@ nv04_display_create(struct drm_device *dev) nouveau_hw_save_vga_fonts(dev, 1); - drm_mode_config_init(dev); - drm_mode_create_scaling_mode_property(dev); - drm_mode_create_dithering_property(dev); - - dev->mode_config.funcs = (void *)&nouveau_mode_config_funcs; - - dev->mode_config.min_width = 0; - dev->mode_config.min_height = 0; - switch (dev_priv->card_type) { - case NV_04: - dev->mode_config.max_width = 2048; - dev->mode_config.max_height = 2048; - break; - default: - dev->mode_config.max_width = 4096; - dev->mode_config.max_height = 4096; - break; - } - - dev->mode_config.fb_base = dev_priv->fb_phys; - nv04_crtc_create(dev, 0); if (nv_two_heads(dev)) nv04_crtc_create(dev, 1); @@ -235,8 +214,6 @@ nv04_display_destroy(struct drm_device *dev) list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) crtc->funcs->restore(crtc); - drm_mode_config_cleanup(dev); - nouveau_hw_save_vga_fonts(dev, 0); } diff --git a/drivers/gpu/drm/nouveau/nv50_crtc.c b/drivers/gpu/drm/nouveau/nv50_crtc.c index 5d989073ba6..882080e0b4f 100644 --- a/drivers/gpu/drm/nouveau/nv50_crtc.c +++ b/drivers/gpu/drm/nouveau/nv50_crtc.c @@ -329,8 +329,6 @@ nv50_crtc_destroy(struct drm_crtc *crtc) drm_crtc_cleanup(&nv_crtc->base); - nv50_cursor_fini(nv_crtc); - nouveau_bo_unmap(nv_crtc->lut.nvbo); nouveau_bo_ref(NULL, &nv_crtc->lut.nvbo); nouveau_bo_unmap(nv_crtc->cursor.nvbo); diff --git a/drivers/gpu/drm/nouveau/nv50_cursor.c b/drivers/gpu/drm/nouveau/nv50_cursor.c index 9752c35bb84..adfc9b607a5 100644 --- a/drivers/gpu/drm/nouveau/nv50_cursor.c +++ b/drivers/gpu/drm/nouveau/nv50_cursor.c @@ -137,21 +137,3 @@ nv50_cursor_init(struct nouveau_crtc *nv_crtc) nv_crtc->cursor.show = nv50_cursor_show; return 0; } - -void -nv50_cursor_fini(struct nouveau_crtc *nv_crtc) -{ - struct drm_device *dev = nv_crtc->base.dev; - int idx = nv_crtc->index; - - NV_DEBUG_KMS(dev, "\n"); - - nv_wr32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(idx), 0); - if (!nv_wait(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(idx), - NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS, 0)) { - NV_ERROR(dev, "timeout: CURSOR_CTRL2_STATUS == 0\n"); - NV_ERROR(dev, "CURSOR_CTRL2 = 0x%08x\n", - nv_rd32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(idx))); - } -} - diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index db1a5f4b711..5754c0ac893 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -247,6 +247,16 @@ static int nv50_display_disable(struct drm_device *dev) } } + for (i = 0; i < 2; i++) { + nv_wr32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i), 0); + if (!nv_wait(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i), + NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS, 0)) { + NV_ERROR(dev, "timeout: CURSOR_CTRL2_STATUS == 0\n"); + NV_ERROR(dev, "CURSOR_CTRL2 = 0x%08x\n", + nv_rd32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i))); + } + } + nv50_evo_fini(dev); for (i = 0; i < 3; i++) { @@ -286,23 +296,6 @@ int nv50_display_create(struct drm_device *dev) return -ENOMEM; dev_priv->engine.display.priv = priv; - /* init basic kernel modesetting */ - drm_mode_config_init(dev); - - /* Initialise some optional connector properties. */ - drm_mode_create_scaling_mode_property(dev); - drm_mode_create_dithering_property(dev); - - dev->mode_config.min_width = 0; - dev->mode_config.min_height = 0; - - dev->mode_config.funcs = (void *)&nouveau_mode_config_funcs; - - dev->mode_config.max_width = 8192; - dev->mode_config.max_height = 8192; - - dev->mode_config.fb_base = dev_priv->fb_phys; - /* Create CRTC objects */ for (i = 0; i < 2; i++) nv50_crtc_create(dev, i); @@ -364,8 +357,6 @@ nv50_display_destroy(struct drm_device *dev) NV_DEBUG_KMS(dev, "\n"); - drm_mode_config_cleanup(dev); - nv50_display_disable(dev); nouveau_irq_unregister(dev, 26); kfree(disp); -- cgit v1.2.3-70-g09d2 From 1575b3646c1c2141cfb68f7581c50d8bd19f17ac Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 4 Jul 2011 11:55:39 +1000 Subject: drm/nouveau: fixup init/fini sequence to deal with no CRTCs Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_object.c | 4 +- drivers/gpu/drm/nouveau/nouveau_state.c | 64 ++++++++++++++------------------ 2 files changed, 30 insertions(+), 38 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_object.c b/drivers/gpu/drm/nouveau/nouveau_object.c index 363379c226e..4406c175106 100644 --- a/drivers/gpu/drm/nouveau/nouveau_object.c +++ b/drivers/gpu/drm/nouveau/nouveau_object.c @@ -793,7 +793,7 @@ nouveau_gpuobj_channel_init(struct nouveau_channel *chan, return ret; /* dma objects for display sync channel semaphore blocks */ - for (i = 0; i < 2; i++) { + for (i = 0; i < dev->mode_config.num_crtc; i++) { struct nouveau_gpuobj *sem = NULL; struct nv50_display_crtc *dispc = &nv50_display(dev)->crtc[i]; @@ -878,7 +878,7 @@ nouveau_gpuobj_channel_takedown(struct nouveau_channel *chan) if (dev_priv->card_type >= NV_50) { struct nv50_display *disp = nv50_display(dev); - for (i = 0; i < 2; i++) { + for (i = 0; i < dev->mode_config.num_crtc; i++) { struct nv50_display_crtc *dispc = &disp->crtc[i]; nouveau_bo_vma_del(dispc->sem.bo, &chan->dispc_vma[i]); } diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index 2d7a4ed6014..d4570220417 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -452,21 +452,6 @@ nouveau_vga_set_decode(void *priv, bool state) return VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM; } -static int -nouveau_card_init_channel(struct drm_device *dev) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - int ret; - - ret = nouveau_channel_alloc(dev, &dev_priv->channel, NULL, - NvDmaFB, NvDmaTT); - if (ret) - return ret; - - mutex_unlock(&dev_priv->channel->mutex); - return 0; -} - static void nouveau_switcheroo_set_state(struct pci_dev *pdev, enum vga_switcheroo_state state) { @@ -657,6 +642,10 @@ nouveau_card_init(struct drm_device *dev) goto out_engine; } + ret = nouveau_irq_init(dev); + if (ret) + goto out_fifo; + /* initialise general modesetting */ drm_mode_config_init(dev); drm_mode_create_scaling_mode_property(dev); @@ -679,39 +668,40 @@ nouveau_card_init(struct drm_device *dev) ret = engine->display.create(dev); if (ret) - goto out_fifo; - - ret = drm_vblank_init(dev, nv_two_heads(dev) ? 2 : 1); - if (ret) - goto out_vblank; - - ret = nouveau_irq_init(dev); - if (ret) - goto out_vblank; - - /* what about PVIDEO/PCRTC/PRAMDAC etc? */ + goto out_irq; if (dev_priv->eng[NVOBJ_ENGINE_GR]) { ret = nouveau_fence_init(dev); if (ret) - goto out_irq; + goto out_disp; - ret = nouveau_card_init_channel(dev); + ret = nouveau_channel_alloc(dev, &dev_priv->channel, NULL, + NvDmaFB, NvDmaTT); if (ret) goto out_fence; + + mutex_unlock(&dev_priv->channel->mutex); + } + + if (dev->mode_config.num_crtc) { + ret = drm_vblank_init(dev, dev->mode_config.num_crtc); + if (ret) + goto out_chan; + + nouveau_fbcon_init(dev); + drm_kms_helper_poll_init(dev); } - nouveau_fbcon_init(dev); - drm_kms_helper_poll_init(dev); return 0; +out_chan: + nouveau_channel_put_unlocked(&dev_priv->channel); out_fence: nouveau_fence_fini(dev); +out_disp: + engine->display.destroy(dev); out_irq: nouveau_irq_fini(dev); -out_vblank: - drm_vblank_cleanup(dev); - engine->display.destroy(dev); out_fifo: if (!dev_priv->noaccel) engine->fifo.takedown(dev); @@ -758,8 +748,11 @@ static void nouveau_card_takedown(struct drm_device *dev) struct nouveau_engine *engine = &dev_priv->engine; int e; - drm_kms_helper_poll_fini(dev); - nouveau_fbcon_fini(dev); + if (dev->mode_config.num_crtc) { + drm_kms_helper_poll_fini(dev); + nouveau_fbcon_fini(dev); + drm_vblank_cleanup(dev); + } if (dev_priv->channel) { nouveau_channel_put_unlocked(&dev_priv->channel); @@ -801,7 +794,6 @@ static void nouveau_card_takedown(struct drm_device *dev) engine->vram.takedown(dev); nouveau_irq_fini(dev); - drm_vblank_cleanup(dev); nouveau_pm_fini(dev); nouveau_bios_takedown(dev); -- cgit v1.2.3-70-g09d2 From 03bc9675d358ded9db07ba966f2f3f3c2fba2a9c Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 4 Jul 2011 13:14:05 +1000 Subject: drm/nouveau: allow modeset module option to select 'headless mode' Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_drv.c | 2 +- drivers/gpu/drm/nouveau/nouveau_drv.h | 1 + drivers/gpu/drm/nouveau/nouveau_state.c | 9 +++++++++ 3 files changed, 11 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.c b/drivers/gpu/drm/nouveau/nouveau_drv.c index b30ddd8d2e2..c1e01f37b9d 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.c +++ b/drivers/gpu/drm/nouveau/nouveau_drv.c @@ -41,7 +41,7 @@ int nouveau_agpmode = -1; module_param_named(agpmode, nouveau_agpmode, int, 0400); MODULE_PARM_DESC(modeset, "Enable kernel modesetting"); -static int nouveau_modeset = -1; /* kms */ +int nouveau_modeset = -1; module_param_named(modeset, nouveau_modeset, int, 0400); MODULE_PARM_DESC(vbios, "Override default VBIOS location"); diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 0661bcc94e5..7fdfad03f96 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -798,6 +798,7 @@ nouveau_bo_ref(struct nouveau_bo *ref, struct nouveau_bo **pnvbo) } /* nouveau_drv.c */ +extern int nouveau_modeset; extern int nouveau_agpmode; extern int nouveau_duallink; extern int nouveau_uscript_lvds; diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index d4570220417..d3b026125af 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -431,6 +431,15 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) return 1; } + /* headless mode */ + if (nouveau_modeset == 2) { + engine->display.early_init = nouveau_stub_init; + engine->display.late_takedown = nouveau_stub_takedown; + engine->display.create = nouveau_stub_init; + engine->display.init = nouveau_stub_init; + engine->display.destroy = nouveau_stub_takedown; + } + return 0; } -- cgit v1.2.3-70-g09d2 From d9f61c2d2847fb2889ed01d2240db38927ab7e18 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 4 Jul 2011 13:25:17 +1000 Subject: drm/nouveau: initial chipset description for nvdX chipsets All the non-stubbed functions should be okay for this chipset, the rest will be added back as they're figured out. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_state.c | 48 +++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index d3b026125af..10b20110223 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -299,7 +299,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) case 0x50: case 0x80: /* gotta love NVIDIA's consistency.. */ case 0x90: - case 0xA0: + case 0xa0: engine->instmem.init = nv50_instmem_init; engine->instmem.takedown = nv50_instmem_takedown; engine->instmem.suspend = nv50_instmem_suspend; @@ -376,7 +376,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->vram.put = nv50_vram_del; engine->vram.flags_valid = nv50_vram_flags_valid; break; - case 0xC0: + case 0xc0: engine->instmem.init = nvc0_instmem_init; engine->instmem.takedown = nvc0_instmem_takedown; engine->instmem.suspend = nvc0_instmem_suspend; @@ -426,6 +426,47 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->pm.voltage_get = nouveau_voltage_gpio_get; engine->pm.voltage_set = nouveau_voltage_gpio_set; break; + case 0xd0: + engine->instmem.init = nvc0_instmem_init; + engine->instmem.takedown = nvc0_instmem_takedown; + engine->instmem.suspend = nvc0_instmem_suspend; + engine->instmem.resume = nvc0_instmem_resume; + engine->instmem.get = nv50_instmem_get; + engine->instmem.put = nv50_instmem_put; + engine->instmem.map = nv50_instmem_map; + engine->instmem.unmap = nv50_instmem_unmap; + engine->instmem.flush = nv84_instmem_flush; + engine->mc.init = nv50_mc_init; + engine->mc.takedown = nv50_mc_takedown; + engine->timer.init = nv04_timer_init; + engine->timer.read = nv04_timer_read; + engine->timer.takedown = nv04_timer_takedown; + engine->fb.init = nvc0_fb_init; + engine->fb.takedown = nvc0_fb_takedown; + engine->fifo.channels = 128; + engine->fifo.init = nvc0_fifo_init; + engine->fifo.takedown = nvc0_fifo_takedown; + engine->fifo.disable = nvc0_fifo_disable; + engine->fifo.enable = nvc0_fifo_enable; + engine->fifo.reassign = nvc0_fifo_reassign; + engine->fifo.channel_id = nvc0_fifo_channel_id; + engine->fifo.create_context = nvc0_fifo_create_context; + engine->fifo.destroy_context = nvc0_fifo_destroy_context; + engine->fifo.load_context = nvc0_fifo_load_context; + engine->fifo.unload_context = nvc0_fifo_unload_context; + engine->display.early_init = nouveau_stub_init; + engine->display.late_takedown = nouveau_stub_takedown; + engine->display.create = nouveau_stub_init; + engine->display.init = nouveau_stub_init; + engine->display.destroy = nouveau_stub_takedown; + engine->gpio.init = nouveau_stub_init; + engine->gpio.takedown = nouveau_stub_takedown; + engine->vram.init = nvc0_vram_init; + engine->vram.takedown = nv50_vram_fini; + engine->vram.get = nvc0_vram_new; + engine->vram.put = nv50_vram_del; + engine->vram.flags_valid = nvc0_vram_flags_valid; + break; default: NV_ERROR(dev, "NV%02x unsupported\n", dev_priv->chipset); return 1; @@ -1015,6 +1056,9 @@ int nouveau_load(struct drm_device *dev, unsigned long flags) 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; -- cgit v1.2.3-70-g09d2 From eeb3ca12b4658c569bd60fe60c4c45c627e842a6 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Sat, 2 Jul 2011 20:43:42 +1000 Subject: drm/nvd0/i2c: initial implementation Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_i2c.c | 51 ++++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 16 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.c b/drivers/gpu/drm/nouveau/nouveau_i2c.c index cb389d01432..739c0ac3a9b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_i2c.c +++ b/drivers/gpu/drm/nouveau/nouveau_i2c.c @@ -107,6 +107,13 @@ nv4e_i2c_getsda(void *data) return !!((nv_rd32(dev, i2c->rd) >> 16) & 8); } +static const uint32_t nv50_i2c_port[] = { + 0x00e138, 0x00e150, 0x00e168, 0x00e180, + 0x00e254, 0x00e274, 0x00e764, 0x00e780, + 0x00e79c, 0x00e7b8 +}; +#define NV50_I2C_PORTS ARRAY_SIZE(nv50_i2c_port) + static int nv50_i2c_getscl(void *data) { @@ -130,28 +137,32 @@ static void nv50_i2c_setscl(void *data, int state) { struct nouveau_i2c_chan *i2c = data; - struct drm_device *dev = i2c->dev; - nv_wr32(dev, i2c->wr, 4 | (i2c->data ? 2 : 0) | (state ? 1 : 0)); + nv_wr32(i2c->dev, i2c->wr, 4 | (i2c->data ? 2 : 0) | (state ? 1 : 0)); } static void nv50_i2c_setsda(void *data, int state) { struct nouveau_i2c_chan *i2c = data; - struct drm_device *dev = i2c->dev; - nv_wr32(dev, i2c->wr, - (nv_rd32(dev, i2c->rd) & 1) | 4 | (state ? 2 : 0)); + nv_mask(i2c->dev, i2c->wr, 0x00000006, 4 | (state ? 2 : 0)); i2c->data = state; } -static const uint32_t nv50_i2c_port[] = { - 0x00e138, 0x00e150, 0x00e168, 0x00e180, - 0x00e254, 0x00e274, 0x00e764, 0x00e780, - 0x00e79c, 0x00e7b8 -}; -#define NV50_I2C_PORTS ARRAY_SIZE(nv50_i2c_port) +static int +nvd0_i2c_getscl(void *data) +{ + struct nouveau_i2c_chan *i2c = data; + return !!(nv_rd32(i2c->dev, i2c->rd) & 0x10); +} + +static int +nvd0_i2c_getsda(void *data) +{ + struct nouveau_i2c_chan *i2c = data; + return !!(nv_rd32(i2c->dev, i2c->rd) & 0x20); +} int nouveau_i2c_init(struct drm_device *dev, struct dcb_i2c_entry *entry, int index) @@ -163,7 +174,8 @@ nouveau_i2c_init(struct drm_device *dev, struct dcb_i2c_entry *entry, int index) if (entry->chan) return -EEXIST; - if (dev_priv->card_type >= NV_50 && entry->read >= NV50_I2C_PORTS) { + if (dev_priv->card_type >= NV_50 && + dev_priv->card_type <= NV_C0 && entry->read >= NV50_I2C_PORTS) { NV_ERROR(dev, "unknown i2c port %d\n", entry->read); return -EINVAL; } @@ -192,10 +204,17 @@ nouveau_i2c_init(struct drm_device *dev, struct dcb_i2c_entry *entry, int index) case 5: i2c->bit.setsda = nv50_i2c_setsda; i2c->bit.setscl = nv50_i2c_setscl; - i2c->bit.getsda = nv50_i2c_getsda; - i2c->bit.getscl = nv50_i2c_getscl; - i2c->rd = nv50_i2c_port[entry->read]; - i2c->wr = i2c->rd; + if (dev_priv->card_type < NV_D0) { + i2c->bit.getsda = nv50_i2c_getsda; + i2c->bit.getscl = nv50_i2c_getscl; + i2c->rd = nv50_i2c_port[entry->read]; + i2c->wr = i2c->rd; + } else { + i2c->bit.getsda = nvd0_i2c_getsda; + i2c->bit.getscl = nvd0_i2c_getscl; + i2c->rd = 0x00d014 + (entry->read * 0x20); + i2c->wr = i2c->rd; + } break; case 6: i2c->rd = entry->read; -- cgit v1.2.3-70-g09d2 From d7f8172ca93b61135d6db293c6440b2e97fc87ee Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Sun, 3 Jul 2011 02:57:35 +1000 Subject: drm/nvd0/gpio: initial implementation Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_bios.c | 12 +++++++++--- drivers/gpu/drm/nouveau/nouveau_drv.h | 2 ++ drivers/gpu/drm/nouveau/nouveau_state.c | 7 ++++++- drivers/gpu/drm/nouveau/nv50_gpio.c | 31 +++++++++++++++++++++++++++++++ 4 files changed, 48 insertions(+), 4 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index b311faba34f..30e723c8106 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -5884,9 +5884,15 @@ parse_dcb_gpio_table(struct nvbios *bios) } e->line = (e->entry & 0x0000001f) >> 0; - e->state_default = (e->entry & 0x01000000) >> 24; - e->state[0] = (e->entry & 0x18000000) >> 27; - e->state[1] = (e->entry & 0x60000000) >> 29; + if (gpio[0] == 0x40) { + e->state_default = (e->entry & 0x01000000) >> 24; + e->state[0] = (e->entry & 0x18000000) >> 27; + e->state[1] = (e->entry & 0x60000000) >> 29; + } else { + e->state_default = (e->entry & 0x00000080) >> 7; + e->state[0] = (entry[4] >> 4) & 3; + e->state[1] = (entry[4] >> 6) & 3; + } } } diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 7fdfad03f96..721845add9b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -1391,6 +1391,8 @@ int nv50_gpio_init(struct drm_device *dev); void nv50_gpio_fini(struct drm_device *dev); int nv50_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag); int nv50_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state); +int nvd0_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag); +int nvd0_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state); int nv50_gpio_irq_register(struct drm_device *, enum dcb_gpio_tag, void (*)(void *, int), void *); void nv50_gpio_irq_unregister(struct drm_device *, enum dcb_gpio_tag, diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index 10b20110223..8cf42e223fc 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -459,8 +459,13 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->display.create = nouveau_stub_init; engine->display.init = nouveau_stub_init; engine->display.destroy = nouveau_stub_takedown; - engine->gpio.init = nouveau_stub_init; + engine->gpio.init = nv50_gpio_init; engine->gpio.takedown = nouveau_stub_takedown; + engine->gpio.get = nvd0_gpio_get; + engine->gpio.set = nvd0_gpio_set; + engine->gpio.irq_register = nv50_gpio_irq_register; + engine->gpio.irq_unregister = nv50_gpio_irq_unregister; + engine->gpio.irq_enable = nv50_gpio_irq_enable; engine->vram.init = nvc0_vram_init; engine->vram.takedown = nv50_vram_fini; engine->vram.get = nvc0_vram_new; diff --git a/drivers/gpu/drm/nouveau/nv50_gpio.c b/drivers/gpu/drm/nouveau/nv50_gpio.c index d4f4206dad7..793a5ccca12 100644 --- a/drivers/gpu/drm/nouveau/nv50_gpio.c +++ b/drivers/gpu/drm/nouveau/nv50_gpio.c @@ -97,6 +97,37 @@ nv50_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state) return 0; } +int +nvd0_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag) +{ + struct dcb_gpio_entry *gpio; + u32 v; + + gpio = nouveau_bios_gpio_entry(dev, tag); + if (!gpio) + return -ENOENT; + + v = nv_rd32(dev, 0x00d610 + (gpio->line * 4)); + v &= 0x00004000; + return (!!v == (gpio->state[1] & 1)); +} + +int +nvd0_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state) +{ + struct dcb_gpio_entry *gpio; + u32 v; + + gpio = nouveau_bios_gpio_entry(dev, tag); + if (!gpio) + return -ENOENT; + + v = gpio->state[state] ^ 2; + + nv_mask(dev, 0x00d610 + (gpio->line * 4), 0x00003000, v << 12); + return 0; +} + int nv50_gpio_irq_register(struct drm_device *dev, enum dcb_gpio_tag tag, void (*handler)(void *, int), void *data) -- cgit v1.2.3-70-g09d2 From 75139063b7a369f7fa849922d5a204b8ba96d582 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Sun, 3 Jul 2011 13:40:01 +1000 Subject: drm/nouveau/bios: fix INIT_GPIO for new chipsets Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_bios.c | 73 ++++++++++++++++++++++++---------- 1 file changed, 51 insertions(+), 22 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index 30e723c8106..f0a77b7ce60 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -3221,6 +3221,49 @@ init_8d(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) return 1; } +static void +init_gpio_unknv50(struct nvbios *bios, struct dcb_gpio_entry *gpio) +{ + const uint32_t nv50_gpio_ctl[2] = { 0xe100, 0xe28c }; + u32 r, s, v; + + /* Not a clue, needs de-magicing */ + r = nv50_gpio_ctl[gpio->line >> 4]; + s = (gpio->line & 0x0f); + v = bios_rd32(bios, r) & ~(0x00010001 << s); + switch ((gpio->entry & 0x06000000) >> 25) { + case 1: + v |= (0x00000001 << s); + break; + case 2: + v |= (0x00010000 << s); + break; + default: + break; + } + + bios_wr32(bios, r, v); +} + +static void +init_gpio_unknvd0(struct nvbios *bios, struct dcb_gpio_entry *gpio) +{ + u32 v, i; + + v = bios_rd32(bios, 0x00d610 + (gpio->line * 4)); + v &= 0xffffff00; + v |= (gpio->entry & 0x00ff0000) >> 16; + bios_wr32(bios, 0x00d610 + (gpio->line * 4), v); + + i = (gpio->entry & 0x1f000000) >> 24; + if (i) { + v = bios_rd32(bios, 0x00d640 + ((i - 1) * 4)); + v &= 0xffffff00; + v |= gpio->line; + bios_wr32(bios, 0x00d640 + ((i - 1) * 4), v); + } +} + static int init_gpio(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) { @@ -3235,7 +3278,6 @@ init_gpio(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) struct drm_nouveau_private *dev_priv = bios->dev->dev_private; struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; - const uint32_t nv50_gpio_ctl[2] = { 0xe100, 0xe28c }; int i; if (dev_priv->card_type < NV_50) { @@ -3248,33 +3290,20 @@ init_gpio(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) for (i = 0; i < bios->dcb.gpio.entries; i++) { struct dcb_gpio_entry *gpio = &bios->dcb.gpio.entry[i]; - uint32_t r, s, v; BIOSLOG(bios, "0x%04X: Entry: 0x%08X\n", offset, gpio->entry); BIOSLOG(bios, "0x%04X: set gpio 0x%02x, state %d\n", offset, gpio->tag, gpio->state_default); - if (bios->execute) - pgpio->set(bios->dev, gpio->tag, gpio->state_default); - /* The NVIDIA binary driver doesn't appear to actually do - * any of this, my VBIOS does however. - */ - /* Not a clue, needs de-magicing */ - r = nv50_gpio_ctl[gpio->line >> 4]; - s = (gpio->line & 0x0f); - v = bios_rd32(bios, r) & ~(0x00010001 << s); - switch ((gpio->entry & 0x06000000) >> 25) { - case 1: - v |= (0x00000001 << s); - break; - case 2: - v |= (0x00010000 << s); - break; - default: - break; - } - bios_wr32(bios, r, v); + if (!bios->execute) + continue; + + pgpio->set(bios->dev, gpio->tag, gpio->state_default); + if (dev_priv->card_type < NV_D0) + init_gpio_unknv50(bios, gpio); + else + init_gpio_unknvd0(bios, gpio); } return 1; -- cgit v1.2.3-70-g09d2 From 4784e4aa47a1754cdd1be24fd5106b722c4c137d Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 4 Jul 2011 14:06:07 +1000 Subject: drm/nvd0/pm: enable clock/voltage hooks Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_state.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index 8cf42e223fc..c4802eb3f9a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -471,6 +471,9 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->vram.get = nvc0_vram_new; engine->vram.put = nv50_vram_del; engine->vram.flags_valid = nvc0_vram_flags_valid; + engine->pm.clocks_get = nvc0_pm_clocks_get; + engine->pm.voltage_get = nouveau_voltage_gpio_get; + engine->pm.voltage_set = nouveau_voltage_gpio_set; break; default: NV_ERROR(dev, "NV%02x unsupported\n", dev_priv->chipset); -- cgit v1.2.3-70-g09d2 From 02e4f5877dc7b963b3dd2beaf9664cf29c12d728 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Wed, 6 Jul 2011 21:21:42 +1000 Subject: drm/nouveau/bios: allow passing in crtc to the init table parser Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_bios.c | 49 +++++++++++++++++++--------------- drivers/gpu/drm/nouveau/nouveau_bios.h | 1 + drivers/gpu/drm/nouveau/nouveau_dp.c | 4 +-- drivers/gpu/drm/nouveau/nouveau_drv.h | 7 +++-- drivers/gpu/drm/nouveau/nv50_display.c | 8 +++--- drivers/gpu/drm/nouveau/nv50_pm.c | 8 +++--- 6 files changed, 41 insertions(+), 36 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index f0a77b7ce60..58d8c85b85d 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -296,6 +296,11 @@ munge_reg(struct nvbios *bios, uint32_t reg) if (dev_priv->card_type < NV_50) return reg; + if (reg & 0x80000000) { + BUG_ON(bios->display.crtc < 0); + reg += bios->display.crtc * 0x800; + } + if (reg & 0x40000000) { BUG_ON(!dcbent); @@ -304,7 +309,7 @@ munge_reg(struct nvbios *bios, uint32_t reg) reg += 0x00000080; } - reg &= ~0x60000000; + reg &= ~0xe0000000; return reg; } @@ -4496,8 +4501,8 @@ nouveau_bios_dp_table(struct drm_device *dev, struct dcb_entry *dcbent, } int -nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent, - uint32_t sub, int pxclk) +nouveau_bios_run_display_table(struct drm_device *dev, u16 type, int pclk, + struct dcb_entry *dcbent, int crtc) { /* * The display script table is located by the BIT 'U' table. @@ -4587,22 +4592,22 @@ nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent, return 1; } - if (pxclk < -2 || pxclk > 0) { + if (pclk < -2 || pclk > 0) { /* Try to find matching script table entry */ for (i = 0; i < otable[5]; i++) { - if (ROM16(otable[table[4] + i*6]) == sub) + if (ROM16(otable[table[4] + i*6]) == type) break; } if (i == otable[5]) { NV_ERROR(dev, "Table 0x%04x not found for %d/%d, " "using first\n", - sub, dcbent->type, dcbent->or); + type, dcbent->type, dcbent->or); i = 0; } } - if (pxclk == 0) { + if (pclk == 0) { script = ROM16(otable[6]); if (!script) { NV_DEBUG_KMS(dev, "output script 0 not found\n"); @@ -4610,9 +4615,9 @@ nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent, } NV_DEBUG_KMS(dev, "0x%04X: parsing output script 0\n", script); - nouveau_bios_run_init_table(dev, script, dcbent); + nouveau_bios_run_init_table(dev, script, dcbent, crtc); } else - if (pxclk == -1) { + if (pclk == -1) { script = ROM16(otable[8]); if (!script) { NV_DEBUG_KMS(dev, "output script 1 not found\n"); @@ -4620,9 +4625,9 @@ nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent, } NV_DEBUG_KMS(dev, "0x%04X: parsing output script 1\n", script); - nouveau_bios_run_init_table(dev, script, dcbent); + nouveau_bios_run_init_table(dev, script, dcbent, crtc); } else - if (pxclk == -2) { + if (pclk == -2) { if (table[4] >= 12) script = ROM16(otable[10]); else @@ -4633,31 +4638,31 @@ nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent, } NV_DEBUG_KMS(dev, "0x%04X: parsing output script 2\n", script); - nouveau_bios_run_init_table(dev, script, dcbent); + nouveau_bios_run_init_table(dev, script, dcbent, crtc); } else - if (pxclk > 0) { + if (pclk > 0) { script = ROM16(otable[table[4] + i*6 + 2]); if (script) - script = clkcmptable(bios, script, pxclk); + script = clkcmptable(bios, script, pclk); if (!script) { NV_DEBUG_KMS(dev, "clock script 0 not found\n"); return 1; } NV_DEBUG_KMS(dev, "0x%04X: parsing clock script 0\n", script); - nouveau_bios_run_init_table(dev, script, dcbent); + nouveau_bios_run_init_table(dev, script, dcbent, crtc); } else - if (pxclk < 0) { + if (pclk < 0) { script = ROM16(otable[table[4] + i*6 + 4]); if (script) - script = clkcmptable(bios, script, -pxclk); + script = clkcmptable(bios, script, -pclk); if (!script) { NV_DEBUG_KMS(dev, "clock script 1 not found\n"); return 1; } NV_DEBUG_KMS(dev, "0x%04X: parsing clock script 1\n", script); - nouveau_bios_run_init_table(dev, script, dcbent); + nouveau_bios_run_init_table(dev, script, dcbent, crtc); } return 0; @@ -6804,7 +6809,7 @@ uint8_t *nouveau_bios_embedded_edid(struct drm_device *dev) void nouveau_bios_run_init_table(struct drm_device *dev, uint16_t table, - struct dcb_entry *dcbent) + struct dcb_entry *dcbent, int crtc) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nvbios *bios = &dev_priv->vbios; @@ -6812,6 +6817,7 @@ nouveau_bios_run_init_table(struct drm_device *dev, uint16_t table, spin_lock_bh(&bios->lock); bios->display.output = dcbent; + bios->display.crtc = crtc; parse_init_table(bios, table, &iexec); bios->display.output = NULL; spin_unlock_bh(&bios->lock); @@ -6898,9 +6904,8 @@ nouveau_run_vbios_init(struct drm_device *dev) if (dev_priv->card_type >= NV_50) { for (i = 0; i < bios->dcb.entries; i++) { - nouveau_bios_run_display_table(dev, - &bios->dcb.entry[i], - 0, 0); + nouveau_bios_run_display_table(dev, 0, 0, + &bios->dcb.entry[i], -1); } } diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.h b/drivers/gpu/drm/nouveau/nouveau_bios.h index 050c314119d..b28f0bceaed 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.h +++ b/drivers/gpu/drm/nouveau/nouveau_bios.h @@ -289,6 +289,7 @@ struct nvbios { struct { struct dcb_entry *output; + int crtc; uint16_t script_table_ptr; uint16_t dp_table_ptr; } display; diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 7beb82a0315..44de23d9a43 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -300,7 +300,7 @@ nouveau_dp_link_train(struct drm_encoder *encoder) if (dpe->script0) { NV_DEBUG_KMS(dev, "SOR-%d: running DP script 0\n", nv_encoder->or); nouveau_bios_run_init_table(dev, le16_to_cpu(dpe->script0), - nv_encoder->dcb); + nv_encoder->dcb, -1); } train: @@ -433,7 +433,7 @@ stop: if (dpe->script1) { NV_DEBUG_KMS(dev, "SOR-%d: running DP script 1\n", nv_encoder->or); nouveau_bios_run_init_table(dev, le16_to_cpu(dpe->script1), - nv_encoder->dcb); + nv_encoder->dcb, -1); } /* re-enable hotplug detect */ diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 721845add9b..6469ffee6fc 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -1039,7 +1039,7 @@ extern int nouveau_bios_init(struct drm_device *); extern void nouveau_bios_takedown(struct drm_device *dev); extern int nouveau_run_vbios_init(struct drm_device *); extern void nouveau_bios_run_init_table(struct drm_device *, uint16_t table, - struct dcb_entry *); + struct dcb_entry *, int crtc); extern struct dcb_gpio_entry *nouveau_bios_gpio_entry(struct drm_device *, enum dcb_gpio_tag); extern struct dcb_connector_table_entry * @@ -1047,9 +1047,8 @@ nouveau_bios_connector_entry(struct drm_device *, int index); extern u32 get_pll_register(struct drm_device *, enum pll_types); extern int get_pll_limits(struct drm_device *, uint32_t limit_match, struct pll_lims *); -extern int nouveau_bios_run_display_table(struct drm_device *, - struct dcb_entry *, - uint32_t script, int pxclk); +extern int nouveau_bios_run_display_table(struct drm_device *, u16 id, int clk, + struct dcb_entry *, int crtc); extern void *nouveau_bios_dp_table(struct drm_device *, struct dcb_entry *, int *length); extern bool nouveau_bios_fp_mode(struct drm_device *, struct drm_display_mode *); diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index 5754c0ac893..8260303c2fc 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -689,7 +689,7 @@ nv50_display_unk10_handler(struct drm_device *dev) struct dcb_entry *dcb = &dev_priv->vbios.dcb.entry[i]; if (dcb->type == type && (dcb->or & (1 << or))) { - nouveau_bios_run_display_table(dev, dcb, 0, -1); + nouveau_bios_run_display_table(dev, 0, -1, dcb, -1); disp->irq.dcb = dcb; goto ack; } @@ -744,7 +744,7 @@ nv50_display_unk20_handler(struct drm_device *dev) NV_DEBUG_KMS(dev, "0x610030: 0x%08x\n", unk30); dcb = disp->irq.dcb; if (dcb) { - nouveau_bios_run_display_table(dev, dcb, 0, -2); + nouveau_bios_run_display_table(dev, 0, -2, dcb, -1); disp->irq.dcb = NULL; } @@ -828,7 +828,7 @@ nv50_display_unk20_handler(struct drm_device *dev) } script = nv50_display_script_select(dev, dcb, mc, pclk); - nouveau_bios_run_display_table(dev, dcb, script, pclk); + nouveau_bios_run_display_table(dev, script, pclk, dcb, -1); nv50_display_unk20_dp_hack(dev, dcb); @@ -895,7 +895,7 @@ nv50_display_unk40_handler(struct drm_device *dev) if (!dcb) goto ack; - nouveau_bios_run_display_table(dev, dcb, script, -pclk); + nouveau_bios_run_display_table(dev, script, -pclk, dcb, -1); nv50_display_unk40_dp_set_tmds(dev, dcb); ack: diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c index 8a2810011bd..3d5a86b9828 100644 --- a/drivers/gpu/drm/nouveau/nv50_pm.c +++ b/drivers/gpu/drm/nouveau/nv50_pm.c @@ -115,15 +115,15 @@ nv50_pm_clock_set(struct drm_device *dev, void *pre_state) BIT_M.version == 1 && BIT_M.length >= 0x0b) { script = ROM16(BIT_M.data[0x05]); if (script) - nouveau_bios_run_init_table(dev, script, NULL); + nouveau_bios_run_init_table(dev, script, NULL, -1); script = ROM16(BIT_M.data[0x07]); if (script) - nouveau_bios_run_init_table(dev, script, NULL); + nouveau_bios_run_init_table(dev, script, NULL, -1); script = ROM16(BIT_M.data[0x09]); if (script) - nouveau_bios_run_init_table(dev, script, NULL); + nouveau_bios_run_init_table(dev, script, NULL, -1); - nouveau_bios_run_init_table(dev, perflvl->memscript, NULL); + nouveau_bios_run_init_table(dev, perflvl->memscript, NULL, -1); } if (state->type == PLL_MEMORY) { -- cgit v1.2.3-70-g09d2 From 26f6d88b32706058866a74ecd6600b84fb82d09a Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 4 Jul 2011 16:25:18 +1000 Subject: drm/nvd0/disp: very initial evo setup Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/Makefile | 1 + drivers/gpu/drm/nouveau/nouveau_drv.h | 5 + drivers/gpu/drm/nouveau/nouveau_state.c | 6 +- drivers/gpu/drm/nouveau/nvd0_display.c | 170 ++++++++++++++++++++++++++++++++ 4 files changed, 179 insertions(+), 3 deletions(-) create mode 100644 drivers/gpu/drm/nouveau/nvd0_display.c (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile index f65ade6ba45..2f621aef97f 100644 --- a/drivers/gpu/drm/nouveau/Makefile +++ b/drivers/gpu/drm/nouveau/Makefile @@ -28,6 +28,7 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \ nv04_dac.o nv04_dfp.o nv04_tv.o nv17_tv.o nv17_tv_modes.o \ nv04_crtc.o nv04_display.o nv04_cursor.o \ nv04_fbcon.o nv50_fbcon.o nvc0_fbcon.o \ + nvd0_display.o \ nv10_gpio.o nv50_gpio.o \ nv50_calc.o \ nv04_pm.o nv50_pm.o nva3_pm.o nvc0_pm.o \ diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 6469ffee6fc..e5d4e7d291b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -1281,6 +1281,11 @@ extern int nv04_display_create(struct drm_device *); extern int nv04_display_init(struct drm_device *); extern void nv04_display_destroy(struct drm_device *); +/* nvd0_display.c */ +extern int nvd0_display_create(struct drm_device *); +extern int nvd0_display_init(struct drm_device *); +extern void nvd0_display_destroy(struct drm_device *); + /* nv04_crtc.c */ extern int nv04_crtc_create(struct drm_device *, int index); diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index c4802eb3f9a..0c990d6256c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -456,9 +456,9 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->fifo.unload_context = nvc0_fifo_unload_context; engine->display.early_init = nouveau_stub_init; engine->display.late_takedown = nouveau_stub_takedown; - engine->display.create = nouveau_stub_init; - engine->display.init = nouveau_stub_init; - engine->display.destroy = nouveau_stub_takedown; + engine->display.create = nvd0_display_create; + engine->display.init = nvd0_display_init; + engine->display.destroy = nvd0_display_destroy; engine->gpio.init = nv50_gpio_init; engine->gpio.takedown = nouveau_stub_takedown; engine->gpio.get = nvd0_gpio_get; diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c new file mode 100644 index 00000000000..6ce1529aaa3 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -0,0 +1,170 @@ +/* + * Copyright 2011 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ + +#include "drmP.h" + +#include "nouveau_drv.h" +#include "nouveau_connector.h" +#include "nouveau_encoder.h" +#include "nouveau_crtc.h" + +struct nvd0_display { + struct nouveau_gpuobj *mem; +}; + +static struct nvd0_display * +nvd0_display(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + return dev_priv->engine.display.priv; +} + +/****************************************************************************** + * DAC + *****************************************************************************/ + +/****************************************************************************** + * SOR + *****************************************************************************/ + +/****************************************************************************** + * IRQ + *****************************************************************************/ + +/****************************************************************************** + * Init + *****************************************************************************/ +static void +nvd0_display_fini(struct drm_device *dev) +{ + int i; + + /* fini cursors */ + for (i = 14; i >= 13; i--) { + if (!(nv_rd32(dev, 0x610490 + (i * 0x10)) & 0x00000001)) + continue; + + nv_mask(dev, 0x610490 + (i * 0x10), 0x00000001, 0x00000000); + nv_wait(dev, 0x610490 + (i * 0x10), 0x00010000, 0x00000000); + nv_mask(dev, 0x610090, 1 << i, 0x00000000); + nv_mask(dev, 0x6100a0, 1 << i, 0x00000000); + } + + /* fini master */ + if (nv_rd32(dev, 0x610490) & 0x00000010) { + nv_mask(dev, 0x610490, 0x00000010, 0x00000000); + nv_mask(dev, 0x610490, 0x00000003, 0x00000000); + nv_wait(dev, 0x610490, 0x80000000, 0x00000000); + nv_mask(dev, 0x610090, 0x00000001, 0x00000000); + nv_mask(dev, 0x6100a0, 0x00000001, 0x00000000); + } +} + +int +nvd0_display_init(struct drm_device *dev) +{ + struct nvd0_display *disp = nvd0_display(dev); + int i; + + if (nv_rd32(dev, 0x6100ac) & 0x00000100) { + nv_wr32(dev, 0x6100ac, 0x00000100); + nv_mask(dev, 0x6194e8, 0x00000001, 0x00000000); + if (!nv_wait(dev, 0x6194e8, 0x00000002, 0x00000000)) { + NV_ERROR(dev, "PDISP: 0x6194e8 0x%08x\n", + nv_rd32(dev, 0x6194e8)); + return -EBUSY; + } + } + + nv_wr32(dev, 0x610010, (disp->mem->vinst >> 8) | 9); + + /* init master */ + nv_wr32(dev, 0x610494, ((disp->mem->vinst + 0x1000) >> 8) | 1); + nv_wr32(dev, 0x610498, 0x00010000); + nv_wr32(dev, 0x61049c, 0x00000000); + nv_mask(dev, 0x610490, 0x00000010, 0x00000010); + nv_wr32(dev, 0x640000, 0x00000000); + nv_wr32(dev, 0x610490, 0x01000013); + if (!nv_wait(dev, 0x610490, 0x80000000, 0x00000000)) { + NV_ERROR(dev, "PDISP: master 0x%08x\n", + nv_rd32(dev, 0x610490)); + return -EBUSY; + } + nv_mask(dev, 0x610090, 0x00000001, 0x00000001); + nv_mask(dev, 0x6100a0, 0x00000001, 0x00000001); + + /* init cursors */ + for (i = 13; i <= 14; i++) { + nv_wr32(dev, 0x610490 + (i * 0x10), 0x00000001); + if (!nv_wait(dev, 0x610490 + (i * 0x10), 0x00010000, 0x00010000)) { + NV_ERROR(dev, "PDISP: curs%d 0x%08x\n", i, + nv_rd32(dev, 0x610490 + (i * 0x10))); + return -EBUSY; + } + + nv_mask(dev, 0x610090, 1 << i, 1 << i); + nv_mask(dev, 0x6100a0, 1 << i, 1 << i); + } + + return 0; +} + +void +nvd0_display_destroy(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nvd0_display *disp = nvd0_display(dev); + + nvd0_display_fini(dev); + + dev_priv->engine.display.priv = NULL; + nouveau_gpuobj_ref(NULL, &disp->mem); + kfree(disp); +} + +int +nvd0_display_create(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nvd0_display *disp; + int ret; + + disp = kzalloc(sizeof(*disp), GFP_KERNEL); + if (!disp) + return -ENOMEM; + dev_priv->engine.display.priv = disp; + + ret = nouveau_gpuobj_new(dev, NULL, 8 * 1024, 0x1000, 0, &disp->mem); + if (ret) + goto out; + + ret = nvd0_display_init(dev); + if (ret) + goto out; + +out: + if (ret) + nvd0_display_destroy(dev); + return ret; +} -- cgit v1.2.3-70-g09d2 From 51beb428e4e0a158a47863cb68069ba57ed6ec7d Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 5 Jul 2011 10:33:08 +1000 Subject: drm/nvd0/disp: whip up some basic dma handling for the evo channels Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvd0_display.c | 68 ++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 3 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index 6ce1529aaa3..cd827cda64e 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -22,6 +22,7 @@ * Authors: Ben Skeggs */ +#include #include "drmP.h" #include "nouveau_drv.h" @@ -31,6 +32,10 @@ struct nvd0_display { struct nouveau_gpuobj *mem; + struct { + dma_addr_t handle; + u32 *ptr; + } evo[1]; }; static struct nvd0_display * @@ -40,6 +45,50 @@ nvd0_display(struct drm_device *dev) return dev_priv->engine.display.priv; } +static int +evo_icmd(struct drm_device *dev, int id, u32 mthd, u32 data) +{ + int ret = 0; + nv_mask(dev, 0x610700 + (id * 0x10), 0x00000001, 0x00000001); + nv_wr32(dev, 0x610704 + (id * 0x10), data); + nv_mask(dev, 0x610704 + (id * 0x10), 0x80000ffc, 0x80000000 | mthd); + if (!nv_wait(dev, 0x610704 + (id * 0x10), 0x80000000, 0x00000000)) + ret = -EBUSY; + nv_mask(dev, 0x610700 + (id * 0x10), 0x00000001, 0x00000000); + return ret; +} + +static u32 * +evo_wait(struct drm_device *dev, int id, int nr) +{ + struct nvd0_display *disp = nvd0_display(dev); + u32 put = nv_rd32(dev, 0x640000 + (id * 0x1000)) / 4; + + if (put + nr >= (PAGE_SIZE / 4)) { + disp->evo[id].ptr[put] = 0x20000000; + + nv_wr32(dev, 0x640000 + (id * 0x1000), 0x00000000); + if (!nv_wait(dev, 0x640004 + (id * 0x1000), ~0, 0x00000000)) { + NV_ERROR(dev, "evo %d dma stalled\n", id); + return NULL; + } + + put = 0; + } + + return disp->evo[id].ptr + put; +} + +static void +evo_kick(u32 *push, struct drm_device *dev, int id) +{ + struct nvd0_display *disp = nvd0_display(dev); + nv_wr32(dev, 0x640000 + (id * 0x1000), (push - disp->evo[id].ptr) << 2); +} + +#define evo_mthd(p,m,s) *((p)++) = (((s) << 18) | (m)) +#define evo_data(p,d) *((p)++) = (d) + /****************************************************************************** * DAC *****************************************************************************/ @@ -100,7 +149,7 @@ nvd0_display_init(struct drm_device *dev) nv_wr32(dev, 0x610010, (disp->mem->vinst >> 8) | 9); /* init master */ - nv_wr32(dev, 0x610494, ((disp->mem->vinst + 0x1000) >> 8) | 1); + nv_wr32(dev, 0x610494, (disp->evo[0].handle >> 8) | 3); nv_wr32(dev, 0x610498, 0x00010000); nv_wr32(dev, 0x61049c, 0x00000000); nv_mask(dev, 0x610490, 0x00000010, 0x00000010); @@ -135,11 +184,14 @@ nvd0_display_destroy(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nvd0_display *disp = nvd0_display(dev); + struct pci_dev *pdev = dev->pdev; nvd0_display_fini(dev); - dev_priv->engine.display.priv = NULL; + pci_free_consistent(pdev, PAGE_SIZE, disp->evo[0].ptr, disp->evo[0].handle); nouveau_gpuobj_ref(NULL, &disp->mem); + + dev_priv->engine.display.priv = NULL; kfree(disp); } @@ -147,6 +199,7 @@ int nvd0_display_create(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; + struct pci_dev *pdev = dev->pdev; struct nvd0_display *disp; int ret; @@ -155,10 +208,19 @@ nvd0_display_create(struct drm_device *dev) return -ENOMEM; dev_priv->engine.display.priv = disp; - ret = nouveau_gpuobj_new(dev, NULL, 8 * 1024, 0x1000, 0, &disp->mem); + /* hash table and dma objects for the memory areas we care about */ + ret = nouveau_gpuobj_new(dev, NULL, 4 * 1024, 0x1000, 0, &disp->mem); if (ret) goto out; + /* push buffers for evo channels */ + disp->evo[0].ptr = + pci_alloc_consistent(pdev, PAGE_SIZE, &disp->evo[0].handle); + if (!disp->evo[0].ptr) { + ret = -ENOMEM; + goto out; + } + ret = nvd0_display_init(dev); if (ret) goto out; -- cgit v1.2.3-70-g09d2 From 4600522a8f93dda05e5fa8bd5261e6c6e888dafa Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 5 Jul 2011 11:01:13 +1000 Subject: drm/nvd0/disp: start on interrupt handling Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvd0_display.c | 42 ++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index cd827cda64e..10a44a1d44f 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -100,6 +100,44 @@ evo_kick(u32 *push, struct drm_device *dev, int id) /****************************************************************************** * IRQ *****************************************************************************/ +static void +nvd0_display_intr(struct drm_device *dev) +{ + u32 intr = nv_rd32(dev, 0x610088); + + if (intr & 0x00000002) { + u32 stat = nv_rd32(dev, 0x61009c); + int chid = ffs(stat) - 1; + if (chid >= 0) { + u32 mthd = nv_rd32(dev, 0x6101f0 + (chid * 12)); + u32 data = nv_rd32(dev, 0x6101f4 + (chid * 12)); + u32 unkn = nv_rd32(dev, 0x6101f8 + (chid * 12)); + + NV_INFO(dev, "EvoCh: chid %d mthd 0x%04x data 0x%08x " + "0x%08x 0x%08x\n", + chid, (mthd & 0x0000ffc), data, mthd, unkn); + nv_wr32(dev, 0x61009c, (1 << chid)); + nv_wr32(dev, 0x6101f0 + (chid * 12), 0x90000000); + } + + intr &= ~0x00000002; + } + + 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; + } + + if (intr) + NV_INFO(dev, "PDISP: unknown intr 0x%08x\n", intr); +} /****************************************************************************** * Init @@ -190,6 +228,7 @@ nvd0_display_destroy(struct drm_device *dev) pci_free_consistent(pdev, PAGE_SIZE, disp->evo[0].ptr, disp->evo[0].handle); nouveau_gpuobj_ref(NULL, &disp->mem); + nouveau_irq_unregister(dev, 26); dev_priv->engine.display.priv = NULL; kfree(disp); @@ -208,6 +247,9 @@ nvd0_display_create(struct drm_device *dev) return -ENOMEM; dev_priv->engine.display.priv = disp; + /* setup interrupt handling */ + nouveau_irq_register(dev, 26, nvd0_display_intr); + /* hash table and dma objects for the memory areas we care about */ ret = nouveau_gpuobj_new(dev, NULL, 4 * 1024, 0x1000, 0, &disp->mem); if (ret) -- cgit v1.2.3-70-g09d2 From efd272a7a03148ca3115da07d849156d0976feaf Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 5 Jul 2011 11:58:58 +1000 Subject: drm/nvd0/disp: setup a couple of dma objects we'll need Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvd0_display.c | 43 ++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index 10a44a1d44f..6339a3d0036 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -30,6 +30,9 @@ #include "nouveau_encoder.h" #include "nouveau_crtc.h" +#define MEM_SYNC 0xe0000001 +#define MEM_VRAM 0xe0010000 + struct nvd0_display { struct nouveau_gpuobj *mem; struct { @@ -172,6 +175,7 @@ int nvd0_display_init(struct drm_device *dev) { struct nvd0_display *disp = nvd0_display(dev); + u32 *push; int i; if (nv_rd32(dev, 0x6100ac) & 0x00000100) { @@ -189,7 +193,7 @@ nvd0_display_init(struct drm_device *dev) /* init master */ nv_wr32(dev, 0x610494, (disp->evo[0].handle >> 8) | 3); nv_wr32(dev, 0x610498, 0x00010000); - nv_wr32(dev, 0x61049c, 0x00000000); + nv_wr32(dev, 0x61049c, 0x00000001); nv_mask(dev, 0x610490, 0x00000010, 0x00000010); nv_wr32(dev, 0x640000, 0x00000000); nv_wr32(dev, 0x610490, 0x01000013); @@ -214,6 +218,19 @@ nvd0_display_init(struct drm_device *dev) nv_mask(dev, 0x6100a0, 1 << i, 1 << i); } + push = evo_wait(dev, 0, 32); + if (!push) + return -EBUSY; + evo_mthd(push, 0x0088, 1); + evo_data(push, MEM_SYNC); + evo_mthd(push, 0x0084, 1); + evo_data(push, 0x00000000); + evo_mthd(push, 0x0084, 1); + evo_data(push, 0x80000000); + evo_mthd(push, 0x008c, 1); + evo_data(push, 0x00000000); + evo_kick(push, dev, 0); + return 0; } @@ -238,6 +255,7 @@ int nvd0_display_create(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_instmem_engine *pinstmem = &dev_priv->engine.instmem; struct pci_dev *pdev = dev->pdev; struct nvd0_display *disp; int ret; @@ -251,10 +269,31 @@ nvd0_display_create(struct drm_device *dev) nouveau_irq_register(dev, 26, nvd0_display_intr); /* hash table and dma objects for the memory areas we care about */ - ret = nouveau_gpuobj_new(dev, NULL, 4 * 1024, 0x1000, 0, &disp->mem); + ret = nouveau_gpuobj_new(dev, NULL, 0x4000, 0x10000, + NVOBJ_FLAG_ZERO_ALLOC, &disp->mem); if (ret) goto out; + nv_wo32(disp->mem, 0x1000, 0x00000049); + nv_wo32(disp->mem, 0x1004, (disp->mem->vinst + 0x2000) >> 8); + nv_wo32(disp->mem, 0x1008, (disp->mem->vinst + 0x2fff) >> 8); + nv_wo32(disp->mem, 0x100c, 0x00000000); + nv_wo32(disp->mem, 0x1010, 0x00000000); + nv_wo32(disp->mem, 0x1014, 0x00000000); + nv_wo32(disp->mem, 0x0000, MEM_SYNC); + nv_wo32(disp->mem, 0x0004, (0x1000 << 9) | 0x00000001); + + nv_wo32(disp->mem, 0x1020, 0x00000009); + nv_wo32(disp->mem, 0x1024, 0x00000000); + nv_wo32(disp->mem, 0x1028, (dev_priv->vram_size - 1) >> 8); + nv_wo32(disp->mem, 0x102c, 0x00000000); + nv_wo32(disp->mem, 0x1030, 0x00000000); + nv_wo32(disp->mem, 0x1034, 0x00000000); + nv_wo32(disp->mem, 0x0008, MEM_VRAM); + nv_wo32(disp->mem, 0x000c, (0x1020 << 9) | 0x00000001); + + pinstmem->flush(dev); + /* push buffers for evo channels */ disp->evo[0].ptr = pci_alloc_consistent(pdev, PAGE_SIZE, &disp->evo[0].handle); -- cgit v1.2.3-70-g09d2 From 83fc083cbbe0147519b1a62770171041c19e8752 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 5 Jul 2011 13:08:40 +1000 Subject: drm/nvd0/disp: start on SOR encoder functions Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvd0_display.c | 203 ++++++++++++++++++++++++++++++++- 1 file changed, 202 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index 6339a3d0036..3a2a4bb1276 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -23,7 +23,9 @@ */ #include + #include "drmP.h" +#include "drm_crtc_helper.h" #include "nouveau_drv.h" #include "nouveau_connector.h" @@ -92,6 +94,12 @@ evo_kick(u32 *push, struct drm_device *dev, int id) #define evo_mthd(p,m,s) *((p)++) = (((s) << 18) | (m)) #define evo_data(p,d) *((p)++) = (d) +static struct drm_crtc * +nvd0_display_crtc_get(struct drm_encoder *encoder) +{ + return nouveau_encoder(encoder)->crtc; +} + /****************************************************************************** * DAC *****************************************************************************/ @@ -99,6 +107,163 @@ evo_kick(u32 *push, struct drm_device *dev, int id) /****************************************************************************** * SOR *****************************************************************************/ +static void +nvd0_sor_dpms(struct drm_encoder *encoder, int mode) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct drm_device *dev = encoder->dev; + struct drm_encoder *partner; + int or = nv_encoder->or; + u32 dpms_ctrl; + + nv_encoder->last_dpms = mode; + + list_for_each_entry(partner, &dev->mode_config.encoder_list, head) { + struct nouveau_encoder *nv_partner = nouveau_encoder(partner); + + if (partner->encoder_type != DRM_MODE_ENCODER_TMDS) + continue; + + if (nv_partner != nv_encoder && + nv_partner->dcb->or == nv_encoder->or) { + if (nv_partner->last_dpms == DRM_MODE_DPMS_ON) + return; + break; + } + } + + dpms_ctrl = (mode == DRM_MODE_DPMS_ON); + dpms_ctrl |= 0x80000000; + + nv_wait(dev, 0x61c004 + (or * 0x0800), 0x80000000, 0x00000000); + 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); +} + +static bool +nvd0_sor_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nouveau_connector *nv_connector; + + nv_connector = nouveau_encoder_connector_get(nv_encoder); + if (nv_connector && nv_connector->native_mode) { + if (nv_connector->scaling_mode != DRM_MODE_SCALE_NONE) { + int id = adjusted_mode->base.id; + *adjusted_mode = *nv_connector->native_mode; + adjusted_mode->base.id = id; + } + } + + return true; +} + +static void +nvd0_sor_prepare(struct drm_encoder *encoder) +{ +} + +static void +nvd0_sor_commit(struct drm_encoder *encoder) +{ +} + +static void +nvd0_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); + u32 mode_ctrl = (1 << nv_crtc->index); + u32 *push; + + if (nv_encoder->dcb->sorconf.link & 1) { + if (adjusted_mode->clock < 165000) + mode_ctrl |= 0x00000100; + else + mode_ctrl |= 0x00000500; + } else { + mode_ctrl |= 0x00000200; + } + + nvd0_sor_dpms(encoder, DRM_MODE_DPMS_ON); + + push = evo_wait(encoder->dev, 0, 2); + if (push) { + evo_mthd(push, 0x0200 + (nv_encoder->or * 0x20), 1); + evo_data(push, mode_ctrl); + } + + 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; + + if (nv_encoder->crtc) { + u32 *push = evo_wait(dev, 0, 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, 0); + } + + nv_encoder->crtc = NULL; + nv_encoder->last_dpms = DRM_MODE_DPMS_OFF; + } +} + +static void +nvd0_sor_destroy(struct drm_encoder *encoder) +{ + drm_encoder_cleanup(encoder); + kfree(encoder); +} + +static const struct drm_encoder_helper_funcs nvd0_sor_hfunc = { + .dpms = nvd0_sor_dpms, + .mode_fixup = nvd0_sor_mode_fixup, + .prepare = nvd0_sor_prepare, + .commit = nvd0_sor_commit, + .mode_set = nvd0_sor_mode_set, + .disable = nvd0_sor_disconnect, + .get_crtc = nvd0_display_crtc_get, +}; + +static const struct drm_encoder_funcs nvd0_sor_func = { + .destroy = nvd0_sor_destroy, +}; + +static int +nvd0_sor_create(struct drm_connector *connector, struct dcb_entry *dcbe) +{ + struct drm_device *dev = connector->dev; + struct nouveau_encoder *nv_encoder; + struct drm_encoder *encoder; + + nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); + if (!nv_encoder) + return -ENOMEM; + nv_encoder->dcb = dcbe; + nv_encoder->or = ffs(dcbe->or) - 1; + nv_encoder->last_dpms = DRM_MODE_DPMS_OFF; + + encoder = to_drm_encoder(nv_encoder); + encoder->possible_crtcs = dcbe->heads; + encoder->possible_clones = 0; + drm_encoder_init(dev, encoder, &nvd0_sor_func, DRM_MODE_ENCODER_TMDS); + drm_encoder_helper_add(encoder, &nvd0_sor_hfunc); + + drm_mode_connector_attach_encoder(connector, encoder); + return 0; +} /****************************************************************************** * IRQ @@ -256,15 +421,51 @@ nvd0_display_create(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_instmem_engine *pinstmem = &dev_priv->engine.instmem; + struct dcb_table *dcb = &dev_priv->vbios.dcb; + struct drm_connector *connector, *tmp; struct pci_dev *pdev = dev->pdev; struct nvd0_display *disp; - int ret; + struct dcb_entry *dcbe; + int ret, i; disp = kzalloc(sizeof(*disp), GFP_KERNEL); if (!disp) return -ENOMEM; dev_priv->engine.display.priv = disp; + /* create encoder/connector objects based on VBIOS DCB table */ + for (i = 0, dcbe = &dcb->entry[0]; i < dcb->entries; i++, dcbe++) { + connector = nouveau_connector_create(dev, dcbe->connector); + if (IS_ERR(connector)) + continue; + + if (dcbe->location != DCB_LOC_ON_CHIP) { + NV_WARN(dev, "skipping off-chip encoder %d/%d\n", + dcbe->type, ffs(dcbe->or) - 1); + continue; + } + + switch (dcbe->type) { + case OUTPUT_TMDS: + nvd0_sor_create(connector, dcbe); + break; + default: + NV_WARN(dev, "skipping unsupported encoder %d/%d\n", + dcbe->type, ffs(dcbe->or) - 1); + continue; + } + } + + /* cull any connectors we created that don't have an encoder */ + list_for_each_entry_safe(connector, tmp, &dev->mode_config.connector_list, head) { + if (connector->encoder_ids[0]) + continue; + + NV_WARN(dev, "%s has no encoders, removing\n", + drm_get_connector_name(connector)); + connector->funcs->destroy(connector); + } + /* setup interrupt handling */ nouveau_irq_register(dev, 26, nvd0_display_intr); -- cgit v1.2.3-70-g09d2 From 270a5747802d4cf43b91b9e03cccb1fb5d5e8a34 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 5 Jul 2011 14:16:05 +1000 Subject: drm/nvd0/disp: skeletal handling of modeset interrupts Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvd0_display.c | 60 ++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index 3a2a4bb1276..65e48f953d4 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -268,6 +268,42 @@ nvd0_sor_create(struct drm_connector *connector, struct dcb_entry *dcbe) /****************************************************************************** * IRQ *****************************************************************************/ +static void +nvd0_display_unk1_handler(struct drm_device *dev) +{ + u32 unk0 = nv_rd32(dev, 0x6101d0); + + NV_INFO(dev, "PDISP: unk1 0x%08x\n", unk0); + + nv_wr32(dev, 0x6101d4, 0x00000000); + nv_wr32(dev, 0x6109d4, 0x00000000); + nv_wr32(dev, 0x6101d0, 0x80000000); +} + +static void +nvd0_display_unk2_handler(struct drm_device *dev) +{ + u32 unk0 = nv_rd32(dev, 0x6101d0); + + NV_INFO(dev, "PDISP: unk2 0x%08x\n", unk0); + + nv_wr32(dev, 0x6101d4, 0x00000000); + nv_wr32(dev, 0x6109d4, 0x00000000); + nv_wr32(dev, 0x6101d0, 0x80000000); +} + +static void +nvd0_display_unk4_handler(struct drm_device *dev) +{ + u32 unk0 = nv_rd32(dev, 0x6101d0); + + NV_INFO(dev, "PDISP: unk4 0x%08x\n", unk0); + + nv_wr32(dev, 0x6101d4, 0x00000000); + nv_wr32(dev, 0x6109d4, 0x00000000); + nv_wr32(dev, 0x6101d0, 0x80000000); +} + static void nvd0_display_intr(struct drm_device *dev) { @@ -291,6 +327,29 @@ nvd0_display_intr(struct drm_device *dev) intr &= ~0x00000002; } + if (intr & 0x00100000) { + u32 stat = nv_rd32(dev, 0x6100ac); + + if (stat & 0x00000007) { + nv_wr32(dev, 0x6100ac, (stat & 0x00000007)); + + if (stat & 0x00000001) + nvd0_display_unk1_handler(dev); + if (stat & 0x00000002) + nvd0_display_unk2_handler(dev); + if (stat & 0x00000004) + nvd0_display_unk4_handler(dev); + stat &= ~0x00000007; + } + + if (stat) { + NV_INFO(dev, "PDISP: unknown intr24 0x%08x\n", stat); + nv_wr32(dev, 0x6100ac, stat); + } + + intr &= ~0x00100000; + } + if (intr & 0x01000000) { u32 stat = nv_rd32(dev, 0x6100bc); nv_wr32(dev, 0x6100bc, stat); @@ -354,6 +413,7 @@ nvd0_display_init(struct drm_device *dev) } nv_wr32(dev, 0x610010, (disp->mem->vinst >> 8) | 9); + nv_mask(dev, 0x6100b0, 0x00000307, 0x00000307); /* init master */ nv_wr32(dev, 0x610494, (disp->evo[0].handle >> 8) | 3); -- cgit v1.2.3-70-g09d2 From 438d99e3b1752074af6d2d763a38906549048067 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 5 Jul 2011 16:48:06 +1000 Subject: drm/nvd0/disp: initial crtc object implementation Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvd0_display.c | 457 ++++++++++++++++++++++++++++++++- 1 file changed, 456 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index 65e48f953d4..cf294886a69 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -31,6 +31,7 @@ #include "nouveau_connector.h" #include "nouveau_encoder.h" #include "nouveau_crtc.h" +#include "nouveau_fb.h" #define MEM_SYNC 0xe0000001 #define MEM_VRAM 0xe0010000 @@ -100,6 +101,449 @@ nvd0_display_crtc_get(struct drm_encoder *encoder) return nouveau_encoder(encoder)->crtc; } +/****************************************************************************** + * CRTC + *****************************************************************************/ +static int +nvd0_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool on, bool update) +{ + struct drm_device *dev = nv_crtc->base.dev; + u32 *push, mode; + + mode = 0x00000000; + if (on) { + /* 0x11: 6bpc dynamic 2x2 + * 0x13: 8bpc dynamic 2x2 + * 0x19: 6bpc static 2x2 + * 0x1b: 8bpc static 2x2 + * 0x21: 6bpc temporal + * 0x23: 8bpc temporal + */ + mode = 0x00000011; + } + + push = evo_wait(dev, 0, 4); + if (push) { + evo_mthd(push, 0x0490 + (nv_crtc->index * 0x300), 1); + evo_data(push, mode); + if (update) { + evo_mthd(push, 0x0080, 1); + evo_data(push, 0x00000000); + } + evo_kick(push, dev, 0); + } + + return 0; +} + +static int +nvd0_crtc_set_scale(struct nouveau_crtc *nv_crtc, int type, bool update) +{ + struct drm_display_mode *mode = &nv_crtc->base.mode; + struct drm_device *dev = nv_crtc->base.dev; + u32 *push; + + /*XXX: actually handle scaling */ + + push = evo_wait(dev, 0, 16); + if (push) { + evo_mthd(push, 0x04c0 + (nv_crtc->index * 0x300), 3); + evo_data(push, (mode->vdisplay << 16) | mode->hdisplay); + evo_data(push, (mode->vdisplay << 16) | mode->hdisplay); + evo_data(push, (mode->vdisplay << 16) | mode->hdisplay); + evo_mthd(push, 0x0494 + (nv_crtc->index * 0x300), 1); + evo_data(push, 0x00000000); + evo_mthd(push, 0x04b0 + (nv_crtc->index * 0x300), 1); + evo_data(push, 0x00000000); + evo_mthd(push, 0x04b8 + (nv_crtc->index * 0x300), 1); + evo_data(push, (mode->vdisplay << 16) | mode->hdisplay); + if (update) { + evo_mthd(push, 0x0080, 1); + evo_data(push, 0x00000000); + } + evo_kick(push, dev, 0); + } + + return 0; +} + +static int +nvd0_crtc_set_image(struct nouveau_crtc *nv_crtc, struct drm_framebuffer *fb, + int x, int y, bool update) +{ + struct nouveau_framebuffer *nvfb = nouveau_framebuffer(fb); + u32 *push; + + /*XXX*/ + nv_crtc->fb.tile_flags = MEM_VRAM; + + push = evo_wait(fb->dev, 0, 16); + if (push) { + evo_mthd(push, 0x0460 + (nv_crtc->index * 0x300), 1); + evo_data(push, nvfb->nvbo->bo.offset >> 8); + evo_mthd(push, 0x0468 + (nv_crtc->index * 0x300), 4); + evo_data(push, (fb->height << 16) | fb->width); + evo_data(push, nvfb->r_pitch); + evo_data(push, nvfb->r_format); + evo_data(push, nv_crtc->fb.tile_flags); + evo_kick(push, fb->dev, 0); + } + + return 0; +} + +static void +nvd0_crtc_cursor_show(struct nouveau_crtc *nv_crtc, bool show, bool update) +{ + struct drm_device *dev = nv_crtc->base.dev; + u32 *push = evo_wait(dev, 0, 16); + if (push) { + if (show) { + evo_mthd(push, 0x0480 + (nv_crtc->index * 0x300), 2); + evo_data(push, 0x85000000); + evo_data(push, nv_crtc->cursor.nvbo->bo.offset >> 8); + evo_mthd(push, 0x048c + (nv_crtc->index * 0x300), 1); + evo_data(push, MEM_VRAM); + } else { + evo_mthd(push, 0x0480 + (nv_crtc->index * 0x300), 1); + evo_data(push, 0x05000000); + evo_mthd(push, 0x048c + (nv_crtc->index * 0x300), 1); + evo_data(push, 0x00000000); + } + + if (update) { + evo_mthd(push, 0x0080, 1); + evo_data(push, 0x00000000); + } + + evo_kick(push, dev, 0); + } +} + +static void +nvd0_crtc_dpms(struct drm_crtc *crtc, int mode) +{ +} + +static void +nvd0_crtc_prepare(struct drm_crtc *crtc) +{ + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + u32 *push; + + push = evo_wait(crtc->dev, 0, 2); + if (push) { + evo_mthd(push, 0x0474 + (nv_crtc->index * 0x300), 1); + evo_data(push, 0x00000000); + evo_mthd(push, 0x0440 + (nv_crtc->index * 0x300), 1); + evo_data(push, 0x03000000); + evo_mthd(push, 0x045c + (nv_crtc->index * 0x300), 1); + evo_data(push, 0x00000000); + evo_kick(push, crtc->dev, 0); + } + + nvd0_crtc_cursor_show(nv_crtc, false, false); +} + +static void +nvd0_crtc_commit(struct drm_crtc *crtc) +{ + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + u32 *push; + + push = evo_wait(crtc->dev, 0, 32); + if (push) { + evo_mthd(push, 0x0474 + (nv_crtc->index * 0x300), 1); + evo_data(push, nv_crtc->fb.tile_flags); + evo_mthd(push, 0x0440 + (nv_crtc->index * 0x300), 4); + evo_data(push, 0x83000000); + evo_data(push, nv_crtc->lut.nvbo->bo.offset >> 8); + evo_data(push, 0x00000000); + evo_data(push, 0x00000000); + evo_mthd(push, 0x045c + (nv_crtc->index * 0x300), 1); + evo_data(push, MEM_VRAM); + evo_kick(push, crtc->dev, 0); + } + + nvd0_crtc_cursor_show(nv_crtc, nv_crtc->cursor.visible, true); +} + +static bool +nvd0_crtc_mode_fixup(struct drm_crtc *crtc, struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static int +nvd0_crtc_swap_fbs(struct drm_crtc *crtc, struct drm_framebuffer *old_fb) +{ + struct nouveau_framebuffer *nvfb = nouveau_framebuffer(crtc->fb); + int ret; + + ret = nouveau_bo_pin(nvfb->nvbo, TTM_PL_FLAG_VRAM); + if (ret) + return ret; + + if (old_fb) { + nvfb = nouveau_framebuffer(old_fb); + nouveau_bo_unpin(nvfb->nvbo); + } + + return 0; +} + +static int +nvd0_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *umode, + struct drm_display_mode *mode, int x, int y, + struct drm_framebuffer *old_fb) +{ + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + struct nouveau_connector *nv_connector; + u32 htotal = mode->htotal; + u32 vtotal = mode->vtotal; + u32 hsyncw = mode->hsync_end - mode->hsync_start - 1; + u32 vsyncw = mode->vsync_end - mode->vsync_start - 1; + u32 hfrntp = mode->hsync_start - mode->hdisplay; + u32 vfrntp = mode->vsync_start - mode->vdisplay; + u32 hbackp = mode->htotal - mode->hsync_end; + u32 vbackp = mode->vtotal - mode->vsync_end; + u32 hss2be = hsyncw + hbackp; + u32 vss2be = vsyncw + vbackp; + u32 hss2de = htotal - hfrntp; + u32 vss2de = vtotal - vfrntp; + u32 hstart = 0; + u32 vstart = 0; + u32 *push; + int ret; + + ret = nvd0_crtc_swap_fbs(crtc, old_fb); + if (ret) + return ret; + + push = evo_wait(crtc->dev, 0, 64); + if (push) { + evo_mthd(push, 0x0410 + (nv_crtc->index * 0x300), 5); + evo_data(push, (vstart << 16) | hstart); + evo_data(push, (vtotal << 16) | htotal); + evo_data(push, (vsyncw << 16) | hsyncw); + evo_data(push, (vss2be << 16) | hss2be); + evo_data(push, (vss2de << 16) | hss2de); + evo_mthd(push, 0x042c + (nv_crtc->index * 0x300), 1); + evo_data(push, 0x00000000); /* ??? */ + evo_mthd(push, 0x0450 + (nv_crtc->index * 0x300), 3); + evo_data(push, mode->clock * 1000); + evo_data(push, 0x00200000); /* ??? */ + evo_data(push, mode->clock * 1000); + evo_mthd(push, 0x0408 + (nv_crtc->index * 0x300), 1); + evo_data(push, 0x31ec6000); /* ??? */ + evo_kick(push, crtc->dev, 0); + } + + nv_connector = nouveau_crtc_connector_get(nv_crtc); + nvd0_crtc_set_dither(nv_crtc, nv_connector->use_dithering, false); + nvd0_crtc_set_scale(nv_crtc, nv_connector->scaling_mode, false); + nvd0_crtc_set_image(nv_crtc, crtc->fb, x, y, false); + return 0; +} + +static int +nvd0_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, + struct drm_framebuffer *old_fb) +{ + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + int ret; + + ret = nvd0_crtc_swap_fbs(crtc, old_fb); + if (ret) + return ret; + + nvd0_crtc_set_image(nv_crtc, crtc->fb, x, y, true); + return 0; +} + +static int +nvd0_crtc_mode_set_base_atomic(struct drm_crtc *crtc, + struct drm_framebuffer *fb, int x, int y, + enum mode_set_atomic state) +{ + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + nvd0_crtc_set_image(nv_crtc, fb, x, y, true); + return 0; +} + +static void +nvd0_crtc_lut_load(struct drm_crtc *crtc) +{ + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + void __iomem *lut = nvbo_kmap_obj_iovirtual(nv_crtc->lut.nvbo); + int i; + + for (i = 0; i < 256; i++) { + writew(nv_crtc->lut.r[i] >> 2, lut + 8*i + 0); + writew(nv_crtc->lut.g[i] >> 2, lut + 8*i + 2); + writew(nv_crtc->lut.b[i] >> 2, lut + 8*i + 4); + } +} + +static int +nvd0_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv, + uint32_t handle, uint32_t width, uint32_t height) +{ + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct drm_gem_object *gem; + struct nouveau_bo *nvbo; + bool visible = (handle != 0); + int i, ret = 0; + + if (visible) { + if (width != 64 || height != 64) + return -EINVAL; + + gem = drm_gem_object_lookup(dev, file_priv, handle); + if (unlikely(!gem)) + return -ENOENT; + nvbo = nouveau_gem_object(gem); + + ret = nouveau_bo_map(nvbo); + if (ret == 0) { + for (i = 0; i < 64 * 64; i++) { + u32 v = nouveau_bo_rd32(nvbo, i); + nouveau_bo_wr32(nv_crtc->cursor.nvbo, i, v); + } + nouveau_bo_unmap(nvbo); + } + + drm_gem_object_unreference_unlocked(gem); + } + + if (visible != nv_crtc->cursor.visible) { + nvd0_crtc_cursor_show(nv_crtc, visible, true); + nv_crtc->cursor.visible = visible; + } + + return ret; +} + +static int +nvd0_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) +{ + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + const u32 data = (y << 16) | x; + + nv_wr32(crtc->dev, 0x64d084 + (nv_crtc->index * 0x1000), data); + nv_wr32(crtc->dev, 0x64d080 + (nv_crtc->index * 0x1000), 0x00000000); + return 0; +} + +static void +nvd0_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, + uint32_t start, uint32_t size) +{ + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + u32 end = max(start + size, (u32)256); + u32 i; + + for (i = start; i < end; i++) { + nv_crtc->lut.r[i] = r[i]; + nv_crtc->lut.g[i] = g[i]; + nv_crtc->lut.b[i] = b[i]; + } + + nvd0_crtc_lut_load(crtc); +} + +static void +nvd0_crtc_destroy(struct drm_crtc *crtc) +{ + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + nouveau_bo_unmap(nv_crtc->cursor.nvbo); + nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo); + nouveau_bo_unmap(nv_crtc->lut.nvbo); + nouveau_bo_ref(NULL, &nv_crtc->lut.nvbo); + drm_crtc_cleanup(crtc); + kfree(crtc); +} + +static const struct drm_crtc_helper_funcs nvd0_crtc_hfunc = { + .dpms = nvd0_crtc_dpms, + .prepare = nvd0_crtc_prepare, + .commit = nvd0_crtc_commit, + .mode_fixup = nvd0_crtc_mode_fixup, + .mode_set = nvd0_crtc_mode_set, + .mode_set_base = nvd0_crtc_mode_set_base, + .mode_set_base_atomic = nvd0_crtc_mode_set_base_atomic, + .load_lut = nvd0_crtc_lut_load, +}; + +static const struct drm_crtc_funcs nvd0_crtc_func = { + .cursor_set = nvd0_crtc_cursor_set, + .cursor_move = nvd0_crtc_cursor_move, + .gamma_set = nvd0_crtc_gamma_set, + .set_config = drm_crtc_helper_set_config, + .destroy = nvd0_crtc_destroy, +}; + +static int +nvd0_crtc_create(struct drm_device *dev, int index) +{ + struct nouveau_crtc *nv_crtc; + struct drm_crtc *crtc; + int ret, i; + + nv_crtc = kzalloc(sizeof(*nv_crtc), GFP_KERNEL); + if (!nv_crtc) + return -ENOMEM; + + nv_crtc->index = index; + nv_crtc->set_dither = nvd0_crtc_set_dither; + nv_crtc->set_scale = nvd0_crtc_set_scale; + for (i = 0; i < 256; i++) { + nv_crtc->lut.r[i] = i << 8; + nv_crtc->lut.g[i] = i << 8; + nv_crtc->lut.b[i] = i << 8; + } + + crtc = &nv_crtc->base; + drm_crtc_init(dev, crtc, &nvd0_crtc_func); + drm_crtc_helper_add(crtc, &nvd0_crtc_hfunc); + drm_mode_crtc_set_gamma_size(crtc, 256); + + ret = nouveau_bo_new(dev, 64 * 64 * 4, 0x100, TTM_PL_FLAG_VRAM, + 0, 0x0000, &nv_crtc->cursor.nvbo); + if (!ret) { + ret = nouveau_bo_pin(nv_crtc->cursor.nvbo, TTM_PL_FLAG_VRAM); + if (!ret) + ret = nouveau_bo_map(nv_crtc->cursor.nvbo); + if (ret) + nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo); + } + + if (ret) + goto out; + + ret = nouveau_bo_new(dev, 4096, 0x100, TTM_PL_FLAG_VRAM, + 0, 0x0000, &nv_crtc->lut.nvbo); + if (!ret) { + ret = nouveau_bo_pin(nv_crtc->lut.nvbo, TTM_PL_FLAG_VRAM); + if (!ret) + ret = nouveau_bo_map(nv_crtc->lut.nvbo); + if (ret) + nouveau_bo_ref(NULL, &nv_crtc->lut.nvbo); + } + + if (ret) + goto out; + + nvd0_crtc_lut_load(crtc); + +out: + if (ret) + nvd0_crtc_destroy(crtc); + return ret; +} + /****************************************************************************** * DAC *****************************************************************************/ @@ -194,6 +638,7 @@ nvd0_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, if (push) { evo_mthd(push, 0x0200 + (nv_encoder->or * 0x20), 1); evo_data(push, mode_ctrl); + evo_kick(push, encoder->dev, 0); } nv_encoder->crtc = encoder->crtc; @@ -204,9 +649,12 @@ 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) { - u32 *push = evo_wait(dev, 0, 4); + nvd0_crtc_prepare(nv_encoder->crtc); + + push = evo_wait(dev, 0, 4); if (push) { evo_mthd(push, 0x0200 + (nv_encoder->or * 0x20), 1); evo_data(push, 0x00000000); @@ -493,6 +941,13 @@ nvd0_display_create(struct drm_device *dev) return -ENOMEM; dev_priv->engine.display.priv = disp; + /* create crtc objects to represent the hw heads */ + for (i = 0; i < 2; i++) { + ret = nvd0_crtc_create(dev, i); + if (ret) + goto out; + } + /* create encoder/connector objects based on VBIOS DCB table */ for (i = 0, dcbe = &dcb->entry[0]; i < dcb->entries; i++, dcbe++) { connector = nouveau_connector_create(dev, dcbe->connector); -- cgit v1.2.3-70-g09d2 From 2fad3d5e2bc8776a2963399a936db13814107646 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Wed, 6 Jul 2011 09:59:40 +1000 Subject: drm/nvd0/disp: they moved the linear flag.. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_display.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index eb514ea2937..ddbabefb427 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -105,9 +105,12 @@ nouveau_framebuffer_init(struct drm_device *dev, if (dev_priv->chipset == 0x50) nv_fb->r_format |= (tile_flags << 8); - if (!tile_flags) - nv_fb->r_pitch = 0x00100000 | fb->pitch; - else { + if (!tile_flags) { + if (dev_priv->card_type < NV_D0) + nv_fb->r_pitch = 0x00100000 | fb->pitch; + else + nv_fb->r_pitch = 0x01000000 | fb->pitch; + } else { u32 mode = nvbo->tile_mode; if (dev_priv->card_type >= NV_C0) mode >>= 4; -- cgit v1.2.3-70-g09d2 From 1d6e7a59f70b3107a75672f365be2fec2ee43a36 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Wed, 6 Jul 2011 10:29:49 +1000 Subject: drm/nvd0/disp: some magic to make evo happeir Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvd0_display.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index cf294886a69..0afc5e1ad84 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -850,6 +850,18 @@ nvd0_display_init(struct drm_device *dev) u32 *push; int i; + /*XXX: wrong, and wtf is it for? */ + for (i = 0; i < 3; i++) { + u32 dac = nv_rd32(dev, 0x61a000 + (i * 0x800)); + nv_wr32(dev, 0x6101c0 + (i * 0x800), dac); + } + + /*XXX: wrong, and wtf is it for? SOR_MODE_CTRL is an error without.. */ + for (i = 0; i < 4; i++) { + u32 sor = nv_rd32(dev, 0x61c000 + (i * 0x800)); + nv_wr32(dev, 0x6301c4 + (i * 0x800), sor); + } + if (nv_rd32(dev, 0x6100ac) & 0x00000100) { nv_wr32(dev, 0x6100ac, 0x00000100); nv_mask(dev, 0x6194e8, 0x00000001, 0x00000000); -- cgit v1.2.3-70-g09d2 From c0cc92a1151447588db6d96e94fc2210b8fc32df Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Wed, 6 Jul 2011 11:40:45 +1000 Subject: drm/nvd0/disp: remove lp reordering from vram dmaobj, create fb dmaobjs Fixes CLUT being messed up. Mostly. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvd0_display.c | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index 0afc5e1ad84..a0cc287aa6f 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -35,6 +35,7 @@ #define MEM_SYNC 0xe0000001 #define MEM_VRAM 0xe0010000 +#include "nouveau_dma.h" struct nvd0_display { struct nouveau_gpuobj *mem; @@ -174,9 +175,6 @@ nvd0_crtc_set_image(struct nouveau_crtc *nv_crtc, struct drm_framebuffer *fb, struct nouveau_framebuffer *nvfb = nouveau_framebuffer(fb); u32 *push; - /*XXX*/ - nv_crtc->fb.tile_flags = MEM_VRAM; - push = evo_wait(fb->dev, 0, 16); if (push) { evo_mthd(push, 0x0460 + (nv_crtc->index * 0x300), 1); @@ -185,10 +183,11 @@ nvd0_crtc_set_image(struct nouveau_crtc *nv_crtc, struct drm_framebuffer *fb, evo_data(push, (fb->height << 16) | fb->width); evo_data(push, nvfb->r_pitch); evo_data(push, nvfb->r_format); - evo_data(push, nv_crtc->fb.tile_flags); + evo_data(push, nvfb->r_dma); evo_kick(push, fb->dev, 0); } + nv_crtc->fb.tile_flags = nvfb->r_dma; return 0; } @@ -1011,7 +1010,7 @@ nvd0_display_create(struct drm_device *dev) nv_wo32(disp->mem, 0x0000, MEM_SYNC); nv_wo32(disp->mem, 0x0004, (0x1000 << 9) | 0x00000001); - nv_wo32(disp->mem, 0x1020, 0x00000009); + nv_wo32(disp->mem, 0x1020, 0x00000049); nv_wo32(disp->mem, 0x1024, 0x00000000); nv_wo32(disp->mem, 0x1028, (dev_priv->vram_size - 1) >> 8); nv_wo32(disp->mem, 0x102c, 0x00000000); @@ -1020,6 +1019,24 @@ nvd0_display_create(struct drm_device *dev) nv_wo32(disp->mem, 0x0008, MEM_VRAM); nv_wo32(disp->mem, 0x000c, (0x1020 << 9) | 0x00000001); + nv_wo32(disp->mem, 0x1040, 0x00000009); + nv_wo32(disp->mem, 0x1044, 0x00000000); + nv_wo32(disp->mem, 0x1048, (dev_priv->vram_size - 1) >> 8); + nv_wo32(disp->mem, 0x104c, 0x00000000); + nv_wo32(disp->mem, 0x1050, 0x00000000); + nv_wo32(disp->mem, 0x1054, 0x00000000); + nv_wo32(disp->mem, 0x0010, NvEvoVRAM_LP); + nv_wo32(disp->mem, 0x0014, (0x1040 << 9) | 0x00000001); + + nv_wo32(disp->mem, 0x1060, 0x0fe00009); + nv_wo32(disp->mem, 0x1064, 0x00000000); + nv_wo32(disp->mem, 0x1068, (dev_priv->vram_size - 1) >> 8); + nv_wo32(disp->mem, 0x106c, 0x00000000); + nv_wo32(disp->mem, 0x1070, 0x00000000); + nv_wo32(disp->mem, 0x1074, 0x00000000); + nv_wo32(disp->mem, 0x0018, NvEvoFB32); + nv_wo32(disp->mem, 0x001c, (0x1060 << 9) | 0x00000001); + pinstmem->flush(dev); /* push buffers for evo channels */ -- cgit v1.2.3-70-g09d2 From a36f04c0447a3d59b9b4faf4ddb3dbe1ea808956 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Wed, 6 Jul 2011 14:39:23 +1000 Subject: drm/nvd0/disp: extend the init voodoo to cover crtcs Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvd0_display.c | 46 ++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 19 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index a0cc287aa6f..d282f2aaacd 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -718,9 +718,8 @@ nvd0_sor_create(struct drm_connector *connector, struct dcb_entry *dcbe) static void nvd0_display_unk1_handler(struct drm_device *dev) { - u32 unk0 = nv_rd32(dev, 0x6101d0); - - NV_INFO(dev, "PDISP: unk1 0x%08x\n", unk0); + NV_INFO(dev, "PDISP: 1 0x%08x 0x%08x 0x%08x\n", nv_rd32(dev, 0x6101d0), + nv_rd32(dev, 0x6101d4), nv_rd32(dev, 0x6109d4)); nv_wr32(dev, 0x6101d4, 0x00000000); nv_wr32(dev, 0x6109d4, 0x00000000); @@ -730,9 +729,8 @@ nvd0_display_unk1_handler(struct drm_device *dev) static void nvd0_display_unk2_handler(struct drm_device *dev) { - u32 unk0 = nv_rd32(dev, 0x6101d0); - - NV_INFO(dev, "PDISP: unk2 0x%08x\n", unk0); + NV_INFO(dev, "PDISP: 2 0x%08x 0x%08x 0x%08x\n", nv_rd32(dev, 0x6101d0), + nv_rd32(dev, 0x6101d4), nv_rd32(dev, 0x6109d4)); nv_wr32(dev, 0x6101d4, 0x00000000); nv_wr32(dev, 0x6109d4, 0x00000000); @@ -742,9 +740,8 @@ nvd0_display_unk2_handler(struct drm_device *dev) static void nvd0_display_unk4_handler(struct drm_device *dev) { - u32 unk0 = nv_rd32(dev, 0x6101d0); - - NV_INFO(dev, "PDISP: unk4 0x%08x\n", unk0); + NV_INFO(dev, "PDISP: 4 0x%08x 0x%08x 0x%08x\n", nv_rd32(dev, 0x6101d0), + nv_rd32(dev, 0x6101d4), nv_rd32(dev, 0x6109d4)); nv_wr32(dev, 0x6101d4, 0x00000000); nv_wr32(dev, 0x6109d4, 0x00000000); @@ -849,28 +846,39 @@ nvd0_display_init(struct drm_device *dev) u32 *push; int i; - /*XXX: wrong, and wtf is it for? */ + if (nv_rd32(dev, 0x6100ac) & 0x00000100) { + nv_wr32(dev, 0x6100ac, 0x00000100); + nv_mask(dev, 0x6194e8, 0x00000001, 0x00000000); + if (!nv_wait(dev, 0x6194e8, 0x00000002, 0x00000000)) { + NV_ERROR(dev, "PDISP: 0x6194e8 0x%08x\n", + nv_rd32(dev, 0x6194e8)); + return -EBUSY; + } + } + + /* nfi what these are exactly, i do know that SOR_MODE_CTRL won't + * work at all unless you do the SOR part below. + */ for (i = 0; i < 3; i++) { u32 dac = nv_rd32(dev, 0x61a000 + (i * 0x800)); nv_wr32(dev, 0x6101c0 + (i * 0x800), dac); } - /*XXX: wrong, and wtf is it for? SOR_MODE_CTRL is an error without.. */ for (i = 0; i < 4; i++) { u32 sor = nv_rd32(dev, 0x61c000 + (i * 0x800)); nv_wr32(dev, 0x6301c4 + (i * 0x800), sor); } - if (nv_rd32(dev, 0x6100ac) & 0x00000100) { - nv_wr32(dev, 0x6100ac, 0x00000100); - nv_mask(dev, 0x6194e8, 0x00000001, 0x00000000); - if (!nv_wait(dev, 0x6194e8, 0x00000002, 0x00000000)) { - NV_ERROR(dev, "PDISP: 0x6194e8 0x%08x\n", - nv_rd32(dev, 0x6194e8)); - return -EBUSY; - } + for (i = 0; i < 2; i++) { + u32 crtc0 = nv_rd32(dev, 0x616104 + (i * 0x800)); + u32 crtc1 = nv_rd32(dev, 0x616108 + (i * 0x800)); + u32 crtc2 = nv_rd32(dev, 0x61610c + (i * 0x800)); + nv_wr32(dev, 0x6101b4 + (i * 0x800), crtc0); + nv_wr32(dev, 0x6101b8 + (i * 0x800), crtc1); + nv_wr32(dev, 0x6101bc + (i * 0x800), crtc2); } + /* point at our hash table / objects, enable interrupts */ nv_wr32(dev, 0x610010, (disp->mem->vinst >> 8) | 9); nv_mask(dev, 0x6100b0, 0x00000307, 0x00000307); -- cgit v1.2.3-70-g09d2 From 8eaa9669f8dc7fec6c7eb5b42c3093114eae9a08 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Wed, 6 Jul 2011 15:25:47 +1000 Subject: drm/nvd0/disp: dac encoder module Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvd0_display.c | 137 +++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index d282f2aaacd..7b3efff1723 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -546,6 +546,140 @@ out: /****************************************************************************** * DAC *****************************************************************************/ +static void +nvd0_dac_dpms(struct drm_encoder *encoder, int mode) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct drm_device *dev = encoder->dev; + int or = nv_encoder->or; + u32 dpms_ctrl; + + dpms_ctrl = 0x80000000; + if (mode == DRM_MODE_DPMS_STANDBY || mode == DRM_MODE_DPMS_OFF) + dpms_ctrl |= 0x00000001; + if (mode == DRM_MODE_DPMS_SUSPEND || mode == DRM_MODE_DPMS_OFF) + dpms_ctrl |= 0x00000004; + + nv_wait(dev, 0x61a004 + (or * 0x0800), 0x80000000, 0x00000000); + nv_mask(dev, 0x61a004 + (or * 0x0800), 0xc000007f, dpms_ctrl); + nv_wait(dev, 0x61a004 + (or * 0x0800), 0x80000000, 0x00000000); +} + +static bool +nvd0_dac_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nouveau_connector *nv_connector; + + nv_connector = nouveau_encoder_connector_get(nv_encoder); + if (nv_connector && nv_connector->native_mode) { + if (nv_connector->scaling_mode != DRM_MODE_SCALE_NONE) { + int id = adjusted_mode->base.id; + *adjusted_mode = *nv_connector->native_mode; + adjusted_mode->base.id = id; + } + } + + return true; +} + +static void +nvd0_dac_prepare(struct drm_encoder *encoder) +{ +} + +static void +nvd0_dac_commit(struct drm_encoder *encoder) +{ +} + +static void +nvd0_dac_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); + u32 *push; + + nvd0_dac_dpms(encoder, DRM_MODE_DPMS_ON); + + push = evo_wait(encoder->dev, 0, 2); + if (push) { + evo_mthd(push, 0x0180 + (nv_encoder->or * 0x20), 1); + evo_data(push, 1 << nv_crtc->index); + evo_kick(push, encoder->dev, 0); + } + + nv_encoder->crtc = encoder->crtc; +} + +static void +nvd0_dac_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, 0, 4); + if (push) { + evo_mthd(push, 0x0180 + (nv_encoder->or * 0x20), 1); + evo_data(push, 0x00000000); + evo_mthd(push, 0x0080, 1); + evo_data(push, 0x00000000); + evo_kick(push, dev, 0); + } + + nv_encoder->crtc = NULL; + } +} + +static void +nvd0_dac_destroy(struct drm_encoder *encoder) +{ + drm_encoder_cleanup(encoder); + kfree(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, + .commit = nvd0_dac_commit, + .mode_set = nvd0_dac_mode_set, + .disable = nvd0_dac_disconnect, + .get_crtc = nvd0_display_crtc_get, +}; + +static const struct drm_encoder_funcs nvd0_dac_func = { + .destroy = nvd0_dac_destroy, +}; + +static int +nvd0_dac_create(struct drm_connector *connector, struct dcb_entry *dcbe) +{ + struct drm_device *dev = connector->dev; + struct nouveau_encoder *nv_encoder; + struct drm_encoder *encoder; + + nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); + if (!nv_encoder) + return -ENOMEM; + nv_encoder->dcb = dcbe; + nv_encoder->or = ffs(dcbe->or) - 1; + + encoder = to_drm_encoder(nv_encoder); + encoder->possible_crtcs = dcbe->heads; + encoder->possible_clones = 0; + drm_encoder_init(dev, encoder, &nvd0_dac_func, DRM_MODE_ENCODER_DAC); + drm_encoder_helper_add(encoder, &nvd0_dac_hfunc); + + drm_mode_connector_attach_encoder(connector, encoder); + return 0; +} /****************************************************************************** * SOR @@ -983,6 +1117,9 @@ nvd0_display_create(struct drm_device *dev) case OUTPUT_TMDS: nvd0_sor_create(connector, dcbe); break; + case OUTPUT_ANALOG: + nvd0_dac_create(connector, dcbe); + break; default: NV_WARN(dev, "skipping unsupported encoder %d/%d\n", dcbe->type, ffs(dcbe->or) - 1); -- cgit v1.2.3-70-g09d2 From b6d8e7ec38843edfc4a4491b746a17ff517ab4be Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 7 Jul 2011 09:51:29 +1000 Subject: drm/nvd0/disp: stub dac load detect, prevents oops Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvd0_display.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index 7b3efff1723..08a54b7b6ef 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -637,6 +637,12 @@ nvd0_dac_disconnect(struct drm_encoder *encoder) } } +static enum drm_connector_status +nvd0_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector) +{ + return connector_status_disconnected; +} + static void nvd0_dac_destroy(struct drm_encoder *encoder) { @@ -652,6 +658,7 @@ static const struct drm_encoder_helper_funcs nvd0_dac_hfunc = { .mode_set = nvd0_dac_mode_set, .disable = nvd0_dac_disconnect, .get_crtc = nvd0_display_crtc_get, + .detect = nvd0_dac_detect }; static const struct drm_encoder_funcs nvd0_dac_func = { -- cgit v1.2.3-70-g09d2 From 3a89cd029267739bc9f16e86e2d4156b68dc9ca2 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 7 Jul 2011 10:47:10 +1000 Subject: drm/nvd0/disp: initial attempt at modeset irq handling Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvd0_display.c | 130 +++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index 08a54b7b6ef..b869ba0fb6c 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -32,6 +32,7 @@ #include "nouveau_encoder.h" #include "nouveau_crtc.h" #include "nouveau_fb.h" +#include "nv50_display.h" #define MEM_SYNC 0xe0000001 #define MEM_VRAM 0xe0010000 @@ -43,6 +44,13 @@ struct nvd0_display { dma_addr_t handle; u32 *ptr; } evo[1]; + struct { + struct dcb_entry *dis; + struct dcb_entry *ena; + int crtc; + int pclk; + u16 script; + } irq; }; static struct nvd0_display * @@ -856,12 +864,82 @@ nvd0_sor_create(struct drm_connector *connector, struct dcb_entry *dcbe) /****************************************************************************** * IRQ *****************************************************************************/ +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; + + if (id < 4) { + type = OUTPUT_ANALOG; + or = id; + } else { + type = OUTPUT_TMDS; + or = id - 4; + } + + 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))) + return dcb; + } + + NV_INFO(dev, "PDISP: DCB for %d/0x%08x not found\n", id, mc); + return NULL; +} + static void nvd0_display_unk1_handler(struct drm_device *dev) { + struct nvd0_display *disp = nvd0_display(dev); + struct dcb_entry *dcb; + u32 unkn, crtc = 0; + int i; + NV_INFO(dev, "PDISP: 1 0x%08x 0x%08x 0x%08x\n", nv_rd32(dev, 0x6101d0), nv_rd32(dev, 0x6101d4), nv_rd32(dev, 0x6109d4)); + unkn = nv_rd32(dev, 0x6101d4); + if (!unkn) { + unkn = nv_rd32(dev, 0x6109d4); + crtc = 1; + } + + disp->irq.ena = NULL; + disp->irq.dis = NULL; + disp->irq.crtc = crtc; + disp->irq.pclk = nv_rd32(dev, 0x660450 + (disp->irq.crtc * 0x300)); + disp->irq.pclk /= 1000; + + for (i = 0; i < 8; i++) { + u32 mcc = nv_rd32(dev, 0x640180 + (i * 0x20)); + u32 mcp = nv_rd32(dev, 0x660180 + (i * 0x20)); + + if (mcc & (1 << crtc)) + disp->irq.dis = lookup_dcb(dev, i, mcc); + + if (mcp & (1 << crtc)) { + disp->irq.ena = lookup_dcb(dev, i, mcp); + switch (disp->irq.ena->type) { + case OUTPUT_ANALOG: + disp->irq.script = 0x00ff; + break; + case OUTPUT_TMDS: + disp->irq.script = (mcp & 0x00000f00) >> 8; + if (disp->irq.pclk >= 165000) + disp->irq.script |= 0x0100; + break; + default: + disp->irq.script = 0xbeef; + break; + } + } + } + + dcb = disp->irq.dis; + if (dcb) + nouveau_bios_run_display_table(dev, 0x0000, -1, dcb, crtc); + nv_wr32(dev, 0x6101d4, 0x00000000); nv_wr32(dev, 0x6109d4, 0x00000000); nv_wr32(dev, 0x6101d0, 0x80000000); @@ -870,9 +948,48 @@ nvd0_display_unk1_handler(struct drm_device *dev) static void nvd0_display_unk2_handler(struct drm_device *dev) { + struct nvd0_display *disp = nvd0_display(dev); + struct dcb_entry *dcb; + int crtc = disp->irq.crtc; + int pclk = disp->irq.pclk; + int or; + u32 tmp; + NV_INFO(dev, "PDISP: 2 0x%08x 0x%08x 0x%08x\n", nv_rd32(dev, 0x6101d0), nv_rd32(dev, 0x6101d4), nv_rd32(dev, 0x6109d4)); + dcb = disp->irq.dis; + disp->irq.dis = NULL; + if (dcb) + nouveau_bios_run_display_table(dev, 0x0000, -2, dcb, crtc); + + nv50_crtc_set_clock(dev, crtc, pclk); + + dcb = disp->irq.ena; + if (!dcb) + goto ack; + or = ffs(dcb->or) - 1; + + nouveau_bios_run_display_table(dev, disp->irq.script, pclk, dcb, crtc); + + nv_wr32(dev, 0x612200 + (crtc * 0x800), 0x00000000); + switch (dcb->type) { + case OUTPUT_ANALOG: + nv_wr32(dev, 0x612280 + (or * 0x800), 0x00000000); + break; + case OUTPUT_TMDS: + if (disp->irq.pclk >= 165000) + tmp = 0x00000101; + else + tmp = 0x00000000; + + nv_mask(dev, 0x612300 + (or * 0x800), 0x00000707, tmp); + break; + default: + break; + } + +ack: nv_wr32(dev, 0x6101d4, 0x00000000); nv_wr32(dev, 0x6109d4, 0x00000000); nv_wr32(dev, 0x6101d0, 0x80000000); @@ -881,9 +998,22 @@ nvd0_display_unk2_handler(struct drm_device *dev) static void nvd0_display_unk4_handler(struct drm_device *dev) { + struct nvd0_display *disp = nvd0_display(dev); + struct dcb_entry *dcb; + int crtc = disp->irq.crtc; + int pclk = disp->irq.pclk; + NV_INFO(dev, "PDISP: 4 0x%08x 0x%08x 0x%08x\n", nv_rd32(dev, 0x6101d0), nv_rd32(dev, 0x6101d4), nv_rd32(dev, 0x6109d4)); + dcb = disp->irq.ena; + disp->irq.ena = NULL; + if (!dcb) + goto ack; + + nouveau_bios_run_display_table(dev, disp->irq.script, pclk, dcb, crtc); + +ack: nv_wr32(dev, 0x6101d4, 0x00000000); nv_wr32(dev, 0x6109d4, 0x00000000); nv_wr32(dev, 0x6101d0, 0x80000000); -- cgit v1.2.3-70-g09d2 From 8ea0d4aa606eabd021926b4c328e4d799253afe6 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 7 Jul 2011 14:49:24 +1000 Subject: drm/nvd0/disp: fixup clut so it actually works Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvd0_display.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index b869ba0fb6c..376acc1619a 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -269,6 +269,8 @@ nvd0_crtc_commit(struct drm_crtc *crtc) evo_data(push, 0x00000000); evo_mthd(push, 0x045c + (nv_crtc->index * 0x300), 1); evo_data(push, MEM_VRAM); + evo_mthd(push, 0x0430 + (nv_crtc->index * 0x300), 1); + evo_data(push, 0xffffff00); evo_kick(push, crtc->dev, 0); } @@ -387,9 +389,9 @@ nvd0_crtc_lut_load(struct drm_crtc *crtc) int i; for (i = 0; i < 256; i++) { - writew(nv_crtc->lut.r[i] >> 2, lut + 8*i + 0); - writew(nv_crtc->lut.g[i] >> 2, lut + 8*i + 2); - writew(nv_crtc->lut.b[i] >> 2, lut + 8*i + 4); + writew(0x6000 + (nv_crtc->lut.r[i] >> 2), lut + (i * 0x20) + 0); + writew(0x6000 + (nv_crtc->lut.g[i] >> 2), lut + (i * 0x20) + 2); + writew(0x6000 + (nv_crtc->lut.b[i] >> 2), lut + (i * 0x20) + 4); } } @@ -530,7 +532,7 @@ nvd0_crtc_create(struct drm_device *dev, int index) if (ret) goto out; - ret = nouveau_bo_new(dev, 4096, 0x100, TTM_PL_FLAG_VRAM, + ret = nouveau_bo_new(dev, 8192, 0x100, TTM_PL_FLAG_VRAM, 0, 0x0000, &nv_crtc->lut.nvbo); if (!ret) { ret = nouveau_bo_pin(nv_crtc->lut.nvbo, TTM_PL_FLAG_VRAM); -- cgit v1.2.3-70-g09d2 From a46232ee09064351246c6f7134c81790ef737874 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 7 Jul 2011 15:23:48 +1000 Subject: drm/nvd0/disp: push the update button in mode_set_base() Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvd0_display.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index 376acc1619a..4fadea47b3b 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -192,6 +192,10 @@ nvd0_crtc_set_image(struct nouveau_crtc *nv_crtc, struct drm_framebuffer *fb, evo_data(push, nvfb->r_pitch); evo_data(push, nvfb->r_format); evo_data(push, nvfb->r_dma); + if (update) { + evo_mthd(push, 0x0080, 1); + evo_data(push, 0x00000000); + } evo_kick(push, fb->dev, 0); } -- cgit v1.2.3-70-g09d2 From f3fdc52dd73a083dcb80f95e5c6ce8a33277b102 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 7 Jul 2011 16:01:57 +1000 Subject: drm/nvd0/disp: scaling Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvd0_display.c | 37 +++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index 4fadea47b3b..d85b2593965 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -150,16 +150,43 @@ nvd0_crtc_set_scale(struct nouveau_crtc *nv_crtc, int type, bool update) { struct drm_display_mode *mode = &nv_crtc->base.mode; struct drm_device *dev = nv_crtc->base.dev; - u32 *push; + struct nouveau_connector *nv_connector; + u32 *push, outX, outY; - /*XXX: actually handle scaling */ + outX = mode->hdisplay; + outY = mode->vdisplay; + + nv_connector = nouveau_crtc_connector_get(nv_crtc); + if (nv_connector && nv_connector->native_mode) { + struct drm_display_mode *native = nv_connector->native_mode; + u32 xratio = (native->hdisplay << 19) / mode->hdisplay; + u32 yratio = (native->vdisplay << 19) / mode->vdisplay; + + switch (type) { + case DRM_MODE_SCALE_ASPECT: + if (xratio > yratio) { + outX = (mode->hdisplay * yratio) >> 19; + outY = (mode->vdisplay * yratio) >> 19; + } else { + outX = (mode->hdisplay * xratio) >> 19; + outY = (mode->vdisplay * xratio) >> 19; + } + break; + case DRM_MODE_SCALE_FULLSCREEN: + outX = native->hdisplay; + outY = native->vdisplay; + break; + default: + break; + } + } push = evo_wait(dev, 0, 16); if (push) { evo_mthd(push, 0x04c0 + (nv_crtc->index * 0x300), 3); - evo_data(push, (mode->vdisplay << 16) | mode->hdisplay); - evo_data(push, (mode->vdisplay << 16) | mode->hdisplay); - evo_data(push, (mode->vdisplay << 16) | mode->hdisplay); + evo_data(push, (outY << 16) | outX); + evo_data(push, (outY << 16) | outX); + evo_data(push, (outY << 16) | outX); evo_mthd(push, 0x0494 + (nv_crtc->index * 0x300), 1); evo_data(push, 0x00000000); evo_mthd(push, 0x04b0 + (nv_crtc->index * 0x300), 1); -- cgit v1.2.3-70-g09d2 From 629c1b9207386b00abd6453b72a19b15cd2202f8 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 8 Jul 2011 09:43:20 +1000 Subject: drm/nvd0/disp: handle sync polarity, kill off some unknown Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvd0_display.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index d85b2593965..6720d63cfcc 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -352,11 +352,15 @@ nvd0_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *umode, u32 vss2be = vsyncw + vbackp; u32 hss2de = htotal - hfrntp; u32 vss2de = vtotal - vfrntp; - u32 hstart = 0; - u32 vstart = 0; - u32 *push; + u32 syncs, *push; int ret; + 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; @@ -364,7 +368,7 @@ nvd0_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *umode, push = evo_wait(crtc->dev, 0, 64); if (push) { evo_mthd(push, 0x0410 + (nv_crtc->index * 0x300), 5); - evo_data(push, (vstart << 16) | hstart); + evo_data(push, 0x00000000); evo_data(push, (vtotal << 16) | htotal); evo_data(push, (vsyncw << 16) | hsyncw); evo_data(push, (vss2be << 16) | hss2be); @@ -375,8 +379,8 @@ 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, 0x0408 + (nv_crtc->index * 0x300), 1); - evo_data(push, 0x31ec6000); /* ??? */ + evo_mthd(push, 0x0404 + (nv_crtc->index * 0x300), 1); + evo_data(push, syncs); evo_kick(push, crtc->dev, 0); } -- cgit v1.2.3-70-g09d2 From b681993f7a48b847f3129abd8e8b3926c108a0d9 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 8 Jul 2011 11:14:50 +1000 Subject: drm/nvd0/disp: dac load detect VBIOS does more than this, as does nv50/nvc0 driver in nouveau. Traces of the NVIDIA binary driver however, show pretty much just this being done... Seems to work for me, it'll be fine for the moment. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvd0_display.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index 6720d63cfcc..f4788d819ed 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -685,7 +685,22 @@ nvd0_dac_disconnect(struct drm_encoder *encoder) static enum drm_connector_status nvd0_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector) { - return connector_status_disconnected; + enum drm_connector_status status = connector_status_disconnected; + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct drm_device *dev = encoder->dev; + int or = nv_encoder->or; + u32 load; + + nv_wr32(dev, 0x61a00c + (or * 0x800), 0x00100000); + udelay(9500); + nv_wr32(dev, 0x61a00c + (or * 0x800), 0x80000000); + + load = nv_rd32(dev, 0x61a00c + (or * 0x800)); + if ((load & 0x38000000) == 0x38000000) + status = connector_status_connected; + + nv_wr32(dev, 0x61a00c + (or * 0x800), 0x00000000); + return status; } static void -- cgit v1.2.3-70-g09d2 From ff8ff50342f377b04d576d723b79f1c98200e501 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 8 Jul 2011 11:53:37 +1000 Subject: drm/nvd0/disp: determine U table config in or_mode_set() Takes a gamble and presumes that we can safely store something random in OR_MODE_CTRL+4, the hw doesn't seem to mind... Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvd0_display.c | 36 ++++++++++++++-------------------- 1 file changed, 15 insertions(+), 21 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index f4788d819ed..60454889118 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -49,7 +49,7 @@ struct nvd0_display { struct dcb_entry *ena; int crtc; int pclk; - u16 script; + u16 cfg; } irq; }; @@ -649,10 +649,11 @@ nvd0_dac_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, nvd0_dac_dpms(encoder, DRM_MODE_DPMS_ON); - push = evo_wait(encoder->dev, 0, 2); + push = evo_wait(encoder->dev, 0, 4); if (push) { - evo_mthd(push, 0x0180 + (nv_encoder->or * 0x20), 1); + evo_mthd(push, 0x0180 + (nv_encoder->or * 0x20), 2); evo_data(push, 1 << nv_crtc->index); + evo_data(push, 0x00ff); evo_kick(push, encoder->dev, 0); } @@ -821,7 +822,7 @@ nvd0_sor_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 mode_ctrl = (1 << nv_crtc->index); - u32 *push; + u32 *push, or_config; if (nv_encoder->dcb->sorconf.link & 1) { if (adjusted_mode->clock < 165000) @@ -832,12 +833,17 @@ nvd0_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, mode_ctrl |= 0x00000200; } + or_config = (mode_ctrl & 0x00000f00) >> 8; + if (adjusted_mode->clock >= 165000) + or_config |= 0x0100; + nvd0_sor_dpms(encoder, DRM_MODE_DPMS_ON); - push = evo_wait(encoder->dev, 0, 2); + push = evo_wait(encoder->dev, 0, 4); if (push) { - evo_mthd(push, 0x0200 + (nv_encoder->or * 0x20), 1); + evo_mthd(push, 0x0200 + (nv_encoder->or * 0x20), 2); evo_data(push, mode_ctrl); + evo_data(push, or_config); evo_kick(push, encoder->dev, 0); } @@ -971,20 +977,8 @@ nvd0_display_unk1_handler(struct drm_device *dev) disp->irq.dis = lookup_dcb(dev, i, mcc); if (mcp & (1 << crtc)) { + disp->irq.cfg = nv_rd32(dev, 0x660184 + (i * 0x20)); disp->irq.ena = lookup_dcb(dev, i, mcp); - switch (disp->irq.ena->type) { - case OUTPUT_ANALOG: - disp->irq.script = 0x00ff; - break; - case OUTPUT_TMDS: - disp->irq.script = (mcp & 0x00000f00) >> 8; - if (disp->irq.pclk >= 165000) - disp->irq.script |= 0x0100; - break; - default: - disp->irq.script = 0xbeef; - break; - } } } @@ -1022,7 +1016,7 @@ nvd0_display_unk2_handler(struct drm_device *dev) goto ack; or = ffs(dcb->or) - 1; - nouveau_bios_run_display_table(dev, disp->irq.script, pclk, dcb, crtc); + nouveau_bios_run_display_table(dev, disp->irq.cfg, pclk, dcb, crtc); nv_wr32(dev, 0x612200 + (crtc * 0x800), 0x00000000); switch (dcb->type) { @@ -1063,7 +1057,7 @@ nvd0_display_unk4_handler(struct drm_device *dev) if (!dcb) goto ack; - nouveau_bios_run_display_table(dev, disp->irq.script, pclk, dcb, crtc); + nouveau_bios_run_display_table(dev, disp->irq.cfg, pclk, dcb, crtc); ack: nv_wr32(dev, 0x6101d4, 0x00000000); -- cgit v1.2.3-70-g09d2 From c6f2f71daed12f57d6f72769b49d29c7391f24ae Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 8 Jul 2011 12:11:58 +1000 Subject: drm/nvd0/disp: track down fb positioning method Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvd0_display.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index 60454889118..45f8a31e9ac 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -189,8 +189,6 @@ nvd0_crtc_set_scale(struct nouveau_crtc *nv_crtc, int type, bool update) evo_data(push, (outY << 16) | outX); evo_mthd(push, 0x0494 + (nv_crtc->index * 0x300), 1); evo_data(push, 0x00000000); - evo_mthd(push, 0x04b0 + (nv_crtc->index * 0x300), 1); - evo_data(push, 0x00000000); evo_mthd(push, 0x04b8 + (nv_crtc->index * 0x300), 1); evo_data(push, (mode->vdisplay << 16) | mode->hdisplay); if (update) { @@ -219,6 +217,8 @@ nvd0_crtc_set_image(struct nouveau_crtc *nv_crtc, struct drm_framebuffer *fb, evo_data(push, nvfb->r_pitch); evo_data(push, nvfb->r_format); evo_data(push, nvfb->r_dma); + evo_mthd(push, 0x04b0 + (nv_crtc->index * 0x300), 1); + evo_data(push, (y << 16) | x); if (update) { evo_mthd(push, 0x0080, 1); evo_data(push, 0x00000000); -- cgit v1.2.3-70-g09d2 From 3b6d83d1b9f9be1c9778c2c6fa6761b440734fdd Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 8 Jul 2011 12:52:14 +1000 Subject: drm/nvd0/disp: untested LVDS support Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvd0_display.c | 82 +++++++++++++++++++++++++++------- 1 file changed, 66 insertions(+), 16 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index 45f8a31e9ac..1a561d30821 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -816,26 +816,66 @@ nvd0_sor_commit(struct drm_encoder *encoder) } static void -nvd0_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) +nvd0_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode, + struct drm_display_mode *mode) { + struct drm_nouveau_private *dev_priv = encoder->dev->dev_private; struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); + struct nouveau_connector *nv_connector; + struct nvbios *bios = &dev_priv->vbios; u32 mode_ctrl = (1 << nv_crtc->index); u32 *push, or_config; - if (nv_encoder->dcb->sorconf.link & 1) { - if (adjusted_mode->clock < 165000) - mode_ctrl |= 0x00000100; - else - mode_ctrl |= 0x00000500; - } else { - mode_ctrl |= 0x00000200; - } + nv_connector = nouveau_encoder_connector_get(nv_encoder); + switch (nv_encoder->dcb->type) { + case OUTPUT_TMDS: + if (nv_encoder->dcb->sorconf.link & 1) { + if (mode->clock < 165000) + mode_ctrl |= 0x00000100; + else + mode_ctrl |= 0x00000500; + } else { + mode_ctrl |= 0x00000200; + } + + or_config = (mode_ctrl & 0x00000f00) >> 8; + if (mode->clock >= 165000) + or_config |= 0x0100; + break; + case OUTPUT_LVDS: + or_config = (mode_ctrl & 0x00000f00) >> 8; + if (bios->fp_no_ddc) { + if (bios->fp.dual_link) + or_config |= 0x0100; + if (bios->fp.if_is_24bit) + or_config |= 0x0200; + } else { + if (nv_connector->dcb->type == DCB_CONNECTOR_LVDS_SPWG) { + if (((u8 *)nv_connector->edid)[121] == 2) + or_config |= 0x0100; + } else + if (mode->clock >= bios->fp.duallink_transition_clk) { + or_config |= 0x0100; + } - or_config = (mode_ctrl & 0x00000f00) >> 8; - if (adjusted_mode->clock >= 165000) - or_config |= 0x0100; + if (or_config & 0x0100) { + if (bios->fp.strapless_is_24bit & 2) + or_config |= 0x0200; + } else { + if (bios->fp.strapless_is_24bit & 1) + or_config |= 0x0200; + } + + if (nv_connector->base.display_info.bpc == 8) + or_config |= 0x0200; + + } + break; + default: + BUG_ON(1); + break; + } nvd0_sor_dpms(encoder, DRM_MODE_DPMS_ON); @@ -932,8 +972,16 @@ lookup_dcb(struct drm_device *dev, int id, u32 mc) type = OUTPUT_ANALOG; or = id; } else { - type = OUTPUT_TMDS; - or = id - 4; + 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; + default: + return NULL; + } + + or = id - 4; } for (i = 0; i < dev_priv->vbios.dcb.entries; i++) { @@ -1024,7 +1072,8 @@ nvd0_display_unk2_handler(struct drm_device *dev) nv_wr32(dev, 0x612280 + (or * 0x800), 0x00000000); break; case OUTPUT_TMDS: - if (disp->irq.pclk >= 165000) + case OUTPUT_LVDS: + if (disp->irq.cfg & 0x00000100) tmp = 0x00000101; else tmp = 0x00000000; @@ -1298,6 +1347,7 @@ nvd0_display_create(struct drm_device *dev) switch (dcbe->type) { case OUTPUT_TMDS: + case OUTPUT_LVDS: nvd0_sor_create(connector, dcbe); break; case OUTPUT_ANALOG: -- cgit v1.2.3-70-g09d2 From f20ce9629f820c00e581acc4c9938fbf6e34475d Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 8 Jul 2011 13:17:01 +1000 Subject: drm/nvd0/disp: do modeset irq handling from tasklet Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvd0_display.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index 1a561d30821..b6a8c6def64 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -44,9 +44,12 @@ struct nvd0_display { dma_addr_t handle; u32 *ptr; } evo[1]; + + struct tasklet_struct tasklet; struct { struct dcb_entry *dis; struct dcb_entry *ena; + u32 modeset; int crtc; int pclk; u16 cfg; @@ -1114,9 +1117,24 @@ ack: nv_wr32(dev, 0x6101d0, 0x80000000); } +static void +nvd0_display_bh(unsigned long data) +{ + struct drm_device *dev = (struct drm_device *)data; + struct nvd0_display *disp = nvd0_display(dev); + + if (disp->irq.modeset & 0x00000001) + nvd0_display_unk1_handler(dev); + if (disp->irq.modeset & 0x00000002) + nvd0_display_unk2_handler(dev); + if (disp->irq.modeset & 0x00000004) + nvd0_display_unk4_handler(dev); +} + static void nvd0_display_intr(struct drm_device *dev) { + struct nvd0_display *disp = nvd0_display(dev); u32 intr = nv_rd32(dev, 0x610088); if (intr & 0x00000002) { @@ -1141,14 +1159,10 @@ nvd0_display_intr(struct drm_device *dev) u32 stat = nv_rd32(dev, 0x6100ac); if (stat & 0x00000007) { - nv_wr32(dev, 0x6100ac, (stat & 0x00000007)); + disp->irq.modeset = stat; + tasklet_schedule(&disp->tasklet); - if (stat & 0x00000001) - nvd0_display_unk1_handler(dev); - if (stat & 0x00000002) - nvd0_display_unk2_handler(dev); - if (stat & 0x00000004) - nvd0_display_unk4_handler(dev); + nv_wr32(dev, 0x6100ac, (stat & 0x00000007)); stat &= ~0x00000007; } @@ -1371,6 +1385,7 @@ nvd0_display_create(struct drm_device *dev) } /* setup interrupt handling */ + tasklet_init(&disp->tasklet, nvd0_display_bh, (unsigned long)dev); nouveau_irq_register(dev, 26, nvd0_display_intr); /* hash table and dma objects for the memory areas we care about */ -- cgit v1.2.3-70-g09d2 From ee41779e76fd69168bce3f0530828a67ecb8db5b Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 8 Jul 2011 14:34:45 +1000 Subject: drm/nvd0/disp: rewrite irq handler, should be somewhat sturdier now Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvd0_display.c | 192 +++++++++++++++++++-------------- 1 file changed, 111 insertions(+), 81 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index b6a8c6def64..725ae9983f1 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -46,14 +46,7 @@ struct nvd0_display { } evo[1]; struct tasklet_struct tasklet; - struct { - struct dcb_entry *dis; - struct dcb_entry *ena; - u32 modeset; - int crtc; - int pclk; - u16 cfg; - } irq; + u32 modeset; }; static struct nvd0_display * @@ -965,6 +958,23 @@ nvd0_sor_create(struct drm_connector *connector, struct dcb_entry *dcbe) /****************************************************************************** * IRQ *****************************************************************************/ +static void +debug_irq(struct drm_device *dev, int i) +{ + if (drm_debug & (DRM_UT_DRIVER | DRM_UT_KMS)) { + NV_INFO(dev, "PDISP: modeset req %d\n", i); + NV_INFO(dev, " STAT: 0x%08x 0x%08x 0x%08x\n", + nv_rd32(dev, 0x6101d0), + nv_rd32(dev, 0x6101d4), nv_rd32(dev, 0x6109d4)); + for (i = 0; i < 8; i++) { + NV_INFO(dev, " %s%d: 0x%08x 0x%08x\n", + i < 4 ? "DAC" : "SOR", i, + nv_rd32(dev, 0x640180 + (i * 0x20)), + nv_rd32(dev, 0x660180 + (i * 0x20))); + } + } +} + static struct dcb_entry * lookup_dcb(struct drm_device *dev, int id, u32 mc) { @@ -981,6 +991,7 @@ lookup_dcb(struct drm_device *dev, int id, u32 mc) case 0x00000200: type = OUTPUT_TMDS; break; case 0x00000500: type = OUTPUT_TMDS; break; default: + NV_ERROR(dev, "PDISP: unknown SOR mc 0x%08x\n", mc); return NULL; } @@ -993,49 +1004,36 @@ lookup_dcb(struct drm_device *dev, int id, u32 mc) return dcb; } - NV_INFO(dev, "PDISP: DCB for %d/0x%08x not found\n", id, mc); + NV_ERROR(dev, "PDISP: DCB for %d/0x%08x not found\n", id, mc); return NULL; } static void nvd0_display_unk1_handler(struct drm_device *dev) { - struct nvd0_display *disp = nvd0_display(dev); struct dcb_entry *dcb; - u32 unkn, crtc = 0; + u32 mask, crtc; int i; - NV_INFO(dev, "PDISP: 1 0x%08x 0x%08x 0x%08x\n", nv_rd32(dev, 0x6101d0), - nv_rd32(dev, 0x6101d4), nv_rd32(dev, 0x6109d4)); - - unkn = nv_rd32(dev, 0x6101d4); - if (!unkn) { - unkn = nv_rd32(dev, 0x6109d4); + mask = nv_rd32(dev, 0x6101d4); + crtc = 0; + if (!mask) { + mask = nv_rd32(dev, 0x6109d4); crtc = 1; } + debug_irq(dev, 1); - disp->irq.ena = NULL; - disp->irq.dis = NULL; - disp->irq.crtc = crtc; - disp->irq.pclk = nv_rd32(dev, 0x660450 + (disp->irq.crtc * 0x300)); - disp->irq.pclk /= 1000; - - for (i = 0; i < 8; i++) { + for (i = 0; mask && i < 8; i++) { u32 mcc = nv_rd32(dev, 0x640180 + (i * 0x20)); - u32 mcp = nv_rd32(dev, 0x660180 + (i * 0x20)); - - if (mcc & (1 << crtc)) - disp->irq.dis = lookup_dcb(dev, i, mcc); + if (!(mcc & (1 << crtc))) + continue; - if (mcp & (1 << crtc)) { - disp->irq.cfg = nv_rd32(dev, 0x660184 + (i * 0x20)); - disp->irq.ena = lookup_dcb(dev, i, mcp); - } - } + dcb = lookup_dcb(dev, i, mcc); + if (!dcb) + continue; - dcb = disp->irq.dis; - if (dcb) nouveau_bios_run_display_table(dev, 0x0000, -1, dcb, crtc); + } nv_wr32(dev, 0x6101d4, 0x00000000); nv_wr32(dev, 0x6109d4, 0x00000000); @@ -1045,49 +1043,70 @@ nvd0_display_unk1_handler(struct drm_device *dev) static void nvd0_display_unk2_handler(struct drm_device *dev) { - struct nvd0_display *disp = nvd0_display(dev); struct dcb_entry *dcb; - int crtc = disp->irq.crtc; - int pclk = disp->irq.pclk; - int or; - u32 tmp; + u32 mask, crtc, pclk; + u32 or, tmp; + int i; + + mask = nv_rd32(dev, 0x6101d4); + crtc = 0; + if (!mask) { + mask = nv_rd32(dev, 0x6109d4); + crtc = 1; + } + debug_irq(dev, 2); - NV_INFO(dev, "PDISP: 2 0x%08x 0x%08x 0x%08x\n", nv_rd32(dev, 0x6101d0), - nv_rd32(dev, 0x6101d4), nv_rd32(dev, 0x6109d4)); + for (i = 0; mask && i < 8; i++) { + u32 mcc = nv_rd32(dev, 0x640180 + (i * 0x20)); + if (!(mcc & (1 << crtc))) + continue; + + dcb = lookup_dcb(dev, i, mcc); + if (!dcb) + continue; - dcb = disp->irq.dis; - disp->irq.dis = NULL; - if (dcb) nouveau_bios_run_display_table(dev, 0x0000, -2, dcb, crtc); + } + + pclk = nv_rd32(dev, 0x660450 + (crtc * 0x300)) / 1000; + if (mask & 0x00010000) { + nv50_crtc_set_clock(dev, crtc, pclk); + } - nv50_crtc_set_clock(dev, crtc, pclk); + for (i = 0; mask && i < 8; i++) { + u32 mcp = nv_rd32(dev, 0x660180 + (i * 0x20)); + u32 cfg = nv_rd32(dev, 0x660184 + (i * 0x20)); + if (!(mcp & (1 << crtc))) + continue; - dcb = disp->irq.ena; - if (!dcb) - goto ack; - or = ffs(dcb->or) - 1; + dcb = lookup_dcb(dev, i, mcp); + if (!dcb) + continue; + or = ffs(dcb->or) - 1; - nouveau_bios_run_display_table(dev, disp->irq.cfg, pclk, dcb, crtc); + nouveau_bios_run_display_table(dev, cfg, pclk, dcb, crtc); - nv_wr32(dev, 0x612200 + (crtc * 0x800), 0x00000000); - switch (dcb->type) { - case OUTPUT_ANALOG: - nv_wr32(dev, 0x612280 + (or * 0x800), 0x00000000); - break; - case OUTPUT_TMDS: - case OUTPUT_LVDS: - if (disp->irq.cfg & 0x00000100) - tmp = 0x00000101; - else - tmp = 0x00000000; + nv_wr32(dev, 0x612200 + (crtc * 0x800), 0x00000000); + switch (dcb->type) { + case OUTPUT_ANALOG: + nv_wr32(dev, 0x612280 + (or * 0x800), 0x00000000); + break; + case OUTPUT_TMDS: + case OUTPUT_LVDS: + if (cfg & 0x00000100) + tmp = 0x00000101; + else + tmp = 0x00000000; + + nv_mask(dev, 0x612300 + (or * 0x800), 0x00000707, tmp); + break; + default: + break; + } - nv_mask(dev, 0x612300 + (or * 0x800), 0x00000707, tmp); - break; - default: break; } -ack: nv_wr32(dev, 0x6101d4, 0x00000000); nv_wr32(dev, 0x6109d4, 0x00000000); nv_wr32(dev, 0x6101d0, 0x80000000); @@ -1096,22 +1115,33 @@ ack: static void nvd0_display_unk4_handler(struct drm_device *dev) { - struct nvd0_display *disp = nvd0_display(dev); struct dcb_entry *dcb; - int crtc = disp->irq.crtc; - int pclk = disp->irq.pclk; + u32 mask, crtc; + int pclk, i; + + mask = nv_rd32(dev, 0x6101d4); + crtc = 0; + if (!mask) { + mask = nv_rd32(dev, 0x6109d4); + crtc = 1; + } + debug_irq(dev, 4); - NV_INFO(dev, "PDISP: 4 0x%08x 0x%08x 0x%08x\n", nv_rd32(dev, 0x6101d0), - nv_rd32(dev, 0x6101d4), nv_rd32(dev, 0x6109d4)); + pclk = nv_rd32(dev, 0x660450 + (crtc * 0x300)) / 1000; - dcb = disp->irq.ena; - disp->irq.ena = NULL; - if (!dcb) - goto ack; + for (i = 0; mask && i < 8; i++) { + u32 mcp = nv_rd32(dev, 0x660180 + (i * 0x20)); + u32 cfg = nv_rd32(dev, 0x660184 + (i * 0x20)); + if (!(mcp & (1 << crtc))) + continue; - nouveau_bios_run_display_table(dev, disp->irq.cfg, pclk, dcb, crtc); + dcb = lookup_dcb(dev, i, mcp); + if (!dcb) + continue; + + nouveau_bios_run_display_table(dev, cfg, -pclk, dcb, crtc); + } -ack: nv_wr32(dev, 0x6101d4, 0x00000000); nv_wr32(dev, 0x6109d4, 0x00000000); nv_wr32(dev, 0x6101d0, 0x80000000); @@ -1123,11 +1153,11 @@ nvd0_display_bh(unsigned long data) struct drm_device *dev = (struct drm_device *)data; struct nvd0_display *disp = nvd0_display(dev); - if (disp->irq.modeset & 0x00000001) + if (disp->modeset & 0x00000001) nvd0_display_unk1_handler(dev); - if (disp->irq.modeset & 0x00000002) + if (disp->modeset & 0x00000002) nvd0_display_unk2_handler(dev); - if (disp->irq.modeset & 0x00000004) + if (disp->modeset & 0x00000004) nvd0_display_unk4_handler(dev); } @@ -1159,7 +1189,7 @@ nvd0_display_intr(struct drm_device *dev) u32 stat = nv_rd32(dev, 0x6100ac); if (stat & 0x00000007) { - disp->irq.modeset = stat; + disp->modeset = stat; tasklet_schedule(&disp->tasklet); nv_wr32(dev, 0x6100ac, (stat & 0x00000007)); -- cgit v1.2.3-70-g09d2 From 37b034a64bef9ba36c504eb7717d2a5537cdef6b Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 8 Jul 2011 14:43:19 +1000 Subject: drm/nvd0/disp: tidy up what we have so far Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/Makefile | 6 +-- drivers/gpu/drm/nouveau/nvd0_display.c | 97 +++++++++++++--------------------- 2 files changed, 39 insertions(+), 64 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile index 2f621aef97f..1e567173c10 100644 --- a/drivers/gpu/drm/nouveau/Makefile +++ b/drivers/gpu/drm/nouveau/Makefile @@ -23,12 +23,12 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \ nva3_copy.o nvc0_copy.o \ nv31_mpeg.o nv50_mpeg.o \ nv04_instmem.o nv50_instmem.o nvc0_instmem.o \ - nv50_evo.o nv50_crtc.o nv50_dac.o nv50_sor.o \ - nv50_cursor.o nv50_display.o \ nv04_dac.o nv04_dfp.o nv04_tv.o nv17_tv.o nv17_tv_modes.o \ nv04_crtc.o nv04_display.o nv04_cursor.o \ - nv04_fbcon.o nv50_fbcon.o nvc0_fbcon.o \ + nv50_evo.o nv50_crtc.o nv50_dac.o nv50_sor.o \ + nv50_cursor.o nv50_display.o \ nvd0_display.o \ + nv04_fbcon.o nv50_fbcon.o nvc0_fbcon.o \ nv10_gpio.o nv50_gpio.o \ nv50_calc.o \ nv04_pm.o nv50_pm.o nva3_pm.o nvc0_pm.o \ diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index 725ae9983f1..20ab2c249b8 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -31,13 +31,10 @@ #include "nouveau_connector.h" #include "nouveau_encoder.h" #include "nouveau_crtc.h" +#include "nouveau_dma.h" #include "nouveau_fb.h" #include "nv50_display.h" -#define MEM_SYNC 0xe0000001 -#define MEM_VRAM 0xe0010000 -#include "nouveau_dma.h" - struct nvd0_display { struct nouveau_gpuobj *mem; struct { @@ -56,7 +53,7 @@ nvd0_display(struct drm_device *dev) return dev_priv->engine.display.priv; } -static int +static inline int evo_icmd(struct drm_device *dev, int id, u32 mthd, u32 data) { int ret = 0; @@ -237,7 +234,7 @@ nvd0_crtc_cursor_show(struct nouveau_crtc *nv_crtc, bool show, bool update) evo_data(push, 0x85000000); evo_data(push, nv_crtc->cursor.nvbo->bo.offset >> 8); evo_mthd(push, 0x048c + (nv_crtc->index * 0x300), 1); - evo_data(push, MEM_VRAM); + evo_data(push, NvEvoVRAM); } else { evo_mthd(push, 0x0480 + (nv_crtc->index * 0x300), 1); evo_data(push, 0x05000000); @@ -295,7 +292,7 @@ nvd0_crtc_commit(struct drm_crtc *crtc) evo_data(push, 0x00000000); evo_data(push, 0x00000000); evo_mthd(push, 0x045c + (nv_crtc->index * 0x300), 1); - evo_data(push, MEM_VRAM); + evo_data(push, NvEvoVRAM); evo_mthd(push, 0x0430 + (nv_crtc->index * 0x300), 1); evo_data(push, 0xffffff00); evo_kick(push, crtc->dev, 0); @@ -958,23 +955,6 @@ nvd0_sor_create(struct drm_connector *connector, struct dcb_entry *dcbe) /****************************************************************************** * IRQ *****************************************************************************/ -static void -debug_irq(struct drm_device *dev, int i) -{ - if (drm_debug & (DRM_UT_DRIVER | DRM_UT_KMS)) { - NV_INFO(dev, "PDISP: modeset req %d\n", i); - NV_INFO(dev, " STAT: 0x%08x 0x%08x 0x%08x\n", - nv_rd32(dev, 0x6101d0), - nv_rd32(dev, 0x6101d4), nv_rd32(dev, 0x6109d4)); - for (i = 0; i < 8; i++) { - NV_INFO(dev, " %s%d: 0x%08x 0x%08x\n", - i < 4 ? "DAC" : "SOR", i, - nv_rd32(dev, 0x640180 + (i * 0x20)), - nv_rd32(dev, 0x660180 + (i * 0x20))); - } - } -} - static struct dcb_entry * lookup_dcb(struct drm_device *dev, int id, u32 mc) { @@ -1009,20 +989,11 @@ lookup_dcb(struct drm_device *dev, int id, u32 mc) } static void -nvd0_display_unk1_handler(struct drm_device *dev) +nvd0_display_unk1_handler(struct drm_device *dev, u32 crtc, u32 mask) { struct dcb_entry *dcb; - u32 mask, crtc; int i; - mask = nv_rd32(dev, 0x6101d4); - crtc = 0; - if (!mask) { - mask = nv_rd32(dev, 0x6109d4); - crtc = 1; - } - debug_irq(dev, 1); - for (i = 0; mask && i < 8; i++) { u32 mcc = nv_rd32(dev, 0x640180 + (i * 0x20)); if (!(mcc & (1 << crtc))) @@ -1041,21 +1012,12 @@ nvd0_display_unk1_handler(struct drm_device *dev) } static void -nvd0_display_unk2_handler(struct drm_device *dev) +nvd0_display_unk2_handler(struct drm_device *dev, u32 crtc, u32 mask) { struct dcb_entry *dcb; - u32 mask, crtc, pclk; - u32 or, tmp; + u32 or, tmp, pclk; int i; - mask = nv_rd32(dev, 0x6101d4); - crtc = 0; - if (!mask) { - mask = nv_rd32(dev, 0x6109d4); - crtc = 1; - } - debug_irq(dev, 2); - for (i = 0; mask && i < 8; i++) { u32 mcc = nv_rd32(dev, 0x640180 + (i * 0x20)); if (!(mcc & (1 << crtc))) @@ -1113,20 +1075,11 @@ nvd0_display_unk2_handler(struct drm_device *dev) } static void -nvd0_display_unk4_handler(struct drm_device *dev) +nvd0_display_unk4_handler(struct drm_device *dev, u32 crtc, u32 mask) { struct dcb_entry *dcb; - u32 mask, crtc; int pclk, i; - mask = nv_rd32(dev, 0x6101d4); - crtc = 0; - if (!mask) { - mask = nv_rd32(dev, 0x6109d4); - crtc = 1; - } - debug_irq(dev, 4); - pclk = nv_rd32(dev, 0x660450 + (crtc * 0x300)) / 1000; for (i = 0; mask && i < 8; i++) { @@ -1152,13 +1105,35 @@ nvd0_display_bh(unsigned long data) { struct drm_device *dev = (struct drm_device *)data; struct nvd0_display *disp = nvd0_display(dev); + u32 mask, crtc; + int i; + + if (drm_debug & (DRM_UT_DRIVER | DRM_UT_KMS)) { + NV_INFO(dev, "PDISP: modeset req %d\n", disp->modeset); + NV_INFO(dev, " STAT: 0x%08x 0x%08x 0x%08x\n", + nv_rd32(dev, 0x6101d0), + nv_rd32(dev, 0x6101d4), nv_rd32(dev, 0x6109d4)); + for (i = 0; i < 8; i++) { + NV_INFO(dev, " %s%d: 0x%08x 0x%08x\n", + i < 4 ? "DAC" : "SOR", i, + nv_rd32(dev, 0x640180 + (i * 0x20)), + nv_rd32(dev, 0x660180 + (i * 0x20))); + } + } + + mask = nv_rd32(dev, 0x6101d4); + crtc = 0; + if (!mask) { + mask = nv_rd32(dev, 0x6109d4); + crtc = 1; + } if (disp->modeset & 0x00000001) - nvd0_display_unk1_handler(dev); + nvd0_display_unk1_handler(dev, crtc, mask); if (disp->modeset & 0x00000002) - nvd0_display_unk2_handler(dev); + nvd0_display_unk2_handler(dev, crtc, mask); if (disp->modeset & 0x00000004) - nvd0_display_unk4_handler(dev); + nvd0_display_unk4_handler(dev, crtc, mask); } static void @@ -1324,7 +1299,7 @@ nvd0_display_init(struct drm_device *dev) if (!push) return -EBUSY; evo_mthd(push, 0x0088, 1); - evo_data(push, MEM_SYNC); + evo_data(push, NvEvoSync); evo_mthd(push, 0x0084, 1); evo_data(push, 0x00000000); evo_mthd(push, 0x0084, 1); @@ -1430,7 +1405,7 @@ nvd0_display_create(struct drm_device *dev) nv_wo32(disp->mem, 0x100c, 0x00000000); nv_wo32(disp->mem, 0x1010, 0x00000000); nv_wo32(disp->mem, 0x1014, 0x00000000); - nv_wo32(disp->mem, 0x0000, MEM_SYNC); + nv_wo32(disp->mem, 0x0000, NvEvoSync); nv_wo32(disp->mem, 0x0004, (0x1000 << 9) | 0x00000001); nv_wo32(disp->mem, 0x1020, 0x00000049); @@ -1439,7 +1414,7 @@ nvd0_display_create(struct drm_device *dev) nv_wo32(disp->mem, 0x102c, 0x00000000); nv_wo32(disp->mem, 0x1030, 0x00000000); nv_wo32(disp->mem, 0x1034, 0x00000000); - nv_wo32(disp->mem, 0x0008, MEM_VRAM); + nv_wo32(disp->mem, 0x0008, NvEvoVRAM); nv_wo32(disp->mem, 0x000c, (0x1020 << 9) | 0x00000001); nv_wo32(disp->mem, 0x1040, 0x00000009); -- cgit v1.2.3-70-g09d2 From 658e86ee2db9500aea529a04008cce10972416bc Mon Sep 17 00:00:00 2001 From: Ken Milmore Date: Sun, 3 Jul 2011 19:54:28 +0100 Subject: drm/nouveau: enable hwmon support when both nouveau/hwmon are built as modules. The nouveau hwmon temperature support currently only functions when hwmon is compiled into the kernel. There's no reason why this shouldn't also work when both hwmon and nouveau are modularised (as is the case with Slackware's stock kernels). Signed-off-by: Ken Milmore Reviewed-by: Martin Peres Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_pm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index db68531b811..a539fd25792 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -312,7 +312,7 @@ nouveau_sysfs_fini(struct drm_device *dev) } } -#ifdef CONFIG_HWMON +#if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE)) static ssize_t nouveau_hwmon_show_temp(struct device *d, struct device_attribute *a, char *buf) { @@ -429,7 +429,7 @@ static const struct attribute_group hwmon_attrgroup = { static int nouveau_hwmon_init(struct drm_device *dev) { -#ifdef CONFIG_HWMON +#if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE)) struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_pm_engine *pm = &dev_priv->engine.pm; struct device *hwmon_dev; @@ -462,7 +462,7 @@ nouveau_hwmon_init(struct drm_device *dev) static void nouveau_hwmon_fini(struct drm_device *dev) { -#ifdef CONFIG_HWMON +#if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE)) struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_pm_engine *pm = &dev_priv->engine.pm; -- cgit v1.2.3-70-g09d2 From e432d48f87543731fff2b19563420877d8a0c4cc Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 11 Jul 2011 15:46:01 +1000 Subject: drm/nvd0: lets not attempt to dereference a nv50_display pointer Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_object.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_object.c b/drivers/gpu/drm/nouveau/nouveau_object.c index 4406c175106..02222c540ae 100644 --- a/drivers/gpu/drm/nouveau/nouveau_object.c +++ b/drivers/gpu/drm/nouveau/nouveau_object.c @@ -693,6 +693,7 @@ nouveau_gpuobj_channel_init_pramin(struct nouveau_channel *chan) static int nvc0_gpuobj_channel_init(struct nouveau_channel *chan, struct nouveau_vm *vm) { + struct drm_nouveau_private *dev_priv = chan->dev->dev_private; struct drm_device *dev = chan->dev; struct nouveau_gpuobj *pgd = NULL; struct nouveau_vm_pgd *vpgd; @@ -722,6 +723,9 @@ nvc0_gpuobj_channel_init(struct nouveau_channel *chan, struct nouveau_vm *vm) nv_wo32(chan->ramin, 0x020c, 0x000000ff); /* map display semaphore buffers into channel's vm */ + if (dev_priv->card_type >= NV_D0) + return 0; + for (i = 0; i < 2; i++) { struct nv50_display_crtc *dispc = &nv50_display(dev)->crtc[i]; @@ -875,18 +879,18 @@ nouveau_gpuobj_channel_takedown(struct nouveau_channel *chan) NV_DEBUG(dev, "ch%d\n", chan->id); - if (dev_priv->card_type >= NV_50) { + if (dev_priv->card_type >= NV_50 && dev_priv->card_type <= NV_C0) { struct nv50_display *disp = nv50_display(dev); for (i = 0; i < dev->mode_config.num_crtc; i++) { struct nv50_display_crtc *dispc = &disp->crtc[i]; nouveau_bo_vma_del(dispc->sem.bo, &chan->dispc_vma[i]); } - - nouveau_vm_ref(NULL, &chan->vm, chan->vm_pd); - nouveau_gpuobj_ref(NULL, &chan->vm_pd); } + nouveau_vm_ref(NULL, &chan->vm, chan->vm_pd); + nouveau_gpuobj_ref(NULL, &chan->vm_pd); + if (drm_mm_initialized(&chan->ramin_heap)) drm_mm_takedown(&chan->ramin_heap); nouveau_gpuobj_ref(NULL, &chan->ramin); -- cgit v1.2.3-70-g09d2 From 64c2502b77bf95b0f30d470109c2829cfa7d200e Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 11 Jul 2011 15:54:20 +1000 Subject: drm/nvc0/gr: remove max tpc count info Just assume a max of 16 everywhere, and hope it's okay. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvc0_grctx.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nvc0_grctx.c b/drivers/gpu/drm/nouveau/nvc0_grctx.c index 0c9737a4914..dd0e6a736b3 100644 --- a/drivers/gpu/drm/nouveau/nvc0_grctx.c +++ b/drivers/gpu/drm/nouveau/nvc0_grctx.c @@ -1786,11 +1786,7 @@ nvc0_grctx_generate(struct nouveau_channel *chan) nv_wr32(dev, 0x40587c, 0x00000000); if (1) { - const u8 chipset_tp_max[] = { 16, 4, 0, 4, 8, 0, 0, 0, - 16, 0, 0, 0, 0, 0, 8, 4 }; - u8 max = chipset_tp_max[dev_priv->chipset & 0x0f]; - u8 tpnr[GPC_MAX]; - u8 data[TP_MAX]; + u8 tpnr[GPC_MAX], data[TP_MAX]; memcpy(tpnr, priv->tp_nr, sizeof(priv->tp_nr)); memset(data, 0x1f, sizeof(data)); @@ -1804,7 +1800,7 @@ nvc0_grctx_generate(struct nouveau_channel *chan) data[tp] = gpc; } - for (i = 0; i < max / 4; i++) + for (i = 0; i < 4; i++) nv_wr32(dev, 0x4060a8 + (i * 4), ((u32 *)data)[i]); } -- cgit v1.2.3-70-g09d2 From bd57e7fc2e6d853661e4b802fe2b0ed528a93dbc Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 12 Jul 2011 12:06:36 +1000 Subject: drm/nvd0: no page flipping at the moment Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_state.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index 0c990d6256c..fd8287ef2f8 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -1210,7 +1210,7 @@ int nouveau_ioctl_getparam(struct drm_device *dev, void *data, getparam->value = 1; break; case NOUVEAU_GETPARAM_HAS_PAGEFLIP: - getparam->value = 1; + getparam->value = dev_priv->card_type < NV_D0; break; case NOUVEAU_GETPARAM_GRAPH_UNITS: /* NV40 and NV50 versions are quite different, but register -- cgit v1.2.3-70-g09d2 From 1cb70b30e4c6f25cf69ec0ab33db0218490f928d Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Wed, 13 Jul 2011 16:15:57 +1000 Subject: drm/nouveau: remove special-casing of hotplug detection type If we support PGPIO interrupts, and know a hotplug GPIO tag for a connector we use HPD, otherwise POLL_CONNECT. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_connector.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 939d4df0777..0acc4c75802 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -871,7 +871,6 @@ nouveau_connector_create(struct drm_device *dev, int index) dev->mode_config.scaling_mode_property, nv_connector->scaling_mode); } - connector->polled = DRM_CONNECTOR_POLL_CONNECT; /* fall-through */ case DCB_CONNECTOR_TV_0: case DCB_CONNECTOR_TV_1: @@ -888,19 +887,16 @@ nouveau_connector_create(struct drm_device *dev, int index) dev->mode_config.dithering_mode_property, nv_connector->use_dithering ? DRM_MODE_DITHERING_ON : DRM_MODE_DITHERING_OFF); - - if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS) { - if (dev_priv->card_type >= NV_50) - connector->polled = DRM_CONNECTOR_POLL_HPD; - else - connector->polled = DRM_CONNECTOR_POLL_CONNECT; - } break; } - if (pgpio->irq_register) { + if (nv_connector->dcb->gpio_tag != 0xff && pgpio->irq_register) { pgpio->irq_register(dev, nv_connector->dcb->gpio_tag, nouveau_connector_hotplug, connector); + + connector->polled = DRM_CONNECTOR_POLL_HPD; + } else { + connector->polled = DRM_CONNECTOR_POLL_CONNECT; } drm_sysfs_connector_add(connector); -- cgit v1.2.3-70-g09d2 From 9a7824887690836448eb73ccf0d8232da2e5bee3 Mon Sep 17 00:00:00 2001 From: Roy Spliet Date: Sat, 9 Jul 2011 21:18:11 +0200 Subject: drm/nouveau/pm: add initial NV3x/NVCx memtiming support, improve other cards NV30: Create framework for memtm NV50: Improve reg creation, NV50: Use P.version instead of card codename/stepping, NVC0: Initial memtiming code for Fermi, Renamed regs for consistency, Overall redesign to improve readability, Avoid kfree on null-pointer Signed-off-by: Roy Spliet --- drivers/gpu/drm/nouveau/nouveau_drv.h | 45 ++++-- drivers/gpu/drm/nouveau/nouveau_mem.c | 278 +++++++++++++++++--------------- drivers/gpu/drm/nouveau/nouveau_perf.c | 24 ++- drivers/gpu/drm/nouveau/nouveau_state.c | 2 - 4 files changed, 206 insertions(+), 143 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index e5d4e7d291b..7991c1b3b91 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -429,17 +429,43 @@ struct nouveau_pm_voltage { struct nouveau_pm_memtiming { int id; - u32 reg_100220; - u32 reg_100224; - u32 reg_100228; - u32 reg_10022c; - u32 reg_100230; - u32 reg_100234; - u32 reg_100238; - u32 reg_10023c; - u32 reg_100240; + 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; }; +struct nouveau_pm_tbl_header{ + u8 version; + u8 header_len; + u8 entry_cnt; + u8 entry_len; +}; + +struct nouveau_pm_tbl_entry{ + u8 tUNK_0, tUNK_1, tUNK_2; + u8 tRP; /* Byte 3 */ + u8 empty_4; + u8 tRAS; /* Byte 5 */ + u8 empty_6; + u8 tRFC; /* 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; +}; + +/* 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; @@ -648,7 +674,6 @@ struct drm_nouveau_private { enum nouveau_card_type card_type; /* exact chipset, derived from NV_PMC_BOOT_0 */ int chipset; - int stepping; int flags; void __iomem *mmio; diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c index bd3c39f6938..65c12ed1425 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mem.c +++ b/drivers/gpu/drm/nouveau/nouveau_mem.c @@ -502,35 +502,146 @@ nouveau_mem_gart_init(struct drm_device *dev) return 0; } +/* XXX: For now a dummy. More samples required, possibly even a card + * Called from nouveau_perf.c */ +void nv30_mem_timing_entry(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr, + struct nouveau_pm_tbl_entry *e, uint8_t magic_number, + struct nouveau_pm_memtiming *timing) { + + NV_DEBUG(dev,"Timing entry format unknown, please contact nouveau developers"); +} + +void nv40_mem_timing_entry(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr, + struct nouveau_pm_tbl_entry *e, uint8_t magic_number, + struct nouveau_pm_memtiming *timing) { + + timing->reg_0 = (e->tRC << 24 | e->tRFC << 16 | e->tRAS << 8 | e->tRP); + + /* XXX: I don't trust the -1's and +1's... they must come + * from somewhere! */ + timing->reg_1 = (e->tUNK_0 + 2 + magic_number) << 24 | + 1 << 16 | + (e->tUNK_1 + 2 + magic_number) << 8 | + (e->tUNK_2 + 2 - magic_number); + timing->reg_2 = (magic_number << 24 | e->tUNK_12 << 16 | e->tUNK_11 << 8 | e->tUNK_10); + timing->reg_2 |= 0x20200000; + + NV_DEBUG(dev, "Entry %d: 220: %08x %08x %08x\n", timing->id, + timing->reg_0, timing->reg_1,timing->reg_2); +} + +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, + unk19 = 1, + unk20 = 0, + unk21 = 0; + + switch (min(hdr->entry_len, (u8) 22)) { + case 22: + unk21 = e->tUNK_21; + case 21: + unk20 = e->tUNK_20; + case 20: + unk19 = e->tUNK_19; + case 19: + unk18 = e->tUNK_18; + break; + } + + timing->reg_0 = (e->tRC << 24 | e->tRFC << 16 | e->tRAS << 8 | e->tRP); + + /* XXX: I don't trust the -1's and +1's... they must come + * from somewhere! */ + timing->reg_1 = (e->tUNK_0 + 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->tUNK_2 - 1); + } else { + timing->reg_1 |= (e->tUNK_2 + 2 - magic_number); + } + timing->reg_2 = (e->tUNK_12 << 16 | e->tUNK_11 << 8 | e->tUNK_10); + + timing->reg_5 = (e->tRAS << 24 | e->tRC); + timing->reg_5 += max(e->tUNK_10, e->tUNK_11) << 16; + + if (P->version == 1) { + timing->reg_2 |= magic_number << 24; + timing->reg_3 = (0x14 + e->tUNK_2) << 24 | + 0x16 << 16 | + (e->tUNK_2 - 1) << 8 | + (e->tUNK_2 - 1); + timing->reg_4 = (nv_rd32(dev,0x10022c) & 0xffff0000) | e->tUNK_13 << 8 | e->tUNK_13; + timing->reg_5 |= (e->tUNK_2 + 2) << 8; + timing->reg_7 = 0x4000202 | (e->tUNK_2 - 1) << 16; + } else { + timing->reg_2 |= (unk19 - 1) << 24; + /* XXX: reg_10022c for recentish cards pretty much unknown*/ + timing->reg_3 = e->tUNK_2 - 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; + } + + 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, " 230: %08x %08x %08x %08x\n", + timing->reg_4, timing->reg_5, + timing->reg_6, timing->reg_7); + NV_DEBUG(dev, " 240: %08x\n", timing->reg_8); +} + +void nvc0_mem_timing_entry(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr, + struct nouveau_pm_tbl_entry *e, struct nouveau_pm_memtiming *timing) { + timing->reg_0 = (e->tRC << 24 | (e->tRFC & 0x7f) << 17 | e->tRAS << 8 | e->tRP); + timing->reg_1 = (nv_rd32(dev,0x10f294) & 0xff000000) | (e->tUNK_11&0x0f) << 20 | (e->tUNK_19 << 7) | (e->tUNK_2 & 0x0f); + timing->reg_2 = (nv_rd32(dev,0x10f298) & 0xff0000ff) | e->tUNK_0 << 16 | e->tUNK_1 << 8; + timing->reg_3 = e->tUNK_20 << 9 | e->tUNK_13; + timing->reg_4 = (nv_rd32(dev,0x10f2a0) & 0xfff000ff) | e->tUNK_12 << 15; + NV_DEBUG(dev, "Entry %d: 290: %08x %08x %08x %08x\n", timing->id, + timing->reg_0, timing->reg_1, + timing->reg_2, timing->reg_3); + NV_DEBUG(dev, " 2a0: %08x %08x %08x %08x\n", + timing->reg_4, timing->reg_5, + timing->reg_6, timing->reg_7); +} + +/** + * 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) { - /* cards < NVC0 only */ struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_pm_engine *pm = &dev_priv->engine.pm; struct nouveau_pm_memtimings *memtimings = &pm->memtimings; struct nvbios *bios = &dev_priv->vbios; struct bit_entry P; - u8 tUNK_0, tUNK_1, tUNK_2; - u8 tRP; /* Byte 3 */ - u8 tRAS; /* Byte 5 */ - u8 tRFC; /* Byte 7 */ - u8 tRC; /* Byte 9 */ - u8 tUNK_10, tUNK_11, tUNK_12, tUNK_13, tUNK_14; - u8 tUNK_18, tUNK_19, tUNK_20, tUNK_21; - u8 magic_number = 0; /* Yeah... sorry*/ - u8 *mem = NULL, *entry; - int i, recordlen, entries; + struct nouveau_pm_tbl_header *hdr = NULL; + uint8_t magic_number; + u8 *entry; + int i; if (bios->type == NVBIOS_BIT) { if (bit_table(dev, 'P', &P)) return; if (P.version == 1) - mem = ROMPTR(bios, P.data[4]); + hdr = (struct nouveau_pm_tbl_header *) ROMPTR(bios, P.data[4]); else if (P.version == 2) - mem = ROMPTR(bios, P.data[8]); + hdr = (struct nouveau_pm_tbl_header *) ROMPTR(bios, P.data[8]); else { NV_WARN(dev, "unknown mem for BIT P %d\n", P.version); } @@ -539,150 +650,54 @@ nouveau_mem_timing_init(struct drm_device *dev) return; } - if (!mem) { + if (!hdr) { NV_DEBUG(dev, "memory timing table pointer invalid\n"); return; } - if (mem[0] != 0x10) { - NV_WARN(dev, "memory timing table 0x%02x unknown\n", mem[0]); + if (hdr->version != 0x10) { + NV_WARN(dev, "memory timing table 0x%02x unknown\n", hdr->version); return; } /* validate record length */ - entries = mem[2]; - recordlen = mem[3]; - if (recordlen < 15) { - NV_ERROR(dev, "mem timing table length unknown: %d\n", mem[3]); + 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(entries, sizeof(*memtimings->timing), GFP_KERNEL); + 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 */ - if (dev_priv->card_type >= NV_40 && dev_priv->chipset < 0x98) { + * Used in calculations later... source unknown */ + magic_number = 0; + if (P.version == 1) { magic_number = (nv_rd32(dev, 0x100228) & 0x0f000000) >> 24; } - entry = mem + mem[1]; - for (i = 0; i < entries; i++, entry += recordlen) { + entry = (u8*) hdr + hdr->header_len; + for (i = 0; i < hdr->entry_cnt; i++, entry += hdr->entry_len) { struct nouveau_pm_memtiming *timing = &pm->memtimings.timing[i]; if (entry[0] == 0) continue; - tUNK_18 = 1; - tUNK_19 = 1; - tUNK_20 = 0; - tUNK_21 = 0; - switch (min(recordlen, 22)) { - case 22: - tUNK_21 = entry[21]; - case 21: - tUNK_20 = entry[20]; - case 20: - tUNK_19 = entry[19]; - case 19: - tUNK_18 = entry[18]; - default: - tUNK_0 = entry[0]; - tUNK_1 = entry[1]; - tUNK_2 = entry[2]; - tRP = entry[3]; - tRAS = entry[5]; - tRFC = entry[7]; - tRC = entry[9]; - tUNK_10 = entry[10]; - tUNK_11 = entry[11]; - tUNK_12 = entry[12]; - tUNK_13 = entry[13]; - tUNK_14 = entry[14]; - break; - } - - timing->reg_100220 = (tRC << 24 | tRFC << 16 | tRAS << 8 | tRP); - - /* XXX: I don't trust the -1's and +1's... they must come - * from somewhere! */ - timing->reg_100224 = (tUNK_0 + tUNK_19 + 1 + magic_number) << 24 | - max(tUNK_18, (u8) 1) << 16 | - (tUNK_1 + tUNK_19 + 1 + magic_number) << 8; - if (dev_priv->chipset == 0xa8) { - timing->reg_100224 |= (tUNK_2 - 1); - } else { - timing->reg_100224 |= (tUNK_2 + 2 - magic_number); - } - - timing->reg_100228 = (tUNK_12 << 16 | tUNK_11 << 8 | tUNK_10); - if (dev_priv->chipset >= 0xa3 && dev_priv->chipset < 0xaa) - timing->reg_100228 |= (tUNK_19 - 1) << 24; - else - timing->reg_100228 |= magic_number << 24; - - if (dev_priv->card_type == NV_40) { - /* NV40: don't know what the rest of the regs are.. - * And don't need to know either */ - timing->reg_100228 |= 0x20200000; - } else if (dev_priv->card_type >= NV_50) { - if (dev_priv->chipset < 0x98 || - (dev_priv->chipset == 0x98 && - dev_priv->stepping <= 0xa1)) { - timing->reg_10022c = (0x14 + tUNK_2) << 24 | - 0x16 << 16 | - (tUNK_2 - 1) << 8 | - (tUNK_2 - 1); - } else { - /* XXX: reg_10022c for recentish cards */ - timing->reg_10022c = tUNK_2 - 1; - } - - timing->reg_100230 = (tUNK_20 << 24 | tUNK_21 << 16 | - tUNK_13 << 8 | tUNK_13); - - timing->reg_100234 = (tRAS << 24 | tRC); - timing->reg_100234 += max(tUNK_10, tUNK_11) << 16; - - if (dev_priv->chipset < 0x98 || - (dev_priv->chipset == 0x98 && - dev_priv->stepping <= 0xa1)) { - timing->reg_100234 |= (tUNK_2 + 2) << 8; - } else { - /* XXX: +6? */ - timing->reg_100234 |= (tUNK_19 + 6) << 8; - } - - /* XXX; reg_100238 - * reg_100238: 0x00?????? */ - timing->reg_10023c = 0x202; - if (dev_priv->chipset < 0x98 || - (dev_priv->chipset == 0x98 && - dev_priv->stepping <= 0xa1)) { - timing->reg_10023c |= 0x4000000 | (tUNK_2 - 1) << 16; - } else { - /* XXX: reg_10023c - * currently unknown - * 10023c seen as 06xxxxxx, 0bxxxxxx or 0fxxxxxx */ - } - - /* XXX: reg_100240? */ - } timing->id = i; - NV_DEBUG(dev, "Entry %d: 220: %08x %08x %08x %08x\n", i, - timing->reg_100220, timing->reg_100224, - timing->reg_100228, timing->reg_10022c); - NV_DEBUG(dev, " 230: %08x %08x %08x %08x\n", - timing->reg_100230, timing->reg_100234, - timing->reg_100238, timing->reg_10023c); - NV_DEBUG(dev, " 240: %08x\n", timing->reg_100240); + 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]); + } } - memtimings->nr_timing = entries; - memtimings->supported = (dev_priv->chipset <= 0x98); + memtimings->nr_timing = hdr->entry_cnt; + memtimings->supported = P.version == 1; } void @@ -691,7 +706,10 @@ 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); + if(mem->timing) { + kfree(mem->timing); + mem->timing = NULL; + } } static int diff --git a/drivers/gpu/drm/nouveau/nouveau_perf.c b/drivers/gpu/drm/nouveau/nouveau_perf.c index b4327dad6e5..854ca857316 100644 --- a/drivers/gpu/drm/nouveau/nouveau_perf.c +++ b/drivers/gpu/drm/nouveau/nouveau_perf.c @@ -185,6 +185,8 @@ nouveau_perf_init(struct drm_device *dev) 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; @@ -232,6 +234,22 @@ nouveau_perf_init(struct drm_device *dev) } entry = perf + headerlen; + + /* For version 0x15, initialize memtiming table */ + if(version == 0x15) { + memtimings->timing = + kcalloc(entries, sizeof(*memtimings->timing), GFP_KERNEL); + if(!memtimings) { + 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++) { struct nouveau_pm_level *perflvl = &pm->perflvl[pm->nr_perflvl]; @@ -321,7 +339,11 @@ nouveau_perf_init(struct drm_device *dev) } /* get the corresponding memory timings */ - if (version > 0x15) { + 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, diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index fd8287ef2f8..f1047254e82 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -1028,13 +1028,11 @@ int nouveau_load(struct drm_device *dev, unsigned long flags) /* Time to determine the card architecture */ reg0 = nv_rd32(dev, NV03_PMC_BOOT_0); - dev_priv->stepping = 0; /* XXX: add stepping for pre-NV10? */ /* We're dealing with >=NV10 */ if ((reg0 & 0x0f000000) > 0) { /* Bit 27-20 contain the architecture in hex */ dev_priv->chipset = (reg0 & 0xff00000) >> 20; - dev_priv->stepping = (reg0 & 0xff); /* NV04 or NV05 */ } else if ((reg0 & 0xff00fff0) == 0x20004000) { if (reg0 & 0x00f00000) -- cgit v1.2.3-70-g09d2 From 2228c6fe04ddc303e90c05dd9430539fbcd8fa18 Mon Sep 17 00:00:00 2001 From: Roy Spliet Date: Thu, 14 Jul 2011 20:40:10 +0200 Subject: drm/nouveau/pm: Document and expose CL and WR for 0x1002Cx Signed-off-by: Roy Spliet --- drivers/gpu/drm/nouveau/nouveau_drv.h | 7 ++++++- drivers/gpu/drm/nouveau/nouveau_mem.c | 28 +++++++++++++++------------- 2 files changed, 21 insertions(+), 14 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 7991c1b3b91..c5993aad2ae 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -438,6 +438,9 @@ struct nouveau_pm_memtiming { u32 reg_6; u32 reg_7; u32 reg_8; + /* To be written to 0x1002c0 */ + u8 CL; + u8 WR; }; struct nouveau_pm_tbl_header{ @@ -448,7 +451,9 @@ struct nouveau_pm_tbl_header{ }; struct nouveau_pm_tbl_entry{ - u8 tUNK_0, tUNK_1, tUNK_2; + u8 tWR; + u8 tUNK_1; + u8 tCL; u8 tRP; /* Byte 3 */ u8 empty_4; u8 tRAS; /* Byte 5 */ diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c index 65c12ed1425..36bec480770 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mem.c +++ b/drivers/gpu/drm/nouveau/nouveau_mem.c @@ -519,10 +519,10 @@ void nv40_mem_timing_entry(struct drm_device *dev, struct nouveau_pm_tbl_header /* XXX: I don't trust the -1's and +1's... they must come * from somewhere! */ - timing->reg_1 = (e->tUNK_0 + 2 + magic_number) << 24 | + timing->reg_1 = (e->tWR + 2 + magic_number) << 24 | 1 << 16 | (e->tUNK_1 + 2 + magic_number) << 8 | - (e->tUNK_2 + 2 - magic_number); + (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; @@ -555,13 +555,13 @@ void nv50_mem_timing_entry(struct drm_device *dev, struct bit_entry *P, struct n /* XXX: I don't trust the -1's and +1's... they must come * from somewhere! */ - timing->reg_1 = (e->tUNK_0 + unk19 + 1 + magic_number) << 24 | + 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->tUNK_2 - 1); + timing->reg_1 |= (e->tCL - 1); } else { - timing->reg_1 |= (e->tUNK_2 + 2 - magic_number); + timing->reg_1 |= (e->tCL + 2 - magic_number); } timing->reg_2 = (e->tUNK_12 << 16 | e->tUNK_11 << 8 | e->tUNK_10); @@ -570,17 +570,17 @@ 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->tUNK_2) << 24 | + timing->reg_3 = (0x14 + e->tCL) << 24 | 0x16 << 16 | - (e->tUNK_2 - 1) << 8 | - (e->tUNK_2 - 1); + (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->tUNK_2 + 2) << 8; - timing->reg_7 = 0x4000202 | (e->tUNK_2 - 1) << 16; + 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->tUNK_2 - 1; + timing->reg_3 = e->tCL - 1; timing->reg_4 = (unk20 << 24 | unk21 << 16 | e->tUNK_13 << 8 | e->tUNK_13); /* XXX: +6? */ @@ -603,8 +603,8 @@ void nv50_mem_timing_entry(struct drm_device *dev, struct bit_entry *P, struct n 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->tUNK_2 & 0x0f); - timing->reg_2 = (nv_rd32(dev,0x10f298) & 0xff0000ff) | e->tUNK_0 << 16 | e->tUNK_1 << 8; + timing->reg_1 = (nv_rd32(dev,0x10f294) & 0xff000000) | (e->tUNK_11&0x0f) << 20 | (e->tUNK_19 << 7) | (e->tCL & 0x0f); + timing->reg_2 = (nv_rd32(dev,0x10f298) & 0xff0000ff) | e->tWR << 16 | e->tUNK_1 << 8; timing->reg_3 = e->tUNK_20 << 9 | e->tUNK_13; timing->reg_4 = (nv_rd32(dev,0x10f2a0) & 0xfff000ff) | e->tUNK_12 << 15; NV_DEBUG(dev, "Entry %d: 290: %08x %08x %08x %08x\n", timing->id, @@ -686,6 +686,8 @@ nouveau_mem_timing_init(struct drm_device *dev) continue; timing->id = i; + 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]); -- cgit v1.2.3-70-g09d2 From 6b70e481671cff087a39d40cd823435d0177000c Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 19 Jul 2011 15:59:39 +1000 Subject: drm/nouveau: magic to make auxch on new macbooks booted in EFI mode work Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_i2c.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.c b/drivers/gpu/drm/nouveau/nouveau_i2c.c index 739c0ac3a9b..f6a27fabcfe 100644 --- a/drivers/gpu/drm/nouveau/nouveau_i2c.c +++ b/drivers/gpu/drm/nouveau/nouveau_i2c.c @@ -286,7 +286,10 @@ nouveau_i2c_find(struct drm_device *dev, int index) val = 0xe001; } - nv_wr32(dev, reg, (nv_rd32(dev, reg) & ~0xf003) | val); + /* nfi, but neither auxch or i2c work if it's 1 */ + nv_mask(dev, reg + 0x0c, 0x00000001, 0x00000000); + /* nfi, but switches auxch vs normal i2c */ + nv_mask(dev, reg + 0x00, 0x0000f003, val); } if (!i2c->chan && nouveau_i2c_init(dev, i2c, index)) -- cgit v1.2.3-70-g09d2 From 93e692dc5f4ad9153a34cfb4bd02144f4368151b Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Wed, 20 Jul 2011 09:59:05 +1000 Subject: drm/nva3/pm: pll disabled if bit 0 of ctrl not set Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nva3_pm.c | 42 +++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 19 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nva3_pm.c b/drivers/gpu/drm/nouveau/nva3_pm.c index 864a15bd612..16d574dbe36 100644 --- a/drivers/gpu/drm/nouveau/nva3_pm.c +++ b/drivers/gpu/drm/nouveau/nva3_pm.c @@ -72,19 +72,21 @@ static u32 read_pll(struct drm_device *dev, int clk, u32 pll) { u32 ctrl = nv_rd32(dev, pll + 0); - u32 sclk, P = 1, N = 1, M = 1; + u32 sclk = 0, P = 1, N = 1, M = 1; if (!(ctrl & 0x00000008)) { - u32 coef = nv_rd32(dev, pll + 4); - M = (coef & 0x000000ff) >> 0; - N = (coef & 0x0000ff00) >> 8; - P = (coef & 0x003f0000) >> 16; + if (ctrl & 0x00000001) { + u32 coef = nv_rd32(dev, pll + 4); + M = (coef & 0x000000ff) >> 0; + N = (coef & 0x0000ff00) >> 8; + P = (coef & 0x003f0000) >> 16; - /* not post-divider on these.. */ - if ((pll & 0x00ff00) == 0x00e800) - P = 1; + /* no post-divider on these.. */ + if ((pll & 0x00ff00) == 0x00e800) + P = 1; - sclk = read_clk(dev, 0x00 + clk, false); + sclk = read_clk(dev, 0x00 + clk, false); + } } else { sclk = read_clk(dev, 0x10 + clk, false); } @@ -306,16 +308,18 @@ nva3_pm_clocks_set(struct drm_device *dev, void *pre_state) prog_clk(dev, 0x20, &info->unka0); prog_clk(dev, 0x21, &info->vdec); - nv_wr32(dev, 0x100210, 0); - nv_wr32(dev, 0x1002dc, 1); - nv_wr32(dev, 0x004018, 0x00001000); - prog_pll(dev, 0x02, 0x004000, &info->mclk); - if (nv_rd32(dev, 0x4000) & 0x00000008) - nv_wr32(dev, 0x004018, 0x1000d000); - else - nv_wr32(dev, 0x004018, 0x10005000); - nv_wr32(dev, 0x1002dc, 0); - nv_wr32(dev, 0x100210, 0x80000000); + if (info->mclk.clk || info->mclk.pll) { + nv_wr32(dev, 0x100210, 0); + nv_wr32(dev, 0x1002dc, 1); + nv_wr32(dev, 0x004018, 0x00001000); + prog_pll(dev, 0x02, 0x004000, &info->mclk); + if (nv_rd32(dev, 0x4000) & 0x00000008) + nv_wr32(dev, 0x004018, 0x1000d000); + else + nv_wr32(dev, 0x004018, 0x10005000); + nv_wr32(dev, 0x1002dc, 0); + nv_wr32(dev, 0x100210, 0x80000000); + } cleanup: /* unfreeze PFIFO */ -- cgit v1.2.3-70-g09d2 From f2cbe46f14427914bdd191795da998588dee4b8c Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 21 Jul 2011 15:39:06 +1000 Subject: drm/nouveau: determine timing crystal freq from straps Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_drv.h | 1 + drivers/gpu/drm/nouveau/nouveau_state.c | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index c5993aad2ae..92c414af207 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -680,6 +680,7 @@ struct drm_nouveau_private { /* exact chipset, derived from NV_PMC_BOOT_0 */ int chipset; int flags; + u32 crystal; void __iomem *mmio; diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index f1047254e82..baaecf10a58 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -984,7 +984,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; + uint32_t reg0, strap; resource_size_t mmio_start_offs; int ret; @@ -1074,6 +1074,23 @@ int nouveau_load(struct drm_device *dev, unsigned long flags) 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 || + (dev_priv->chipset >= 0x20 && dev_priv->chipset <= 0x25)) + strap &= 0x00000040; + else + strap &= 0x00400040; + + switch (strap) { + case 0x00000000: dev_priv->crystal = 13500; break; + case 0x00000040: dev_priv->crystal = 14318; break; + case 0x00400000: dev_priv->crystal = 27000; break; + case 0x00400040: dev_priv->crystal = 25000; break; + } + + NV_DEBUG(dev, "crystal freq: %dKHz\n", dev_priv->crystal); + /* Determine whether we'll attempt acceleration or not, some * cards are disabled by default here due to them being known * non-functional, or never been tested due to lack of hw. -- cgit v1.2.3-70-g09d2 From 64e740bb3d43a3abcd0b51cda3ba46b35bff30b2 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 21 Jul 2011 15:52:52 +1000 Subject: drm/nva3/pm: use crystal freq where appropriate Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nva3_pm.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nva3_pm.c b/drivers/gpu/drm/nouveau/nva3_pm.c index 16d574dbe36..ec684f56dfc 100644 --- a/drivers/gpu/drm/nouveau/nva3_pm.c +++ b/drivers/gpu/drm/nouveau/nva3_pm.c @@ -42,11 +42,12 @@ read_vco(struct drm_device *dev, int clk) static u32 read_clk(struct drm_device *dev, int clk, bool ignore_en) { + struct drm_nouveau_private *dev_priv = dev->dev_private; u32 sctl, sdiv, sclk; - /* refclk for the 0xe8xx plls always 27KHz */ + /* refclk for the 0xe8xx plls is a fixed frequency */ if (clk >= 0x40) - return 27000; + return dev_priv->crystal; sctl = nv_rd32(dev, 0x4120 + (clk * 4)); if (!ignore_en && !(sctl & 0x00000100)) @@ -54,7 +55,7 @@ read_clk(struct drm_device *dev, int clk, bool ignore_en) switch (sctl & 0x00003000) { case 0x00000000: - return 27000; + return dev_priv->crystal; case 0x00002000: if (sctl & 0x00000040) return 108000; -- cgit v1.2.3-70-g09d2 From 378f85ed54a424bc7e1edb9c3c7cd3a7efef9f9c Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 21 Jul 2011 15:54:48 +1000 Subject: drm/nva3/pm: fixup for NVAF special Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nva3_pm.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nva3_pm.c b/drivers/gpu/drm/nouveau/nva3_pm.c index ec684f56dfc..618c144b7a3 100644 --- a/drivers/gpu/drm/nouveau/nva3_pm.c +++ b/drivers/gpu/drm/nouveau/nva3_pm.c @@ -46,8 +46,14 @@ read_clk(struct drm_device *dev, int clk, bool ignore_en) u32 sctl, sdiv, sclk; /* refclk for the 0xe8xx plls is a fixed frequency */ - if (clk >= 0x40) + if (clk >= 0x40) { + if (dev_priv->chipset == 0xaf) { + /* no joke.. seriously.. sigh.. */ + return nv_rd32(dev, 0x00471c) * 1000; + } + return dev_priv->crystal; + } sctl = nv_rd32(dev, 0x4120 + (clk * 4)); if (!ignore_en && !(sctl & 0x00000100)) -- cgit v1.2.3-70-g09d2 From afb0c796d8002a0052662ff337dbd18b5dc5ff97 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 21 Jul 2011 16:12:58 +1000 Subject: drm/nouveau/tmr: fix miscalculation of ratio on pre-nv4x chipsets The clock_get() hook returns KHz, not Hz. Also fixed to use crystal freq from dev_priv. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nv04_timer.c | 28 ++-------------------------- 1 file changed, 2 insertions(+), 26 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nv04_timer.c b/drivers/gpu/drm/nouveau/nv04_timer.c index afb9d4b6a02..263301b809d 100644 --- a/drivers/gpu/drm/nouveau/nv04_timer.c +++ b/drivers/gpu/drm/nouveau/nv04_timer.c @@ -3,30 +3,6 @@ #include "nouveau_drv.h" #include "nouveau_drm.h" -static u32 -nv04_crystal_freq(struct drm_device *dev) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - u32 extdev_boot0 = nv_rd32(dev, 0x101000); - int type; - - type = !!(extdev_boot0 & 0x00000040); - if ((dev_priv->chipset >= 0x17 && dev_priv->chipset < 0x20) || - dev_priv->chipset >= 0x25) - type |= (extdev_boot0 & 0x00400000) ? 2 : 0; - - switch (type) { - case 0: return 13500000; - case 1: return 14318180; - case 2: return 27000000; - case 3: return 25000000; - default: - break; - } - - return 0; -} - int nv04_timer_init(struct drm_device *dev) { @@ -37,7 +13,7 @@ nv04_timer_init(struct drm_device *dev) nv_wr32(dev, NV04_PTIMER_INTR_0, 0xFFFFFFFF); /* aim for 31.25MHz, which gives us nanosecond timestamps */ - d = 1000000000 / 32; + d = 1000000 / 32; /* determine base clock for timer source */ if (dev_priv->chipset < 0x40) { @@ -47,7 +23,7 @@ nv04_timer_init(struct drm_device *dev) /*XXX: figure this out */ n = 0; } else { - n = nv04_crystal_freq(dev); + n = dev_priv->crystal; m = 1; while (n < (d * 2)) { n += (n / m); -- cgit v1.2.3-70-g09d2 From 91a8f1ea4bd98a7de888f7d56e28b72cc0c63ca1 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 25 Jul 2011 20:26:19 +1000 Subject: drm/nouveau: workaround semaphore hw bug causing unnecessary interrupts The HW will only accept the DMA_FROM_MEMORY class for DMA_SEMAPHORE without asking the driver to intervene. It appears that semaphores will work correctly even without DMA_IN_MEMORY, so lets avoid the large amount of interrupts generated by x-chan sync. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_fence.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c index c919cfc8f2f..81116cfea27 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fence.c +++ b/drivers/gpu/drm/nouveau/nouveau_fence.c @@ -519,7 +519,7 @@ nouveau_fence_channel_init(struct nouveau_channel *chan) if (USE_SEMA(dev) && dev_priv->chipset < 0x84) { struct ttm_mem_reg *mem = &dev_priv->fence.bo->bo.mem; - ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY, + ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_FROM_MEMORY, mem->start << PAGE_SHIFT, mem->size, NV_MEM_ACCESS_RW, NV_MEM_TARGET_VRAM, &obj); -- cgit v1.2.3-70-g09d2 From 43720133888f3713b534aec520783498f1bf5db3 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Wed, 20 Jul 2011 15:50:14 +1000 Subject: drm/nouveau/dp: rewrite auxch transaction routines Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_dp.c | 236 +++++++++++++++++++--------------- drivers/gpu/drm/nouveau/nouveau_drv.h | 1 + 2 files changed, 132 insertions(+), 105 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 44de23d9a43..f8ebd09ee3a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -29,6 +29,136 @@ #include "nouveau_connector.h" #include "nouveau_encoder.h" +/****************************************************************************** + * aux channel util functions + *****************************************************************************/ +#define AUX_DBG(fmt, args...) do { \ + if (nouveau_reg_debug & NOUVEAU_REG_DEBUG_AUXCH) { \ + NV_PRINTK(KERN_DEBUG, dev, "AUXCH(%d): " fmt, ch, ##args); \ + } \ +} while (0) +#define AUX_ERR(fmt, args...) NV_ERROR(dev, "AUXCH(%d): " fmt, ch, ##args) + +static void +auxch_fini(struct drm_device *dev, int ch) +{ + nv_mask(dev, 0x00e4e4 + (ch * 0x50), 0x00310000, 0x00000000); +} + +static int +auxch_init(struct drm_device *dev, int ch) +{ + const u32 unksel = 1; /* nfi which to use, or if it matters.. */ + const u32 ureq = unksel ? 0x00100000 : 0x00200000; + const u32 urep = unksel ? 0x01000000 : 0x02000000; + u32 ctrl, timeout; + + /* wait up to 1ms for any previous transaction to be done... */ + timeout = 1000; + do { + ctrl = nv_rd32(dev, 0x00e4e4 + (ch * 0x50)); + udelay(1); + if (!timeout--) { + AUX_ERR("begin idle timeout 0x%08x", ctrl); + return -EBUSY; + } + } while (ctrl & 0x03010000); + + /* set some magic, and wait up to 1ms for it to appear */ + nv_mask(dev, 0x00e4e4 + (ch * 0x50), 0x00300000, ureq); + timeout = 1000; + do { + ctrl = nv_rd32(dev, 0x00e4e4 + (ch * 0x50)); + udelay(1); + if (!timeout--) { + AUX_ERR("magic wait 0x%08x\n", ctrl); + auxch_fini(dev, ch); + return -EBUSY; + } + } while ((ctrl & 0x03000000) != urep); + + return 0; +} + +static int +auxch_tx(struct drm_device *dev, int ch, u8 type, u32 addr, u8 *data, u8 size) +{ + u32 ctrl, stat, timeout, retries; + u32 xbuf[4] = {}; + int ret, i; + + AUX_DBG("%d: 0x%08x %d\n", type, addr, size); + + ret = auxch_init(dev, ch); + if (ret) + goto out; + + stat = nv_rd32(dev, 0x00e4e8 + (ch * 0x50)); + if (!(stat & 0x10000000)) { + AUX_DBG("sink not detected\n"); + ret = -ENXIO; + goto out; + } + + if (!(type & 1)) { + memcpy(xbuf, data, size); + for (i = 0; i < 16; i += 4) { + AUX_DBG("wr 0x%08x\n", xbuf[i / 4]); + nv_wr32(dev, 0x00e4c0 + (ch * 0x50) + i, xbuf[i / 4]); + } + } + + ctrl = nv_rd32(dev, 0x00e4e4 + (ch * 0x50)); + ctrl &= ~0x0001f0ff; + ctrl |= type << 12; + ctrl |= size - 1; + nv_wr32(dev, 0x00e4e0 + (ch * 0x50), addr); + + /* retry transaction a number of times on failure... */ + ret = -EREMOTEIO; + for (retries = 0; retries < 32; retries++) { + /* reset, and delay a while if this is a retry */ + nv_wr32(dev, 0x00e4e4 + (ch * 0x50), 0x80000000 | ctrl); + nv_wr32(dev, 0x00e4e4 + (ch * 0x50), 0x00000000 | ctrl); + if (retries) + udelay(400); + + /* transaction request, wait up to 1ms for it to complete */ + nv_wr32(dev, 0x00e4e4 + (ch * 0x50), 0x00010000 | ctrl); + + timeout = 1000; + do { + ctrl = nv_rd32(dev, 0x00e4e4 + (ch * 0x50)); + udelay(1); + if (!timeout--) { + AUX_ERR("tx req timeout 0x%08x\n", ctrl); + goto out; + } + } while (ctrl & 0x00010000); + + /* read status, and check if transaction completed ok */ + stat = nv_mask(dev, 0x00e4e8 + (ch * 0x50), 0, 0); + if (!(stat & 0x000f0f00)) { + ret = 0; + break; + } + + AUX_DBG("%02d 0x%08x 0x%08x\n", retries, ctrl, stat); + } + + if (type & 1) { + for (i = 0; i < 16; i += 4) { + xbuf[i / 4] = nv_rd32(dev, 0x00e4d0 + (ch * 0x50) + i); + AUX_DBG("rd 0x%08x\n", xbuf[i / 4]); + } + memcpy(data, xbuf, size); + } + +out: + auxch_fini(dev, ch); + return ret; +} + static int auxch_rd(struct drm_encoder *encoder, int address, uint8_t *buf, int size) { @@ -480,98 +610,7 @@ int nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr, uint8_t *data, int data_nr) { - struct drm_device *dev = auxch->dev; - uint32_t tmp, ctrl, stat = 0, data32[4] = {}; - int ret = 0, i, index = auxch->rd; - - NV_DEBUG_KMS(dev, "ch %d cmd %d addr 0x%x len %d\n", index, cmd, addr, data_nr); - - tmp = nv_rd32(dev, NV50_AUXCH_CTRL(auxch->rd)); - nv_wr32(dev, NV50_AUXCH_CTRL(auxch->rd), tmp | 0x00100000); - tmp = nv_rd32(dev, NV50_AUXCH_CTRL(auxch->rd)); - if (!(tmp & 0x01000000)) { - NV_ERROR(dev, "expected bit 24 == 1, got 0x%08x\n", tmp); - ret = -EIO; - goto out; - } - - for (i = 0; i < 3; i++) { - tmp = nv_rd32(dev, NV50_AUXCH_STAT(auxch->rd)); - if (tmp & NV50_AUXCH_STAT_STATE_READY) - break; - udelay(100); - } - - if (i == 3) { - ret = -EBUSY; - goto out; - } - - if (!(cmd & 1)) { - memcpy(data32, data, data_nr); - for (i = 0; i < 4; i++) { - NV_DEBUG_KMS(dev, "wr %d: 0x%08x\n", i, data32[i]); - nv_wr32(dev, NV50_AUXCH_DATA_OUT(index, i), data32[i]); - } - } - - nv_wr32(dev, NV50_AUXCH_ADDR(index), addr); - ctrl = nv_rd32(dev, NV50_AUXCH_CTRL(index)); - ctrl &= ~(NV50_AUXCH_CTRL_CMD | NV50_AUXCH_CTRL_LEN); - ctrl |= (cmd << NV50_AUXCH_CTRL_CMD_SHIFT); - ctrl |= ((data_nr - 1) << NV50_AUXCH_CTRL_LEN_SHIFT); - - for (i = 0; i < 16; i++) { - nv_wr32(dev, NV50_AUXCH_CTRL(index), ctrl | 0x80000000); - nv_wr32(dev, NV50_AUXCH_CTRL(index), ctrl); - nv_wr32(dev, NV50_AUXCH_CTRL(index), ctrl | 0x00010000); - if (!nv_wait(dev, NV50_AUXCH_CTRL(index), - 0x00010000, 0x00000000)) { - NV_ERROR(dev, "expected bit 16 == 0, got 0x%08x\n", - nv_rd32(dev, NV50_AUXCH_CTRL(index))); - ret = -EBUSY; - goto out; - } - - udelay(400); - - stat = nv_rd32(dev, NV50_AUXCH_STAT(index)); - if ((stat & NV50_AUXCH_STAT_REPLY_AUX) != - NV50_AUXCH_STAT_REPLY_AUX_DEFER) - break; - } - - if (i == 16) { - NV_ERROR(dev, "auxch DEFER too many times, bailing\n"); - ret = -EREMOTEIO; - goto out; - } - - if (cmd & 1) { - if ((stat & NV50_AUXCH_STAT_COUNT) != data_nr) { - ret = -EREMOTEIO; - goto out; - } - - for (i = 0; i < 4; i++) { - data32[i] = nv_rd32(dev, NV50_AUXCH_DATA_IN(index, i)); - NV_DEBUG_KMS(dev, "rd %d: 0x%08x\n", i, data32[i]); - } - memcpy(data, data32, data_nr); - } - -out: - tmp = nv_rd32(dev, NV50_AUXCH_CTRL(auxch->rd)); - nv_wr32(dev, NV50_AUXCH_CTRL(auxch->rd), tmp & ~0x00100000); - tmp = nv_rd32(dev, NV50_AUXCH_CTRL(auxch->rd)); - if (tmp & 0x01000000) { - NV_ERROR(dev, "expected bit 24 == 0, got 0x%08x\n", tmp); - ret = -EIO; - } - - udelay(400); - - return ret ? ret : (stat & NV50_AUXCH_STAT_REPLY); + return auxch_tx(auxch->dev, auxch->rd, cmd, addr, data, data_nr); } static int @@ -602,19 +641,6 @@ nouveau_dp_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) if (ret < 0) return ret; - switch (ret & NV50_AUXCH_STAT_REPLY_I2C) { - case NV50_AUXCH_STAT_REPLY_I2C_ACK: - break; - case NV50_AUXCH_STAT_REPLY_I2C_NACK: - return -EREMOTEIO; - case NV50_AUXCH_STAT_REPLY_I2C_DEFER: - udelay(100); - continue; - default: - NV_ERROR(dev, "bad auxch reply: 0x%08x\n", ret); - return -EREMOTEIO; - } - ptr += cnt; remaining -= cnt; } diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 92c414af207..bc035c4f42a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -1570,6 +1570,7 @@ enum { NOUVEAU_REG_DEBUG_RMVIO = 0x80, NOUVEAU_REG_DEBUG_VGAATTR = 0x100, NOUVEAU_REG_DEBUG_EVO = 0x200, + NOUVEAU_REG_DEBUG_AUXCH = 0x400 }; #define NV_REG_DEBUG(type, dev, fmt, arg...) do { \ -- cgit v1.2.3-70-g09d2 From 46959b7790e3609e795c3b5e70e58dcd22c9e207 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 1 Jul 2011 15:51:49 +1000 Subject: drm/nouveau/dp: remove reliance on vbios for native displayport Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_dp.c | 111 +++++++++++++++++++++++++++++- drivers/gpu/drm/nouveau/nouveau_drv.h | 1 + drivers/gpu/drm/nouveau/nouveau_encoder.h | 3 - drivers/gpu/drm/nouveau/nouveau_reg.h | 2 +- drivers/gpu/drm/nouveau/nv50_display.c | 39 ++--------- drivers/gpu/drm/nouveau/nv50_sor.c | 33 ++------- 6 files changed, 126 insertions(+), 63 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index f8ebd09ee3a..ae1b6e00bd9 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -194,6 +194,116 @@ auxch_wr(struct drm_encoder *encoder, int address, uint8_t *buf, int size) 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); +} + static int nouveau_dp_lane_count_set(struct drm_encoder *encoder, uint8_t cmd) { @@ -617,7 +727,6 @@ static int nouveau_dp_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { struct nouveau_i2c_chan *auxch = (struct nouveau_i2c_chan *)adap; - struct drm_device *dev = auxch->dev; struct i2c_msg *msg = msgs; int ret, mcnt = num; diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index bc035c4f42a..ee0f0d129d3 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -1101,6 +1101,7 @@ 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 *); +void nouveau_dp_tu_update(struct drm_device *, int, int, u32, u32); /* nv04_fb.c */ 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 ae69b61d93d..2bb316d2421 100644 --- a/drivers/gpu/drm/nouveau/nouveau_encoder.h +++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h @@ -49,9 +49,6 @@ struct nouveau_encoder { union { struct { - int mc_unknown; - uint32_t unk0; - uint32_t unk1; int dpcd_version; int link_nr; int link_bw; diff --git a/drivers/gpu/drm/nouveau/nouveau_reg.h b/drivers/gpu/drm/nouveau/nouveau_reg.h index f18cdfc3400..d9632ae38c6 100644 --- a/drivers/gpu/drm/nouveau/nouveau_reg.h +++ b/drivers/gpu/drm/nouveau/nouveau_reg.h @@ -843,7 +843,7 @@ #define NV50_SOR_DP_CTRL_TRAINING_PATTERN_2 0x02000000 #define NV50_SOR_DP_UNK118(i, l) (0x0061c118 + (i) * 0x800 + (l) * 0x80) #define NV50_SOR_DP_UNK120(i, l) (0x0061c120 + (i) * 0x800 + (l) * 0x80) -#define NV50_SOR_DP_UNK128(i, l) (0x0061c128 + (i) * 0x800 + (l) * 0x80) +#define NV50_SOR_DP_SCFG(i, l) (0x0061c128 + (i) * 0x800 + (l) * 0x80) #define NV50_SOR_DP_UNK130(i, l) (0x0061c130 + (i) * 0x800 + (l) * 0x80) #define NV50_PDISPLAY_USER(i) ((i) * 0x1000 + 0x00640000) diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index 8260303c2fc..d23ca00e7d6 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -701,37 +701,6 @@ ack: nv_wr32(dev, 0x610030, 0x80000000); } -static void -nv50_display_unk20_dp_hack(struct drm_device *dev, struct dcb_entry *dcb) -{ - int or = ffs(dcb->or) - 1, link = !(dcb->dpconf.sor.link & 1); - struct drm_encoder *encoder; - uint32_t tmp, unk0 = 0, unk1 = 0; - - if (dcb->type != OUTPUT_DP) - return; - - list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - - if (nv_encoder->dcb == dcb) { - unk0 = nv_encoder->dp.unk0; - unk1 = nv_encoder->dp.unk1; - break; - } - } - - if (unk0 || unk1) { - tmp = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link)); - tmp &= 0xfffffe03; - nv_wr32(dev, NV50_SOR_DP_CTRL(or, link), tmp | unk0); - - tmp = nv_rd32(dev, NV50_SOR_DP_UNK128(or, link)); - tmp &= 0xfef080c0; - nv_wr32(dev, NV50_SOR_DP_UNK128(or, link), tmp | unk1); - } -} - static void nv50_display_unk20_handler(struct drm_device *dev) { @@ -830,7 +799,13 @@ nv50_display_unk20_handler(struct drm_device *dev) script = nv50_display_script_select(dev, dcb, mc, pclk); nouveau_bios_run_display_table(dev, script, pclk, dcb, -1); - nv50_display_unk20_dp_hack(dev, dcb); + if (type == OUTPUT_DP) { + int link = !(dcb->dpconf.sor.link & 1); + if ((mc & 0x000f0000) == 0x00020000) + nouveau_dp_tu_update(dev, or, link, pclk, 18); + else + nouveau_dp_tu_update(dev, or, link, pclk, 24); + } if (dcb->type != OUTPUT_ANALOG) { tmp = nv_rd32(dev, NV50_PDISPLAY_SOR_CLK_CTRL2(or)); diff --git a/drivers/gpu/drm/nouveau/nv50_sor.c b/drivers/gpu/drm/nouveau/nv50_sor.c index ffe8b483b7b..f359f94626c 100644 --- a/drivers/gpu/drm/nouveau/nv50_sor.c +++ b/drivers/gpu/drm/nouveau/nv50_sor.c @@ -187,6 +187,7 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); struct drm_device *dev = encoder->dev; struct nouveau_crtc *crtc = nouveau_crtc(encoder->crtc); + struct nouveau_connector *nv_connector; uint32_t mode_ctl = 0; int ret; @@ -206,7 +207,12 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, mode_ctl = 0x0200; break; case OUTPUT_DP: - mode_ctl |= (nv_encoder->dp.mc_unknown << 16); + nv_connector = nouveau_encoder_connector_get(nv_encoder); + if (nv_connector && nv_connector->base.display_info.bpc == 6) + mode_ctl |= 0x00020000; + else + mode_ctl |= 0x00050000; + if (nv_encoder->dcb->sorconf.link & 1) mode_ctl |= 0x00000800; else @@ -313,31 +319,6 @@ nv50_sor_create(struct drm_connector *connector, struct dcb_entry *entry) encoder->possible_crtcs = entry->heads; encoder->possible_clones = 0; - if (nv_encoder->dcb->type == OUTPUT_DP) { - int or = nv_encoder->or, link = !(entry->dpconf.sor.link & 1); - uint32_t tmp; - - tmp = nv_rd32(dev, 0x61c700 + (or * 0x800)); - if (!tmp) - tmp = nv_rd32(dev, 0x610798 + (or * 8)); - - switch ((tmp & 0x00000f00) >> 8) { - case 8: - case 9: - nv_encoder->dp.mc_unknown = (tmp & 0x000f0000) >> 16; - tmp = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link)); - nv_encoder->dp.unk0 = tmp & 0x000001fc; - tmp = nv_rd32(dev, NV50_SOR_DP_UNK128(or, link)); - nv_encoder->dp.unk1 = tmp & 0x010f7f3f; - break; - default: - break; - } - - if (!nv_encoder->dp.mc_unknown) - nv_encoder->dp.mc_unknown = 5; - } - drm_mode_connector_attach_encoder(connector, encoder); return 0; } -- cgit v1.2.3-70-g09d2 From 68c9918479834c19d0c2a1d38b75227b01355674 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 2 Aug 2011 13:57:10 +1000 Subject: drm/nouveau: tidy connector hotplug handler, punt messages to debug Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_connector.c | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 0acc4c75802..561d4c6677e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -921,22 +921,13 @@ nouveau_connector_hotplug(void *data, int plugged) struct drm_connector *connector = data; struct drm_device *dev = connector->dev; - NV_INFO(dev, "%splugged %s\n", plugged ? "" : "un", - drm_get_connector_name(connector)); + NV_DEBUG(dev, "%splugged %s\n", plugged ? "" : "un", + drm_get_connector_name(connector)); - if (connector->encoder && connector->encoder->crtc && - connector->encoder->crtc->enabled) { - struct nouveau_encoder *nv_encoder = nouveau_encoder(connector->encoder); - struct drm_encoder_helper_funcs *helper = - connector->encoder->helper_private; - - if (nv_encoder->dcb->type == OUTPUT_DP) { - if (plugged) - helper->dpms(connector->encoder, DRM_MODE_DPMS_ON); - else - helper->dpms(connector->encoder, DRM_MODE_DPMS_OFF); - } - } + if (plugged) + drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON); + else + drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); drm_helper_hpd_irq_event(dev); } -- cgit v1.2.3-70-g09d2 From 730764812ded8b53643670131219b3abbdab52c8 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 2 Aug 2011 18:54:43 +1000 Subject: drm/nouveau/backlight: make more consistent with rest of driver style Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_backlight.c | 70 ++++++++++++++++------------- 1 file changed, 39 insertions(+), 31 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_backlight.c b/drivers/gpu/drm/nouveau/nouveau_backlight.c index 00a55dfdba8..d412adc6740 100644 --- a/drivers/gpu/drm/nouveau/nouveau_backlight.c +++ b/drivers/gpu/drm/nouveau/nouveau_backlight.c @@ -38,7 +38,8 @@ #include "nouveau_drm.h" #include "nouveau_reg.h" -static int nv40_get_intensity(struct backlight_device *bd) +static int +nv40_get_intensity(struct backlight_device *bd) { struct drm_device *dev = bl_get_data(bd); int val = (nv_rd32(dev, NV40_PMC_BACKLIGHT) & NV40_PMC_BACKLIGHT_MASK) @@ -47,7 +48,8 @@ static int nv40_get_intensity(struct backlight_device *bd) return val; } -static int nv40_set_intensity(struct backlight_device *bd) +static int +nv40_set_intensity(struct backlight_device *bd) { struct drm_device *dev = bl_get_data(bd); int val = bd->props.brightness; @@ -65,30 +67,8 @@ static const struct backlight_ops nv40_bl_ops = { .update_status = nv40_set_intensity, }; -static int nv50_get_intensity(struct backlight_device *bd) -{ - struct drm_device *dev = bl_get_data(bd); - - return nv_rd32(dev, NV50_PDISPLAY_SOR_BACKLIGHT); -} - -static int nv50_set_intensity(struct backlight_device *bd) -{ - struct drm_device *dev = bl_get_data(bd); - int val = bd->props.brightness; - - nv_wr32(dev, NV50_PDISPLAY_SOR_BACKLIGHT, - val | NV50_PDISPLAY_SOR_BACKLIGHT_ENABLE); - return 0; -} - -static const struct backlight_ops nv50_bl_ops = { - .options = BL_CORE_SUSPENDRESUME, - .get_brightness = nv50_get_intensity, - .update_status = nv50_set_intensity, -}; - -static int nouveau_nv40_backlight_init(struct drm_connector *connector) +static int +nv40_backlight_init(struct drm_connector *connector) { struct drm_device *dev = connector->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; @@ -113,7 +93,33 @@ static int nouveau_nv40_backlight_init(struct drm_connector *connector) return 0; } -static int nouveau_nv50_backlight_init(struct drm_connector *connector) +static int +nv50_get_intensity(struct backlight_device *bd) +{ + struct drm_device *dev = bl_get_data(bd); + + return nv_rd32(dev, NV50_PDISPLAY_SOR_BACKLIGHT); +} + +static int +nv50_set_intensity(struct backlight_device *bd) +{ + struct drm_device *dev = bl_get_data(bd); + int val = bd->props.brightness; + + nv_wr32(dev, NV50_PDISPLAY_SOR_BACKLIGHT, + val | NV50_PDISPLAY_SOR_BACKLIGHT_ENABLE); + return 0; +} + +static const struct backlight_ops nv50_bl_ops = { + .options = BL_CORE_SUSPENDRESUME, + .get_brightness = nv50_get_intensity, + .update_status = nv50_set_intensity, +}; + +static int +nv50_backlight_init(struct drm_connector *connector) { struct drm_device *dev = connector->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; @@ -137,7 +143,8 @@ static int nouveau_nv50_backlight_init(struct drm_connector *connector) return 0; } -int nouveau_backlight_init(struct drm_connector *connector) +int +nouveau_backlight_init(struct drm_connector *connector) { struct drm_device *dev = connector->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; @@ -152,9 +159,9 @@ int nouveau_backlight_init(struct drm_connector *connector) switch (dev_priv->card_type) { case NV_40: - return nouveau_nv40_backlight_init(connector); + return nv40_backlight_init(connector); case NV_50: - return nouveau_nv50_backlight_init(connector); + return nv50_backlight_init(connector); default: break; } @@ -162,7 +169,8 @@ int nouveau_backlight_init(struct drm_connector *connector) return 0; } -void nouveau_backlight_exit(struct drm_connector *connector) +void +nouveau_backlight_exit(struct drm_connector *connector) { struct drm_device *dev = connector->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; -- cgit v1.2.3-70-g09d2 From 10b461e40a5a5522fe996805a0625c9cd4e5c1a7 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 2 Aug 2011 19:29:37 +1000 Subject: drm/nv50/backlight: take the sor into account when bashing regs I'm sure that out there somewhere, someone will need this. We currently haven't seen an example of LVDS being on a non-0 SOR so far though. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_backlight.c | 58 ++++++++++++++++++++--------- drivers/gpu/drm/nouveau/nouveau_connector.c | 10 +---- drivers/gpu/drm/nouveau/nouveau_drv.h | 8 ++-- drivers/gpu/drm/nouveau/nouveau_encoder.h | 3 ++ drivers/gpu/drm/nouveau/nouveau_state.c | 4 ++ 5 files changed, 52 insertions(+), 31 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_backlight.c b/drivers/gpu/drm/nouveau/nouveau_backlight.c index d412adc6740..4f3d1ff3e47 100644 --- a/drivers/gpu/drm/nouveau/nouveau_backlight.c +++ b/drivers/gpu/drm/nouveau/nouveau_backlight.c @@ -37,6 +37,7 @@ #include "nouveau_drv.h" #include "nouveau_drm.h" #include "nouveau_reg.h" +#include "nouveau_encoder.h" static int nv40_get_intensity(struct backlight_device *bd) @@ -96,18 +97,22 @@ nv40_backlight_init(struct drm_connector *connector) static int nv50_get_intensity(struct backlight_device *bd) { - struct drm_device *dev = bl_get_data(bd); + struct nouveau_encoder *nv_encoder = bl_get_data(bd); + struct drm_device *dev = nv_encoder->base.base.dev; + int or = nv_encoder->or; - return nv_rd32(dev, NV50_PDISPLAY_SOR_BACKLIGHT); + return nv_rd32(dev, NV50_PDISPLAY_SOR_BACKLIGHT + (or * 0x800)); } static int nv50_set_intensity(struct backlight_device *bd) { - struct drm_device *dev = bl_get_data(bd); + struct nouveau_encoder *nv_encoder = bl_get_data(bd); + struct drm_device *dev = nv_encoder->base.base.dev; int val = bd->props.brightness; + int or = nv_encoder->or; - nv_wr32(dev, NV50_PDISPLAY_SOR_BACKLIGHT, + nv_wr32(dev, NV50_PDISPLAY_SOR_BACKLIGHT + (or * 0x800), val | NV50_PDISPLAY_SOR_BACKLIGHT_ENABLE); return 0; } @@ -123,17 +128,28 @@ nv50_backlight_init(struct drm_connector *connector) { struct drm_device *dev = connector->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_encoder *nv_encoder; struct backlight_properties props; struct backlight_device *bd; + int or; + + nv_encoder = find_encoder(connector, OUTPUT_LVDS); + if (!nv_encoder) { + nv_encoder = find_encoder(connector, OUTPUT_DP); + if (!nv_encoder) + return -ENODEV; + } + + or = nv_encoder->or; - if (!nv_rd32(dev, NV50_PDISPLAY_SOR_BACKLIGHT)) + if (!nv_rd32(dev, NV50_PDISPLAY_SOR_BACKLIGHT + (or * 0x800))) return 0; memset(&props, 0, sizeof(struct backlight_properties)); props.type = BACKLIGHT_RAW; props.max_brightness = 1025; - bd = backlight_device_register("nv_backlight", &connector->kdev, dev, - &nv50_bl_ops, &props); + bd = backlight_device_register("nv_backlight", &connector->kdev, + nv_encoder, &nv50_bl_ops, &props); if (IS_ERR(bd)) return PTR_ERR(bd); @@ -144,10 +160,10 @@ nv50_backlight_init(struct drm_connector *connector) } int -nouveau_backlight_init(struct drm_connector *connector) +nouveau_backlight_init(struct drm_device *dev) { - struct drm_device *dev = connector->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; + struct drm_connector *connector; #ifdef CONFIG_ACPI if (acpi_video_backlight_support()) { @@ -157,22 +173,28 @@ nouveau_backlight_init(struct drm_connector *connector) } #endif - switch (dev_priv->card_type) { - case NV_40: - return nv40_backlight_init(connector); - case NV_50: - return nv50_backlight_init(connector); - default: - break; + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS && + connector->connector_type != DRM_MODE_CONNECTOR_eDP) + continue; + + switch (dev_priv->card_type) { + case NV_40: + return nv40_backlight_init(connector); + case NV_50: + return nv50_backlight_init(connector); + default: + break; + } } + return 0; } void -nouveau_backlight_exit(struct drm_connector *connector) +nouveau_backlight_exit(struct drm_device *dev) { - struct drm_device *dev = connector->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; if (dev_priv->backlight) { diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 561d4c6677e..5308024ce56 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -39,7 +39,7 @@ static void nouveau_connector_hotplug(void *, int); -static struct nouveau_encoder * +struct nouveau_encoder * find_encoder(struct drm_connector *connector, int type) { struct drm_device *dev = connector->dev; @@ -116,10 +116,6 @@ nouveau_connector_destroy(struct drm_connector *connector) nouveau_connector_hotplug, connector); } - if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS || - connector->connector_type == DRM_MODE_CONNECTOR_eDP) - nouveau_backlight_exit(connector); - kfree(nv_connector->edid); drm_sysfs_connector_remove(connector); drm_connector_cleanup(connector); @@ -901,10 +897,6 @@ nouveau_connector_create(struct drm_device *dev, int index) drm_sysfs_connector_add(connector); - if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS || - connector->connector_type == DRM_MODE_CONNECTOR_eDP) - nouveau_backlight_init(connector); - dcb->drm = connector; return dcb->drm; diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index ee0f0d129d3..3e9e7cc09a7 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -1054,15 +1054,15 @@ static inline int nouveau_acpi_edid(struct drm_device *dev, struct drm_connector /* nouveau_backlight.c */ #ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT -extern int nouveau_backlight_init(struct drm_connector *); -extern void nouveau_backlight_exit(struct drm_connector *); +extern int nouveau_backlight_init(struct drm_device *); +extern void nouveau_backlight_exit(struct drm_device *); #else -static inline int nouveau_backlight_init(struct drm_connector *dev) +static inline int nouveau_backlight_init(struct drm_device *dev) { return 0; } -static inline void nouveau_backlight_exit(struct drm_connector *dev) { } +static inline void nouveau_backlight_exit(struct drm_device *dev) { } #endif /* nouveau_bios.c */ diff --git a/drivers/gpu/drm/nouveau/nouveau_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h index 2bb316d2421..70f0232558f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_encoder.h +++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h @@ -57,6 +57,9 @@ struct nouveau_encoder { }; }; +struct nouveau_encoder * +find_encoder(struct drm_connector *connector, int type); + static inline struct nouveau_encoder *nouveau_encoder(struct drm_encoder *enc) { struct drm_encoder_slave *slave = to_encoder_slave(enc); diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index baaecf10a58..50df52da3aa 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -728,6 +728,8 @@ nouveau_card_init(struct drm_device *dev) if (ret) goto out_irq; + nouveau_backlight_init(dev); + if (dev_priv->eng[NVOBJ_ENGINE_GR]) { ret = nouveau_fence_init(dev); if (ret) @@ -757,6 +759,7 @@ out_chan: out_fence: nouveau_fence_fini(dev); out_disp: + nouveau_backlight_exit(dev); engine->display.destroy(dev); out_irq: nouveau_irq_fini(dev); @@ -817,6 +820,7 @@ static void nouveau_card_takedown(struct drm_device *dev) nouveau_fence_fini(dev); } + nouveau_backlight_exit(dev); engine->display.destroy(dev); drm_mode_config_cleanup(dev); -- cgit v1.2.3-70-g09d2 From 09461459e12019375dbda88f81d1fe8926ce139c Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 2 Aug 2011 20:45:35 +1000 Subject: drm/nv50/backlight: express brightness level in percent Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_backlight.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_backlight.c b/drivers/gpu/drm/nouveau/nouveau_backlight.c index 4f3d1ff3e47..757a6f87edf 100644 --- a/drivers/gpu/drm/nouveau/nouveau_backlight.c +++ b/drivers/gpu/drm/nouveau/nouveau_backlight.c @@ -100,8 +100,11 @@ nv50_get_intensity(struct backlight_device *bd) struct nouveau_encoder *nv_encoder = bl_get_data(bd); struct drm_device *dev = nv_encoder->base.base.dev; int or = nv_encoder->or; + u32 div = 1025; + u32 val; - return nv_rd32(dev, NV50_PDISPLAY_SOR_BACKLIGHT + (or * 0x800)); + val = nv_rd32(dev, NV50_PDISPLAY_SOR_BACKLIGHT + (or * 0x800)); + return ((val * 100) + (div / 2)) / div; } static int @@ -109,8 +112,9 @@ nv50_set_intensity(struct backlight_device *bd) { struct nouveau_encoder *nv_encoder = bl_get_data(bd); struct drm_device *dev = nv_encoder->base.base.dev; - int val = bd->props.brightness; int or = nv_encoder->or; + u32 div = 1025; + u32 val = (bd->props.brightness * div) / 100; nv_wr32(dev, NV50_PDISPLAY_SOR_BACKLIGHT + (or * 0x800), val | NV50_PDISPLAY_SOR_BACKLIGHT_ENABLE); @@ -147,7 +151,7 @@ nv50_backlight_init(struct drm_connector *connector) memset(&props, 0, sizeof(struct backlight_properties)); props.type = BACKLIGHT_RAW; - props.max_brightness = 1025; + props.max_brightness = 100; bd = backlight_device_register("nv_backlight", &connector->kdev, nv_encoder, &nv50_bl_ops, &props); if (IS_ERR(bd)) -- cgit v1.2.3-70-g09d2 From 5024c54b5cc6e93a8e2713f53981423d0deb60d7 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Wed, 3 Aug 2011 08:52:39 +1000 Subject: drm/nva3/backlight: add suppport for newer style backlight regs Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_backlight.c | 67 +++++++++++++++++++++++++---- drivers/gpu/drm/nouveau/nouveau_reg.h | 9 ++-- 2 files changed, 64 insertions(+), 12 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_backlight.c b/drivers/gpu/drm/nouveau/nouveau_backlight.c index 757a6f87edf..fa22b28e877 100644 --- a/drivers/gpu/drm/nouveau/nouveau_backlight.c +++ b/drivers/gpu/drm/nouveau/nouveau_backlight.c @@ -103,7 +103,8 @@ nv50_get_intensity(struct backlight_device *bd) u32 div = 1025; u32 val; - val = nv_rd32(dev, NV50_PDISPLAY_SOR_BACKLIGHT + (or * 0x800)); + val = nv_rd32(dev, NV50_PDISP_SOR_PWM_CTL(or)); + val &= NV50_PDISP_SOR_PWM_CTL_VAL; return ((val * 100) + (div / 2)) / div; } @@ -116,8 +117,8 @@ nv50_set_intensity(struct backlight_device *bd) u32 div = 1025; u32 val = (bd->props.brightness * div) / 100; - nv_wr32(dev, NV50_PDISPLAY_SOR_BACKLIGHT + (or * 0x800), - val | NV50_PDISPLAY_SOR_BACKLIGHT_ENABLE); + nv_wr32(dev, NV50_PDISP_SOR_PWM_CTL(or), + NV50_PDISP_SOR_PWM_CTL_NEW | val); return 0; } @@ -127,6 +128,49 @@ static const struct backlight_ops nv50_bl_ops = { .update_status = nv50_set_intensity, }; +static int +nva3_get_intensity(struct backlight_device *bd) +{ + struct nouveau_encoder *nv_encoder = bl_get_data(bd); + struct drm_device *dev = nv_encoder->base.base.dev; + int or = nv_encoder->or; + u32 div, val; + + div = nv_rd32(dev, NV50_PDISP_SOR_PWM_DIV(or)); + val = nv_rd32(dev, NV50_PDISP_SOR_PWM_CTL(or)); + val &= NVA3_PDISP_SOR_PWM_CTL_VAL; + if (div && div >= val) + return ((val * 100) + (div / 2)) / div; + + return 100; +} + +static int +nva3_set_intensity(struct backlight_device *bd) +{ + struct nouveau_encoder *nv_encoder = bl_get_data(bd); + struct drm_device *dev = nv_encoder->base.base.dev; + int or = nv_encoder->or; + u32 div, val; + + div = nv_rd32(dev, NV50_PDISP_SOR_PWM_DIV(or)); + val = (bd->props.brightness * div) / 100; + if (div) { + nv_wr32(dev, NV50_PDISP_SOR_PWM_CTL(or), val | + NV50_PDISP_SOR_PWM_CTL_NEW | + NVA3_PDISP_SOR_PWM_CTL_UNK); + return 0; + } + + return -EINVAL; +} + +static const struct backlight_ops nva3_bl_ops = { + .options = BL_CORE_SUSPENDRESUME, + .get_brightness = nva3_get_intensity, + .update_status = nva3_set_intensity, +}; + static int nv50_backlight_init(struct drm_connector *connector) { @@ -135,7 +179,7 @@ nv50_backlight_init(struct drm_connector *connector) struct nouveau_encoder *nv_encoder; struct backlight_properties props; struct backlight_device *bd; - int or; + const struct backlight_ops *ops; nv_encoder = find_encoder(connector, OUTPUT_LVDS); if (!nv_encoder) { @@ -144,21 +188,26 @@ nv50_backlight_init(struct drm_connector *connector) return -ENODEV; } - or = nv_encoder->or; - - if (!nv_rd32(dev, NV50_PDISPLAY_SOR_BACKLIGHT + (or * 0x800))) + if (!nv_rd32(dev, NV50_PDISP_SOR_PWM_CTL(nv_encoder->or))) return 0; + if (dev_priv->chipset <= 0xa0 || + dev_priv->chipset == 0xaa || + dev_priv->chipset == 0xac) + ops = &nv50_bl_ops; + else + ops = &nva3_bl_ops; + memset(&props, 0, sizeof(struct backlight_properties)); props.type = BACKLIGHT_RAW; props.max_brightness = 100; bd = backlight_device_register("nv_backlight", &connector->kdev, - nv_encoder, &nv50_bl_ops, &props); + nv_encoder, ops, &props); if (IS_ERR(bd)) return PTR_ERR(bd); dev_priv->backlight = bd; - bd->props.brightness = nv50_get_intensity(bd); + bd->props.brightness = bd->ops->get_brightness(bd); backlight_update_status(bd); return 0; } diff --git a/drivers/gpu/drm/nouveau/nouveau_reg.h b/drivers/gpu/drm/nouveau/nouveau_reg.h index d9632ae38c6..43a96b99e18 100644 --- a/drivers/gpu/drm/nouveau/nouveau_reg.h +++ b/drivers/gpu/drm/nouveau/nouveau_reg.h @@ -826,9 +826,12 @@ #define NV50_PDISPLAY_SOR_DPMS_STATE_ACTIVE 0x00030000 #define NV50_PDISPLAY_SOR_DPMS_STATE_BLANKED 0x00080000 #define NV50_PDISPLAY_SOR_DPMS_STATE_WAIT 0x10000000 -#define NV50_PDISPLAY_SOR_BACKLIGHT 0x0061c084 -#define NV50_PDISPLAY_SOR_BACKLIGHT_ENABLE 0x80000000 -#define NV50_PDISPLAY_SOR_BACKLIGHT_LEVEL 0x00000fff +#define NV50_PDISP_SOR_PWM_DIV(i) (0x0061c080 + (i) * 0x800) +#define NV50_PDISP_SOR_PWM_CTL(i) (0x0061c084 + (i) * 0x800) +#define NV50_PDISP_SOR_PWM_CTL_NEW 0x80000000 +#define NVA3_PDISP_SOR_PWM_CTL_UNK 0x40000000 +#define NV50_PDISP_SOR_PWM_CTL_VAL 0x000007ff +#define NVA3_PDISP_SOR_PWM_CTL_VAL 0x00ffffff #define NV50_SOR_DP_CTRL(i, l) (0x0061c10c + (i) * 0x800 + (l) * 0x80) #define NV50_SOR_DP_CTRL_ENABLED 0x00000001 #define NV50_SOR_DP_CTRL_ENHANCED_FRAME_ENABLED 0x00004000 -- cgit v1.2.3-70-g09d2 From a8e415d3eb1bed184d73e22c62cd1f992bd33d63 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 4 Aug 2011 13:57:33 +1000 Subject: drm/nouveau/bios: check for null script pointers in parser Allows us to be lazy elsewhere... Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_bios.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index 58d8c85b85d..528fb608781 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -3771,6 +3771,10 @@ parse_init_table(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) int count = 0, i, ret; uint8_t id; + /* catch NULL script pointers */ + if (offset == 0) + return 0; + /* * Loop until INIT_DONE causes us to break out of the loop * (or until offset > bios length just in case... ) -- cgit v1.2.3-70-g09d2 From a002feceb7852964af579ece784b0123869f1e3f Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 4 Aug 2011 11:04:47 +1000 Subject: drm/nouveau/dp: pass in required datarate to link training Not used currently, but it will be used in preference to pre-determined lane/bandwidth numbers at a later point. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_dp.c | 2 +- drivers/gpu/drm/nouveau/nouveau_drv.h | 2 +- drivers/gpu/drm/nouveau/nouveau_encoder.h | 1 + drivers/gpu/drm/nouveau/nv50_sor.c | 13 ++++++++----- 4 files changed, 11 insertions(+), 7 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index ae1b6e00bd9..6a756a00d00 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -507,7 +507,7 @@ nouveau_dp_link_train_commit(struct drm_encoder *encoder, uint8_t *config) } bool -nouveau_dp_link_train(struct drm_encoder *encoder) +nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate) { struct drm_device *dev = encoder->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 3e9e7cc09a7..8cca5ff3add 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -1100,7 +1100,7 @@ int nouveau_ttm_mmap(struct file *, struct vm_area_struct *); 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 *); +bool nouveau_dp_link_train(struct drm_encoder *, u32 datarate); void nouveau_dp_tu_update(struct drm_device *, int, int, u32, u32); /* nv04_fb.c */ diff --git a/drivers/gpu/drm/nouveau/nouveau_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h index 70f0232558f..fc5ee0d6857 100644 --- a/drivers/gpu/drm/nouveau/nouveau_encoder.h +++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h @@ -53,6 +53,7 @@ struct nouveau_encoder { int link_nr; int link_bw; bool enhanced_frame; + u32 datarate; } dp; }; }; diff --git a/drivers/gpu/drm/nouveau/nv50_sor.c b/drivers/gpu/drm/nouveau/nv50_sor.c index f359f94626c..2633aa8554e 100644 --- a/drivers/gpu/drm/nouveau/nv50_sor.c +++ b/drivers/gpu/drm/nouveau/nv50_sor.c @@ -124,7 +124,7 @@ nv50_sor_dpms(struct drm_encoder *encoder, int mode) if (mode == DRM_MODE_DPMS_ON) { u8 status = DP_SET_POWER_D0; nouveau_dp_auxch(auxch, 8, DP_SET_POWER, &status, 1); - nouveau_dp_link_train(encoder); + nouveau_dp_link_train(encoder, nv_encoder->dp.datarate); } else { u8 status = DP_SET_POWER_D3; nouveau_dp_auxch(auxch, 8, DP_SET_POWER, &status, 1); @@ -194,8 +194,6 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, NV_DEBUG_KMS(dev, "or %d type %d -> crtc %d\n", nv_encoder->or, nv_encoder->dcb->type, crtc->index); - nv50_sor_dpms(encoder, DRM_MODE_DPMS_ON); - switch (nv_encoder->dcb->type) { case OUTPUT_TMDS: if (nv_encoder->dcb->sorconf.link & 1) { @@ -208,10 +206,13 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, break; case OUTPUT_DP: nv_connector = nouveau_encoder_connector_get(nv_encoder); - if (nv_connector && nv_connector->base.display_info.bpc == 6) + if (nv_connector && nv_connector->base.display_info.bpc == 6) { + nv_encoder->dp.datarate = crtc->mode->clock * 18 / 8; mode_ctl |= 0x00020000; - else + } else { + nv_encoder->dp.datarate = crtc->mode->clock * 24 / 8; mode_ctl |= 0x00050000; + } if (nv_encoder->dcb->sorconf.link & 1) mode_ctl |= 0x00000800; @@ -233,6 +234,8 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) mode_ctl |= NV50_EVO_SOR_MODE_CTRL_NVSYNC; + nv50_sor_dpms(encoder, DRM_MODE_DPMS_ON); + ret = RING_SPACE(evo, 2); if (ret) { NV_ERROR(dev, "no space while connecting SOR\n"); -- cgit v1.2.3-70-g09d2 From 27a4598737f8b315ba7827cb84578ba38c9b883c Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 4 Aug 2011 09:26:44 +1000 Subject: drm/nouveau/dp: restructure link training code Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_bios.c | 13 +- drivers/gpu/drm/nouveau/nouveau_dp.c | 526 +++++++++++------------------- drivers/gpu/drm/nouveau/nouveau_drv.h | 2 +- drivers/gpu/drm/nouveau/nouveau_encoder.h | 17 - 4 files changed, 206 insertions(+), 352 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index 528fb608781..b6efa8c7cdb 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -1179,19 +1179,18 @@ init_dp_condition(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) * */ - struct bit_displayport_encoder_table *dpe = NULL; struct dcb_entry *dcb = bios->display.output; struct drm_device *dev = bios->dev; uint8_t cond = bios->data[offset + 1]; - int dummy; + uint8_t *table, headerlen; BIOSLOG(bios, "0x%04X: subop 0x%02X\n", offset, cond); if (!iexec->execute) return 3; - dpe = nouveau_bios_dp_table(dev, dcb, &dummy); - if (!dpe) { + table = nouveau_bios_dp_table(dev, dcb, &headerlen); + if (!table) { NV_ERROR(dev, "0x%04X: INIT_3A: no encoder table!!\n", offset); return 3; } @@ -1208,7 +1207,7 @@ init_dp_condition(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) break; case 1: case 2: - if (!(dpe->unknown & cond)) + if (!(table[5] & cond)) iexec->execute = false; break; case 5: @@ -4480,7 +4479,7 @@ bios_output_config_match(struct drm_device *dev, struct dcb_entry *dcbent, void * nouveau_bios_dp_table(struct drm_device *dev, struct dcb_entry *dcbent, - int *length) + uint8_t *headerlen) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nvbios *bios = &dev_priv->vbios; @@ -4498,7 +4497,7 @@ nouveau_bios_dp_table(struct drm_device *dev, struct dcb_entry *dcbent, return NULL; } - *length = table[4]; + *headerlen = table[4]; return bios_output_config_match(dev, dcbent, bios->display.dp_table_ptr + table[1], table[2], table[3], table[0] >= 0x21); diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 6a756a00d00..726d0ac63b9 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -28,6 +28,7 @@ #include "nouveau_i2c.h" #include "nouveau_connector.h" #include "nouveau_encoder.h" +#include "nouveau_crtc.h" /****************************************************************************** * aux channel util functions @@ -178,22 +179,6 @@ auxch_rd(struct drm_encoder *encoder, int address, uint8_t *buf, int size) return 0; } -static int -auxch_wr(struct drm_encoder *encoder, int address, uint8_t *buf, int size) -{ - struct drm_device *dev = encoder->dev; - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct nouveau_i2c_chan *auxch; - int ret; - - auxch = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index); - if (!auxch) - return -ENODEV; - - ret = nouveau_dp_auxch(auxch, 8, address, buf, size); - return ret; -} - static u32 dp_link_bw_get(struct drm_device *dev, int or, int link) { @@ -304,382 +289,269 @@ nouveau_dp_tu_update(struct drm_device *dev, int or, int link, u32 clk, u32 bpp) unk); } -static int -nouveau_dp_lane_count_set(struct drm_encoder *encoder, uint8_t cmd) -{ - struct drm_device *dev = encoder->dev; - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - uint32_t tmp; - int or = nv_encoder->or, link = !(nv_encoder->dcb->sorconf.link & 1); - - tmp = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link)); - tmp &= ~(NV50_SOR_DP_CTRL_ENHANCED_FRAME_ENABLED | - NV50_SOR_DP_CTRL_LANE_MASK); - tmp |= ((1 << (cmd & DP_LANE_COUNT_MASK)) - 1) << 16; - if (cmd & DP_LANE_COUNT_ENHANCED_FRAME_EN) - tmp |= NV50_SOR_DP_CTRL_ENHANCED_FRAME_ENABLED; - nv_wr32(dev, NV50_SOR_DP_CTRL(or, link), tmp); - - return auxch_wr(encoder, DP_LANE_COUNT_SET, &cmd, 1); -} +/****************************************************************************** + * link training + *****************************************************************************/ +struct dp_state { + struct dcb_entry *dcb; + int auxch; + int crtc; + int or; + int link; + int enh_frame; + int link_nr; + u32 link_bw; + u8 stat[6]; + u8 conf[4]; +}; -static int -nouveau_dp_link_bw_set(struct drm_encoder *encoder, uint8_t cmd) +static void +dp_set_link_config(struct drm_device *dev, struct dp_state *dp) { - struct drm_device *dev = encoder->dev; - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - uint32_t tmp; - int reg = 0x614300 + (nv_encoder->or * 0x800); + int or = dp->or, link = dp->link; + u32 clk_sor, dp_ctrl; + u8 sink[2]; - tmp = nv_rd32(dev, reg); - tmp &= 0xfff3ffff; - if (cmd == DP_LINK_BW_2_7) - tmp |= 0x00040000; - nv_wr32(dev, reg, tmp); + NV_DEBUG_KMS(dev, "%d lanes at %d KB/s\n", dp->link_nr, dp->link_bw); - return auxch_wr(encoder, DP_LINK_BW_SET, &cmd, 1); -} + switch (dp->link_bw) { + case 270000: + clk_sor = 0x00040000; + sink[0] = DP_LINK_BW_2_7; + break; + default: + clk_sor = 0x00000000; + sink[0] = DP_LINK_BW_1_62; + break; + } -static int -nouveau_dp_link_train_set(struct drm_encoder *encoder, int pattern) -{ - struct drm_device *dev = encoder->dev; - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - uint32_t tmp; - uint8_t cmd; - int or = nv_encoder->or, link = !(nv_encoder->dcb->sorconf.link & 1); - int ret; + dp_ctrl = ((1 << dp->link_nr) - 1) << 16; + sink[1] = dp->link_nr; + if (dp->enh_frame) { + dp_ctrl |= 0x00004000; + sink[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; + } - tmp = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link)); - tmp &= ~NV50_SOR_DP_CTRL_TRAINING_PATTERN; - tmp |= (pattern << 24); - nv_wr32(dev, NV50_SOR_DP_CTRL(or, link), tmp); + nv_mask(dev, 0x614300 + (or * 0x800), 0x000c0000, clk_sor); + nv_mask(dev, NV50_SOR_DP_CTRL(or, link), 0x001f4000, dp_ctrl); - ret = auxch_rd(encoder, DP_TRAINING_PATTERN_SET, &cmd, 1); - if (ret) - return ret; - cmd &= ~DP_TRAINING_PATTERN_MASK; - cmd |= (pattern & DP_TRAINING_PATTERN_MASK); - return auxch_wr(encoder, DP_TRAINING_PATTERN_SET, &cmd, 1); + auxch_tx(dev, dp->auxch, 8, DP_LINK_BW_SET, sink, 2); } -static int -nouveau_dp_max_voltage_swing(struct drm_encoder *encoder) +static void +dp_set_training_pattern(struct drm_device *dev, struct dp_state *dp, u8 tp) { - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct drm_device *dev = encoder->dev; - struct bit_displayport_encoder_table_entry *dpse; - struct bit_displayport_encoder_table *dpe; - int i, dpe_headerlen, max_vs = 0; - - dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen); - if (!dpe) - return false; - dpse = (void *)((char *)dpe + dpe_headerlen); - - for (i = 0; i < dpe_headerlen; i++, dpse++) { - if (dpse->vs_level > max_vs) - max_vs = dpse->vs_level; - } - - return max_vs; + NV_DEBUG_KMS(dev, "training pattern %d\n", tp); + nv_mask(dev, NV50_SOR_DP_CTRL(dp->or, dp->link), 0x0f000000, tp << 24); + auxch_tx(dev, dp->auxch, 8, DP_TRAINING_PATTERN_SET, &tp, 1); } static int -nouveau_dp_max_pre_emphasis(struct drm_encoder *encoder, int vs) +dp_link_train_commit(struct drm_device *dev, struct dp_state *dp) { - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct drm_device *dev = encoder->dev; - struct bit_displayport_encoder_table_entry *dpse; - struct bit_displayport_encoder_table *dpe; - int i, dpe_headerlen, max_pre = 0; + u32 mask = 0, drv = 0, pre = 0, unk = 0; + u8 shifts[4] = { 16, 8, 0, 24 }; + u8 *bios, *last, headerlen; + int link = dp->link; + int or = dp->or; + int i; + + bios = nouveau_bios_dp_table(dev, dp->dcb, &headerlen); + last = bios + headerlen + (bios[4] * 5); + for (i = 0; i < dp->link_nr; i++) { + u8 lane = (dp->stat[4 + (i >> 1)] >> ((i & 1) * 4)) & 0xf; + u8 *conf = bios + headerlen; + + while (conf < last) { + if ((lane & 3) == conf[0] && + (lane >> 2) == conf[1]) + break; + conf += 5; + } - dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen); - if (!dpe) - return false; - dpse = (void *)((char *)dpe + dpe_headerlen); + if (conf == last) + return -EINVAL; - for (i = 0; i < dpe_headerlen; i++, dpse++) { - if (dpse->vs_level != vs) - continue; + dp->conf[i] = (conf[1] << 3) | conf[0]; + if (conf[0] == DP_TRAIN_VOLTAGE_SWING_1200) + dp->conf[i] |= DP_TRAIN_MAX_SWING_REACHED; + if (conf[1] == DP_TRAIN_PRE_EMPHASIS_9_5) + dp->conf[i] |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED; - if (dpse->pre_level > max_pre) - max_pre = dpse->pre_level; + NV_DEBUG_KMS(dev, "config lane %d %02x\n", i, dp->conf[i]); + + mask |= 0xff << shifts[i]; + drv |= conf[2] << shifts[i]; + pre |= conf[3] << shifts[i]; + unk = (unk & ~0x0000ff00) | (conf[4] << 8); + unk |= 1 << (shifts[i] >> 3); } - return max_pre; + 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); } -static bool -nouveau_dp_link_train_adjust(struct drm_encoder *encoder, uint8_t *config) +static int +dp_link_train_update(struct drm_device *dev, struct dp_state *dp, u32 delay) { - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct drm_device *dev = encoder->dev; - struct bit_displayport_encoder_table *dpe; - int ret, i, dpe_headerlen, vs = 0, pre = 0; - uint8_t request[2]; + int ret; - dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen); - if (!dpe) - return false; + udelay(delay); - ret = auxch_rd(encoder, DP_ADJUST_REQUEST_LANE0_1, request, 2); + ret = auxch_tx(dev, dp->auxch, 9, DP_LANE0_1_STATUS, dp->stat, 6); if (ret) - return false; + return ret; - NV_DEBUG_KMS(dev, "\t\tadjust 0x%02x 0x%02x\n", request[0], request[1]); + NV_DEBUG_KMS(dev, "status %02x %02x %02x %02x %02x %02x\n", + dp->stat[0], dp->stat[1], dp->stat[2], dp->stat[3], + dp->stat[4], dp->stat[5]); + return 0; +} - /* Keep all lanes at the same level.. */ - for (i = 0; i < nv_encoder->dp.link_nr; i++) { - int lane_req = (request[i >> 1] >> ((i & 1) << 2)) & 0xf; - int lane_vs = lane_req & 3; - int lane_pre = (lane_req >> 2) & 3; +static int +dp_link_train_cr(struct drm_device *dev, struct dp_state *dp) +{ + bool cr_done = false, abort = false; + int voltage = dp->conf[0] & DP_TRAIN_VOLTAGE_SWING_MASK; + int tries = 0, i; - if (lane_vs > vs) - vs = lane_vs; - if (lane_pre > pre) - pre = lane_pre; - } + dp_set_training_pattern(dev, dp, DP_TRAINING_PATTERN_1); - if (vs >= nouveau_dp_max_voltage_swing(encoder)) { - vs = nouveau_dp_max_voltage_swing(encoder); - vs |= 4; - } + do { + if (dp_link_train_commit(dev, dp) || + dp_link_train_update(dev, dp, 100)) + break; - if (pre >= nouveau_dp_max_pre_emphasis(encoder, vs & 3)) { - pre = nouveau_dp_max_pre_emphasis(encoder, vs & 3); - pre |= 4; - } + cr_done = true; + for (i = 0; i < dp->link_nr; i++) { + u8 lane = (dp->stat[i >> 1] >> ((i & 1) * 4)) & 0xf; + if (!(lane & DP_LANE_CR_DONE)) { + cr_done = false; + if (dp->conf[i] & DP_TRAIN_MAX_SWING_REACHED) + abort = true; + break; + } + } - /* Update the configuration for all lanes.. */ - for (i = 0; i < nv_encoder->dp.link_nr; i++) - config[i] = (pre << 3) | vs; + if ((dp->conf[0] & DP_TRAIN_VOLTAGE_SWING_MASK) != voltage) { + voltage = dp->conf[0] & DP_TRAIN_VOLTAGE_SWING_MASK; + tries = 0; + } + } while (!cr_done && !abort && ++tries < 5); - return true; + return cr_done ? 0 : -1; } -static bool -nouveau_dp_link_train_commit(struct drm_encoder *encoder, uint8_t *config) +static int +dp_link_train_eq(struct drm_device *dev, struct dp_state *dp) { - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct drm_device *dev = encoder->dev; - struct bit_displayport_encoder_table_entry *dpse; - struct bit_displayport_encoder_table *dpe; - int or = nv_encoder->or, link = !(nv_encoder->dcb->sorconf.link & 1); - int dpe_headerlen, ret, i; + bool eq_done, cr_done = true; + int tries = 0, i; - NV_DEBUG_KMS(dev, "\t\tconfig 0x%02x 0x%02x 0x%02x 0x%02x\n", - config[0], config[1], config[2], config[3]); + dp_set_training_pattern(dev, dp, DP_TRAINING_PATTERN_2); - dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen); - if (!dpe) - return false; - dpse = (void *)((char *)dpe + dpe_headerlen); - - for (i = 0; i < dpe->record_nr; i++, dpse++) { - if (dpse->vs_level == (config[0] & 3) && - dpse->pre_level == ((config[0] >> 3) & 3)) + do { + if (dp_link_train_update(dev, dp, 400)) break; - } - BUG_ON(i == dpe->record_nr); - - for (i = 0; i < nv_encoder->dp.link_nr; i++) { - const int shift[4] = { 16, 8, 0, 24 }; - uint32_t mask = 0xff << shift[i]; - uint32_t reg0, reg1, reg2; - - reg0 = nv_rd32(dev, NV50_SOR_DP_UNK118(or, link)) & ~mask; - reg0 |= (dpse->reg0 << shift[i]); - reg1 = nv_rd32(dev, NV50_SOR_DP_UNK120(or, link)) & ~mask; - reg1 |= (dpse->reg1 << shift[i]); - reg2 = nv_rd32(dev, NV50_SOR_DP_UNK130(or, link)) & 0xffff00ff; - reg2 |= (dpse->reg2 << 8); - nv_wr32(dev, NV50_SOR_DP_UNK118(or, link), reg0); - nv_wr32(dev, NV50_SOR_DP_UNK120(or, link), reg1); - nv_wr32(dev, NV50_SOR_DP_UNK130(or, link), reg2); - } - ret = auxch_wr(encoder, DP_TRAINING_LANE0_SET, config, 4); - if (ret) - return false; + eq_done = !!(dp->stat[2] & DP_INTERLANE_ALIGN_DONE); + for (i = 0; i < dp->link_nr && eq_done; i++) { + u8 lane = (dp->stat[i >> 1] >> ((i & 1) * 4)) & 0xf; + if (!(lane & DP_LANE_CR_DONE)) + cr_done = false; + if (!(lane & DP_LANE_CHANNEL_EQ_DONE) || + !(lane & DP_LANE_SYMBOL_LOCKED)) + eq_done = false; + } - return true; + if (dp_link_train_commit(dev, dp)) + break; + } while (!eq_done && cr_done && ++tries <= 5); + + return eq_done ? 0 : -1; } bool nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate) { - struct drm_device *dev = encoder->dev; - struct drm_nouveau_private *dev_priv = dev->dev_private; + struct drm_nouveau_private *dev_priv = encoder->dev->dev_private; struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct nouveau_connector *nv_connector; - struct bit_displayport_encoder_table *dpe; - int dpe_headerlen; - uint8_t config[4], status[3]; - bool cr_done, cr_max_vs, eq_done, hpd_state; - int ret = 0, i, tries, voltage; - - NV_DEBUG_KMS(dev, "link training!!\n"); - - nv_connector = nouveau_encoder_connector_get(nv_encoder); - if (!nv_connector) - return false; - - dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen); - if (!dpe) { - NV_ERROR(dev, "SOR-%d: no DP encoder table!\n", nv_encoder->or); - return false; - } - - /* disable hotplug detect, this flips around on some panels during - * link training. - */ - hpd_state = pgpio->irq_enable(dev, nv_connector->dcb->gpio_tag, false); - - if (dpe->script0) { - NV_DEBUG_KMS(dev, "SOR-%d: running DP script 0\n", nv_encoder->or); - nouveau_bios_run_init_table(dev, le16_to_cpu(dpe->script0), - nv_encoder->dcb, -1); - } - -train: - cr_done = eq_done = false; - - /* set link configuration */ - NV_DEBUG_KMS(dev, "\tbegin train: bw %d, lanes %d\n", - nv_encoder->dp.link_bw, nv_encoder->dp.link_nr); - - ret = nouveau_dp_link_bw_set(encoder, nv_encoder->dp.link_bw); - if (ret) - return false; - - config[0] = nv_encoder->dp.link_nr; - if (nv_encoder->dp.dpcd_version >= 0x11 && - nv_encoder->dp.enhanced_frame) - config[0] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; + struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); + struct nouveau_connector *nv_connector = + nouveau_encoder_connector_get(nv_encoder); + struct drm_device *dev = encoder->dev; + struct nouveau_i2c_chan *auxch; + const u32 bw_list[] = { 270000, 162000, 0 }; + const u32 *link_bw = bw_list; + struct dp_state dp; + u8 *bios, headerlen; + u16 script; - ret = nouveau_dp_lane_count_set(encoder, config[0]); - if (ret) + auxch = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index); + if (!auxch) return false; - /* clock recovery */ - NV_DEBUG_KMS(dev, "\tbegin cr\n"); - ret = nouveau_dp_link_train_set(encoder, DP_TRAINING_PATTERN_1); - if (ret) - goto stop; - - tries = 0; - voltage = -1; - memset(config, 0x00, sizeof(config)); - for (;;) { - if (!nouveau_dp_link_train_commit(encoder, config)) - break; - - udelay(100); + bios = nouveau_bios_dp_table(dev, nv_encoder->dcb, &headerlen); + if (!bios) + return -EINVAL; - ret = auxch_rd(encoder, DP_LANE0_1_STATUS, status, 2); - if (ret) - break; - NV_DEBUG_KMS(dev, "\t\tstatus: 0x%02x 0x%02x\n", - status[0], status[1]); - - cr_done = true; - cr_max_vs = false; - for (i = 0; i < nv_encoder->dp.link_nr; i++) { - int lane = (status[i >> 1] >> ((i & 1) * 4)) & 0xf; - - if (!(lane & DP_LANE_CR_DONE)) { - cr_done = false; - if (config[i] & DP_TRAIN_MAX_PRE_EMPHASIS_REACHED) - cr_max_vs = true; - break; - } - } - - if ((config[0] & DP_TRAIN_VOLTAGE_SWING_MASK) != voltage) { - voltage = config[0] & DP_TRAIN_VOLTAGE_SWING_MASK; - tries = 0; - } - - if (cr_done || cr_max_vs || (++tries == 5)) - break; - - if (!nouveau_dp_link_train_adjust(encoder, config)) - break; - } - - if (!cr_done) - goto stop; - - /* channel equalisation */ - NV_DEBUG_KMS(dev, "\tbegin eq\n"); - ret = nouveau_dp_link_train_set(encoder, DP_TRAINING_PATTERN_2); - if (ret) - goto stop; + dp.dcb = nv_encoder->dcb; + dp.crtc = nv_crtc->index; + dp.auxch = auxch->rd; + dp.or = nv_encoder->or; + dp.link = !(nv_encoder->dcb->sorconf.link & 1); + dp.enh_frame = nv_encoder->dp.enhanced_frame; - for (tries = 0; tries <= 5; tries++) { - udelay(400); + /* 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. + */ + pgpio->irq_enable(dev, nv_connector->dcb->gpio_tag, false); - ret = auxch_rd(encoder, DP_LANE0_1_STATUS, status, 3); - if (ret) - break; - NV_DEBUG_KMS(dev, "\t\tstatus: 0x%02x 0x%02x\n", - status[0], status[1]); + /* execute pre-train script from vbios */ + nouveau_bios_run_init_table(dev, ROM16(bios[6]), dp.dcb, dp.crtc); - eq_done = true; - if (!(status[2] & DP_INTERLANE_ALIGN_DONE)) - eq_done = false; + /* start off at highest link rate supported by encoder and display */ + if (nv_encoder->dp.link_bw == DP_LINK_BW_1_62) + link_bw++; - for (i = 0; eq_done && i < nv_encoder->dp.link_nr; i++) { - int lane = (status[i >> 1] >> ((i & 1) * 4)) & 0xf; + while (link_bw[0]) { + /* find minimum required lane count at this link rate */ + dp.link_nr = nv_encoder->dp.link_nr; + while ((dp.link_nr >> 1) * link_bw[0] > datarate) + dp.link_nr >>= 1; - if (!(lane & DP_LANE_CR_DONE)) { - cr_done = false; - break; - } + /* drop link rate to minimum with this lane count */ + while ((link_bw[1] * dp.link_nr) > datarate) + link_bw++; + dp.link_bw = link_bw[0]; - if (!(lane & DP_LANE_CHANNEL_EQ_DONE) || - !(lane & DP_LANE_SYMBOL_LOCKED)) { - eq_done = false; - break; - } - } + /* program selected link configuration */ + dp_set_link_config(dev, &dp); - if (eq_done || !cr_done) + /* attempt to train the link at this configuration */ + memset(dp.stat, 0x00, sizeof(dp.stat)); + if (!dp_link_train_cr(dev, &dp) && + !dp_link_train_eq(dev, &dp)) break; - if (!nouveau_dp_link_train_adjust(encoder, config) || - !nouveau_dp_link_train_commit(encoder, config)) - break; + /* retry at lower rate */ + link_bw++; } -stop: - /* end link training */ - ret = nouveau_dp_link_train_set(encoder, DP_TRAINING_PATTERN_DISABLE); - if (ret) - return false; - - /* retry at a lower setting, if possible */ - if (!ret && !(eq_done && cr_done)) { - NV_DEBUG_KMS(dev, "\twe failed\n"); - if (nv_encoder->dp.link_bw != DP_LINK_BW_1_62) { - NV_DEBUG_KMS(dev, "retry link training at low rate\n"); - nv_encoder->dp.link_bw = DP_LINK_BW_1_62; - goto train; - } - } + /* finish link training */ + dp_set_training_pattern(dev, &dp, DP_TRAINING_PATTERN_DISABLE); - if (dpe->script1) { - NV_DEBUG_KMS(dev, "SOR-%d: running DP script 1\n", nv_encoder->or); - nouveau_bios_run_init_table(dev, le16_to_cpu(dpe->script1), - nv_encoder->dcb, -1); - } + /* execute post-train script from vbios */ + nouveau_bios_run_init_table(dev, ROM16(bios[8]), dp.dcb, dp.crtc); /* re-enable hotplug detect */ - pgpio->irq_enable(dev, nv_connector->dcb->gpio_tag, hpd_state); - - return eq_done; + pgpio->irq_enable(dev, nv_connector->dcb->gpio_tag, true); + return true; } bool diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 8cca5ff3add..3cf8e6a10e9 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -1081,7 +1081,7 @@ extern int get_pll_limits(struct drm_device *, uint32_t limit_match, extern int nouveau_bios_run_display_table(struct drm_device *, u16 id, int clk, struct dcb_entry *, int crtc); extern void *nouveau_bios_dp_table(struct drm_device *, struct dcb_entry *, - int *length); + u8 *headerlen); extern bool nouveau_bios_fp_mode(struct drm_device *, struct drm_display_mode *); extern uint8_t *nouveau_bios_embedded_edid(struct drm_device *); extern int nouveau_bios_parse_lvds_table(struct drm_device *, int pxclk, diff --git a/drivers/gpu/drm/nouveau/nouveau_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h index fc5ee0d6857..deafe7b1f52 100644 --- a/drivers/gpu/drm/nouveau/nouveau_encoder.h +++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h @@ -84,21 +84,4 @@ nouveau_encoder_connector_get(struct nouveau_encoder *encoder); int nv50_sor_create(struct drm_connector *, struct dcb_entry *); int nv50_dac_create(struct drm_connector *, struct dcb_entry *); -struct bit_displayport_encoder_table { - uint32_t match; - uint8_t record_nr; - uint8_t unknown; - uint16_t script0; - uint16_t script1; - uint16_t unknown_table; -} __attribute__ ((packed)); - -struct bit_displayport_encoder_table_entry { - uint8_t vs_level; - uint8_t pre_level; - uint8_t reg0; - uint8_t reg1; - uint8_t reg2; -} __attribute__ ((packed)); - #endif /* __NOUVEAU_ENCODER_H__ */ -- cgit v1.2.3-70-g09d2 From 75a1fccf92ac6703e7cd4d2a2d1d25b87102402b Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 4 Aug 2011 09:55:44 +1000 Subject: drm/nouveau/dp: store unencoded link_bw everywhere Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_bios.c | 9 ++++++++- drivers/gpu/drm/nouveau/nouveau_connector.c | 7 ++----- drivers/gpu/drm/nouveau/nouveau_dp.c | 28 ++++++++++++++-------------- 3 files changed, 24 insertions(+), 20 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index b6efa8c7cdb..bea5df7cf93 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -6199,7 +6199,14 @@ parse_dcb20_entry(struct drm_device *dev, struct dcb_table *dcb, } case OUTPUT_DP: entry->dpconf.sor.link = (conf & 0x00000030) >> 4; - entry->dpconf.link_bw = (conf & 0x00e00000) >> 21; + switch ((conf & 0x00e00000) >> 21) { + case 0: + entry->dpconf.link_bw = 162000; + break; + default: + entry->dpconf.link_bw = 270000; + break; + } switch ((conf & 0x0f000000) >> 24) { case 0xf: entry->dpconf.link_nr = 4; diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 5308024ce56..e0d275e1c96 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -708,11 +708,8 @@ nouveau_connector_mode_valid(struct drm_connector *connector, case OUTPUT_TV: return get_slave_funcs(encoder)->mode_valid(encoder, mode); case OUTPUT_DP: - if (nv_encoder->dp.link_bw == DP_LINK_BW_2_7) - max_clock = nv_encoder->dp.link_nr * 270000; - else - max_clock = nv_encoder->dp.link_nr * 162000; - + max_clock = nv_encoder->dp.link_nr; + max_clock *= nv_encoder->dp.link_bw; clock = clock * nouveau_connector_bpp(connector) / 8; break; default: diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 726d0ac63b9..47bd3ada6a3 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -516,7 +516,7 @@ nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate) nouveau_bios_run_init_table(dev, ROM16(bios[6]), dp.dcb, dp.crtc); /* start off at highest link rate supported by encoder and display */ - if (nv_encoder->dp.link_bw == DP_LINK_BW_1_62) + while (*link_bw > nv_encoder->dp.link_bw) link_bw++; while (link_bw[0]) { @@ -566,24 +566,24 @@ nouveau_dp_detect(struct drm_encoder *encoder) if (ret) return false; - NV_DEBUG_KMS(dev, "encoder: link_bw %d, link_nr %d\n" - "display: link_bw %d, link_nr %d version 0x%02x\n", - nv_encoder->dcb->dpconf.link_bw, - nv_encoder->dcb->dpconf.link_nr, - dpcd[1], dpcd[2] & 0x0f, dpcd[0]); - nv_encoder->dp.dpcd_version = dpcd[0]; + nv_encoder->dp.link_bw = 27000 * dpcd[1]; + nv_encoder->dp.link_nr = dpcd[2] & DP_MAX_LANE_COUNT_MASK; + nv_encoder->dp.enhanced_frame = dpcd[2] & DP_ENHANCED_FRAME_CAP; - nv_encoder->dp.link_bw = dpcd[1]; - if (nv_encoder->dp.link_bw != DP_LINK_BW_1_62 && - !nv_encoder->dcb->dpconf.link_bw) - nv_encoder->dp.link_bw = DP_LINK_BW_1_62; + NV_DEBUG_KMS(dev, "display: %dx%d dpcd 0x%02x\n", + nv_encoder->dp.link_nr, nv_encoder->dp.link_bw, dpcd[0]); + NV_DEBUG_KMS(dev, "encoder: %dx%d\n", + nv_encoder->dcb->dpconf.link_nr, + nv_encoder->dcb->dpconf.link_bw); - nv_encoder->dp.link_nr = dpcd[2] & DP_MAX_LANE_COUNT_MASK; - if (nv_encoder->dp.link_nr > nv_encoder->dcb->dpconf.link_nr) + if (nv_encoder->dcb->dpconf.link_nr < nv_encoder->dp.link_nr) nv_encoder->dp.link_nr = nv_encoder->dcb->dpconf.link_nr; + if (nv_encoder->dcb->dpconf.link_bw < nv_encoder->dp.link_bw) + nv_encoder->dp.link_bw = nv_encoder->dcb->dpconf.link_bw; - nv_encoder->dp.enhanced_frame = (dpcd[2] & DP_ENHANCED_FRAME_CAP); + NV_DEBUG_KMS(dev, "maximum: %dx%d\n", + nv_encoder->dp.link_nr, nv_encoder->dp.link_bw); return true; } -- cgit v1.2.3-70-g09d2 From 28e2d12405f1afb34435a23d6bba223ff378de91 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 4 Aug 2011 14:16:45 +1000 Subject: drm/nouveau/dp: execute some more vbios tables relating to link rate Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_dp.c | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 47bd3ada6a3..3777616cc15 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -308,23 +308,39 @@ struct dp_state { static void dp_set_link_config(struct drm_device *dev, struct dp_state *dp) { + struct drm_nouveau_private *dev_priv = dev->dev_private; int or = dp->or, link = dp->link; - u32 clk_sor, dp_ctrl; - u8 sink[2]; + u8 *bios, headerlen, sink[2]; + u32 dp_ctrl; 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: - clk_sor = 0x00040000; + nv_mask(dev, 0x614300 + (or * 0x800), 0x000c0000, 0x00040000); sink[0] = DP_LINK_BW_2_7; break; default: - clk_sor = 0x00000000; + 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. + */ + bios = nouveau_bios_dp_table(dev, dp->dcb, &headerlen); + if (bios && (bios = ROMPTR(&dev_priv->vbios, bios[10]))) { + u16 script = ROM16(bios[2]); + if (dp->link_bw != 270000) + script = ROM16(bios[6]); + + 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->enh_frame) { @@ -332,9 +348,9 @@ dp_set_link_config(struct drm_device *dev, struct dp_state *dp) sink[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; } - nv_mask(dev, 0x614300 + (or * 0x800), 0x000c0000, clk_sor); nv_mask(dev, NV50_SOR_DP_CTRL(or, link), 0x001f4000, dp_ctrl); + /* inform the sink of the new configuration */ auxch_tx(dev, dp->auxch, 8, DP_LINK_BW_SET, sink, 2); } -- cgit v1.2.3-70-g09d2 From 52e0d0ecc8f82d4da12c31f0701bf490db5abe72 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 4 Aug 2011 14:31:28 +1000 Subject: drm/nouveau/dp: enable down-spread if vbios and sink support it Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_dp.c | 46 +++++++++++++------------------ drivers/gpu/drm/nouveau/nouveau_encoder.h | 3 +- 2 files changed, 20 insertions(+), 29 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 3777616cc15..89d5c161c8b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -160,25 +160,6 @@ out: return ret; } -static int -auxch_rd(struct drm_encoder *encoder, int address, uint8_t *buf, int size) -{ - struct drm_device *dev = encoder->dev; - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct nouveau_i2c_chan *auxch; - int ret; - - auxch = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index); - if (!auxch) - return -ENODEV; - - ret = nouveau_dp_auxch(auxch, 9, address, buf, size); - if (ret) - return ret; - - return 0; -} - static u32 dp_link_bw_get(struct drm_device *dev, int or, int link) { @@ -298,7 +279,7 @@ struct dp_state { int crtc; int or; int link; - int enh_frame; + u8 *dpcd; int link_nr; u32 link_bw; u8 stat[6]; @@ -343,7 +324,7 @@ dp_set_link_config(struct drm_device *dev, struct dp_state *dp) /* configure lane count on the source */ dp_ctrl = ((1 << dp->link_nr) - 1) << 16; sink[1] = dp->link_nr; - if (dp->enh_frame) { + if (dp->dpcd[2] & DP_ENHANCED_FRAME_CAP) { dp_ctrl |= 0x00004000; sink[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; } @@ -505,7 +486,6 @@ nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate) const u32 *link_bw = bw_list; struct dp_state dp; u8 *bios, headerlen; - u16 script; auxch = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index); if (!auxch) @@ -520,7 +500,7 @@ nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate) dp.auxch = auxch->rd; dp.or = nv_encoder->or; dp.link = !(nv_encoder->dcb->sorconf.link & 1); - dp.enh_frame = nv_encoder->dp.enhanced_frame; + dp.dpcd = nv_encoder->dp.dpcd; /* some sinks toggle hotplug in response to some of the actions * we take during link training (DP_SET_POWER is one), we need @@ -528,6 +508,15 @@ nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate) */ pgpio->irq_enable(dev, nv_connector->dcb->gpio_tag, false); + /* enable down-spreading, if possible */ + if (headerlen >= 16) { + u16 script = ROM16(bios[14]); + if (nv_encoder->dp.dpcd[3] & 1) + script = ROM16(bios[12]); + + nouveau_bios_run_init_table(dev, script, dp.dcb, dp.crtc); + } + /* execute pre-train script from vbios */ nouveau_bios_run_init_table(dev, ROM16(bios[6]), dp.dcb, dp.crtc); @@ -575,17 +564,20 @@ nouveau_dp_detect(struct drm_encoder *encoder) { struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); struct drm_device *dev = encoder->dev; - uint8_t dpcd[4]; + struct nouveau_i2c_chan *auxch; + u8 *dpcd = nv_encoder->dp.dpcd; int ret; - ret = auxch_rd(encoder, 0x0000, dpcd, 4); + auxch = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index); + if (!auxch) + return false; + + ret = auxch_tx(dev, auxch->rd, 9, DP_DPCD_REV, dpcd, 8); if (ret) return false; - nv_encoder->dp.dpcd_version = dpcd[0]; nv_encoder->dp.link_bw = 27000 * dpcd[1]; nv_encoder->dp.link_nr = dpcd[2] & DP_MAX_LANE_COUNT_MASK; - nv_encoder->dp.enhanced_frame = dpcd[2] & DP_ENHANCED_FRAME_CAP; NV_DEBUG_KMS(dev, "display: %dx%d dpcd 0x%02x\n", nv_encoder->dp.link_nr, nv_encoder->dp.link_bw, dpcd[0]); diff --git a/drivers/gpu/drm/nouveau/nouveau_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h index deafe7b1f52..e5d6e3faff3 100644 --- a/drivers/gpu/drm/nouveau/nouveau_encoder.h +++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h @@ -49,10 +49,9 @@ struct nouveau_encoder { union { struct { - int dpcd_version; + u8 dpcd[8]; int link_nr; int link_bw; - bool enhanced_frame; u32 datarate; } dp; }; -- cgit v1.2.3-70-g09d2 From 9f403603f29f748b50b85a1be1c2ca2c792ae200 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 18 Jul 2011 16:02:37 +1000 Subject: drm/nv40/pm: parse geometric delta clock from vbios This changes the meaning of what we reported as "core" clock previously. The shader/rop units are allegedly supposed to be run at the base clock listed in the perf table, while the geometric clock can be bumped from this value on some boards. So that we can report both, we'll report the base clock as "shader" (since the shaders *do* run at it), and the geometric clock as "core". Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_perf.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_perf.c b/drivers/gpu/drm/nouveau/nouveau_perf.c index 854ca857316..9f178aa9416 100644 --- a/drivers/gpu/drm/nouveau/nouveau_perf.c +++ b/drivers/gpu/drm/nouveau/nouveau_perf.c @@ -275,8 +275,9 @@ nouveau_perf_init(struct drm_device *dev) case 0x24: perflvl->fanspeed = entry[4]; perflvl->volt_min = entry[5]; - perflvl->core = ROM16(entry[6]) * 1000; - + perflvl->shader = ROM16(entry[6]) * 1000; + perflvl->core = perflvl->shader; + perflvl->core += (signed char)entry[8] * 1000; if (dev_priv->chipset == 0x49 || dev_priv->chipset == 0x4b) perflvl->memory = ROM16(entry[11]) * 1000; -- cgit v1.2.3-70-g09d2 From 1262a206da7d28340dfce954e31bd5fe5ae9e2dd Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 18 Jul 2011 15:15:34 +1000 Subject: drm/nv40/pm: write nv40-specific reclocking routines Not 100% perfect yet, but a good start towards what it'll look like in the end. Actually seems stable on a NV44 I have here, as much as running around OA for a fair amount of time constantly switching between performance levels can prove.. My NV49 isn't quite so happy, and semaphores mess up somehow (sometimes) as a result of the memory reclocking. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/Makefile | 2 +- drivers/gpu/drm/nouveau/nouveau_pm.h | 5 + drivers/gpu/drm/nouveau/nouveau_state.c | 6 +- drivers/gpu/drm/nouveau/nv40_pm.c | 333 ++++++++++++++++++++++++++++++++ 4 files changed, 342 insertions(+), 4 deletions(-) create mode 100644 drivers/gpu/drm/nouveau/nv40_pm.c (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile index 1e567173c10..35ef5b1e356 100644 --- a/drivers/gpu/drm/nouveau/Makefile +++ b/drivers/gpu/drm/nouveau/Makefile @@ -31,7 +31,7 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \ nv04_fbcon.o nv50_fbcon.o nvc0_fbcon.o \ nv10_gpio.o nv50_gpio.o \ nv50_calc.o \ - nv04_pm.o nv50_pm.o nva3_pm.o nvc0_pm.o \ + nv04_pm.o nv40_pm.o nv50_pm.o nva3_pm.o nvc0_pm.o \ nv50_vram.o nvc0_vram.o \ nv50_vm.o nvc0_vm.o diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.h b/drivers/gpu/drm/nouveau/nouveau_pm.h index f519883d9a4..8ac02cdd03a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.h +++ b/drivers/gpu/drm/nouveau/nouveau_pm.h @@ -52,6 +52,11 @@ void *nv04_pm_clock_pre(struct drm_device *, struct nouveau_pm_level *, u32 id, int khz); void nv04_pm_clock_set(struct drm_device *, void *); +/* nv40_pm.c */ +int nv40_pm_clocks_get(struct drm_device *, struct nouveau_pm_level *); +void *nv40_pm_clocks_pre(struct drm_device *, struct nouveau_pm_level *); +void nv40_pm_clocks_set(struct drm_device *, void *); + /* nv50_pm.c */ int nv50_pm_clock_get(struct drm_device *, u32 id); void *nv50_pm_clock_pre(struct drm_device *, struct nouveau_pm_level *, diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index 50df52da3aa..bc0b592788b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -286,9 +286,9 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->gpio.get = nv10_gpio_get; engine->gpio.set = nv10_gpio_set; engine->gpio.irq_enable = NULL; - engine->pm.clock_get = nv04_pm_clock_get; - engine->pm.clock_pre = nv04_pm_clock_pre; - engine->pm.clock_set = nv04_pm_clock_set; + engine->pm.clocks_get = nv40_pm_clocks_get; + engine->pm.clocks_pre = nv40_pm_clocks_pre; + engine->pm.clocks_set = nv40_pm_clocks_set; engine->pm.voltage_get = nouveau_voltage_gpio_get; engine->pm.voltage_set = nouveau_voltage_gpio_set; engine->pm.temp_get = nv40_temp_get; diff --git a/drivers/gpu/drm/nouveau/nv40_pm.c b/drivers/gpu/drm/nouveau/nv40_pm.c new file mode 100644 index 00000000000..491688676df --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv40_pm.c @@ -0,0 +1,333 @@ +/* + * Copyright 2011 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ + +#include "drmP.h" +#include "nouveau_drv.h" +#include "nouveau_bios.h" +#include "nouveau_pm.h" +#include "nouveau_hw.h" + +#define min2(a,b) ((a) < (b) ? (a) : (b)) + +static u32 +read_pll_1(struct drm_device *dev, u32 reg) +{ + u32 ctrl = nv_rd32(dev, reg + 0x00); + int P = (ctrl & 0x00070000) >> 16; + int N = (ctrl & 0x0000ff00) >> 8; + int M = (ctrl & 0x000000ff) >> 0; + u32 ref = 27000, clk = 0; + + if (ctrl & 0x80000000) + clk = ref * N / M; + + return clk >> P; +} + +static u32 +read_pll_2(struct drm_device *dev, u32 reg) +{ + u32 ctrl = nv_rd32(dev, reg + 0x00); + u32 coef = nv_rd32(dev, reg + 0x04); + int N2 = (coef & 0xff000000) >> 24; + int M2 = (coef & 0x00ff0000) >> 16; + int N1 = (coef & 0x0000ff00) >> 8; + int M1 = (coef & 0x000000ff) >> 0; + int P = (ctrl & 0x00070000) >> 16; + u32 ref = 27000, clk = 0; + + if (ctrl & 0x80000000) + clk = ref * N1 / M1; + + if (!(ctrl & 0x00000100)) { + if (ctrl & 0x40000000) + clk = clk * N2 / M2; + } + + return clk >> P; +} + +static u32 +read_clk(struct drm_device *dev, u32 src) +{ + switch (src) { + case 3: + return read_pll_2(dev, 0x004000); + case 2: + return read_pll_1(dev, 0x004008); + default: + break; + } + + return 0; +} + +int +nv40_pm_clocks_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) +{ + u32 ctrl = nv_rd32(dev, 0x00c040); + + perflvl->core = read_clk(dev, (ctrl & 0x00000003) >> 0); + perflvl->shader = read_clk(dev, (ctrl & 0x00000030) >> 4); + perflvl->memory = read_pll_2(dev, 0x4020); + return 0; +} + +struct nv40_pm_state { + u32 ctrl; + u32 npll_ctrl; + u32 npll_coef; + u32 spll; + u32 mpll_ctrl; + u32 mpll_coef; +}; + +static int +nv40_calc_pll(struct drm_device *dev, u32 reg, struct pll_lims *pll, + u32 clk, int *N1, int *M1, int *N2, int *M2, int *log2P) +{ + struct nouveau_pll_vals coef; + int ret; + + ret = get_pll_limits(dev, reg, pll); + if (ret) + return ret; + + if (clk < pll->vco1.maxfreq) + pll->vco2.maxfreq = 0; + + ret = nouveau_calc_pll_mnp(dev, pll, clk, &coef); + if (ret == 0) + return -ERANGE; + + *N1 = coef.N1; + *M1 = coef.M1; + if (N2 && M2) { + if (pll->vco2.maxfreq) { + *N2 = coef.N2; + *M2 = coef.M2; + } else { + *N2 = 1; + *M2 = 1; + } + } + *log2P = coef.log2P; + return 0; +} + +void * +nv40_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl) +{ + struct nv40_pm_state *info; + struct pll_lims pll; + int N1, N2, M1, M2, log2P; + int ret; + + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return ERR_PTR(-ENOMEM); + + /* core/geometric clock */ + ret = nv40_calc_pll(dev, 0x004000, &pll, perflvl->core, + &N1, &M1, &N2, &M2, &log2P); + if (ret < 0) + goto out; + + if (N2 == M2) { + info->npll_ctrl = 0x80000100 | (log2P << 16); + info->npll_coef = (N1 << 8) | M1; + } else { + info->npll_ctrl = 0xc0000000 | (log2P << 16); + info->npll_coef = (N2 << 24) | (M2 << 16) | (N1 << 8) | M1; + } + + /* use the second PLL for shader/rop clock, if it differs from core */ + if (perflvl->shader && perflvl->shader != perflvl->core) { + ret = nv40_calc_pll(dev, 0x004008, &pll, perflvl->shader, + &N1, &M1, NULL, NULL, &log2P); + if (ret < 0) + goto out; + + info->spll = 0xc0000000 | (log2P << 16) | (N1 << 8) | M1; + info->ctrl = 0x00000223; + } else { + info->spll = 0x00000000; + info->ctrl = 0x00000333; + } + + /* memory clock */ + ret = nv40_calc_pll(dev, 0x004020, &pll, perflvl->memory, + &N1, &M1, &N2, &M2, &log2P); + if (ret < 0) + goto out; + + info->mpll_ctrl = 0x80000000 | (log2P << 16); + info->mpll_ctrl |= min2(pll.log2p_bias + log2P, pll.max_log2p) << 20; + if (N2 == M2) { + info->mpll_ctrl |= 0x00000100; + info->mpll_coef = (N1 << 8) | M1; + } else { + info->mpll_ctrl |= 0x40000000; + info->mpll_coef = (N2 << 24) | (M2 << 16) | (N1 << 8) | M1; + } + +out: + if (ret < 0) { + kfree(info); + info = ERR_PTR(ret); + } + return info; +} + +static bool +nv40_pm_gr_idle(void *data) +{ + struct drm_device *dev = data; + + if ((nv_rd32(dev, 0x400760) & 0x000000f0) >> 4 != + (nv_rd32(dev, 0x400760) & 0x0000000f)) + return false; + + if (nv_rd32(dev, 0x400700)) + return false; + + return true; +} + +void +nv40_pm_clocks_set(struct drm_device *dev, void *pre_state) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nv40_pm_state *info = pre_state; + unsigned long flags; + u32 crtc_mask = 0; + u8 sr1[2]; + int i; + + /* determine which CRTCs are active, fetch VGA_SR1 for each */ + for (i = 0; i < 2; i++) { + u32 vbl = nv_rd32(dev, 0x600808 + (i * 0x2000)); + u32 cnt = 0; + do { + if (vbl != nv_rd32(dev, 0x600808 + (i * 0x2000))) { + nv_wr08(dev, 0x0c03c4 + (i * 0x2000), 0x01); + sr1[i] = nv_rd08(dev, 0x0c03c5 + (i * 0x2000)); + if (!(sr1[i] & 0x20)) + crtc_mask |= (1 << i); + break; + } + udelay(1); + } while (cnt++ < 32); + } + + /* halt and idle engines */ + spin_lock_irqsave(&dev_priv->context_switch_lock, flags); + nv_mask(dev, 0x002500, 0x00000001, 0x00000000); + if (!nv_wait(dev, 0x002500, 0x00000010, 0x00000000)) + goto resume; + nv_mask(dev, 0x003220, 0x00000001, 0x00000000); + if (!nv_wait(dev, 0x003220, 0x00000010, 0x00000000)) + goto resume; + nv_mask(dev, 0x003200, 0x00000001, 0x00000000); + nv04_fifo_cache_pull(dev, false); + + if (!nv_wait_cb(dev, nv40_pm_gr_idle, dev)) + goto resume; + + /* set engine clocks */ + nv_mask(dev, 0x00c040, 0x00000333, 0x00000000); + nv_wr32(dev, 0x004004, info->npll_coef); + nv_mask(dev, 0x004000, 0xc0070100, info->npll_ctrl); + nv_mask(dev, 0x004008, 0xc007ffff, info->spll); + mdelay(5); + nv_mask(dev, 0x00c040, 0x00000333, info->ctrl); + + /* wait for vblank start on active crtcs, disable memory access */ + for (i = 0; i < 2; i++) { + if (!(crtc_mask & (1 << i))) + continue; + nv_wait(dev, 0x600808 + (i * 0x2000), 0x00010000, 0x00000000); + nv_wait(dev, 0x600808 + (i * 0x2000), 0x00010000, 0x00010000); + nv_wr08(dev, 0x0c03c4 + (i * 0x2000), 0x01); + nv_wr08(dev, 0x0c03c5 + (i * 0x2000), sr1[i] | 0x20); + } + + /* prepare ram for reclocking */ + nv_wr32(dev, 0x1002d4, 0x00000001); /* precharge */ + nv_wr32(dev, 0x1002d0, 0x00000001); /* refresh */ + nv_wr32(dev, 0x1002d0, 0x00000001); /* refresh */ + nv_mask(dev, 0x100210, 0x80000000, 0x00000000); /* no auto refresh */ + nv_wr32(dev, 0x1002dc, 0x00000001); /* enable self-refresh */ + + /* change the PLL of each memory partition */ + nv_mask(dev, 0x00c040, 0x0000c000, 0x00000000); + switch (dev_priv->chipset) { + case 0x40: + case 0x45: + case 0x41: + case 0x42: + case 0x47: + nv_mask(dev, 0x004044, 0xc0771100, info->mpll_ctrl); + nv_mask(dev, 0x00402c, 0xc0771100, info->mpll_ctrl); + nv_wr32(dev, 0x004048, info->mpll_coef); + nv_wr32(dev, 0x004030, info->mpll_coef); + case 0x43: + case 0x49: + case 0x4b: + nv_mask(dev, 0x004038, 0xc0771100, info->mpll_ctrl); + nv_wr32(dev, 0x00403c, info->mpll_coef); + default: + nv_mask(dev, 0x004020, 0xc0771100, info->mpll_ctrl); + nv_wr32(dev, 0x004024, info->mpll_coef); + break; + } + udelay(100); + nv_mask(dev, 0x00c040, 0x0000c000, 0x0000c000); + + /* re-enable normal operation of memory controller */ + nv_wr32(dev, 0x1002dc, 0x00000000); + nv_mask(dev, 0x100210, 0x80000000, 0x80000000); + udelay(100); + + /* make sure we're in vblank (hopefully the same one as before), and + * then re-enable crtc memory access + */ + for (i = 0; i < 2; i++) { + if (!(crtc_mask & (1 << i))) + continue; + nv_wait(dev, 0x600808 + (i * 0x2000), 0x00010000, 0x00010000); + nv_wr08(dev, 0x0c03c4 + (i * 0x2000), 0x01); + nv_wr08(dev, 0x0c03c5 + (i * 0x2000), sr1[i]); + } + + /* resume engines */ +resume: + nv_wr32(dev, 0x003250, 0x00000001); + nv_mask(dev, 0x003220, 0x00000001, 0x00000001); + nv_wr32(dev, 0x003200, 0x00000001); + nv_wr32(dev, 0x002500, 0x00000001); + spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); + + kfree(info); +} -- cgit v1.2.3-70-g09d2 From 856ed888754e98b5933878f56b9c540b68dab1d6 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 5 Aug 2011 10:28:52 +1000 Subject: drm/nouveau/dp: link rate scripts are selected with a comparison table Not hardcoded as originally thought. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_dp.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 89d5c161c8b..55782bd7df0 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -314,11 +314,10 @@ dp_set_link_config(struct drm_device *dev, struct dp_state *dp) */ bios = nouveau_bios_dp_table(dev, dp->dcb, &headerlen); if (bios && (bios = ROMPTR(&dev_priv->vbios, bios[10]))) { - u16 script = ROM16(bios[2]); - if (dp->link_bw != 270000) - script = ROM16(bios[6]); + while (dp->link_bw < (ROM16(bios[0]) * 10)) + bios += 4; - nouveau_bios_run_init_table(dev, script, dp->dcb, dp->crtc); + nouveau_bios_run_init_table(dev, ROM16(bios[2]), dp->dcb, dp->crtc); } /* configure lane count on the source */ -- cgit v1.2.3-70-g09d2 From 1b45dbe0bc5af68b87d4a535c520eec542c45447 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 5 Aug 2011 11:09:21 +1000 Subject: drm/nouveau/dp: use alternate lane mask for nvaf Naturally... Because Macs can't just be the same as everything else now can they? Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_dp.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 55782bd7df0..7eb841c8ddd 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -342,16 +342,25 @@ dp_set_training_pattern(struct drm_device *dev, struct dp_state *dp, u8 tp) auxch_tx(dev, dp->auxch, 8, DP_TRAINING_PATTERN_SET, &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; - u8 shifts[4] = { 16, 8, 0, 24 }; u8 *bios, *last, headerlen; + 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; + bios = nouveau_bios_dp_table(dev, dp->dcb, &headerlen); last = bios + headerlen + (bios[4] * 5); for (i = 0; i < dp->link_nr; i++) { -- cgit v1.2.3-70-g09d2 From 2834f86864a10a1cbad1e0543f68b4edb2e03bc7 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 8 Aug 2011 08:57:55 +1000 Subject: drm/nvc0/gr: remove MODULE_FIRMWARE() lines We don't use these by default anymore, and there's been complaints from a number of places thinking that the firmware blobs are required still. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvc0_graph.c | 17 ----------------- 1 file changed, 17 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nvc0_graph.c b/drivers/gpu/drm/nouveau/nvc0_graph.c index 1a17530efb8..ecf0fb46918 100644 --- a/drivers/gpu/drm/nouveau/nvc0_graph.c +++ b/drivers/gpu/drm/nouveau/nvc0_graph.c @@ -892,20 +892,3 @@ error: nvc0_graph_destroy(dev, NVOBJ_ENGINE_GR); return ret; } - -MODULE_FIRMWARE("nouveau/nvc0_fuc409c"); -MODULE_FIRMWARE("nouveau/nvc0_fuc409d"); -MODULE_FIRMWARE("nouveau/nvc0_fuc41ac"); -MODULE_FIRMWARE("nouveau/nvc0_fuc41ad"); -MODULE_FIRMWARE("nouveau/nvc3_fuc409c"); -MODULE_FIRMWARE("nouveau/nvc3_fuc409d"); -MODULE_FIRMWARE("nouveau/nvc3_fuc41ac"); -MODULE_FIRMWARE("nouveau/nvc3_fuc41ad"); -MODULE_FIRMWARE("nouveau/nvc4_fuc409c"); -MODULE_FIRMWARE("nouveau/nvc4_fuc409d"); -MODULE_FIRMWARE("nouveau/nvc4_fuc41ac"); -MODULE_FIRMWARE("nouveau/nvc4_fuc41ad"); -MODULE_FIRMWARE("nouveau/fuc409c"); -MODULE_FIRMWARE("nouveau/fuc409d"); -MODULE_FIRMWARE("nouveau/fuc41ac"); -MODULE_FIRMWARE("nouveau/fuc41ad"); -- cgit v1.2.3-70-g09d2 From 5b3eb95fd83861a8520a50aee517209b8c8b0505 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 5 Aug 2011 15:56:53 +1000 Subject: drm/nouveau/dp: preserve non-pattern bits in DP_TRAINING_PATTERN_SET Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_dp.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 7eb841c8ddd..d3552e76664 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -337,9 +337,16 @@ dp_set_link_config(struct drm_device *dev, struct dp_state *dp) static void dp_set_training_pattern(struct drm_device *dev, struct dp_state *dp, u8 tp) { + u8 sink_tp; + NV_DEBUG_KMS(dev, "training pattern %d\n", tp); + nv_mask(dev, NV50_SOR_DP_CTRL(dp->or, dp->link), 0x0f000000, tp << 24); - auxch_tx(dev, dp->auxch, 8, DP_TRAINING_PATTERN_SET, &tp, 1); + + auxch_tx(dev, dp->auxch, 9, DP_TRAINING_PATTERN_SET, &sink_tp, 1); + sink_tp &= ~DP_TRAINING_PATTERN_MASK; + sink_tp |= tp; + auxch_tx(dev, dp->auxch, 8, DP_TRAINING_PATTERN_SET, &sink_tp, 1); } static const u8 nv50_lane_map[] = { 16, 8, 0, 24 }; -- cgit v1.2.3-70-g09d2 From 721b0821ad8fea80ea1b6b84cb9646881959e662 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 5 Aug 2011 13:42:49 +1000 Subject: drm/nouveau/bios: simplify U/d table hash matching func to just match The caller is now responsible for parsing its own lists (or whatever) of possible encoders. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_bios.c | 90 +++++++++++++++------------------- drivers/gpu/drm/nouveau/nouveau_drv.h | 1 + 2 files changed, 40 insertions(+), 51 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index bea5df7cf93..73b590a7194 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -4426,55 +4426,32 @@ int nouveau_bios_parse_lvds_table(struct drm_device *dev, int pxclk, bool *dl, b return 0; } -static uint8_t * -bios_output_config_match(struct drm_device *dev, struct dcb_entry *dcbent, - uint16_t record, int record_len, int record_nr, - bool match_link) +/* BIT 'U'/'d' table encoder subtables have hashes matching them to + * a particular set of encoders. + * + * This function returns true if a particular DCB entry matches. + */ +bool +bios_encoder_match(struct dcb_entry *dcb, u32 hash) { - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nvbios *bios = &dev_priv->vbios; - uint32_t entry; - uint16_t table; - int i, v; + if ((hash & 0x000000f0) != (dcb->location << 4)) + return false; + if ((hash & 0x0000000f) != dcb->type) + return false; + if (!(hash & (dcb->or << 16))) + return false; - switch (dcbent->type) { + switch (dcb->type) { case OUTPUT_TMDS: case OUTPUT_LVDS: case OUTPUT_DP: - break; - default: - match_link = false; - break; - } - - for (i = 0; i < record_nr; i++, record += record_len) { - table = ROM16(bios->data[record]); - if (!table) - continue; - entry = ROM32(bios->data[table]); - - if (match_link) { - v = (entry & 0x00c00000) >> 22; - if (!(v & dcbent->sorconf.link)) - continue; + if (hash & 0x00c00000) { + if (!(hash & (dcb->sorconf.link << 22))) + return false; } - - v = (entry & 0x000f0000) >> 16; - if (!(v & dcbent->or)) - continue; - - v = (entry & 0x000000f0) >> 4; - if (v != dcbent->location) - continue; - - v = (entry & 0x0000000f); - if (v != dcbent->type) - continue; - - return &bios->data[table]; + default: + return true; } - - return NULL; } void * @@ -4483,7 +4460,8 @@ nouveau_bios_dp_table(struct drm_device *dev, struct dcb_entry *dcbent, { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nvbios *bios = &dev_priv->vbios; - uint8_t *table; + uint8_t *table, *entry; + int i; if (!bios->display.dp_table_ptr) { NV_ERROR(dev, "No pointer to DisplayPort table\n"); @@ -4497,10 +4475,17 @@ nouveau_bios_dp_table(struct drm_device *dev, struct dcb_entry *dcbent, return NULL; } - *headerlen = table[4]; - return bios_output_config_match(dev, dcbent, - bios->display.dp_table_ptr + table[1], - table[2], table[3], table[0] >= 0x21); + entry = table + table[1]; + for (i = 0; i < table[3]; i++, entry += table[2]) { + u8 *etable = ROMPTR(bios, entry[0]); + if (etable && bios_encoder_match(dcbent, ROM32(etable[0]))) { + *headerlen = table[4]; + return etable; + } + } + + NV_ERROR(dev, "DisplayPort encoder table not found\n"); + return NULL; } int @@ -4535,7 +4520,7 @@ nouveau_bios_run_display_table(struct drm_device *dev, u16 type, int pclk, uint8_t *table = &bios->data[bios->display.script_table_ptr]; uint8_t *otable = NULL; uint16_t script; - int i = 0; + int i; if (!bios->display.script_table_ptr) { NV_ERROR(dev, "No pointer to output script table\n"); @@ -4587,9 +4572,12 @@ nouveau_bios_run_display_table(struct drm_device *dev, u16 type, int pclk, NV_DEBUG_KMS(dev, "Searching for output entry for %d %d %d\n", dcbent->type, dcbent->location, dcbent->or); - otable = bios_output_config_match(dev, dcbent, table[1] + - bios->display.script_table_ptr, - table[2], table[3], table[0] >= 0x21); + for (i = 0; i < table[3]; i++) { + otable = ROMPTR(bios, table[table[1] + (i * table[2])]); + if (otable && bios_encoder_match(dcbent, ROM32(otable[0]))) + break; + } + if (!otable) { NV_DEBUG_KMS(dev, "failed to match any output table\n"); return 1; diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 3cf8e6a10e9..d269b7ba45c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -1090,6 +1090,7 @@ extern int run_tmds_table(struct drm_device *, struct dcb_entry *, int head, int pxclk); extern int call_lvds_script(struct drm_device *, struct dcb_entry *, int head, enum LVDS_script, int pxclk); +bool bios_encoder_match(struct dcb_entry *, u32 hash); /* nouveau_ttm.c */ int nouveau_ttm_global_init(struct drm_nouveau_private *); -- cgit v1.2.3-70-g09d2 From 5f1800bd8a774f773e3be71702da7ec77188b283 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 5 Aug 2011 14:07:04 +1000 Subject: drm/nouveau/dp: return master dp table pointer too when looking up encoder Will need to be able to distinguish 2.0/2.1 from 3.0 soon. Also, move the vbios parsing to nouveau_dp where it belongs. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_bios.c | 53 ++--------------------- drivers/gpu/drm/nouveau/nouveau_bios.h | 1 - drivers/gpu/drm/nouveau/nouveau_dp.c | 79 ++++++++++++++++++++++++++-------- drivers/gpu/drm/nouveau/nouveau_drv.h | 3 +- 4 files changed, 66 insertions(+), 70 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index 73b590a7194..58b2535e3b6 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -1182,18 +1182,16 @@ init_dp_condition(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) struct dcb_entry *dcb = bios->display.output; struct drm_device *dev = bios->dev; uint8_t cond = bios->data[offset + 1]; - uint8_t *table, headerlen; + uint8_t *table, *entry; BIOSLOG(bios, "0x%04X: subop 0x%02X\n", offset, cond); if (!iexec->execute) return 3; - table = nouveau_bios_dp_table(dev, dcb, &headerlen); - if (!table) { - NV_ERROR(dev, "0x%04X: INIT_3A: no encoder table!!\n", offset); + table = nouveau_dp_bios_data(dev, dcb, &entry); + if (!table) return 3; - } switch (cond) { case 0: @@ -1207,7 +1205,7 @@ init_dp_condition(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) break; case 1: case 2: - if (!(table[5] & cond)) + if (!(entry[5] & cond)) iexec->execute = false; break; case 5: @@ -4454,40 +4452,6 @@ bios_encoder_match(struct dcb_entry *dcb, u32 hash) } } -void * -nouveau_bios_dp_table(struct drm_device *dev, struct dcb_entry *dcbent, - uint8_t *headerlen) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nvbios *bios = &dev_priv->vbios; - uint8_t *table, *entry; - int i; - - if (!bios->display.dp_table_ptr) { - NV_ERROR(dev, "No pointer to DisplayPort table\n"); - return NULL; - } - table = &bios->data[bios->display.dp_table_ptr]; - - if (table[0] != 0x20 && table[0] != 0x21) { - NV_ERROR(dev, "DisplayPort table version 0x%02x unknown\n", - table[0]); - return NULL; - } - - entry = table + table[1]; - for (i = 0; i < table[3]; i++, entry += table[2]) { - u8 *etable = ROMPTR(bios, entry[0]); - if (etable && bios_encoder_match(dcbent, ROM32(etable[0]))) { - *headerlen = table[4]; - return etable; - } - } - - NV_ERROR(dev, "DisplayPort encoder table not found\n"); - return NULL; -} - int nouveau_bios_run_display_table(struct drm_device *dev, u16 type, int pclk, struct dcb_entry *dcbent, int crtc) @@ -5503,14 +5467,6 @@ parse_bit_U_tbl_entry(struct drm_device *dev, struct nvbios *bios, return 0; } -static int -parse_bit_displayport_tbl_entry(struct drm_device *dev, struct nvbios *bios, - struct bit_entry *bitentry) -{ - bios->display.dp_table_ptr = ROM16(bios->data[bitentry->offset]); - return 0; -} - struct bit_table { const char id; int (* const parse_fn)(struct drm_device *, struct nvbios *, struct bit_entry *); @@ -5584,7 +5540,6 @@ parse_bit_structure(struct nvbios *bios, const uint16_t bitoffset) parse_bit_table(bios, bitoffset, &BIT_TABLE('L', lvds)); parse_bit_table(bios, bitoffset, &BIT_TABLE('T', tmds)); parse_bit_table(bios, bitoffset, &BIT_TABLE('U', U)); - parse_bit_table(bios, bitoffset, &BIT_TABLE('d', displayport)); return 0; } diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.h b/drivers/gpu/drm/nouveau/nouveau_bios.h index b28f0bceaed..8adb69e4a6b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.h +++ b/drivers/gpu/drm/nouveau/nouveau_bios.h @@ -291,7 +291,6 @@ struct nvbios { struct dcb_entry *output; int crtc; uint16_t script_table_ptr; - uint16_t dp_table_ptr; } display; struct { diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index d3552e76664..25ecb776c7b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -270,11 +270,57 @@ nouveau_dp_tu_update(struct drm_device *dev, int or, int link, u32 clk, u32 bpp) unk); } +u8 * +nouveau_dp_bios_data(struct drm_device *dev, struct dcb_entry *dcb, u8 **entry) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nvbios *bios = &dev_priv->vbios; + struct bit_entry d; + u8 *table; + int i; + + if (bit_table(dev, 'd', &d)) { + NV_ERROR(dev, "BIT 'd' table not found\n"); + return NULL; + } + + if (d.version != 1) { + NV_ERROR(dev, "BIT 'd' table version %d unknown\n", d.version); + return NULL; + } + + table = ROMPTR(bios, d.data[0]); + if (!table) { + NV_ERROR(dev, "displayport table pointer invalid\n"); + return NULL; + } + + switch (table[0]) { + case 0x20: + case 0x21: + break; + default: + NV_ERROR(dev, "displayport table 0x%02x unknown\n", table[0]); + return NULL; + } + + for (i = 0; i < table[3]; i++) { + *entry = ROMPTR(bios, table[table[1] + (i * table[2])]); + if (*entry && bios_encoder_match(dcb, ROM32((*entry)[0]))) + return table; + } + + NV_ERROR(dev, "displayport encoder table not found\n"); + return NULL; +} + /****************************************************************************** * link training *****************************************************************************/ struct dp_state { struct dcb_entry *dcb; + u8 *table; + u8 *entry; int auxch; int crtc; int or; @@ -291,7 +337,7 @@ dp_set_link_config(struct drm_device *dev, struct dp_state *dp) { struct drm_nouveau_private *dev_priv = dev->dev_private; int or = dp->or, link = dp->link; - u8 *bios, headerlen, sink[2]; + u8 *entry, sink[2]; u32 dp_ctrl; NV_DEBUG_KMS(dev, "%d lanes at %d KB/s\n", dp->link_nr, dp->link_bw); @@ -312,12 +358,12 @@ dp_set_link_config(struct drm_device *dev, struct dp_state *dp) * table, that has (among other things) pointers to more scripts that * need to be executed, this time depending on link speed. */ - bios = nouveau_bios_dp_table(dev, dp->dcb, &headerlen); - if (bios && (bios = ROMPTR(&dev_priv->vbios, bios[10]))) { - while (dp->link_bw < (ROM16(bios[0]) * 10)) - bios += 4; + entry = ROMPTR(&dev_priv->vbios, dp->entry[10]); + if (entry) { + while (dp->link_bw < (ROM16(entry[0]) * 10)) + entry += 4; - nouveau_bios_run_init_table(dev, ROM16(bios[2]), dp->dcb, dp->crtc); + nouveau_bios_run_init_table(dev, ROM16(entry[2]), dp->dcb, dp->crtc); } /* configure lane count on the source */ @@ -357,7 +403,6 @@ 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; - u8 *bios, *last, headerlen; const u8 *shifts; int link = dp->link; int or = dp->or; @@ -368,11 +413,10 @@ dp_link_train_commit(struct drm_device *dev, struct dp_state *dp) else shifts = nvaf_lane_map; - bios = nouveau_bios_dp_table(dev, dp->dcb, &headerlen); - last = bios + headerlen + (bios[4] * 5); for (i = 0; i < dp->link_nr; i++) { u8 lane = (dp->stat[4 + (i >> 1)] >> ((i & 1) * 4)) & 0xf; - u8 *conf = bios + headerlen; + u8 *conf = dp->entry + dp->table[4]; + u8 *last = conf + (dp->entry[4] * dp->table[5]); while (conf < last) { if ((lane & 3) == conf[0] && @@ -500,14 +544,13 @@ nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate) const u32 bw_list[] = { 270000, 162000, 0 }; const u32 *link_bw = bw_list; struct dp_state dp; - u8 *bios, headerlen; auxch = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index); if (!auxch) return false; - bios = nouveau_bios_dp_table(dev, nv_encoder->dcb, &headerlen); - if (!bios) + dp.table = nouveau_dp_bios_data(dev, nv_encoder->dcb, &dp.entry); + if (!dp.table) return -EINVAL; dp.dcb = nv_encoder->dcb; @@ -524,16 +567,16 @@ nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate) pgpio->irq_enable(dev, nv_connector->dcb->gpio_tag, false); /* enable down-spreading, if possible */ - if (headerlen >= 16) { - u16 script = ROM16(bios[14]); + if (dp.table[1] >= 16) { + u16 script = ROM16(dp.entry[14]); if (nv_encoder->dp.dpcd[3] & 1) - script = ROM16(bios[12]); + script = ROM16(dp.entry[12]); nouveau_bios_run_init_table(dev, script, dp.dcb, dp.crtc); } /* execute pre-train script from vbios */ - nouveau_bios_run_init_table(dev, ROM16(bios[6]), dp.dcb, dp.crtc); + nouveau_bios_run_init_table(dev, ROM16(dp.entry[6]), dp.dcb, dp.crtc); /* start off at highest link rate supported by encoder and display */ while (*link_bw > nv_encoder->dp.link_bw) @@ -567,7 +610,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(bios[8]), dp.dcb, dp.crtc); + nouveau_bios_run_init_table(dev, ROM16(dp.entry[8]), dp.dcb, dp.crtc); /* re-enable hotplug detect */ pgpio->irq_enable(dev, nv_connector->dcb->gpio_tag, true); diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index d269b7ba45c..ecaa4ffbeab 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -1080,8 +1080,6 @@ extern int get_pll_limits(struct drm_device *, uint32_t limit_match, struct pll_lims *); extern int nouveau_bios_run_display_table(struct drm_device *, u16 id, int clk, struct dcb_entry *, int crtc); -extern void *nouveau_bios_dp_table(struct drm_device *, struct dcb_entry *, - u8 *headerlen); extern bool nouveau_bios_fp_mode(struct drm_device *, struct drm_display_mode *); extern uint8_t *nouveau_bios_embedded_edid(struct drm_device *); extern int nouveau_bios_parse_lvds_table(struct drm_device *, int pxclk, @@ -1103,6 +1101,7 @@ int nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr, 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_init(struct drm_device *); -- cgit v1.2.3-70-g09d2 From c16a3a358b6460696b2dc275cbbab1adbbbd1f67 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 5 Aug 2011 14:47:28 +1000 Subject: drm/nouveau/dp: add support for displayport table 0x30 Written from observations of my NVD9's vbios, completely untested due to my NVD9 lacking actual DisplayPort connectors.. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_dp.c | 70 ++++++++++++++++++++++++------------ 1 file changed, 48 insertions(+), 22 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 25ecb776c7b..de5efe71fef 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -298,6 +298,7 @@ nouveau_dp_bios_data(struct drm_device *dev, struct dcb_entry *dcb, u8 **entry) switch (table[0]) { case 0x20: case 0x21: + case 0x30: break; default: NV_ERROR(dev, "displayport table 0x%02x unknown\n", table[0]); @@ -339,6 +340,7 @@ 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; NV_DEBUG_KMS(dev, "%d lanes at %d KB/s\n", dp->link_nr, dp->link_bw); @@ -360,10 +362,17 @@ dp_set_link_config(struct drm_device *dev, struct dp_state *dp) */ entry = ROMPTR(&dev_priv->vbios, dp->entry[10]); if (entry) { - while (dp->link_bw < (ROM16(entry[0]) * 10)) - entry += 4; + 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, ROM16(entry[2]), dp->dcb, dp->crtc); + nouveau_bios_run_init_table(dev, script, dp->dcb, dp->crtc); } /* configure lane count on the source */ @@ -414,33 +423,50 @@ dp_link_train_commit(struct drm_device *dev, struct dp_state *dp) shifts = nvaf_lane_map; for (i = 0; i < dp->link_nr; i++) { - u8 lane = (dp->stat[4 + (i >> 1)] >> ((i & 1) * 4)) & 0xf; u8 *conf = dp->entry + dp->table[4]; - u8 *last = conf + (dp->entry[4] * dp->table[5]); - - while (conf < last) { - if ((lane & 3) == conf[0] && - (lane >> 2) == conf[1]) - break; - conf += 5; - } + u8 lane = (dp->stat[4 + (i >> 1)] >> ((i & 1) * 4)) & 0xf; + u8 lpre = (lane & 0x0c) >> 2; + u8 lvsw = (lane & 0x03) >> 0; - if (conf == last) - return -EINVAL; + mask |= 0xff << shifts[i]; + unk |= 1 << (shifts[i] >> 3); - dp->conf[i] = (conf[1] << 3) | conf[0]; - if (conf[0] == DP_TRAIN_VOLTAGE_SWING_1200) + dp->conf[i] = (lpre << 3) | lvsw; + if (lvsw == DP_TRAIN_VOLTAGE_SWING_1200) dp->conf[i] |= DP_TRAIN_MAX_SWING_REACHED; - if (conf[1] == DP_TRAIN_PRE_EMPHASIS_9_5) + if (lpre == 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]); - mask |= 0xff << shifts[i]; - drv |= conf[2] << shifts[i]; - pre |= conf[3] << shifts[i]; - unk = (unk & ~0x0000ff00) | (conf[4] << 8); - unk |= 1 << (shifts[i] >> 3); + 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); } nv_mask(dev, NV50_SOR_DP_UNK118(or, link), mask, drv); -- cgit v1.2.3-70-g09d2 From fbba036a56fe0e5c5e8c91daf3fa211f88d94a03 Mon Sep 17 00:00:00 2001 From: Martin Peres Date: Sat, 30 Jul 2011 23:08:45 +0200 Subject: drm/nv50/gr: enable ctxprog xfer only when we need it to save power This patch adds instructions to ctxprog and by doing, impacts context switching performance. My testcase showed a 1% performance cost using glxgears that is a context-switch bound application. Please test and report bugs/performance/power/other. Many thanks to Maxim Levitsky for his dedicated work on lowering power consumption with nouveau. More patches are coming thanks to his work: https://bugs.freedesktop.org/show_bug.cgi?id=37922 Signed-off-by: Martin Peres Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nv50_grctx.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nv50_grctx.c b/drivers/gpu/drm/nouveau/nv50_grctx.c index e04fb4483c5..d05c2c3b244 100644 --- a/drivers/gpu/drm/nouveau/nv50_grctx.c +++ b/drivers/gpu/drm/nouveau/nv50_grctx.c @@ -40,6 +40,9 @@ #define CP_FLAG_UNK0B ((0 * 32) + 0xb) #define CP_FLAG_UNK0B_CLEAR 0 #define CP_FLAG_UNK0B_SET 1 +#define CP_FLAG_XFER_SWITCH ((0 * 32) + 0xe) +#define CP_FLAG_XFER_SWITCH_DISABLE 0 +#define CP_FLAG_XFER_SWITCH_ENABLE 1 #define CP_FLAG_STATE ((0 * 32) + 0x1c) #define CP_FLAG_STATE_STOPPED 0 #define CP_FLAG_STATE_RUNNING 1 @@ -199,6 +202,7 @@ nv50_grctx_init(struct nouveau_grctx *ctx) } cp_set (ctx, STATE, RUNNING); + cp_set (ctx, XFER_SWITCH, ENABLE); /* decide whether we're loading/unloading the context */ cp_bra (ctx, AUTO_SAVE, PENDING, cp_setup_save); cp_bra (ctx, USER_SAVE, PENDING, cp_setup_save); @@ -265,6 +269,7 @@ nv50_grctx_init(struct nouveau_grctx *ctx) cp_name(ctx, cp_exit); cp_set (ctx, USER_SAVE, NOT_PENDING); cp_set (ctx, USER_LOAD, NOT_PENDING); + cp_set (ctx, XFER_SWITCH, DISABLE); cp_set (ctx, STATE, STOPPED); cp_out (ctx, CP_END); ctx->ctxvals_pos += 0x400; /* padding... no idea why you need it */ -- cgit v1.2.3-70-g09d2 From 8c06e60ed4ce35590ef38327c93f351ba37dae47 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 12 Aug 2011 08:56:06 +1000 Subject: drm/nouveau: if requested, try harder at disabling sysmem pushbufs On >=nv50, userspace would still end up allocating pushbufs in GART. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_channel.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_channel.c b/drivers/gpu/drm/nouveau/nouveau_channel.c index b0d753f45bb..a319d5646ea 100644 --- a/drivers/gpu/drm/nouveau/nouveau_channel.c +++ b/drivers/gpu/drm/nouveau/nouveau_channel.c @@ -411,13 +411,17 @@ nouveau_ioctl_fifo_alloc(struct drm_device *dev, void *data, return ret; init->channel = chan->id; - if (chan->dma.ib_max) - init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_VRAM | - NOUVEAU_GEM_DOMAIN_GART; - else if (chan->pushbuf_bo->bo.mem.mem_type == TTM_PL_VRAM) + if (nouveau_vram_pushbuf == 0) { + if (chan->dma.ib_max) + init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_VRAM | + NOUVEAU_GEM_DOMAIN_GART; + else if (chan->pushbuf_bo->bo.mem.mem_type == TTM_PL_VRAM) + init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_VRAM; + else + init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_GART; + } else { init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_VRAM; - else - init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_GART; + } if (dev_priv->card_type < NV_C0) { init->subchan[0].handle = NvM2MF; -- cgit v1.2.3-70-g09d2 From 6d6538a0c33e29781151d03c150b7e31ab4f6411 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 12 Aug 2011 09:30:58 +1000 Subject: drm/nv50/gr: refactor initialisation Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nv50_graph.c | 118 +++++++++++------------------------ 1 file changed, 37 insertions(+), 81 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nv50_graph.c b/drivers/gpu/drm/nouveau/nv50_graph.c index d43c46caa76..8c979b31ff6 100644 --- a/drivers/gpu/drm/nouveau/nv50_graph.c +++ b/drivers/gpu/drm/nouveau/nv50_graph.c @@ -120,70 +120,62 @@ nv50_graph_unload_context(struct drm_device *dev) return 0; } -static void -nv50_graph_init_reset(struct drm_device *dev) -{ - uint32_t pmc_e = NV_PMC_ENABLE_PGRAPH | (1 << 21); - NV_DEBUG(dev, "\n"); - - nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) & ~pmc_e); - nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) | pmc_e); -} - -static void -nv50_graph_init_intr(struct drm_device *dev) -{ - NV_DEBUG(dev, "\n"); - - nv_wr32(dev, NV03_PGRAPH_INTR, 0xffffffff); - nv_wr32(dev, 0x400138, 0xffffffff); - nv_wr32(dev, NV40_PGRAPH_INTR_EN, 0xffffffff); -} - -static void -nv50_graph_init_regs__nv(struct drm_device *dev) +static int +nv50_graph_init(struct drm_device *dev, int engine) { struct drm_nouveau_private *dev_priv = dev->dev_private; - uint32_t units = nv_rd32(dev, 0x1540); + struct nv50_graph_engine *pgraph = nv_engine(dev, engine); + u32 units = nv_rd32(dev, 0x001540); int i; NV_DEBUG(dev, "\n"); + /* master reset */ + nv_mask(dev, 0x000200, 0x00200100, 0x00000000); + nv_mask(dev, 0x000200, 0x00200100, 0x00200100); + nv_wr32(dev, 0x40008c, 0x00000004); /* HW_CTX_SWITCH_ENABLED */ + + /* reset/enable traps and interrupts */ nv_wr32(dev, 0x400804, 0xc0000000); nv_wr32(dev, 0x406800, 0xc0000000); nv_wr32(dev, 0x400c04, 0xc0000000); nv_wr32(dev, 0x401800, 0xc0000000); nv_wr32(dev, 0x405018, 0xc0000000); nv_wr32(dev, 0x402000, 0xc0000000); - for (i = 0; i < 16; i++) { - if (units & 1 << i) { - if (dev_priv->chipset < 0xa0) { - nv_wr32(dev, 0x408900 + (i << 12), 0xc0000000); - nv_wr32(dev, 0x408e08 + (i << 12), 0xc0000000); - nv_wr32(dev, 0x408314 + (i << 12), 0xc0000000); - } else { - nv_wr32(dev, 0x408600 + (i << 11), 0xc0000000); - nv_wr32(dev, 0x408708 + (i << 11), 0xc0000000); - nv_wr32(dev, 0x40831c + (i << 11), 0xc0000000); - } + if (!(units & (1 << i))) + continue; + + if (dev_priv->chipset < 0xa0) { + nv_wr32(dev, 0x408900 + (i << 12), 0xc0000000); + nv_wr32(dev, 0x408e08 + (i << 12), 0xc0000000); + nv_wr32(dev, 0x408314 + (i << 12), 0xc0000000); + } else { + nv_wr32(dev, 0x408600 + (i << 11), 0xc0000000); + nv_wr32(dev, 0x408708 + (i << 11), 0xc0000000); + nv_wr32(dev, 0x40831c + (i << 11), 0xc0000000); } } nv_wr32(dev, 0x400108, 0xffffffff); - - nv_wr32(dev, 0x400824, 0x00004000); + nv_wr32(dev, 0x400138, 0xffffffff); + nv_wr32(dev, 0x400100, 0xffffffff); + nv_wr32(dev, 0x40013c, 0xffffffff); nv_wr32(dev, 0x400500, 0x00010001); -} - -static void -nv50_graph_init_zcull(struct drm_device *dev) -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - int i; - - NV_DEBUG(dev, "\n"); + /* upload context program, initialise ctxctl defaults */ + nv_wr32(dev, 0x400324, 0x00000000); + for (i = 0; i < pgraph->ctxprog_size; i++) + nv_wr32(dev, 0x400328, pgraph->ctxprog[i]); + nv_wr32(dev, 0x400824, 0x00000000); + nv_wr32(dev, 0x400828, 0x00000000); + nv_wr32(dev, 0x40082c, 0x00000000); + nv_wr32(dev, 0x400830, 0x00000000); + nv_wr32(dev, 0x400724, 0x00000000); + nv_wr32(dev, 0x40032c, 0x00000000); + nv_wr32(dev, 0x400320, 4); /* CTXCTL_CMD = NEWCTXDMA */ + + /* some unknown zcull magic */ switch (dev_priv->chipset & 0xf0) { case 0x50: case 0x80: @@ -212,43 +204,7 @@ nv50_graph_init_zcull(struct drm_device *dev) nv_wr32(dev, 0x402c28 + (i * 8), 0x00000000); nv_wr32(dev, 0x402c2c + (i * 8), 0x00000000); } -} - -static int -nv50_graph_init_ctxctl(struct drm_device *dev) -{ - struct nv50_graph_engine *pgraph = nv_engine(dev, NVOBJ_ENGINE_GR); - int i; - - NV_DEBUG(dev, "\n"); - - nv_wr32(dev, NV40_PGRAPH_CTXCTL_UCODE_INDEX, 0); - for (i = 0; i < pgraph->ctxprog_size; i++) - nv_wr32(dev, NV40_PGRAPH_CTXCTL_UCODE_DATA, pgraph->ctxprog[i]); - - nv_wr32(dev, 0x40008c, 0x00000004); /* HW_CTX_SWITCH_ENABLED */ - nv_wr32(dev, 0x400320, 4); - nv_wr32(dev, NV40_PGRAPH_CTXCTL_CUR, 0); - nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, 0); - return 0; -} - -static int -nv50_graph_init(struct drm_device *dev, int engine) -{ - int ret; - - NV_DEBUG(dev, "\n"); - - nv50_graph_init_reset(dev); - nv50_graph_init_regs__nv(dev); - nv50_graph_init_zcull(dev); - - ret = nv50_graph_init_ctxctl(dev); - if (ret) - return ret; - nv50_graph_init_intr(dev); return 0; } -- cgit v1.2.3-70-g09d2 From 59ef9742f6b24d1f3062b975ec959512519f8987 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 12 Aug 2011 10:05:43 +1000 Subject: drm/nv40/pm: execute memory reset script from vbios Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_bios.c | 10 ++++++++++ drivers/gpu/drm/nouveau/nouveau_drv.h | 1 + drivers/gpu/drm/nouveau/nv40_pm.c | 5 +++++ 3 files changed, 16 insertions(+) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index 58b2535e3b6..032a8209813 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -6776,6 +6776,16 @@ nouveau_bios_run_init_table(struct drm_device *dev, uint16_t table, spin_unlock_bh(&bios->lock); } +void +nouveau_bios_init_exec(struct drm_device *dev, uint16_t table) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nvbios *bios = &dev_priv->vbios; + struct init_exec iexec = { true, false }; + + parse_init_table(bios, table, &iexec); +} + static bool NVInitVBIOS(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index ecaa4ffbeab..29837da1098 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -1071,6 +1071,7 @@ extern void nouveau_bios_takedown(struct drm_device *dev); extern int nouveau_run_vbios_init(struct drm_device *); extern void nouveau_bios_run_init_table(struct drm_device *, uint16_t table, struct dcb_entry *, int crtc); +extern void nouveau_bios_init_exec(struct drm_device *, uint16_t table); extern struct dcb_gpio_entry *nouveau_bios_gpio_entry(struct drm_device *, enum dcb_gpio_tag); extern struct dcb_connector_table_entry * diff --git a/drivers/gpu/drm/nouveau/nv40_pm.c b/drivers/gpu/drm/nouveau/nv40_pm.c index 491688676df..bbc0b9c7e1f 100644 --- a/drivers/gpu/drm/nouveau/nv40_pm.c +++ b/drivers/gpu/drm/nouveau/nv40_pm.c @@ -221,6 +221,7 @@ nv40_pm_clocks_set(struct drm_device *dev, void *pre_state) struct drm_nouveau_private *dev_priv = dev->dev_private; struct nv40_pm_state *info = pre_state; unsigned long flags; + struct bit_entry M; u32 crtc_mask = 0; u8 sr1[2]; int i; @@ -310,6 +311,10 @@ nv40_pm_clocks_set(struct drm_device *dev, void *pre_state) nv_mask(dev, 0x100210, 0x80000000, 0x80000000); udelay(100); + /* execute memory reset script from vbios */ + if (!bit_table(dev, 'M', &M)) + nouveau_bios_init_exec(dev, ROM16(M.data[0])); + /* make sure we're in vblank (hopefully the same one as before), and * then re-enable crtc memory access */ -- cgit v1.2.3-70-g09d2 From 8ce51fcfee2355cc38ea6fd3062d94bb38dfbaf0 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 23 Jun 2011 16:37:00 +1000 Subject: drm/nvc0/pm: minor clock readback fixes Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvc0_pm.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nvc0_pm.c b/drivers/gpu/drm/nouveau/nvc0_pm.c index 6dc1a974b57..929aded35cb 100644 --- a/drivers/gpu/drm/nouveau/nvc0_pm.c +++ b/drivers/gpu/drm/nouveau/nvc0_pm.c @@ -42,12 +42,16 @@ read_vco(struct drm_device *dev, u32 dsrc) static u32 read_pll(struct drm_device *dev, u32 pll) { + u32 ctrl = nv_rd32(dev, pll + 0); u32 coef = nv_rd32(dev, pll + 4); u32 P = (coef & 0x003f0000) >> 16; u32 N = (coef & 0x0000ff00) >> 8; u32 M = (coef & 0x000000ff) >> 0; u32 sclk, doff; + if (!(ctrl & 0x00000001)) + return 0; + switch (pll & 0xfff000) { case 0x00e000: sclk = 27000; @@ -91,12 +95,12 @@ read_div(struct drm_device *dev, int doff, u32 dsrc, u32 dctl) return 100000; case 3: if (sctl & 0x80000000) { - u32 sclk = read_vco(dev, dsrc); + u32 sclk = read_vco(dev, dsrc + (doff * 4)); u32 sdiv = (sctl & 0x0000003f) + 2; return (sclk * 2) / sdiv; } - return read_vco(dev, dsrc); + return read_vco(dev, dsrc + (doff * 4)); default: return 0; } -- cgit v1.2.3-70-g09d2 From ff920bfbe63f240bca6c86ee26daca8a1c43f780 Mon Sep 17 00:00:00 2001 From: Marcin Slusarz Date: Mon, 22 Aug 2011 23:28:56 +0200 Subject: drm/nouveau: fix printk typo in ioremap failure path Signed-off-by: Marcin Slusarz Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_state.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index bc0b592788b..82478e0998e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -1129,7 +1129,7 @@ int nouveau_load(struct drm_device *dev, unsigned long flags) ioremap(pci_resource_start(dev->pdev, ramin_bar), dev_priv->ramin_size); if (!dev_priv->ramin) { - NV_ERROR(dev, "Failed to PRAMIN BAR"); + NV_ERROR(dev, "Failed to map PRAMIN BAR\n"); ret = -ENOMEM; goto err_mmio; } -- cgit v1.2.3-70-g09d2 From c20ab3e1cb0bb8e2c9a54a49b4637c61512ed856 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 25 Aug 2011 14:09:43 +1000 Subject: drm/nvd9/disp: stub some more api hooks so we don't oops on resume Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvd0_display.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index 20ab2c249b8..20072fb784a 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -522,6 +522,16 @@ static const struct drm_crtc_funcs nvd0_crtc_func = { .destroy = nvd0_crtc_destroy, }; +static void +nvd0_cursor_set_pos(struct nouveau_crtc *nv_crtc, int x, int y) +{ +} + +static void +nvd0_cursor_set_offset(struct nouveau_crtc *nv_crtc, uint32_t offset) +{ +} + static int nvd0_crtc_create(struct drm_device *dev, int index) { @@ -536,6 +546,8 @@ nvd0_crtc_create(struct drm_device *dev, int index) nv_crtc->index = index; nv_crtc->set_dither = nvd0_crtc_set_dither; nv_crtc->set_scale = nvd0_crtc_set_scale; + nv_crtc->cursor.set_offset = nvd0_cursor_set_offset; + nv_crtc->cursor.set_pos = nvd0_cursor_set_pos; for (i = 0; i < 256; i++) { nv_crtc->lut.r[i] = i << 8; nv_crtc->lut.g[i] = i << 8; -- cgit v1.2.3-70-g09d2 From 84e2ad8b7b0e9f089d0f2ac9b1c32105bffb8b33 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 26 Aug 2011 09:40:39 +1000 Subject: drm/nvd9/disp: bail out of mode_set_base if no fb bound to crtc Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvd0_display.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index 20072fb784a..23d63b4b3d7 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c @@ -391,6 +391,11 @@ nvd0_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); int ret; + if (!crtc->fb) { + NV_DEBUG_KMS(crtc->dev, "No FB bound\n"); + return 0; + } + ret = nvd0_crtc_swap_fbs(crtc, old_fb); if (ret) return ret; -- cgit v1.2.3-70-g09d2 From 3dcbb02b3a9ad1722005290e7c9ac47097de517d Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 25 Aug 2011 15:53:57 +1000 Subject: drm/nvc0/fifo: avoid touching missing subfifos Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvc0_fifo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nvc0_fifo.c b/drivers/gpu/drm/nouveau/nvc0_fifo.c index 6f9f341c3e8..dcbe0d5d024 100644 --- a/drivers/gpu/drm/nouveau/nvc0_fifo.c +++ b/drivers/gpu/drm/nouveau/nvc0_fifo.c @@ -322,7 +322,7 @@ nvc0_fifo_init(struct drm_device *dev) } /* PSUBFIFO[n] */ - for (i = 0; i < 3; i++) { + for (i = 0; i < priv->spoon_nr; i++) { nv_mask(dev, 0x04013c + (i * 0x2000), 0x10000100, 0x00000000); nv_wr32(dev, 0x040108 + (i * 0x2000), 0xffffffff); /* INTR */ nv_wr32(dev, 0x04010c + (i * 0x2000), 0xfffffeff); /* INTR_EN */ -- cgit v1.2.3-70-g09d2 From a14845121c1e9cfe302d23ca4ffcfc62cf8e1033 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 26 Aug 2011 13:05:23 +1000 Subject: drm/nvc0/fb: slightly improve PMFB intr handling, move out of nvc0_graph.c I'm still not certain how to determine the number of SUBPs are present on a given board. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvc0_fb.c | 27 +++++++++++++++++++++++++++ drivers/gpu/drm/nouveau/nvc0_graph.c | 18 ------------------ 2 files changed, 27 insertions(+), 18 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nvc0_fb.c b/drivers/gpu/drm/nouveau/nvc0_fb.c index 08e6b118f02..5bf55038fd9 100644 --- a/drivers/gpu/drm/nouveau/nvc0_fb.c +++ b/drivers/gpu/drm/nouveau/nvc0_fb.c @@ -32,6 +32,30 @@ struct nvc0_fb_priv { dma_addr_t r100c10; }; +static inline void +nvc0_mfb_subp_isr(struct drm_device *dev, int unit, int subp) +{ + u32 subp_base = 0x141000 + (unit * 0x2000) + (subp * 0x400); + u32 stat = nv_rd32(dev, subp_base + 0x020); + + if (stat) { + NV_INFO(dev, "PMFB%d_SUBP%d: 0x%08x\n", unit, subp, stat); + nv_wr32(dev, subp_base + 0x020, stat); + } +} + +static void +nvc0_mfb_isr(struct drm_device *dev) +{ + u32 units = nv_rd32(dev, 0x00017c); + while (units) { + u32 subp, unit = ffs(units) - 1; + for (subp = 0; subp < 2; subp++) + nvc0_mfb_subp_isr(dev, unit, subp); + units &= ~(1 << unit); + } +} + static void nvc0_fb_destroy(struct drm_device *dev) { @@ -39,6 +63,8 @@ nvc0_fb_destroy(struct drm_device *dev) struct nouveau_fb_engine *pfb = &dev_priv->engine.fb; struct nvc0_fb_priv *priv = pfb->priv; + nouveau_irq_unregister(dev, 25); + if (priv->r100c10_page) { pci_unmap_page(dev->pdev, priv->r100c10, PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); @@ -74,6 +100,7 @@ nvc0_fb_create(struct drm_device *dev) return -EFAULT; } + nouveau_irq_register(dev, 25, nvc0_mfb_isr); return 0; } diff --git a/drivers/gpu/drm/nouveau/nvc0_graph.c b/drivers/gpu/drm/nouveau/nvc0_graph.c index ecf0fb46918..4b8d0b3f7d2 100644 --- a/drivers/gpu/drm/nouveau/nvc0_graph.c +++ b/drivers/gpu/drm/nouveau/nvc0_graph.c @@ -700,22 +700,6 @@ nvc0_graph_isr(struct drm_device *dev) nv_wr32(dev, 0x400500, 0x00010001); } -static void -nvc0_runk140_isr(struct drm_device *dev) -{ - u32 units = nv_rd32(dev, 0x00017c) & 0x1f; - - while (units) { - u32 unit = ffs(units) - 1; - u32 reg = 0x140000 + unit * 0x2000; - u32 st0 = nv_mask(dev, reg + 0x1020, 0, 0); - u32 st1 = nv_mask(dev, reg + 0x1420, 0, 0); - - NV_DEBUG(dev, "PRUNK140: %d 0x%08x 0x%08x\n", unit, st0, st1); - units &= ~(1 << unit); - } -} - static int nvc0_graph_create_fw(struct drm_device *dev, const char *fwname, struct nvc0_graph_fuc *fuc) @@ -764,7 +748,6 @@ nvc0_graph_destroy(struct drm_device *dev, int engine) } nouveau_irq_unregister(dev, 12); - nouveau_irq_unregister(dev, 25); nouveau_gpuobj_ref(NULL, &priv->unk4188b8); nouveau_gpuobj_ref(NULL, &priv->unk4188b4); @@ -803,7 +786,6 @@ nvc0_graph_create(struct drm_device *dev) NVOBJ_ENGINE_ADD(dev, GR, &priv->base); nouveau_irq_register(dev, 12, nvc0_graph_isr); - nouveau_irq_register(dev, 25, nvc0_runk140_isr); if (nouveau_ctxfw) { NV_INFO(dev, "PGRAPH: using external firmware\n"); -- cgit v1.2.3-70-g09d2 From a0d9a8feb928465f3cb525a19e5fafd06ef66ced Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 30 Aug 2011 14:30:11 +1000 Subject: drm/nouveau: remove allocations from gart populate() hook Since some somewhat questionable changes a while back, TTM provides a completely empty array of struct dma_address that stays around for the entire lifetime of the TTM object. Lets use this array, *always*, rather than wasting yet more memory on another array who's purpose is identical, as well as yet another bool array of the same size saying *which* of the previous two arrays to use... This change will also solve the high order allocation failures seen by some people while using nouveau. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_sgdma.c | 66 +++++++++++---------------------- 1 file changed, 21 insertions(+), 45 deletions(-) (limited to 'drivers/gpu/drm/nouveau') diff --git a/drivers/gpu/drm/nouveau/nouveau_sgdma.c b/drivers/gpu/drm/nouveau/nouveau_sgdma.c index 2706cb3d871..b75258a9fe4 100644 --- a/drivers/gpu/drm/nouveau/nouveau_sgdma.c +++ b/drivers/gpu/drm/nouveau/nouveau_sgdma.c @@ -12,8 +12,8 @@ struct nouveau_sgdma_be { struct drm_device *dev; dma_addr_t *pages; - bool *ttm_alloced; unsigned nr_pages; + bool unmap_pages; u64 offset; bool bound; @@ -26,43 +26,28 @@ nouveau_sgdma_populate(struct ttm_backend *be, unsigned long num_pages, { struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be; struct drm_device *dev = nvbe->dev; + int i; NV_DEBUG(nvbe->dev, "num_pages = %ld\n", num_pages); - if (nvbe->pages) - return -EINVAL; - - nvbe->pages = kmalloc(sizeof(dma_addr_t) * num_pages, GFP_KERNEL); - if (!nvbe->pages) - return -ENOMEM; + nvbe->pages = dma_addrs; + nvbe->nr_pages = num_pages; + nvbe->unmap_pages = true; - nvbe->ttm_alloced = kmalloc(sizeof(bool) * num_pages, GFP_KERNEL); - if (!nvbe->ttm_alloced) { - kfree(nvbe->pages); - nvbe->pages = NULL; - return -ENOMEM; + /* this code path isn't called and is incorrect anyways */ + if (0) { /* dma_addrs[0] != DMA_ERROR_CODE) { */ + nvbe->unmap_pages = false; + return 0; } - nvbe->nr_pages = 0; - while (num_pages--) { - /* this code path isn't called and is incorrect anyways */ - if (0) { /*dma_addrs[nvbe->nr_pages] != DMA_ERROR_CODE)*/ - nvbe->pages[nvbe->nr_pages] = - dma_addrs[nvbe->nr_pages]; - nvbe->ttm_alloced[nvbe->nr_pages] = true; - } else { - nvbe->pages[nvbe->nr_pages] = - pci_map_page(dev->pdev, pages[nvbe->nr_pages], 0, - PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); - if (pci_dma_mapping_error(dev->pdev, - nvbe->pages[nvbe->nr_pages])) { - be->func->clear(be); - return -EFAULT; - } - nvbe->ttm_alloced[nvbe->nr_pages] = false; + for (i = 0; i < num_pages; i++) { + nvbe->pages[i] = pci_map_page(dev->pdev, pages[i], 0, + PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); + if (pci_dma_mapping_error(dev->pdev, nvbe->pages[i])) { + nvbe->nr_pages = --i; + be->func->clear(be); + return -EFAULT; } - - nvbe->nr_pages++; } return 0; @@ -72,25 +57,16 @@ static void nouveau_sgdma_clear(struct ttm_backend *be) { struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be; - struct drm_device *dev; - - if (nvbe && nvbe->pages) { - dev = nvbe->dev; - NV_DEBUG(dev, "\n"); + struct drm_device *dev = nvbe->dev; - if (nvbe->bound) - be->func->unbind(be); + if (nvbe->bound) + be->func->unbind(be); + if (nvbe->unmap_pages) { while (nvbe->nr_pages--) { - if (!nvbe->ttm_alloced[nvbe->nr_pages]) - pci_unmap_page(dev->pdev, nvbe->pages[nvbe->nr_pages], + pci_unmap_page(dev->pdev, nvbe->pages[nvbe->nr_pages], PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); } - kfree(nvbe->pages); - kfree(nvbe->ttm_alloced); - nvbe->pages = NULL; - nvbe->ttm_alloced = NULL; - nvbe->nr_pages = 0; } } -- cgit v1.2.3-70-g09d2